data-import.tsx 5.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
import { useNamespace } from '@ibiz-template/vue-util';
import { defineComponent, ref } from 'vue';
import '@ibiz-template/theme/style/components/common/data-import/data-import.scss';
import { HttpError } from '@ibiz-template/core';

export const DataImport = defineComponent({
  name: 'DataImport',
  props: {
    templateUrl: {
      type: String,
      required: true,
    },
    importUrl: {
      type: String,
      required: true,
    },
  },
  setup(props, { emit }) {
    const ns = useNamespace('data-import');
    const inputUpLoad = ref();
    const message = ref<{
      state: 'ready' | 'success' | 'fail';
      message: string;
      rowError: Array<{ index: number; info: string }>;
    }>({
      state: 'ready',
      message: '',
      rowError: [],
    });

    // 上传数据
    const uploadFile = async (file: Blob) => {
      const data = new FormData();
      data.append('file', file);
      try {
        const res = await ibiz.net.request(props.importUrl, {
          method: 'post',
          data,
          headers: { 'Content-Type': 'multipart/form-data' },
        });
        message.value.state = 'success';
        message.value.message = '导入成功';
        const { errorinfo, success, total } = res.data;
        const totalNum = total ? Number(total) : 0;
        const successNum = success ? Number(success) : 0;
        const errorNum = total - success;
        message.value.message = `共计导入数据 ${totalNum} 条,错误[${errorNum}],成功[${successNum}]`;
        if (errorinfo && Object.keys(errorinfo).length > 0) {
          message.value.rowError = [];
          Object.keys(errorinfo).forEach((key: string) => {
            message.value.rowError.push({
              index: Number(key) + 1,
              info: errorinfo[key].errorInfo,
            });
          });
        }
      } catch (error) {
        message.value.state = 'fail';
        message.value.message = `导入失败!${(error as HttpError).message}`;
      }
    };

    // 文件选择变更事件
    const onFileChange = ($event: Event) => {
      const obj = $event.target as IData;
      if (!obj.files) {
        return;
      }
      const selectedFile = obj.files[0];
      obj.value = null; // 置空不然下次文件不变不触发
      uploadFile(selectedFile);
    };

    // 下载模板文件
    const onLinkClick = async () => {
      const res = await ibiz.net.request(props.templateUrl, {
        responseType: 'blob',
      });
      if (res.status === 200) {
        let fileName =
          res.headers['content-disposition']!.split(';')
            .find((str: string) => str.indexOf('filename=') !== -1)
            ?.slice(9) || '';
        fileName = decodeURIComponent(fileName);
        const blob = new Blob([res.data as Blob], {
          type: 'application/vnd.ms-excel',
        });
        const elink = document.createElement('a');
        elink.download = fileName;
        elink.style.display = 'none';
        elink.href = URL.createObjectURL(blob);
        document.body.appendChild(elink);
        elink.click();
        URL.revokeObjectURL(elink.href); // 释放URL 对象
        document.body.removeChild(elink);
      }
    };

    const selectFile = () => {
      inputUpLoad.value.click();
    };

    const onCancelButtonClick = () => {
      emit('close', { ok: false, data: {} });
    };

    return {
      ns,
      onLinkClick,
      selectFile,
      onCancelButtonClick,
      onFileChange,
      inputUpLoad,
      message,
    };
  },
  render() {
    return (
      <div class={this.ns.b()}>
        <input
          ref='inputUpLoad'
          type='file'
          style='display: none'
          accept='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
          onChange={this.onFileChange}
        />
        <div class={this.ns.e('caption')}>导入数据</div>
        {this.message.state === 'ready' ? (
          <div class={this.ns.b('upload')} onClick={this.selectFile}>
            <img
              class={this.ns.be('upload', 'img')}
              src='./assets/img/icon-import.svg'
            ></img>
            <span class={this.ns.be('upload', 'text')}>单击此区域进行上传</span>
          </div>
        ) : (
          <div class={this.ns.b('message')}>
            <div class={this.ns.be('message', 'title')}>导入结果</div>
            <div class={this.ns.be('message', 'content')}>
              {this.message.message}
            </div>
            {this.message.rowError.length > 0 && [
              <div class={this.ns.be('message', 'title')}>错误信息</div>,
              <div class={this.ns.be('message', 'content')}>
                {this.message.rowError.map(item => {
                  return <div>{`第 ${item.index} 行:${item.info}`}</div>;
                })}
              </div>,
            ]}
          </div>
        )}
        <div class={this.ns.e('template-container')}>
          <div class={this.ns.e('template-description')}>
            下载导入模版,并按要求填写:
          </div>
          <div class={this.ns.e('template-link')} onClick={this.onLinkClick}>
            <i-icon type='ios-link' />
            数据导入模板文件
          </div>
        </div>
        <div class={this.ns.e('button-bar')}>
          <i-button onClick={this.onCancelButtonClick}>取消</i-button>
          {this.message.state !== 'ready' && (
            <i-button onClick={this.selectFile}>重新上传</i-button>
          )}
        </div>
      </div>
    );
  },
});