|
|
@@ -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>
|