Przeglądaj źródła

设备检定校准记录列表导入对话框

cyy 1 miesiąc temu
rodzic
commit
35fecb1a60
1 zmienionych plików z 771 dodań i 0 usunięć
  1. 771 0
      src/views/component/device/smallEquipmentImport.vue

+ 771 - 0
src/views/component/device/smallEquipmentImport.vue

@@ -0,0 +1,771 @@
+<template>
+  <el-dialog
+    :title="labelTitle"
+    :visible.sync="dialogVisible"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    width="40%"
+    custom-class="monthly-stat-dialog"
+    class="dialog"
+    top="15vh"
+    @close="closeDialog"
+  >
+    <el-alert
+      title="提示:请先下载导入模板填写设备信息;相关附件可打包为 ZIP 一并上传(非必填),每个类型仅支持上传 1 个文件。"
+      type="info"
+      :closable="false"
+      show-icon
+      class="import-tip"
+    />
+    <el-form
+      ref="form"
+      :label-width="formLabelWidth"
+      :model="paramForm"
+      class="dialog-form"
+      @submit.native.prevent
+    >
+      <el-form-item label="压缩包:" class="upload-form-item">
+        <div class="upload-row">
+          <el-upload
+            ref="zipUpload"
+            class="upload-trigger"
+            action="#"
+            accept=".zip"
+            :show-file-list="false"
+            :file-list="paramForm.zipFileList"
+            :on-change="handleZipChange"
+            :on-exceed="handleZipExceed"
+            :auto-upload="false"
+            :limit="1"
+            :multiple="false"
+          >
+            <el-button
+              v-if="!paramForm.zipFile"
+              class="upload-btn"
+              type="primary"
+              icon="el-icon-upload"
+              style="width: 140px"
+              >选择ZIP文件</el-button
+            >
+          </el-upload>
+          <div v-if="paramForm.zipFile" class="upload-filename">
+            <i class="el-icon-document" />
+            <span :title="paramForm.zipFile.name">{{
+              paramForm.zipFile.name
+            }}</span>
+            <i class="el-icon-close" @click="handleZipRemove" />
+          </div>
+        </div>
+        <div class="el-upload__tip">
+          请上传 *.zip 压缩包文件(设备相关附件打包,非必填)
+        </div>
+      </el-form-item>
+      <el-form-item label="Excel文件:" required class="upload-form-item">
+        <div class="upload-row">
+          <el-upload
+            ref="xlsxUpload"
+            class="upload-trigger"
+            action="#"
+            accept=".xlsx,.xls"
+            :show-file-list="false"
+            :file-list="paramForm.xlsxFileList"
+            :on-change="handleXlsxChange"
+            :on-exceed="handleXlsxExceed"
+            :auto-upload="false"
+            :limit="1"
+            :multiple="false"
+          >
+            <el-button
+              v-if="!paramForm.xlsxFile"
+              class="upload-btn"
+              type="primary"
+              icon="el-icon-upload"
+              style="width: 140px"
+              >选择Excel文件</el-button
+            >
+          </el-upload>
+          <div v-if="paramForm.xlsxFile" class="upload-filename">
+            <i class="el-icon-document" />
+            <span :title="paramForm.xlsxFile.name">{{
+              paramForm.xlsxFile.name
+            }}</span>
+            <i class="el-icon-close" @click="handleXlsxRemove" />
+          </div>
+        </div>
+        <div class="el-upload__tip">
+          请上传 *.xlsx 或 *.xls 格式的设备导入模板文件(必填)
+        </div>
+      </el-form-item>
+    </el-form>
+    <div slot="footer" class="el-dialog--center">
+      <ibps-toolbar :actions="toolbars" @action-event="handleActionEvent" />
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import request from '@/utils/request'
+import { uploadFile } from '@/api/platform/file/attachment'
+import IbpsImport from '@/plugins/import'
+
+export default {
+  components: {
+    IbpsCustomDialog: () =>
+      import('@/business/platform/data/templaterender/custom-dialog'),
+    IbpsLinkData: () =>
+      import('@/business/platform/data/templaterender/link-data')
+  },
+  props: {
+    fields: {
+      type: Array,
+      default: () => []
+    },
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    labelTitle: {
+      type: String,
+      default: '导入'
+    }
+  },
+  data() {
+    return {
+      formLabelWidth: '100px',
+      level: '',
+      paramForm: {
+        zipFile: null,
+        zipFileList: [],
+        zipAttachmentId: '',
+        zipList: [],
+        xlsxFile: null,
+        xlsxFileList: [],
+        xlsxAttachmentId: '',
+        xlsxList: []
+      },
+      dialogVisible: this.visible,
+      maxHeight: '400px',
+      title: '导入',
+      toolbars: [
+        {
+          key: 'save',
+          icon: 'ibps-icon-save',
+          label: '确定',
+          type: 'primary'
+        },
+        {
+          key: 'cancel',
+          icon: 'el-icon-close',
+          label: '关闭',
+          type: 'danger'
+        }
+      ]
+    }
+  },
+  watch: {
+    visible: {
+      handler(val) {
+        this.dialogVisible = val
+        if (val) {
+          this.resetUpload()
+        }
+      },
+      immediate: true
+    }
+  },
+  mounted() {
+    const level = this.$store.getters.level || {}
+    this.level = level.second || level.first || ''
+    // 增加默认值属性
+    this.fields.forEach((item) => {
+      if (item.default) {
+        this.$set(this.paramForm, item.key, item.default)
+      }
+    })
+  },
+  methods: {
+    handleActionEvent({ key }) {
+      switch (key) {
+        case 'save':
+          this.handleSave()
+          break
+        case 'cancel':
+          this.closeDialog()
+          break
+        default:
+          break
+      }
+    },
+    handleZipChange(file, fileList) {
+      const name = (file.name || '').toLowerCase()
+      if (!name.endsWith('.zip')) {
+        this.$message.warning('请选择 *.zip 文件进行上传!')
+        this.clearZipData()
+        return
+      }
+      const latest = fileList.slice(-1)
+      this.paramForm.zipFileList = latest
+      this.paramForm.zipFile = latest[0] ? latest[0].raw : null
+      if (this.paramForm.zipFile) {
+        this.uploadZipAndUnzip(this.paramForm.zipFile)
+      }
+    },
+    uploadZipAndUnzip(file) {
+      uploadFile(file, {})
+        .then((response) => {
+          const attachmentId = response && response.data ? response.data.id : ''
+          if (this.$utils.isEmpty(attachmentId)) {
+            this.$message.error('压缩包上传失败,未获取到附件ID!')
+            this.clearZipData()
+            return Promise.reject(new Error('attachmentId empty'))
+          }
+          this.paramForm.zipAttachmentId = attachmentId
+          return request({
+            url:
+              '/platform/v3/file/unCalibrationZip?attachmentId=' + attachmentId,
+            method: 'GET',
+            isLoading: true
+          })
+        })
+        .then((res) => {
+          this.paramForm.zipList =
+            res && res.variables && res.variables.data ? res.variables.data : []
+          console.log(this.paramForm.zipList, 'this.paramForm.zipList')
+          this.$message.success('压缩包上传并解析成功!')
+        })
+        .catch((err) => {
+          console.error(err)
+          this.clearZipData()
+        })
+    },
+    handleZipRemove() {
+      this.clearZipData()
+    },
+    clearZipData() {
+      this.paramForm.zipFileList = []
+      this.paramForm.zipFile = null
+      this.paramForm.zipAttachmentId = ''
+      this.paramForm.zipList = []
+    },
+    handleZipExceed() {
+      this.$message.warning(
+        'ZIP 压缩包仅支持上传 1 个文件,请先删除后再重新选择!'
+      )
+    },
+    handleXlsxChange(file, fileList) {
+      const name = (file.name || '').toLowerCase()
+      if (!name.endsWith('.xlsx') && !name.endsWith('.xls')) {
+        this.$message.warning('请选择 *.xlsx 或 *.xls 文件进行上传!')
+        this.clearXlsxData()
+        return
+      }
+      const latest = fileList.slice(-1)
+      this.paramForm.xlsxFileList = latest
+      this.paramForm.xlsxFile = latest[0] ? latest[0].raw : null
+      if (this.paramForm.xlsxFile) {
+        this.uploadXlsx(this.paramForm.xlsxFile)
+      }
+    },
+    // 同 template.handleImport(this.getField("import")) 的字段映射解析
+    getKeys(data) {
+      return Array.isArray(data)
+        ? data.reduce((acc, item) => {
+            if (item && item.label && item.name) {
+              acc[item.label] = item.name
+            }
+            return acc
+          }, {})
+        : {}
+    },
+    setValue(data) {
+      return Object.values(data).reduce((obj, item) => {
+        obj[item] = ''
+        return obj
+      }, {})
+    },
+    uploadXlsx(file) {
+      uploadFile(file, {})
+        .then((response) => {
+          const attachmentId = response && response.data ? response.data.id : ''
+          if (this.$utils.isEmpty(attachmentId)) {
+            this.$message.error('Excel 上传失败,未获取到附件ID!')
+            this.clearXlsxData()
+            return
+          }
+          this.paramForm.xlsxAttachmentId = attachmentId
+          // xlsx 上传成功后调用解析
+          this.handleImport(this.getField('import'))
+        })
+        .catch((err) => {
+          console.error(err)
+          this.clearXlsxData()
+        })
+    },
+    handleImport(importFields, options = { raw: false }) {
+      if (this.$utils.isEmpty(importFields)) {
+        this.$message.warning('未获取到导入字段配置!')
+        this.clearXlsxData()
+        return
+      }
+      const file = this.paramForm.xlsxFile
+      if (this.$utils.isEmpty(file)) {
+        this.$message.warning('请选择 Excel 文件!')
+        return
+      }
+      const importValue = this.getKeys(importFields)
+      const formData = this.setValue(importValue)
+      IbpsImport.xlsx(file, options)
+        .then(({ results }) => {
+          const list = []
+          ;(results || []).forEach((item) => {
+            const data = JSON.parse(JSON.stringify(formData))
+            for (const key in item) {
+              if (importValue[key]) {
+                data[importValue[key]] = item[key]
+              }
+            }
+            if (data.yuan_she_bei_bian) {
+              data.yuan_she_bei_bian = String(data.yuan_she_bei_bian).replace(/\s/g, '')
+            }
+            if (data.she_bei_ming_chen) {
+              data.she_bei_ming_chen = String(data.she_bei_ming_chen).replace(/\s/g, '')
+            }
+            list.push(data)
+          })
+          console.log(list, 'llllll')
+          this.paramForm.xlsxList = list
+          this.$message.success(`Excel 解析成功,共 ${list.length} 条数据`)
+        })
+        .catch((err) => {
+          console.error(err)
+          this.$message.error('Excel 解析失败,请检查文件格式!')
+          this.clearXlsxData()
+        })
+    },
+    handleXlsxRemove() {
+      this.clearXlsxData()
+    },
+    clearXlsxData() {
+      this.paramForm.xlsxFileList = []
+      this.paramForm.xlsxFile = null
+      this.paramForm.xlsxAttachmentId = ''
+      this.paramForm.xlsxList = []
+    },
+    handleXlsxExceed() {
+      this.$message.warning(
+        'Excel 文件仅支持上传 1 个文件,请先删除后再重新选择!'
+      )
+    },
+
+    resetUpload() {
+      this.clearZipData()
+      this.clearXlsxData()
+    },
+    /**
+     * 根据 xlsx 行的 fu_jian_ 匹配 zipList 中的 name,将对应 id 用英文逗号拼接写入 bei_zhu_
+     */
+    bindZipIdsToXlsxList() {
+      const xlsxList = this.paramForm.xlsxList || []
+      const zipList = this.paramForm.zipList || []
+      if (this.$utils.isEmpty(xlsxList) || this.$utils.isEmpty(zipList)) {
+        return
+      }
+      xlsxList.forEach((row) => {
+        const fuJian = row.fu_jian_
+        if (this.$utils.isEmpty(fuJian)) {
+          this.$set(row, 'bei_zhu_', '')
+          return
+        }
+        const fileNames = String(fuJian)
+          .split(/[,,;;|、\s]+/)
+          .map((name) => name.trim())
+          .filter((name) => name)
+        const ids = []
+        zipList.forEach((zipItem) => {
+          const zipName =
+            zipItem && zipItem.name ? String(zipItem.name).trim() : ''
+          if (!zipName) return
+          const matched = fileNames.some(
+            (name) =>
+              name === zipName ||
+              fuJian.indexOf(zipName) !== -1 ||
+              zipName.indexOf(name) !== -1
+          )
+          if (matched && zipItem.id != null && zipItem.id !== '') {
+            ids.push(zipItem.id)
+          }
+        })
+        this.$set(row, 'bei_zhu_', ids.join(','))
+      })
+    },
+    handleSave() {
+      if (
+        this.fields.some(
+          (i) =>
+            i.required !== false && this.$utils.isEmpty(this.paramForm[i.key])
+        )
+      ) {
+        return this.$message.warning('请填写所有必填项!')
+      }
+      // zip 非必填:若已选择则必须上传成功
+      if (
+        this.$utils.isNotEmpty(this.paramForm.zipFile) &&
+        this.$utils.isEmpty(this.paramForm.zipAttachmentId)
+      ) {
+        return this.$message.warning('压缩包尚未上传成功,请重新上传或清除!')
+      }
+      if (this.$utils.isEmpty(this.paramForm.xlsxFile)) {
+        return this.$message.warning('请上传 Excel 导入文件!')
+      }
+      if (this.$utils.isEmpty(this.paramForm.xlsxAttachmentId)) {
+        return this.$message.warning('Excel 尚未上传成功,请重新上传!')
+      }
+      if (this.$utils.isEmpty(this.paramForm.xlsxList)) {
+        return this.$message.warning('Excel 尚未解析出数据,请重新上传!')
+      }
+      if (this.$utils.isNotEmpty(this.paramForm.zipList)) {
+        this.bindZipIdsToXlsxList()
+      }
+      this.handleImportData(() => {
+        this.closeDialog()
+      }, this.paramForm.xlsxList)
+    },
+    closeDialog() {
+      this.resetUpload()
+      this.$emit('close', false)
+    },
+    getField(type = 'export') {
+      const fields = {
+        bian_zhi_bu_men_: '部门',
+        she_bei_ming_chen: '设备名称',
+        yuan_she_bei_bian: '设备编号',
+        she_bei_lei_xing_: '设备类型',
+        chang_jia_pin_pai: '厂家品牌',
+        xing_hao_gui_ge_: '规格型号',
+        jian_ding_dan_wei: '检定/校准单位',
+        yi_jian_xiao_ri_q: '上次检定/校准日期',
+        shi_shi_ri_qi_: '实施日期',
+        jian_ding_zhou_qi: '检定/校准周期(月)',
+        chu_chang_bian_ha: '检定/校准有效期至',
+        fu_ze_ren_: '负责人',
+        jing_ban_ren_: '经办人',
+        zhun_que_du_: '证书编号',
+        fu_jian_: '附件名称'
+      }
+
+      const fieldMap = {
+        export: [
+          'bian_zhi_bu_men_',
+          'she_bei_ming_chen',
+          'yuan_she_bei_bian',
+          'she_bei_lei_xing_',
+          'chang_jia_pin_pai',
+          'xing_hao_gui_ge_',
+          'jian_ding_dan_wei',
+          'yi_jian_xiao_ri_q',
+          'shi_shi_ri_qi_',
+          'jian_ding_zhou_qi',
+          'chu_chang_bian_ha',
+          'fu_ze_ren_',
+          'jing_ban_ren_',
+          'zhun_que_du_',
+          'fu_jian_'
+        ],
+        import: [
+          'bian_zhi_bu_men_',
+          'she_bei_ming_chen',
+          'yuan_she_bei_bian',
+          'jian_ding_dan_wei',
+          'yi_jian_xiao_ri_q',
+          'shi_shi_ri_qi_',
+          'jian_ding_zhou_qi',
+          'jing_ban_ren_',
+          'zhun_que_du_',
+          'fu_jian_'
+        ]
+      }
+
+      const requireField = [
+        'bian_zhi_bu_men_',
+        'she_bei_ming_chen',
+        'yuan_she_bei_bian',
+        'jian_ding_dan_wei',
+        'shi_shi_ri_qi_',
+        'jian_ding_zhou_qi',
+        'jing_ban_ren_'
+      ]
+
+      return fieldMap[type].map((i) => ({
+        field_name: i,
+        //label: fields[i] + (type === 'import' ? requireField.includes(i) ? '(必填)' : '' : ''),
+        label: fields[i],
+        name: i
+      }))
+    },
+    getTransformData(type = 'user') {
+      const keyMap = {
+        user: 'sbjdxzjlryzh',
+        supplier: 'sbjdxzjlgyszh',
+        device: 'sbjdxzjlsbzh'
+      }
+      const paramMap = {
+        user: [null],
+        supplier: [this.level],
+        device: [this.level]
+      }
+      return new Promise((resolve, reject) => {
+        this.$common
+          .request('query', {
+            key: keyMap[type],
+            params: paramMap[type]
+          })
+          .then((res) => {
+            resolve(res.variables.data || [])
+          })
+          .catch((error) => {
+            reject(error)
+          })
+      })
+    },
+    async handleImportData(callback, data) {
+      const userInfo = await this.getTransformData()
+      const deviceList = await this.getTransformData('device')
+      const supplierList = await this.getTransformData('supplier')
+      const defaultData = {
+        bian_zhi_shi_jian: this.$common.getDateNow(16),
+        di_dian_: this.level,
+        marks_: 'record',
+        shi_fou_guo_shen_: '已完成',
+        bian_zhi_ren_: this.$store.getters?.userId
+      }
+      const list = data.map((item) => {
+        const device =
+          deviceList.find(
+            (d) => d.yuan_she_bei_bian === item.yuan_she_bei_bian
+          ) || {}
+        const userId = this.dataTransfer(
+          userInfo,
+          item.jing_ban_ren_,
+          'name_',
+          'id_'
+        )
+        const deptList = this.$store.getters?.deptList || []
+        const { fu_jian_, ...restItem } = item
+        return {
+          ...defaultData,
+          ...restItem,
+          bian_zhi_bu_men_: this.dataTransfer(
+            deptList,
+            item.bian_zhi_bu_men_,
+            'positionName',
+            'positionId'
+          ),
+          bian_zhi_ren_: userId,
+          jing_ban_ren_: userId,
+          yi_jian_xiao_ri_q: item.yi_jian_xiao_ri_q?.trim(),
+          shi_shi_ri_qi_: item.shi_shi_ri_qi_?.trim(),
+          dan_wei_id: this.dataTransfer(
+            supplierList,
+            item.jian_ding_dan_wei,
+            'gong_ying_shang_m',
+            'id_'
+          ),
+          // 计算检定校准有效期至
+          you_xiao_qi_zhi_: this.getExpireDate(
+            item.shi_shi_ri_qi_,
+            item.jian_ding_zhou_qi
+          ),
+          chu_chang_bian_ha: this.getExpireDate(
+            item.shi_shi_ri_qi_,
+            item.jian_ding_zhou_qi
+          ),
+          // 设备档案-ID
+          she_bei_bian_hao_: device.id_ || '',
+          // 设备档案-设备识别号
+          bian_hao_zhong_we: device.she_bei_shi_bie_h || '',
+          // 设备档案-设备类型
+          she_bei_lei_xing_: device.she_bei_lei_xing_ || '',
+          // 设备档案-规格型号
+          xing_hao_gui_ge_: device.gui_ge_xing_hao_ || '',
+          // 设备档案-厂商
+          chang_jia_pin_pai: device.chang_shang_ || '',
+          // 设备档案-保管人
+          fu_ze_ren_: device.guan_li_ren_ || '',
+          // 设备档案-检定校准参数
+          can_shu_: device.jian_ding_xiao_ || '',
+          // 设备档案-测量工作范围
+          liang_cheng_: device.ce_liang_gong_zuo || '',
+          // 设备档案-修正因子
+          xiu_zheng_yin_zi_: device.xiu_zheng_zhi_xiu || '',
+          // 附件ID(zip 匹配结果优先)
+          bei_zhu_: item.bei_zhu_ || device.bei_zhu_ || '',
+          shu_ju_lai_yuan_: device.id_
+            ? 'importAssociation'
+            : 'importNotAssociation'
+        }
+      })
+
+      console.log('处理后导入数据:\n', list)
+
+      // 数据校验
+      let index = 0
+      let isError = false
+      let errorMsg = ''
+      for (const item of list) {
+        index++
+        if (!item.bian_zhi_bu_men_) {
+          isError = true
+          errorMsg = `第【${index}】行,部门信息无法匹配系统数据,请检查!`
+          break
+        }
+        if (
+          !item.she_bei_bian_hao_ &&
+          !this.$utils.isNotEmpty(this.paramForm.zipList)
+        ) {
+          isError = true
+          errorMsg = `第【${index}】行,设备编号【${item.yuan_she_bei_bian}】无法匹配系统数据,请检查!`
+          break
+        }
+        if (!this.isValidDate(item.yi_jian_xiao_ri_q)) {
+          isError = true
+          errorMsg = `第【${index}】行,上次检定/校准日期格式错误,请严格按【YYYY-MM-DD】日期格式填写!`
+          break
+        }
+        if (!this.isValidDate(item.shi_shi_ri_qi_)) {
+          isError = true
+          errorMsg = `第【${index}】行,实施日期格式错误,请严格按【YYYY-MM-DD】日期格式填写!`
+          break
+        }
+        if (!item.chu_chang_bian_ha) {
+          isError = true
+          errorMsg = `第【${index}】行,实施日期或检定校准周期错误,请检查!`
+          break
+        }
+        if (!item.jing_ban_ren_) {
+          isError = true
+          errorMsg = `第【${index}】行,经办人信息无法匹配系统数据,请检查!`
+          break
+        }
+      }
+      if (isError) {
+        return this.$message.warning(errorMsg)
+      }
+      const params = {
+        tableName: 't_mjsbjdxzjhzb',
+        paramWhere: list
+      }
+      this.$common.request('add', params).then(() => {
+        this.$message.success('导入成功')
+        if (typeof callback === 'function') {
+          callback(list)
+        }
+        this.$emit('confirm', this.paramForm)
+      })
+    },
+    dataTransfer(dataset, data, from = 'id_', to = 'name_') {
+      const list = data ? data.split(',') : []
+      const names = list.map((item) => {
+        const temp = dataset.find((i) => i[from] === item.trim())
+        return temp ? temp[to] : ''
+      })
+      return names.filter((i) => i).join(',')
+    },
+    getExpireDate(date, cycle) {
+      if (
+        this.$common.Utils.isEmpty(date) ||
+        this.$common.Utils.isEmpty(cycle)
+      ) {
+        return null
+      }
+      // 获取指定周期后的前一天
+      const expiredDate = this.$common.getFormatDate(
+        'string',
+        10,
+        this.$common.getDate('month', parseInt(cycle), date)
+      )
+      return this.$common.getFormatDate(
+        'string',
+        10,
+        this.$common.getDate('day', -1, expiredDate)
+      )
+    },
+    isValidDate(dateString) {
+      const regex =
+        /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$/
+      return regex.test(dateString)
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.dialog-form {
+  padding: 20px;
+  ::v-deep {
+    .el-input {
+      width: calc(100% - 100px);
+    }
+    .el-select {
+      width: 100%;
+    }
+    .el-range-editor--small.el-input__inner {
+      width: calc(100% - 100px);
+    }
+    .upload-form-item {
+      .el-form-item__content {
+        line-height: 32px;
+      }
+    }
+  }
+  .upload-row {
+    display: flex;
+    align-items: center;
+    min-height: 32px;
+  }
+  .upload-trigger {
+    display: inline-block;
+  }
+  .upload-filename {
+    display: inline-flex;
+    align-items: center;
+    max-width: 100%;
+    color: #606266;
+    .el-icon-document {
+      margin-right: 6px;
+      color: #909399;
+    }
+    span {
+      max-width: 280px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .el-icon-close {
+      margin-left: 8px;
+      cursor: pointer;
+      color: #909399;
+      &:hover {
+        color: #f56c6c;
+      }
+    }
+  }
+  .el-upload__tip {
+    margin-top: 4px;
+    line-height: 1.4;
+  }
+}
+.import-tip {
+}
+.toolbar {
+  text-align: right;
+}
+</style>
+<style lang="scss">
+.monthly-stat-dialog {
+  .upload-btn.el-button {
+    width: 140px !important;
+    box-sizing: border-box;
+  }
+}
+</style>