|
|
@@ -0,0 +1,457 @@
|
|
|
+<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="py-2 title">DDL解析</div> -->
|
|
|
+ <v-tabs v-model="uploadTab" @change="handleTabChange">
|
|
|
+ <v-tab>DDL解析</v-tab>
|
|
|
+ <v-tab>Excel解析</v-tab>
|
|
|
+ <v-tab>文本解析</v-tab>
|
|
|
+ </v-tabs>
|
|
|
+ <v-spacer></v-spacer>
|
|
|
+ <upload-btn
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ @change="handleChangeFile"
|
|
|
+ >
|
|
|
+ <v-icon left>mdi-import</v-icon>
|
|
|
+ {{ uploadBtnText }}
|
|
|
+ </upload-btn>
|
|
|
+ </div>
|
|
|
+ </v-banner>
|
|
|
+ <v-tabs-items v-model="uploadTab">
|
|
|
+ <!-- DDL解析 -->
|
|
|
+ <v-tab-item>
|
|
|
+ <div class="pa-3">
|
|
|
+ <v-text-field
|
|
|
+ v-model="name"
|
|
|
+ label="名称查找"
|
|
|
+ placeholder="请输入名称"
|
|
|
+ dense
|
|
|
+ hide-details
|
|
|
+ outlined
|
|
|
+ clearable
|
|
|
+ append-icon="mdi-magnify"
|
|
|
+ @input="handleSearch"
|
|
|
+ @click:append="handleSearch"
|
|
|
+ @keydown.enter.native="handleSearch"
|
|
|
+ ></v-text-field>
|
|
|
+ <div class="pt-3 pl-3 d-flex justify-end">
|
|
|
+ 共 {{ Object.keys(items).length }} 个数据
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="scrollBox">
|
|
|
+ <v-expansion-panels>
|
|
|
+ <v-expansion-panel
|
|
|
+ v-for="(item, key) in items"
|
|
|
+ :key="key"
|
|
|
+ >
|
|
|
+ <v-expansion-panel-header>
|
|
|
+ <div class="d-flex justify-space-between align-center">
|
|
|
+ <div>
|
|
|
+ <v-chip color="error" small v-if="item.exist">已存在</v-chip>
|
|
|
+ {{ key }}
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <v-btn
|
|
|
+ :disabled="select === key"
|
|
|
+ text
|
|
|
+ color="primary"
|
|
|
+ small
|
|
|
+ @click.stop="handleSelect(item, key)">使用该资源</v-btn>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </v-expansion-panel-header>
|
|
|
+ <v-expansion-panel-content>
|
|
|
+ <v-simple-table
|
|
|
+ fixed-header
|
|
|
+ height="400px"
|
|
|
+ >
|
|
|
+ <template v-slot:default>
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th class="text-left">
|
|
|
+ 元数据中文名
|
|
|
+ </th>
|
|
|
+ <th class="text-left">
|
|
|
+ 元数据英文名
|
|
|
+ </th>
|
|
|
+ <th class="text-left">
|
|
|
+ 元数据类型
|
|
|
+ </th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="val in item.meta" :key="key + val.map.name_zh">
|
|
|
+ <td>{{ val.map.name_zh }}</td>
|
|
|
+ <td>{{ val.map.name_en }}</td>
|
|
|
+ <td>{{ val.map.data_type }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </template>
|
|
|
+ </v-simple-table>
|
|
|
+ </v-expansion-panel-content>
|
|
|
+ </v-expansion-panel>
|
|
|
+ </v-expansion-panels>
|
|
|
+ </div>
|
|
|
+ </v-tab-item>
|
|
|
+ <!-- EXCEL解析 -->
|
|
|
+ <v-tab-item>
|
|
|
+ <div class="box-resource-content-excel" ref="excelBox">
|
|
|
+ <file-review
|
|
|
+ v-loading="showExcel"
|
|
|
+ v-if="dataSource"
|
|
|
+ ref="excel"
|
|
|
+ :style="`height: ${ExcelHeight}px;`"
|
|
|
+ :src="dataSource"
|
|
|
+ @click:bottom-bar="initMap"
|
|
|
+ @rendered="handleRendered"
|
|
|
+ @error="showExcel = false"
|
|
|
+ ></file-review>
|
|
|
+ <template v-else>
|
|
|
+ <none-page></none-page>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </v-tab-item>
|
|
|
+ </v-tabs-items>
|
|
|
+ </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"
|
|
|
+ ></edit-selected> -->
|
|
|
+ <edit-selected
|
|
|
+ style="position: relative;height: 100%;"
|
|
|
+ ref="selected"
|
|
|
+ v-model="headMap"
|
|
|
+ v-loading="loading"
|
|
|
+ >
|
|
|
+ <template #head>
|
|
|
+ <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 && !multipartFile"
|
|
|
+ @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 FileReview from '@/components/FileReview'
|
|
|
+import NonePage from '@/components/Common/empty.vue'
|
|
|
+import EditBase from './editBase'
|
|
|
+import { api } from '@/api/dataGovernance'
|
|
|
+export default {
|
|
|
+ name: 'database-connect',
|
|
|
+ components: {
|
|
|
+ UploadBtn,
|
|
|
+ EditSelected,
|
|
|
+ EditBase,
|
|
|
+ FileReview,
|
|
|
+ NonePage
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ itemData: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({})
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ name: null,
|
|
|
+ items: {},
|
|
|
+ origin: {},
|
|
|
+ tab: 0,
|
|
|
+ uploadTab: 0,
|
|
|
+ names: {},
|
|
|
+ fileName: undefined,
|
|
|
+ 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数据
|
|
|
+ ExcelHeight: 0,
|
|
|
+ ExcelWidth: 0,
|
|
|
+ showExcel: false,
|
|
|
+ selectModel: null,
|
|
|
+ uploadBtnText: '导入DDL',
|
|
|
+ isUpdateMap: true,
|
|
|
+ renderSheet: true // 自动渲染
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.ExcelHeight = this.$refs.excelBox.clientHeight
|
|
|
+ this.ExcelWidth = this.$refs.excelBox.clientWidth
|
|
|
+ })
|
|
|
+ },
|
|
|
+ created () {
|
|
|
+ if (!Object.keys(this.itemData).length) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ 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.excel?.getRow(row - 1)
|
|
|
+ if (!mapItems || !mapItems.length) {
|
|
|
+ this.headMap = []
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.headMap = mapItems.map(_e => {
|
|
|
+ return {
|
|
|
+ map: null,
|
|
|
+ text: _e
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleTabChange (tab) {
|
|
|
+ this.uploadTab = tab
|
|
|
+ this.uploadBtnText = tab === 0 ? '导入DDL' : tab === 1 ? '导入Excel' : '导入文本'
|
|
|
+ },
|
|
|
+ async handleChangeFile (file) {
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSearch () {
|
|
|
+ if (!this.name) {
|
|
|
+ this.items = this.origin
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.items = Object.keys(this.origin).filter(e => e.includes(this.name)).reduce((res, key) => {
|
|
|
+ res[key] = this.origin[key]
|
|
|
+ return res
|
|
|
+ }, {})
|
|
|
+ },
|
|
|
+
|
|
|
+ async handleSubmit () {
|
|
|
+ if (!this.selectModel || !this.selectModel.length) {
|
|
|
+ this.$snackbar.error('请先选择数据资源')
|
|
|
+ this.tab = 0
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const obj = this.$refs.base.getValue()
|
|
|
+ if (!obj) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const { ...params } = obj
|
|
|
+ // 上传文件
|
|
|
+ if (this.itemData.id) {
|
|
|
+ Object.assign(params, {
|
|
|
+ id: this.itemData.id,
|
|
|
+ parsed_data: this.selectModel.map(e => {
|
|
|
+ const { data_standard: dataStandard, ...obj } = e.map
|
|
|
+ return {
|
|
|
+ data_standard: dataStandard?.id ?? null,
|
|
|
+ ...obj
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ type: 'database',
|
|
|
+ name_en: this.names.name_en
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ Object.assign(params, {
|
|
|
+ additional_info: {
|
|
|
+ head_data: this.items[this.select].meta.map(e => {
|
|
|
+ const { data_standard: dataStandard, ...obj } = e.map
|
|
|
+ return {
|
|
|
+ data_standard: dataStandard?.id ?? null,
|
|
|
+ ...obj
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ data_resource: {
|
|
|
+ name_en: this.names.name_en,
|
|
|
+ name_zh: this.names.name_zh
|
|
|
+ }
|
|
|
+ },
|
|
|
+ type: 'database',
|
|
|
+ url: '' // 文件上传路径
|
|
|
+ })
|
|
|
+ }
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+}
|
|
|
+.scrollBox {
|
|
|
+ height: 0;
|
|
|
+ flex: 1;
|
|
|
+ overflow: auto;
|
|
|
+}
|
|
|
+.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>
|