Zzhangcheng修复缺陷
6395c6f8创建于 2024年6月5日历史提交
<template>
  <div class="import-dialog common-dialog-wrapper">
    <el-dialog
      v-model="visible"
      :title="title"
      :width="750"
      align-center
      :close-on-click-modal="false"
      @opened="handleOpen"
      @close="handleClose"
    >
      <div class="dialog_body">
        <el-form :model="form" ref="ruleFormRef" :rules="rules" label-width="140px">
          <el-form-item prop="columnList" :label="$t('table.export.selectColumns')">
            <el-table
              ref="multipleTableRef"
              :data="tableData"
              @selection-change="handleSelectionChange"
              max-height="300"
              border
            >
              <el-table-column type="selection" width="55" align="center" />
              <el-table-column
                property="columnName"
                :label="t('table.column.columnName')"
                align="center"
              />
            </el-table>
          </el-form-item>
          <el-form-item prop="file" :label="$t('table.data.importFile')">
            <el-upload
              ref="uploadRef"
              class="upload-demo"
              v-model:file-list="form.file"
              :limit="1"
              action="#"
              :data="uploadParams"
              :auto-upload="false"
              :on-exceed="handleFileExceed"
            >
              <template #trigger>
                <el-button type="primary">
                  {{ $t('table.data.chooseFile') }}
                </el-button>
              </template>
            </el-upload>
          </el-form-item>
          <el-row :gutter="10">
            <el-col :span="13">
              <el-form-item prop="fileType" :label="$t('table.export.format')">
                <el-select v-model="form.fileType" @change="handleChangeFormat">
                  <el-option v-for="item in formatList" :key="item" :label="item" :value="item" />
                </el-select>
              </el-form-item>
              <el-form-item prop="quote" :label="$t('table.export.quotes')">
                <el-input
                  v-model="form.quote"
                  :disabled="!['CSV', 'Text'].includes(form.fileType)"
                />
              </el-form-item>
              <el-form-item prop="escape" :label="$t('table.export.escape')">
                <el-input
                  v-model="form.escape"
                  :disabled="!['CSV', 'Text'].includes(form.fileType)"
                />
              </el-form-item>
              <el-form-item prop="replaceNull" :label="$t('table.export.replaceNullWith')">
                <el-input
                  v-model="form.replaceNull"
                  :disabled="!['CSV', 'Text'].includes(form.fileType)"
                />
              </el-form-item>
              <el-form-item prop="encoding" :label="$t('table.export.encoding')">
                <el-select v-model="form.encoding" placement="top">
                  <el-option v-for="item in encodingList" :key="item" :label="item" :value="item" />
                </el-select>
              </el-form-item>
              <el-form-item prop="timeFormat" :label="$t('table.export.timeFormat')">
                <el-select
                  v-model="form.timeFormat"
                  :disabled="!['Excel(xlsx)', 'Excel(xls)'].includes(form.fileType)"
                  placement="top"
                >
                  <el-option
                    v-for="item in timeFormatList"
                    :key="item"
                    :label="item"
                    :value="item"
                  />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="11">
              <el-form-item prop="includeHeader" :label="$t('table.export.includeHeader')">
                <el-checkbox v-model="form.includeHeader" :disabled="form.fileType == 'Binary'" />
              </el-form-item>
              <el-form-item prop="delimiter" :label="$t('table.export.delimiter')">
                <el-radio-group
                  v-model="form.delimiter"
                  :disabled="!['CSV', 'Text'].includes(form.fileType)"
                  @change="handleChangeDelimiter"
                >
                  <el-radio value="comma">{{ t('table.export.comma') }}(,)</el-radio>
                  <el-radio value="tab">
                    {{ t('table.export.tab') }}({{ t('table.export.fourSpaces') }})
                  </el-radio>
                  <el-radio value="pipe">{{ t('table.export.pipe') }}(|)</el-radio>
                  <el-radio value="semicolon">{{ t('table.export.semicolon') }}(;)</el-radio>
                  <el-radio value="other">
                    {{ t('table.export.other') }}{{ t('common.colon') }}
                  </el-radio>
                  <el-input
                    v-model="form.delimiterOther"
                    :disabled="form.delimiter != 'other'"
                    maxlength="10"
                  />
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
      <template #footer>
        <el-button @click="handleClose">{{ $t('button.cancel') }}</el-button>
        <el-button type="primary" @click="confirmForm">
          {{ $t('button.confirm') }}
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script lang="ts" setup>
  import { ElMessage, ElTable, FormInstance, FormRules, genFileId } from 'element-plus';
  import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
  import { useI18n } from 'vue-i18n';
  import { getTableColumn } from '@/api/table';
  import { loadingInstance } from '@/utils';
  import { importTableData } from '@/api/table';

  const { t } = useI18n();
  const props = withDefaults(
    defineProps<{
      modelValue: boolean;
      uuid: string;
      schema: string;
      tableName: string;
      oid: string;
    }>(),
    {
      modelValue: false,
      nodeData: () => ({}),
    },
  );
  const myEmit = defineEmits<{
    (event: 'update:modelValue', text: boolean): void;
    (event: 'success'): void;
  }>();

  const loading = ref(null);
  const ruleFormRef = ref<FormInstance>();
  const uploadRef = ref<UploadInstance>();
  const tableData = ref<{ columnName: string; dataType: string }[]>([]);
  const multipleTableRef = ref<InstanceType<typeof ElTable>>();
  const formatList = computed(() => ['Excel(xlsx)', 'Excel(xls)', 'CSV', 'Text', 'Binary']);
  const encodingList = reactive(['UTF-8', 'GBK', 'LATIN1']);
  const timeFormatList = ['yyyy-MM-dd HH:mm:ss', 'yyyy-MM-dd hh:mm:ss'];
  const form = reactive({
    columnList: [],
    file: [],
    fileType: '',
    quote: '',
    escape: '',
    replaceNull: '',
    encoding: 'UTF-8',
    timeFormat: 'yyyy-MM-dd HH:mm:ss',
    includeHeader: true,
    delimiter: '',
    delimiterOther: '',
  });
  const rules = reactive<FormRules>({
    file: [
      {
        required: true,
        type: 'array',
        len: 1,
        message: t('rules.empty', [t('table.data.importFile')]),
        trigger: 'change',
      },
    ],
    columnList: [
      {
        required: true,
        type: 'array',
        message: t('rules.empty', [t('table.export.selectColumns')]),
        trigger: 'change',
      },
    ],
  });
  const delimiterMap = {
    comma: ',',
    tab: '    ',
    pipe: '|',
    semicolon: ';',
  };
  const visible = computed({
    get: () => props.modelValue,
    set: (val) => myEmit('update:modelValue', val),
  });
  const title = computed(() => {
    return `${t('import.tableData')}${t('common.colon')} ${props.schema ? props.schema + '.' : ''}${
      props.tableName
    }`;
  });
  const uploadParams = {};

  const handleSelectionChange = (selections) => {
    form.columnList = tableData.value.filter((row) => {
      return selections.findIndex((s) => s.columnName == row.columnName) > -1;
    });
  };
  const handleChangeFormat = (val) => {
    Object.assign(form, {
      quote: '',
      escape: '',
      replaceNull: '',
      includeHeader: true,
      delimiter: val == 'Text' ? 'comma' : '',
      delimiterOther: '',
    });
  };
  const handleChangeDelimiter = () => {
    form.delimiterOther = '';
  };

  const handleFileExceed: UploadProps['onExceed'] = (files) => {
    uploadRef.value!.clearFiles();
    const file = files[0] as UploadRawFile;
    file.uid = genFileId();
    uploadRef.value!.handleStart(file);
  };

  const handleFileUpload = () => {
    const params = {
      file: form.file[0]?.raw,
      uuid: props.uuid,
      schema: props.schema,
      tableName: props.tableName,
      columnString: form.columnList.map((item) => item.columnName).join(),
      fileType: form.fileType,
      titleFlag: form.includeHeader,
      quote: form.quote || undefined,
      escape: form.escape || undefined,
      replaceNull: form.replaceNull || undefined,
      delimiter: form.delimiter == 'other' ? form.delimiterOther : delimiterMap[form.delimiter],
      encoding: form.encoding,
      timeFormat: form.timeFormat,
    };
    const formData = new FormData();
    for (const [key, value] of Object.entries(params)) {
      if (['quote', 'escape', 'replaceNull', 'delimiter'].includes(key)) {
        // if value === undefined, this formdata will also retain this field, and there will be problem in the backend.
        value && formData.append(key, value);
      } else {
        formData.append(key, value);
      }
    }
    return importTableData(formData);
  };

  const handleOpen = async () => {
    Object.assign(form, {
      fileType: formatList.value[0],
    });
    try {
      loading.value = loadingInstance();
      const res = await getTableColumn({
        uuid: props.uuid,
        schema: props.schema,
        tableName: props.tableName,
        oid: props.oid,
      });
      tableData.value = res.result.map((row) => {
        return {
          columnName: row[0],
          dataType: row[1],
        };
      });
      nextTick(() => {
        multipleTableRef.value?.toggleAllSelection();
      });
    } finally {
      loading.value.close();
    }
  };
  const handleClose = () => {
    myEmit('update:modelValue', false);
    ruleFormRef.value.resetFields();
  };
  const confirmForm = async () => {
    if (form.delimiter == 'other' && !form.delimiterOther) {
      return ElMessage.error(t('rules.enter', [t('table.export.delimiter')]));
    }
    ruleFormRef.value.validate(async (valid) => {
      if (valid) {
        try {
          loading.value = loadingInstance();
          await handleFileUpload();
          ElMessage.success(t('message.success'));
          myEmit('success');
          visible.value = false;
        } finally {
          loading.value.close();
        }
      }
    });
  };
</script>

<style lang="scss" scoped>
  .import-dialog {
    :deep(.el-select) {
      width: 100%;
    }
    :deep(.el-input-number .el-input__inner) {
      text-align: left;
    }
    :deep(.el-radio-group) {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }
  }
  .upload-demo {
    display: flex;
    width: 100%;
    :deep(.el-upload-list) {
      margin: 0;
      flex: 1;
      .el-upload-list__item {
        margin-bottom: 0;
      }
    }
  }
</style>