zhengnaiwen_citu 3 kuukautta sitten
vanhempi
commit
7999617b16

+ 1 - 1
public/version.json

@@ -1,3 +1,3 @@
 {
-  "version": 1743493627978
+  "version": 1743504708005
 }

+ 2 - 1
src/api/dataGovernance.js

@@ -108,9 +108,10 @@ const dataModel = {
   getModelListToGraph: (param) => {
     return http.post('/model/data/model/graph/all', param)
   },
-  // 新增模型
+  // 新增模型 -> 通过资源新增模型
   addModel: (param) => {
     return http.post('/model/model/data/model/add', param)
+    // return http.post('/model/data/search', param)
   },
   // 删除模型
   deleteModel: (param) => {

+ 29 - 0
src/api/dataOrigin.js

@@ -0,0 +1,29 @@
+// 数据源
+
+import http from '@/utils/request'
+
+// 数据源列表
+export function getDatasourceList (data) {
+  return http.post('/datasource/list', data)
+}
+
+// 数据源 保存
+export function saveDatasource (data) {
+  return http.post('/datasource/save', data)
+}
+// 数据源 连接测试
+export function datasourceConnectTest (data) {
+  return http.post('/datasource/conntest', data)
+}
+// 数据源 连接测试
+export function datasourceValid (data) {
+  return http.post('/datasource/valid', data)
+}
+// 数据源 快捷解析
+export function datasourceParse (data) {
+  return http.post('/datasource/parse', data)
+}
+// 数据源 快捷解析
+export function deleteDatasource (data) {
+  return http.post('/datasource/delete', data)
+}

+ 386 - 0
src/components/MForm/index.vue

@@ -0,0 +1,386 @@
+<template>
+  <v-form ref="form" v-model="valid" class="login-form" @submit.native.prevent>
+      <div>
+          <v-row dense no-gutters class="justify-space-between">
+            <template v-for="(item, index) in items">
+              <slot :name="item.prevSlot"></slot>
+              <v-col :key="item.key" v-if="!item.hide" :cols="item.col || '12'" class="position">
+                <template v-if="item.slotTitle">
+                  <div :class="item.class" :style="item.slotTitleStyle">{{ item.slotTitle }}</div>
+                </template>
+                <div :class="item.flexStyle || 'flex-row'" class="px-2 d-flex">
+                  <v-text-field
+                      v-if="['text', 'password', 'number'].includes(item.type)"
+                      :type="item.type"
+                      v-model="query[item.key]"
+                      :rules="item.rules"
+                      :hide-details="item.hideDetails ?? false"
+                      :disabled="item.disabled"
+                      :dense="item.dense"
+                      :style="{width: item.width}"
+                      :color="item.color"
+                      :label="item.label"
+                      :placeholder="item.placeholder || item.label"
+                      :outlined="item.outlined"
+                      :autofocus="item.autofocus"
+                      :required="item.required"
+                      :class="item.class"
+                      :suffix="item.suffix"
+                      :append-icon="item.appendIcon"
+                      :clearable="item.clearable"
+                      :readonly="item.readonly"
+                      :prepend-inner-icon="item.prependInnerIcon"
+                      hide-spin-buttons
+                      @wheel.native="$event => handleWheel($event, item)"
+                      @keyup.enter.native="item.keyupEnterNative && item.keyupEnterNative(index)"
+                      @click="item.click && item.click(index)"
+                      @click:append="item.clickAppend && item.clickAppend(index)"
+                      @change="handleChange(item)"
+                      @input="item.onInput && item.onInput(item)"
+                  />
+                  <v-autocomplete
+                      v-if="item.type === 'autocomplete'"
+                      :rules="item.rules"
+                      v-model="query[item.key]"
+                      :attach="!item.noAttach"
+                      :loading="item.loading"
+                      :label="item.label"
+                      :placeholder="item.placeholder || item.label"
+                      :items="item.items"
+                      :item-text="item.itemText || 'label'"
+                      :item-value="item.itemValue || 'value'"
+                      :outlined="item.outlined"
+                      :dense="item.dense"
+                      :disabled="item.disabled"
+                      :multiple="item.multiple"
+                      :clearable="item.clearable"
+                      :search-input.sync="item.searchInput"
+                      :readonly="item.readonly"
+                      :hide-no-data="item.hideNoData"
+                      :hide-details="item.hideDetails ?? false"
+                      :no-data-text="item.noDataText || 'No data available'"
+                      :hide-selected="item.hideSelected"
+                      :return-object="item.returnObject"
+                      @change="handleChange(item)"
+                  >
+                    <template v-if="item.slotAppendItem" v-slot:append-item>
+                      <slot :name="item.slotAppendItem" :item="item"></slot>
+                    </template>
+                    <template v-if="item.prependItem" #prepend-item>
+                      <slot :name="item.prependItem" :item="item"></slot>
+                    </template>
+                  </v-autocomplete>
+                  <!-- autocomplete2 多选纸片样式 -->
+                  <v-autocomplete
+                      v-if="item.type === 'autocomplete2'"
+                      v-model="query[item.key]"
+                      :rules="item.rules"
+                      :attach="!item.noAttach"
+                      :loading="item.loading"
+                      :label="item.label"
+                      :placeholder="item.placeholder || item.label"
+                      :items="item.canCreate ? [inputUpdateValue, ...item.items].filter(Boolean) : item.items"
+                      :item-text="item.itemText || 'label'"
+                      :item-value="item.itemValue || 'value'"
+                      :outlined="item.outlined"
+                      :dense="item.dense"
+                      :multiple="item.multiple"
+                      :clearable="item.clearable"
+                      :search-input.sync="item.searchInput"
+                      :hide-no-data="item.hideNoData"
+                      :hide-selected="item.hideSelected"
+                      :readonly="item.readonly"
+                      @change="handleChange(item)"
+                      @update:search-input="$event => item.canCreate ? inputUpdateValue = $event : inputUpdateAutocomplete($event)"
+                      :hide-details="item.hideDetails ?? false"
+                      deletable-chips
+                      cache-items
+                      small-chips
+                    ></v-autocomplete>
+                  <v-combobox
+                      v-if="item.type === 'combobox'"
+                      :rules="item.rules"
+                      v-model="query[item.key]"
+                      :attach="true"
+                      :label="item.label"
+                      :placeholder="item.placeholder || item.label"
+                      :items="item.items"
+                      :item-text="item.itemText || 'label'"
+                      :item-value="item.itemValue || 'value'"
+                      :outlined="item.outlined"
+                      :dense="item.dense"
+                      :clearable="item.clearable"
+                      :disabled="item.disabled"
+                      @change="handleChange(item)"
+                  >
+                    <template v-if="item.hasIcon" v-slot:selection="data">
+                      <v-icon color="blue darken-2">{{ data.item.label }}</v-icon>
+                    </template>
+                    <template v-if="item.hasIcon" v-slot:item="data">
+                      <v-list-item-avatar>
+                        <v-icon>{{ data.item.label }}</v-icon>
+                      </v-list-item-avatar>
+                      <v-list-item-content>
+                        {{ data.item.label }}
+                      </v-list-item-content>
+                    </template>
+                  </v-combobox>
+                  <v-textarea
+                    v-if="item.type === 'textarea'"
+                    :rules="item.rules"
+                    v-model="query[item.key]"
+                    :label="item.label"
+                    :placeholder="item.placeholder || item.label"
+                    :no-resize="!item.resize"
+                    :outlined="item.outlined"
+                    :readonly="item.readonly"
+                    :dense="item.dense"
+                    :rows="item.rows || 3"
+                    :disabled="item.disabled"
+                    @change="handleChange(item)"
+                  ></v-textarea>
+                  <v-radio-group
+                    v-if="item.type === 'ifRadio'"
+                    v-model="query[item.key]"
+                    :disabled="item.disabled"
+                    :style="item.selfStyle"
+                    :class="item.selfClass"
+                    mandatory
+                    row
+                    @change="handleChange(item)"
+                  >
+                    <template v-if="item.label" v-slot:label>
+                      <div :style="`width: ${item.width || 120}px;`">{{ item.label }}</div>
+                    </template>
+                    <v-radio
+                      v-for="radio in item.items"
+                      :key="`${item.key}_radio_${radio.label}`"
+                      :readonly="radio.readonly"
+                      :label="radio.label"
+                      :value="radio.value"
+                      class="mr-8"
+                    ></v-radio>
+                  </v-radio-group>
+                  <template v-if="item.type === 'checkbox'">
+                    <div style="width: 120px;" class="mt-4 label text-left">{{ item.label }}</div>
+                    <div :style="item.style">
+                      <v-checkbox
+                        v-model="query[item.key]"
+                        v-for="k in item.items"
+                        :key="k.key"
+                        :label="k.label"
+                        :color="item.color"
+                        :value="k.value"
+                        :readonly="k.readonly"
+                        hide-details
+                        :multiple="true"
+                        class="mr-3"
+                      ></v-checkbox>
+                    </div>
+                  </template>
+                  <v-file-input
+                    v-if="item.type === 'upload'"
+                    :prepend-icon="item.prependIcon || ''"
+                    :append-icon="item.appendIcon"
+                    :append-outer-icon="item.appendOuterIcon"
+                    :show-size="item.showSize"
+                    :outlined="item.outlined"
+                    :dense="item.dense"
+                    v-model="query[item.key]"
+                    :placeholder="item.placeholder || item.label"
+                    :hint="item.hint"
+                    :rules="item.rules"
+                    :label="item.label"
+                    :persistent-hint="item.persistentHint"
+                    :loading= "item.loading"
+                    :disabled="item.disabled"
+                    :multiple="item.multiple"
+                    :success="item.success"
+                    :error="item.error"
+                    :accept="item.accept || '.xlsx, .xls, .csv, .pdf, .txt, .doc'"
+                    @change="handleChange(item)"
+                  >
+                    <template v-if="item.selfAppend" #append>
+                      <slot :name="item.selfAppend" :data="query[item.key]"></slot>
+                    </template>
+                  </v-file-input>
+                  <v-color-picker
+                    v-if="item.type === 'colorPicker'"
+                    class="mb-5"
+                    v-model="query[item.key]"
+                    :elevation="item.elevation || 5"
+                    :dot-size="item.dotSize || 25"
+                    :show-swatches="item.showSwatches || false"
+                    swatches-max-height="200"
+                    :mode="item.mode || 'hexa'"
+                    :hide-mode-switch="true"
+                    @input="item.change"
+                  />
+                  <template v-if="item.type === 'switch'">
+                    <span v-if="item.describe"> {{ item.describe }} </span>
+                    <span class="ml-2" v-if="item.trueLabel"> {{ item.trueLabel }}</span>
+                    <v-switch
+                      dense hide-details class="mt-0 ml-2 pa-0"
+                      v-model="query[item.key]"
+                      :label="item.label"
+                      :disabled="item.disabled || false"
+                      :color="item.color || 'primary'"
+                      :true-value="(item.trueValue !== undefined) ? item.trueValue : true"
+                      :false-value="(item.falseValue !== undefined) ? item.falseValue : false"
+                    ></v-switch>
+                    <span v-if="item.falseLabel"> {{ item.falseLabel }} </span>
+                  </template>
+                  <template v-if="item.type === 'date'">
+                    <div class="d-flex" style="margin-bottom: 22px;flex: 1;">
+                      <span v-if="item.width !== -1" class="label d-flex align-center" :style="`width: ${item.width || 120}px;`">{{ item.label }}</span>
+                      <date-picker
+                        :is-valid="isValid"
+                        :option="{ ...item.option, disabled: item.disabled }"
+                        v-model="query[item.key]"
+                        :style="item.style"
+                        @change="query[item.key] = $event; handleChange(item); handleCheck(item)"></date-picker>
+                    </div>
+                  </template>
+                  <template v-if="item.type === 'dataPicker'">
+                    <m-data-picker
+                      v-model="query[item.key]"
+                      style="flex: 1"
+                      :label="item.label"
+                      :placeholder="item.placeholder || item.label"
+                      :multiple="item.multiple"
+                      :collapse-tags="item.collapseTags"
+                      :max-collapse-tags="item.maxCollapseTags"
+                      :item-children="item.itemChildren ?? 'children'"
+                      :item-label="item.itemLabel ?? 'label'"
+                      :item-value="item.itemValue ?? 'value'"
+                      :items="item.items"
+                      :rules="item.rules"
+                      @change="handleChange(item)"
+                    ></m-data-picker>
+                  </template>
+                  <template v-if="item.slotName">
+                      <slot :name="item.slotName" :item="item"></slot>
+                  </template>
+                </div>
+              </v-col>
+              <slot :name="item.nextSlot"></slot>
+            </template>
+          </v-row>
+      </div>
+      <slot></slot>
+  </v-form>
+</template>
+
+<script>
+import DatePicker from '@/components/Form/datePicker.vue'
+import MDataPicker from '@/components/MDataPicker'
+export default {
+  name: 'MForm',
+  components: { DatePicker, MDataPicker },
+  props: {
+    value: {
+      type: Object,
+      default: () => ({})
+    },
+    items: {
+      type: Object,
+      default: () => {},
+      required: true
+    }
+  },
+  data () {
+    return {
+      query: {
+        ...this.value
+      },
+      inputUpdateValue: '',
+      valid: false,
+      isValid: true
+    }
+  },
+  watch: {
+    value: {
+      handler (val) {
+        this.query = val
+      },
+      deep: true
+    },
+    query: {
+      handler (val) {
+        this.$emit('input', val)
+      },
+      deep: true
+    }
+  },
+  methods: {
+    handleWheel (event, item) {
+      if (item.type !== 'number') return
+      event.preventDefault()
+      if (event.deltaY > 0) {
+        typeof this.query === 'number' && this.query[item.key]--
+      } else {
+        typeof this.query === 'number' && this.query[item.key]++
+      }
+      this.handleChange(item)
+    },
+    handleCheck (e) {
+      if (e.type !== 'date' || e.hide || !e.rules) return
+      const rules = e.rules[0]
+      const check = rules(this.query[e.key])
+      if (typeof check === 'string') {
+        e.option.error = true
+        e.option.errorMsg = check
+        return
+      }
+      e.option.error = false
+      e.option.errorMsg = null
+    },
+    validateTime () {
+      this.isValid = true
+      this.items.forEach((e, index) => {
+        if (e.type !== 'date' || e.hide || !e.rules) return
+        const rules = e.rules[0]
+        const check = rules(e.value)
+        if (typeof check === 'string') {
+          this.isValid = false
+          e.option.error = true
+          e.option.errorMsg = check
+        } else {
+          e.option.error = false
+          e.option.errorMsg = null
+        }
+      })
+      return this.isValid
+    },
+    validate () {
+      const form = this.$refs.form.validate()
+      const time = this.validateTime()
+      return form && time
+    },
+    inputUpdateAutocomplete (val) {
+      this.$emit('inputUpdateAutocomplete', val)
+    },
+    resetValidation () {
+      this.$refs.form.resetValidation()
+    },
+    reset () {
+      this.$refs.form.reset()
+    },
+    handleChange (item) {
+      if (item.type === 'date' && this.query[item.key]) item.option.validate = false
+      if (item?.change) item.change(this.query[item.key], item)
+      this.$emit('change', false)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.position {
+  position: relative;
+}
+.label {
+  font-size: 14px;
+  color: rgba(0, 0, 0, .6);
+}
+</style>

+ 2 - 3
src/views/dataGovernance/dataResource/components/Database/index.vue

@@ -190,9 +190,8 @@ export default {
       query.append('file', file)
       try {
         const { data } = await api.resourceParseDDL(query)
-        const { tables } = data
-        this.items = Object.keys(tables).reduce((res, key) => {
-          const { meta, ...obj } = tables[key]
+        this.items = Object.keys(data).reduce((res, key) => {
+          const { meta, ...obj } = data[key]
           res[key] = {
             ...obj,
             meta: meta.map(e => {

+ 195 - 195
src/views/dataGovernance/dataResource/components/editBase.vue

@@ -1,33 +1,24 @@
 <template>
   <div>
-    <form-list ref="form" :items="formItems">
-      <!-- <template #parentId="{ item }">
-        <search-nodes v-model="item.value" v-bind="item.options" :search-value="item.search"></search-nodes>
-      </template> -->
-      <!-- <template #childrenId="{ item }">
-        <search-nodes v-model="item.value" v-bind="item.options"></search-nodes>
-      </template> -->
-    </form-list>
+    <m-form ref="form" :items="formItems" v-model="formValues">
+    </m-form>
   </div>
 </template>
 
 <script>
-// import SearchNodes from '../../components/searchNodes.vue'
-import FormList from '@/components/Form/list'
-// import { combinationTagsPage } from '@/api/dataBook'
+import MForm from '@/components/MForm'
 import {
   metadata,
   frequency,
   sensitivity
 } from '@/utils/dataGovernance'
+import {
+  getDatasourceList
+} from '@/api/dataOrigin'
 import { api } from '@/api/dataGovernance'
 export default {
   name: 'edit-base',
   props: {
-    // hideEnName: {
-    //   type: Boolean,
-    //   default: false
-    // },
     names: {
       type: Object,
       default: () => ({})
@@ -37,189 +28,202 @@ export default {
       default: () => ({})
     }
   },
-  components: { FormList },
+  components: { MForm },
   data () {
     return {
-      formItems: {
-        options: [
-          {
-            type: 'text',
-            key: 'name',
-            value: '数据资源_' + new Date().getTime(),
-            label: '请输入名称 *',
-            outlined: true,
-            dense: true,
-            rules: [v => !!v || '请输入名称']
-          },
-          // {
-          //   type: 'text',
-          //   hide: this.hideEnName,
-          //   key: 'en_name',
-          //   value: null,
-          //   label: '请输入英文名称 *',
-          //   outlined: true,
-          //   dense: true,
-          //   rules: [v => !!v || '请输入英文名称']
-          // },
-          {
-            type: 'autocomplete',
-            key: 'category',
-            value: '应用类',
-            label: '请选择分类 *',
-            outlined: true,
-            dense: true,
-            rules: [v => !!v || '请选择分类'],
-            items: metadata
-          },
-          {
-            type: 'text',
-            key: 'organization',
-            value: this.$store.getters.userInfo.username,
-            label: '请输入所属机构 *',
-            // col: 6,
-            outlined: true,
-            dense: true,
-            rules: [v => !!v || '请输入所属机构']
-          },
-          // {
-          //   type: 'text',
-          //   key: 'owner',
-          //   value: this.$store.getters.userInfo.username,
-          //   label: '请输入所有者 *',
-          //   outlined: true,
-          //   dense: true,
-          //   rules: [v => !!v || '请输入所有者']
-          // },
-          {
-            type: 'text',
-            key: 'leader',
-            value: this.$store.getters.userInfo.username,
-            label: '请输入负责人 *',
-            // col: 6,
-            outlined: true,
-            dense: true,
-            rules: [v => !!v || '请输入负责人']
-          },
-          // {
-          //   key: 'parentId',
-          //   slotName: 'parentId',
-          //   search: null,
-          //   options: {
-          //     label: '请选择下级节点',
-          //     attach: true
-          //   },
-          //   value: null
-          // },
-          {
-            type: 'autocomplete',
-            key: 'frequency',
-            value: '日',
-            label: '请选择更新频率 *',
-            // col: 6,
-            noAttach: true,
-            outlined: true,
-            dense: true,
-            rules: [v => !!v || '请选择更新频率'],
-            items: frequency
-          },
-          {
-            type: 'autocomplete',
-            key: 'data_sensitivity',
-            value: '低',
-            label: '请选择数据敏感度 *',
-            // col: 6,
-            noAttach: true,
-            outlined: true,
-            dense: true,
-            rules: [v => !!v || '请选择数据敏感度'],
-            items: sensitivity
-          },
-          {
-            type: 'text',
-            key: 'storage_location',
-            value: '/',
-            outlined: true,
-            dense: true,
-            label: '请输入存储位置 *',
-            rules: [v => !!v || '请输入存储位置']
-          },
-          {
-            type: 'autocomplete',
-            key: 'tag',
-            value: null,
-            label: '请选择标签',
-            noAttach: true,
-            outlined: true,
-            dense: true,
-            itemText: 'name',
-            itemValue: 'id',
-            items: []
-          },
-          {
-            type: 'text',
-            key: 'describe',
-            value: null,
-            outlined: true,
-            dense: true,
-            label: '请输入描述'
-          },
-          // {
-          //   type: 'ifRadio',
-          //   key: 'addLine',
-          //   value: true,
-          //   label: '添加生产线',
-          //   width: 120,
-          //   items: [{ label: '是', value: true }, { label: '否', value: false }]
-          // },
-          {
-            type: 'ifRadio',
-            key: 'status',
-            value: true,
-            label: '启用',
-            width: 120,
-            items: [{ label: '是', value: true }, { label: '否', value: false }]
-          }
-        ]
+      formValues: {
+        name: '数据资源_' + new Date().getTime(),
+        category: '应用类',
+        organization: this.$store.getters.userInfo.username,
+        leader: this.$store.getters.userInfo.username,
+        frequency: '日',
+        data_sensitivity: '低',
+        chooseStorage: true,
+        storage_location: '/',
+        tag: null,
+        describe: null,
+        status: true
       },
       pageInfo: {
-        size: 10,
+        size: 999,
         current: 1
       },
-      total: 0,
-      loading: false
+      loading: false,
+      labelItems: [],
+      dataSourceItems: []
     }
   },
-  watch: {
-    names: {
-      handler (val) {
-        this.formItems.options.forEach(e => {
-          if (e.key === 'name' || e.key === 'en_name') {
-            e.value = val[e.key] ? val[e.key] : e.value
-          }
-        })
-      },
-      deep: true,
-      immediate: true
+  computed: {
+    formItems () {
+      return [
+        {
+          type: 'text',
+          key: 'name',
+          label: '请输入名称 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入名称']
+        },
+        {
+          type: 'text',
+          key: 'en_name',
+          label: '请输入英文名称 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入名称']
+        },
+        {
+          type: 'autocomplete',
+          key: 'category',
+          label: '请选择分类 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请选择分类'],
+          items: metadata
+        },
+        {
+          type: 'text',
+          key: 'organization',
+          label: '请输入所属机构 *',
+          // col: 6,
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入所属机构']
+        },
+        {
+          type: 'text',
+          key: 'leader',
+          label: '请输入负责人 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入负责人']
+        },
+        {
+          type: 'autocomplete',
+          key: 'frequency',
+          label: '请选择更新频率 *',
+          noAttach: true,
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请选择更新频率'],
+          items: frequency
+        },
+        {
+          type: 'autocomplete',
+          key: 'data_sensitivity',
+          label: '请选择数据敏感度 *',
+          noAttach: true,
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请选择数据敏感度'],
+          items: sensitivity
+        },
+        {
+          type: 'ifRadio',
+          key: 'chooseStorage',
+          label: '使用数据源',
+          width: 120,
+          items: [{ label: '是', value: true }, { label: '否', value: false }]
+        },
+        {
+          type: 'text',
+          key: 'storage_location',
+          hide: this.formValues.chooseStorage,
+          outlined: true,
+          dense: true,
+          label: '请输入存储位置 *',
+          rules: [v => !!v || '请输入存储位置']
+        },
+        {
+          type: 'autocomplete',
+          key: 'data_source',
+          label: '请选择数据源',
+          hide: !this.formValues.chooseStorage,
+          noAttach: true,
+          outlined: true,
+          dense: true,
+          items: this.dataSourceItems
+        },
+        {
+          type: 'autocomplete',
+          key: 'tag',
+          label: '请选择标签',
+          noAttach: true,
+          outlined: true,
+          dense: true,
+          itemText: 'name',
+          itemValue: 'id',
+          items: this.labelItems
+        },
+        {
+          type: 'text',
+          key: 'describe',
+          outlined: true,
+          dense: true,
+          label: '请输入描述'
+        },
+        // {
+        //   type: 'ifRadio',
+        //   key: 'addLine',
+        //   value: true,
+        //   label: '添加生产线',
+        //   width: 120,
+        //   items: [{ label: '是', value: true }, { label: '否', value: false }]
+        // },
+        {
+          type: 'ifRadio',
+          key: 'status',
+          label: '启用',
+          width: 120,
+          items: [{ label: '是', value: true }, { label: '否', value: false }]
+        }
+      ]
     }
   },
+  // watch: {
+  //   names: {
+  //     handler (val) {
+
+  //       this.formItems.options.forEach(e => {
+  //         if (e.key === 'name' || e.key === 'en_name') {
+  //           e.value = val[e.key] ? val[e.key] : e.value
+  //         }
+  //       })
+  //     },
+  //     deep: true,
+  //     immediate: true
+  //   }
+  // },
   created () {
     this.init()
-    if (!Object.keys(this.itemData).length) {
-      return
-    }
-    const objItems = ['parentId', 'tag']
-    this.formItems.options.forEach(item => {
-      if (Object.prototype.hasOwnProperty.call(this.itemData, item.key)) {
-        if (objItems.includes(item.key) && this.itemData[item.key]) {
-          item.value = this.itemData[item.key]?.id
-          if (Object.prototype.hasOwnProperty.call(item, 'search')) {
-            item.search = this.itemData[item.key]?.name
-          }
-          return
-        }
-        item.value = this.itemData[item.key]
-      }
-    })
+    // if (!Object.keys(this.itemData).length) {
+    //   return
+    // }
+    // const objItems = ['parentId', 'tag']
+    // Object.keys(this.formValues).forEach(key => {
+    //   if (Object.prototype.hasOwnProperty.call(this.itemData, key)) {
+    //     if (objItems.includes(key) && this.itemData[key]) {
+    //       this.formValues[key] = this.itemData[key]?.id
+    //       if (Object.prototype.hasOwnProperty.call(item, 'search')) {
+    //         item.search = this.itemData[item.key]?.name
+    //       }
+    //       return
+    //     }
+    //     item.value = this.itemData[item.key]
+    //   }
+    // })
+    // this.formItems.options.forEach(item => {
+    //   if (Object.prototype.hasOwnProperty.call(this.itemData, item.key)) {
+    //     if (objItems.includes(item.key) && this.itemData[item.key]) {
+    //       item.value = this.itemData[item.key]?.id
+    //       if (Object.prototype.hasOwnProperty.call(item, 'search')) {
+    //         item.search = this.itemData[item.key]?.name
+    //       }
+    //       return
+    //     }
+    //     item.value = this.itemData[item.key]
+    //   }
+    // })
   },
   methods: {
     async init () {
@@ -229,8 +233,11 @@ export default {
           const { data } = await api.dataLabelList({
             ...this.pageInfo
           })
-          this.formItems.options.find(e => e.key === 'tag').items = data.records
-          this.total = data.total
+          this.labelItems = data.records
+          const { data: dataSource } = await getDatasourceList({
+            ...this.pageInfo
+          })
+          this.dataSourceItems = dataSource.data_source
         } catch (error) {
           this.$snackbar.error(error)
         } finally {
@@ -248,13 +255,6 @@ export default {
         if (item.hide) {
           return res
         }
-        // if (item.key === 'tag' && item.value) {
-        //   res.tag = {
-        //     id: item.value,
-        //     name: item.items.find(e => e.id === item.value)?.name ?? null
-        //   }
-        //   return res
-        // }
         res[item.key] = item.value
         return res
       }, {})

+ 242 - 0
src/views/dataOrigin/rdbmsDataSource/components/edit.vue

@@ -0,0 +1,242 @@
+<template>
+  <div>
+    <!-- <div class="pa-2 d-flex">
+      <v-textarea rows="2" outlined dense placeholder="快捷解析"></v-textarea>
+      <v-btn class="ml-3">解析</v-btn>
+    </div> -->
+    <MForm ref="formData" :items="formData" v-model="formValues">
+      <div class="pa-2 d-flex align-center justify-center">
+        <v-btn @click="handleValid" rounded color="primary" class="buttons mr-3">
+          验证
+        </v-btn>
+        <v-btn @click="handleTest" rounded color="warning" class="buttons mr-3">
+          连接测试
+        </v-btn>
+        <v-btn @click="handleParse" rounded color="info" class="buttons">
+          快捷解析
+        </v-btn>
+      </div>
+    </MForm>
+    <m-dialog title="快捷解析" :visible.sync="show" @submit="onSubmit">
+      <v-textarea rows="3" outlined dense placeholder="快捷解析" v-model="text"></v-textarea>
+
+      <Linear :visible.sync="loading"></Linear>
+    </m-dialog>
+  </div>
+</template>
+
+<script>
+import MForm from '@/components/MForm'
+import MDialog from '@/components/Dialog'
+import Linear from '@/components/Progress/linear.vue'
+import {
+  datasourceConnectTest,
+  datasourceValid,
+  datasourceParse
+} from '@/api/dataOrigin'
+export default {
+  name: 'source-edit',
+  components: { MForm, MDialog, Linear },
+  props: {
+    // typeItems: {
+    //   type: Array,
+    //   default: () => []
+    // },
+    itemData: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  data () {
+    return {
+      loading: false,
+      text: null,
+      show: false,
+      formValues: {
+        name: null,
+        en_name: null,
+        type: null,
+        host: null,
+        port: null,
+        database: null,
+        username: null,
+        password: null,
+        param: null,
+        status: true,
+        desc: null
+      }
+    }
+  },
+  computed: {
+    formData () {
+      return [
+        {
+          type: 'text',
+          key: 'name',
+          label: '请输入数据源中文名称 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入数据源中文名称']
+        },
+        {
+          type: 'text',
+          key: 'en_name',
+          label: '请输入数据源英文名称 *',
+          disabled: this.itemData.id,
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入数据源英文名称']
+        },
+        {
+          type: 'text',
+          key: 'type',
+          label: '请选择数据库类型 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请选择数据库类型']
+          // items: this.typeItems
+        },
+        // {
+        //   type: 'autocomplete',
+        //   key: 'type',
+        //   label: '请选择数据库类型 *',
+        //   outlined: true,
+        //   dense: true,
+        //   rules: [v => !!v || '请选择数据库类型'],
+        //   items: this.typeItems
+        // },
+        {
+          type: 'text',
+          key: 'host',
+          label: '请输入数据库服务器IP地址 *',
+          col: 8,
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入数据库服务器IP地址']
+        },
+        {
+          type: 'text',
+          key: 'port',
+          label: '请输入数据库服务器IP端口 *',
+          col: 4,
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入数据库服务器IP端口']
+        },
+        {
+          type: 'text',
+          key: 'database',
+          label: '请输入数据库名称 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入数据库名称']
+        },
+        {
+          type: 'text',
+          key: 'username',
+          col: '6',
+          label: '请输入用户名 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入用户名']
+        },
+        {
+          type: 'password',
+          key: 'password',
+          col: '6',
+          label: '请输入密码 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入密码']
+        },
+        {
+          type: 'text',
+          key: 'param',
+          label: '请输入连接参数 *',
+          outlined: true,
+          dense: true,
+          rules: [v => !!v || '请输入连接参数']
+        },
+        {
+          type: 'ifRadio',
+          key: 'status',
+          label: '状态',
+          items: [
+            { value: true, label: '启用' },
+            { value: false, label: '关闭' }
+          ]
+        },
+        {
+          type: 'textarea',
+          key: 'desc',
+          outlined: true,
+          label: '请输入描述'
+        }
+      ]
+    }
+  },
+  created () {
+    if (Object.keys(this.itemData).length) {
+      Object.assign(this.formValues, this.itemData)
+    }
+  },
+  methods: {
+    validate () {
+      return this.$refs.formData.validate()
+    },
+    getQuery () {
+      return this.formValues
+    },
+    async handleTest () {
+      const { status, ...obj } = this.formValues
+      try {
+        const { data } = await datasourceConnectTest(obj)
+        if (data.connected) {
+          this.$snackbar.success(data.message)
+        } else {
+          this.$snackbar.error(data.message)
+        }
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    async handleValid () {
+      const { status, ...obj } = this.formValues
+      try {
+        const { data } = await datasourceValid(obj)
+        if (data.exists) {
+          this.$snackbar.warning(data.message)
+        } else {
+          this.$snackbar.success(data.message)
+        }
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    handleParse () {
+      this.show = true
+    },
+    async onSubmit () {
+      if (!this.text) {
+        this.$snackbar.warning('请输入解析字符串')
+        return
+      }
+      try {
+        this.loading = true
+        const { data } = await datasourceParse({ conn_str: this.text })
+        Object.assign(this.formValues, data.data_source)
+        this.$snackbar.success('解析成功')
+        this.show = false
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 210 - 2
src/views/dataOrigin/rdbmsDataSource/index.vue

@@ -1,12 +1,220 @@
 <template>
-  <div>
+  <div class="pa-3" style="background-color: #FFF;">
+    <filter-list :option="filter" @search="handleSearch" />
+    <table-list
+      class="mt-3"
+      :loading="loading"
+      :headers="headers"
+      :items="items"
+      :total="total"
+      :page-info="pageInfo"
+      @add="handleAdd"
+      @pageHandleChange="pageHandleChange"
+      @sort="handleSort"
+    >
+      <template #actions="{ item }">
+        <v-btn text color="primary" @click="handleEdit(item)">编辑</v-btn>
+        <v-btn text color="error" @click="handleDelete(item.id)">删除</v-btn>
+      </template>
+      <template v-slot:remark="{item}">
+        <td >
+          {{ item.remark && item.remark.substr(0, 8) }}
+          <template v-if="item.remark && item.remark.length > 8">...</template>
+        </td>
+      </template>
+      <template #status="{ item }">
+        <v-chip :color="item.status ? 'success' : 'error'" small>{{ item.status ? '启用' : '禁用' }}</v-chip>
+      </template>
+    </table-list>
+    <edit-dialog :title="title" :visible.sync="show" @submit="handleSubmit">
+      <edit-page class="pt-5" v-if="show" ref="edit" :type-items="dataSource_type" :item-data="itemData" @test="handleTest"></edit-page>
+    </edit-dialog>
 
+    <Linear :visible.sync="submitLoading"></Linear>
   </div>
 </template>
 
 <script>
+import EditPage from './components/edit.vue'
+import FilterList from '@/components/Filter'
+import TableList from '@/components/List/table.vue'
+import EditDialog from '@/components/Dialog'
+import Linear from '@/components/Progress/linear.vue'
+import {
+  dataFactoryDictList,
+  sourceConfigTest
+  // sourceAdd,
+  // sourceUpdate,
+  // sourceDelete
+} from '@/api/dataFactory'
+import {
+  getDatasourceList,
+  saveDatasource,
+  deleteDatasource
+} from '@/api/dataOrigin'
+import {
+  setLocalStorageAndTime
+  // getLocalStorageAndTime
+} from '@/utils/localStorageAndTime'
 export default {
-  name: 'rdbmsDataSource'
+  name: 'RDbmsDataSource',
+  components: { FilterList, TableList, EditDialog, EditPage, Linear },
+  data () {
+    return {
+      show: false,
+      filter: {
+        list: [
+          { type: 'textField', value: '', label: '数据源名称', key: 'name' }
+          // { type: 'autocomplete', value: null, label: '配置类型', key: 'type', items: [] }
+        ]
+      },
+      queryForm: {
+        name: '' // 配置名称
+      },
+      pageInfo: {
+        current: 1,
+        size: 10
+      },
+      total: 0,
+      orders: [],
+      loading: true,
+      submitLoading: false,
+      headers: [
+        { text: '中文名称', align: 'start', value: 'name', slotName: 'name' },
+        { text: '英文名称', align: 'start', value: 'en_name' },
+        { text: '数据库类型', align: 'start', value: 'type' },
+        { text: '主机地址', align: 'start', value: 'host' },
+        { text: '端口', align: 'start', value: 'port' },
+        { text: '数据库名称', align: 'start', value: 'database' },
+        { text: '用户名', align: 'start', value: 'username' },
+        { text: '连接参数', align: 'start', value: 'param' },
+        { text: '状态', align: 'start', value: 'status' },
+        { text: '更新时间', align: 'start', value: 'updateTime' },
+        { text: '备注', align: 'start', value: 'desc', useSlot: true },
+        { text: '操作', align: 'start', value: 'actions' }
+      ],
+      items: [],
+      dataSource_type: [],
+      itemData: {}
+    }
+  },
+  computed: {
+    title () {
+      return `${Object.keys(this.itemData).length ? '新增' : '编辑'}数据源`
+    }
+  },
+  created () {
+    // this.init()
+    // this.initPage()
+  },
+  methods: {
+    // async init () {
+    //   // const dictionary = getLocalStorageAndTime('dataFactory', 'dataSource_type') || {}
+    //   // if (!dictionary.dataSource_type) {
+    //   //   await this.initDictionary(dictionary)
+    //   // } else this.dataSource_type = dictionary.dataSource_type
+    //   // // this.formData.options.find(e => e.key === 'type').items = this.dataSource_type
+    //   // this.filter.list.find(e => e.key === 'type').items = this.dataSource_type
+    //   this.initPage()
+    // },
+    async initDictionary (dictionary) {
+      try {
+        const { data } = await dataFactoryDictList({ type: 'dataSource_type' })
+        this.dataSource_type = dictionary.dataSource_type = data[0].itemList
+        setLocalStorageAndTime('dataFactory', 'dataSource_type', dictionary)
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    async initPage () {
+      try {
+        this.loading = true
+        const { data } = await getDatasourceList({
+          ...this.queryForm
+          // ...this.pageInfo
+          // orders: this.orders
+        })
+        this.total = data.total
+        this.items = data.data_source
+        // .map(item => {
+        //   item.type = this.dataSource_type.find(filter => filter.value === item.type).label
+        //   return item
+        // })
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.loading = false
+      }
+    },
+    handleSearch (val) {
+      this.pageInfo.current = 1
+      Object.assign(this.queryForm, val)
+      this.initPage()
+    },
+    async handleSubmit () {
+      const _ref = this.$refs.edit
+      if (_ref.validate()) {
+        const isEdit = Object.keys(this.itemData).length
+        const data = {}
+        if (isEdit) data.id = this.itemData.id
+        Object.assign(data, _ref.getQuery())
+        try {
+          this.submitLoading = true
+          const { msg } = await saveDatasource(data)
+          this.$snackbar.success(msg)
+          this.show = false
+          this.initPage()
+        } catch (error) {
+          this.$snackbar.error(error)
+        } finally {
+          this.submitLoading = false
+        }
+      }
+    },
+    async handleTest (data) {
+      try {
+        this.submitLoading = true
+        const { msg } = await sourceConfigTest(data)
+        this.$snackbar.success(msg)
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.submitLoading = false
+      }
+    },
+    // table 关联操作 新增
+    handleAdd () {
+      this.show = true
+      this.itemData = {}
+    },
+    // table 关联操作 编辑
+    async handleEdit (item) {
+      this.itemData = item
+      this.show = true
+    },
+    // table 关联操作 删除
+    handleDelete (id) {
+      // if (!ids.length) return this.$snackbar.warning('请选择删除项')
+      this.$confirm('提示', '是否确定删除该选项').then(async () => {
+        try {
+          await deleteDatasource({ id })
+          this.$snackbar.success('删除成功')
+          this.initPage()
+        } catch (error) {
+          this.$snackbar.error(error)
+        }
+      })
+    },
+    // table 关联操作 切换分页
+    pageHandleChange (index) {
+      this.pageInfo.current = index
+      this.initPage()
+    },
+    handleSort (val) {
+      this.orders = val
+      this.initPage()
+    }
+  }
 }
 </script>