Przeglądaj źródła

业务域备份

Xiao_123 16 godzin temu
rodzic
commit
9f7a9ce177

+ 424 - 0
src/views/dataGovernance/businessDomain/components/editCopy1.vue

@@ -0,0 +1,424 @@
+<template>
+  <div class="db">
+    <v-card class="db-box d-flex flex-column" elevation="5" v-loading="loadingUpload">
+      <v-banner single-line>
+        <div class="d-flex align-center">
+          <div class="pa-2 title">文件解析</div>
+          <v-spacer></v-spacer>
+          <upload-btn
+            text
+            color="primary"
+            accept=".sql,.txt,.xls,.xlsx"
+            @change="handleChangeFile"
+          >
+            <v-icon left>mdi-import</v-icon>
+            导入文件
+          </upload-btn>
+        </div>
+      </v-banner>
+      <div v-if="fileType" style="height: 100%;">
+        <DDLPage
+          v-if="fileType === 'sql'"
+          :items="items"
+          :origin="origin"
+          :select="select"
+          @select="handleSelect"
+        ></DDLPage>
+        <ExcelPage
+          v-if="fileType === 'excel'"
+          ref="excelPage"
+          style="position: relative;height: 100%;"
+          :data-source="dataSource"
+          :render-sheet="renderSheet"
+          @rendered="handleRendered"
+          @click:bottom-bar="initMap"
+          @error="handleExcelError"
+        ></ExcelPage>
+        <div v-if="fileType === 'txt'" class="pa-3 d-flex align-center justify-center" style="height: 100%;">
+          <div class="text-center">
+            <v-icon size="64" color="grey lighten-1">mdi-file-document-outline</v-icon>
+            <div class="mt-4 grey--text">文本解析功能开发中</div>
+          </div>
+        </div>
+      </div>
+      <none-page v-else></none-page>
+    </v-card>
+
+    <v-card class="db-box d-flex flex-column" elevation="5">
+      <div>
+        <v-tabs v-model="tab">
+          <v-tab>元数据设置</v-tab>
+          <v-tab>基础信息配置</v-tab>
+        </v-tabs>
+      </div>
+      <v-divider></v-divider>
+      <v-tabs-items v-model="tab">
+        <v-tab-item>
+          <edit-selected
+            style="position: relative;height: 100%;"
+            ref="selected"
+            v-model="selectModel"
+            v-loading="loading"
+          >
+            <template #head v-if="fileType === 'excel'">
+              <form-list ref="form" :items="formItems">
+                <template #actions>
+                  <div style="width: 100%;" class="d-flex justify-space-between">
+                    <v-btn
+                      v-for="btn in controlBtn"
+                      :key="btn.text"
+                      rounded
+                      class="buttons ml-3"
+                      :color="btn.color"
+                      :disabled="btn.disabled && !file"
+                      @click="btn.handle(btn)"
+                    >
+                      <v-progress-circular
+                        v-if="btn.loading"
+                        indeterminate
+                        :size="18"
+                        width="2"
+                        color="white"
+                        class="mr-1"
+                      ></v-progress-circular>
+                      <v-icon left v-else>{{ btn.icon }}</v-icon>
+                      {{ btn.text }}
+                    </v-btn>
+                  </div>
+                </template>
+              </form-list>
+            </template>
+          </edit-selected>
+        </v-tab-item>
+        <v-tab-item>
+          <div class="pa-3 d-flex align-center justify-center flex-column">
+            <edit-base ref="base" style="max-width: 500px;" :names="names" :item-data="itemData"></edit-base>
+            <v-btn
+              class="buttons"
+              rounded
+              color="primary"
+              :disabled="loading"
+              @click="handleSubmit"
+            >
+              <v-icon left>mdi-send-variant</v-icon>
+              保存
+            </v-btn>
+          </div>
+        </v-tab-item>
+      </v-tabs-items>
+    </v-card>
+
+    <v-overlay :value="overlay" z-index="9">
+      <div class="d-flex flex-column align-center justify-center" style="width: 300px;">
+        <div class="mb-3">正在提交</div>
+        <v-progress-linear
+          color="primary"
+          indeterminate
+          rounded
+          height="6"
+        ></v-progress-linear>
+      </div>
+    </v-overlay>
+  </div>
+</template>
+
+<script>
+import EditSelected from './editSelected'
+import UploadBtn from '@/components/UploadBtn'
+import FormList from '@/components/Form/list'
+import EditBase from './editBase'
+import DDLPage from './ddl/index.vue'
+import ExcelPage from './excel/index.vue'
+import NonePage from '@/components/Common/empty.vue'
+
+import { api } from '@/api/dataGovernance'
+// import { handleReadFile } from '@/utils/file'
+export default {
+  name: 'database-connect',
+  components: {
+    UploadBtn,
+    EditSelected,
+    EditBase,
+    FormList,
+    DDLPage,
+    ExcelPage,
+    NonePage
+  },
+  props: {
+    itemData: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data () {
+    return {
+      items: {},
+      origin: {},
+      tab: 0,
+      names: {},
+      fileType: null,
+      loading: false,
+      loadingUpload: false,
+      controlBtn: [
+        {
+          icon: 'mdi-swap-horizontal',
+          key: 'analysis',
+          color: 'primary',
+          loading: false,
+          disabled: false,
+          text: '解析',
+          handle: this.handleAnalysis
+        }
+      ],
+      select: null,
+      overlay: false,
+      dataSource: null, // excel数据
+      selectModel: null,
+      isUpdateMap: true,
+      renderSheet: true, // 自动渲染
+      file: null,
+      formItems: {
+        options: [
+          {
+            type: 'number',
+            key: 'headRowNumber',
+            value: 1,
+            col: 4,
+            label: '请输入表头所在行数 *',
+            outlined: true,
+            dense: true,
+            rules: [v => v > 0 || '请输入表头所在行数']
+          },
+          {
+            col: 8,
+            slotName: 'actions'
+          }
+        ]
+      }
+    }
+  },
+  created () {
+    if (!Object.keys(this.itemData).length) {
+      return
+    }
+    this.fileType = this.itemData.type === 'database' ? 'sql' : this.itemData.type === 'structure' ? 'excel' : 'txt'
+    this.selectModel = this.itemData.parsed_data.map(e => {
+      return {
+        map: e,
+        text: e.name_zh
+      }
+    })
+    this.select = this.itemData.name_zh
+    this.names = {
+      name_zh: this.itemData.name_zh,
+      name_en: this.itemData.name_en
+    }
+    // 有url时,下载文件
+    if (this.itemData?.url) {
+      this.getFile(this.itemData.url)
+    }
+  },
+  methods: {
+    // 文件下载,用于编辑时下载文件
+    async getFile (url) {
+      try {
+        const { data } = await api.downloadFile({ url })
+        this.file = new File([data], url, { type: data.type || 'text/plain' })
+        // await this.handleChangeFile(file)
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    handleRendered () {
+      this.showExcel = false
+      if (!this.renderSheet) {
+        return
+      }
+      // 渲染完毕
+      this.initMap()
+    },
+    async initMap () {
+      if (!this.isUpdateMap || !this.dataSource) {
+        return
+      }
+      const row = this.formItems.options.find(e => e.key === 'headRowNumber').value
+      const mapItems = this.$refs.excelPage?.getRow(row - 1)
+      if (!mapItems || !mapItems.length) {
+        this.selectModel = []
+        return
+      }
+      this.selectModel = mapItems.map(_e => {
+        return {
+          map: null,
+          text: _e
+        }
+      })
+      // 需解析excel内容,解析后接口返回元数据列表
+    },
+    // EXCEL文件解析
+    async handleAnalysis (item) {
+      item.loading = true
+      await this.initMap()
+      item.loading = false
+    },
+    // 文件解析处理
+    async handleChangeFile (file) {
+      if (!file) {
+        return
+      }
+
+      // 获取文件扩展名
+      const fileName = file.name || ''
+      const fileExt = fileName.split('.').pop()?.toLowerCase()
+
+      // 验证文件类型
+      const allowedTypes = ['sql', 'txt'] // ['sql', 'txt', 'xls', 'xlsx']
+      if (!fileExt || !allowedTypes.includes(fileExt)) {
+        this.$snackbar.warning('只支持上传 sql、txt 类型的文件')
+        return
+      }
+
+      this.file = file
+
+      if (fileExt === 'sql') {
+        // DDL 文件处理
+        this.fileType = 'sql'
+        this.loadingUpload = true
+        const query = new FormData()
+        query.append('file', file)
+        try {
+          const { data } = await api.parseDDLFile(query)
+          this.items = data.reduce((res, item) => {
+            const { columns, exist, table_info: tableInfo } = item
+            const key = tableInfo.name_zh || tableInfo.name_en
+            res[key] = {
+              exist,
+              table_info: tableInfo,
+              meta: columns.map(e => {
+                return {
+                  text: e.name_zh,
+                  map: {
+                    ...e,
+                    data_standard: null
+                  }
+                }
+              })
+            }
+            return res
+          }, {})
+          this.origin = { ...this.items }
+        } catch (error) {
+          this.$snackbar.error(error)
+        } finally {
+          this.loadingUpload = false
+        }
+      } else if (fileExt === 'xls' || fileExt === 'xlsx') {
+        // this.fileType = 'excel'
+        // this.renderSheet = true
+        // handleReadFile(file, fileExt, files => {
+        //   // 返回blob
+        //   this.dataSource = files
+        // })
+      } else if (fileExt === 'txt') {
+        this.fileType = 'txt'
+        // 文本解析功能待实现
+      }
+    },
+    handleExcelError () {
+      // Excel 加载错误处理
+    },
+    // DDL使用该数据源
+    handleSelect (item, key) {
+      this.select = key
+      this.selectModel = item.meta
+      this.names = {
+        name_zh: item.table_info?.name_zh,
+        name_en: item.table_info?.name_en
+      }
+    },
+
+    async handleSubmit () {
+      // DDL解析需要选择数据资源
+      if (this.fileType === 'sql' && (!this.selectModel || !this.selectModel.length)) {
+        this.$snackbar.error('请先选择数据资源')
+        this.tab = 0
+        return
+      }
+      const type = this.fileType === 'sql' ? 'database' : this.fileType === 'excel' ? 'structure' : 'unstructured'
+      try {
+        const obj = this.$refs.base.getValue()
+        if (!obj) {
+          return
+        }
+
+        const params = {
+          ...obj,
+          type
+        }
+        // 有id时,更新
+        if (this.itemData.id) {
+          params.id = this.itemData.id
+        }
+        // 上传文件
+        if (this.file) {
+          const { data } = await api.uploadFile({ file: this.file })
+          params.url = data.url // 文件上传路径
+        }
+
+        // 如果有解析数据,添加 parsed_data
+        if (this.selectModel && this.selectModel.length) {
+          params.parsed_data = this.selectModel.map(e => {
+            const { data_standard: dataStandard, ...obj } = e.map
+            return {
+              data_standard: dataStandard?.id ?? null,
+              ...obj
+            }
+          })
+        }
+
+        this.overlay = true
+        const submitApi = this.itemData.id ? api.updateBusinessDomain : api.saveBusinessDomain
+        await submitApi(params)
+        this.$snackbar.success('保存成功')
+        this.$emit('close')
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.overlay = false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.db {
+  width: 100%;
+  height: 100%;
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  grid-gap: 15px;
+  &-box {
+    height: 100%;
+    overflow: hidden;
+  }
+}
+.title {
+  color: #1976D2;
+  font-weight: 600;
+}
+::v-deep .v-tabs-items {
+  height: 0;
+  flex: 1;
+  .v-window-item {
+    overflow: auto;
+    height: 100%;
+  }
+  .v-window__container {
+    height: 100%;
+  }
+}
+::v-deep .v-banner--single-line .v-banner__wrapper {
+  padding: 0 8px 0 0;
+}
+</style>