Переглянути джерело

更新数据订单表单,添加业务域插入功能及上下文菜单,优化文本区域光标处理

Xiao_123 2 днів тому
батько
коміт
ce9a2309ed

+ 8 - 8
src/api/dataOrder.js

@@ -41,14 +41,14 @@ const dataOrder = {
     })
   },
 
-  // 设置数据产品就绪状态(数据工厂回调)
-  setOrderOnboard (orderId, options = {}) {
-    return http.post(`/dataservice/orders/${orderId}/onboard`, {
-      product_id: options.productId,
-      dataflow_id: options.dataflowId,
-      processed_by: options.processedBy || 'n8n-workflow'
-    })
-  },
+  // 设置数据产品就绪状态 - n8n工作流自动调用
+  // setOrderOnboard (orderId, options = {}) {
+  //   return http.post(`/dataservice/orders/${orderId}/onboard`, {
+  //     product_id: options.productId,
+  //     dataflow_id: options.dataflowId,
+  //     processed_by: options.processedBy || 'n8n-workflow'
+  //   })
+  // },
 
   // 完成订单
   completeOrder (orderId, processedBy = 'user') {

+ 11 - 1
src/components/MCard/index.vue

@@ -1,6 +1,12 @@
 <template>
   <!--  overflow-hidden -->
-  <v-card class="elevation-5" :class="{'elevation-0': noElevation, 'px-5': px5}" :min-height="minHeight" style="overflow: hidden;">
+  <v-card
+    class="elevation-5"
+    :class="{'elevation-0': noElevation, 'px-5': px5}"
+    :min-height="minHeight"
+    style="overflow: hidden;"
+    :outlined="outlined"
+  >
     <template v-if="noTitle">
       <v-card-title style="color: #1976D2; font-weight: 600;" v-if="$scopedSlots.autoTitle">
         <slot name="autoTitle"></slot>
@@ -59,6 +65,10 @@ export default {
     noTitle: {
       type: Boolean,
       default: false
+    },
+    outlined: {
+      type: Boolean,
+      default: false
     }
   }
 }

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

@@ -62,6 +62,10 @@
                     :rows="item.rows || 3"
                     :disabled="item.disabled"
                     @change="handleChange(item)"
+                    @focus.native="handleTextareaCursor(item, $event)"
+                    @click.native="handleTextareaCursor(item, $event)"
+                    @input.native="handleTextareaCursor(item, $event)"
+                    @contextmenu.native="handleTextareaContextMenu(item, $event)"
                   ></v-textarea>
                   <v-radio-group
                     v-if="item.type === 'ifRadio'"
@@ -290,6 +294,18 @@ export default {
     reset () {
       this.$refs.form.reset()
     },
+    handleTextareaCursor (item, event) {
+      if (!item?.cursorRef || !event?.target || event.target.tagName !== 'TEXTAREA') return
+      item.cursorRef.start = event.target.selectionStart
+      item.cursorRef.end = event.target.selectionEnd
+      if (item.onCursorSync) item.onCursorSync()
+    },
+    handleTextareaContextMenu (item, event) {
+      if (!item?.onContextMenu || !event?.target || event.target.tagName !== 'TEXTAREA') return
+      event.preventDefault()
+      this.handleTextareaCursor(item, event)
+      item.onContextMenu(item, event)
+    },
     handleChange (item) {
       if (item.type === 'date' && this.query[item.key]) item.option.validate = false
       if (item?.change) item.change(this.query[item.key], item)

+ 3 - 1
src/views/dataGovernance/metadata/index.vue

@@ -245,9 +245,11 @@ export default {
       }
       try {
         await api.createAuditRecord(obj)
-        this.$snackbar.success('审核记录创建成功')
         this.handleAuditClose()
         this.init()
+        this.$confirm('提示', '审核记录创建成功,是否前往审核页面').then(async () => {
+          this.$router.push('/dataReview')
+        })
       } catch (error) {
         this.$snackbar.error(error)
       }

+ 37 - 11
src/views/dataReview/components/details.vue

@@ -9,7 +9,9 @@
         <div class="mt-3">
           <v-row>
             <v-col cols="12">
-              <m-card title="基本信息" style="min-height: 260px;">
+              <!-- <m-card title="基本信息" outlined noElevation style="min-height: 260px;"> -->
+              <div class="card-wrapper">
+                <div class="card-title">基本信息</div>
                 <div class="snapshot-wrapper mt-3">
                   <ul class="snapshot-labels">
                     <li>状态:</li>
@@ -32,8 +34,9 @@
                         v-if="info?.status === 'pending'"
                         v-model="notes"
                         label=""
-                        filled
                         clearable
+                        filled
+                        rounded
                         dense
                         hide-details
                         placeholder="请输入备注信息"
@@ -43,13 +46,16 @@
                     </li>
                   </ul>
                 </div>
-              </m-card>
+              </div>
+              <!-- </m-card> -->
             </v-col>
           </v-row>
         </div>
         <v-row>
           <v-col cols="6">
-            <m-card title="新解析元数据信息">
+            <!-- <m-card title="新解析元数据信息" outlined noElevation> -->
+            <div class="card-wrapper">
+              <div class="card-title">新解析元数据信息</div>
               <div class="snapshot-wrapper mt-3">
                 <ul class="snapshot-labels">
                   <li>中文名称:</li>
@@ -69,12 +75,13 @@
                   </li>
                 </ul>
               </div>
-              <div class="text-end" v-if="info.status === 'pending'">
+              <div class="mt-5" v-if="info.status === 'pending'">
                 <v-tooltip top>
                   <template v-slot:activator="{ on, attrs }">
                     <v-btn
                       color="primary"
                       outlined
+                      block
                       @click="handleAction('alias', 'new_meta')"
                       v-bind="attrs"
                       v-on="on"
@@ -85,11 +92,14 @@
                   <span>设为主元数据,候选元数据则作为别名</span>
                 </v-tooltip>
               </div>
-            </m-card>
+            </div>
+            <!-- </m-card> -->
           </v-col>
 
           <v-col cols="6">
-            <m-card title="候选元数据信息">
+            <!-- <m-card title="候选元数据信息" outlined noElevation> -->
+            <div class="card-wrapper">
+              <div class="card-title">候选元数据信息</div>
               <div class="snapshot-wrapper mt-3">
                 <ul class="snapshot-labels">
                   <li>中文名称:</li>
@@ -109,12 +119,13 @@
                   </li>
                 </ul>
               </div>
-              <div class="text-end" v-if="info.status === 'pending'">
+              <div class="mt-5" v-if="info.status === 'pending'">
                 <v-tooltip top>
                   <template v-slot:activator="{ on, attrs }">
                     <v-btn
                       color="primary"
                       outlined
+                      block
                       @click="handleAction('alias', 'candidate')"
                       v-bind="attrs"
                       v-on="on"
@@ -125,12 +136,14 @@
                   <span>设为主元数据,新解析的元数据则作为别名</span>
                 </v-tooltip>
               </div>
-            </m-card>
+            </div>
+            <!-- </m-card> -->
           </v-col>
         </v-row>
       </v-tab-item>
+      <!-- 关系图谱 -->
       <v-tab-item v-if="info.record_type === 'change'">
-        <m-card style="height: calc(100% - 12px);" no-title class="d-flex flex-column mt-3" bodyStyle="flex: 1">
+        <m-card style="height: 70vh;" no-title class="d-flex flex-column mt-3" bodyStyle="flex: 1">
           <Graph :graphData="info.impact_graph"></Graph>
         </m-card>
       </v-tab-item>
@@ -190,12 +203,25 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-  ::v-deep .v-window {
+::v-deep .v-window {
   height: 100%;
 }
 ::v-deep .v-window__container, .v-window-item {
   height: 100%;
 }
+
+.card-wrapper {
+  border: 1px solid #0000001f;
+  border-radius: 6px;
+  padding: 15px;
+}
+.card-title {
+  font-size: 20px;
+  font-weight: 600;
+  color: #1976D2;
+  margin-bottom: 12px;
+}
+
 .snapshot-wrapper {
   display: flex;
   align-items: flex-start;

+ 2 - 2
src/views/dataReview/index.vue

@@ -30,7 +30,7 @@
       </template>
     </m-table>
 
-    <edit-dialog :visible.sync="show" title="数据审核详情" fullscreen :footer="false">
+    <edit-dialog :visible.sync="show" title="数据审核详情" :fullscreen="false" :width-type="0" :footer="false" style="min-height: 80vh;">
       <DetailsComponent
         :info="info"
         @action="handleSubmit"
@@ -40,7 +40,7 @@
         <v-divider></v-divider>
           <v-card-actions>
             <v-spacer></v-spacer>
-            <v-btn outlined color="warning" @click="handleSubmit('ignore')">忽略</v-btn>
+            <v-btn color="warning" @click="handleSubmit('ignore')">忽略</v-btn>
           </v-card-actions>
       </template>
     </edit-dialog>

+ 122 - 9
src/views/dataService/dataOrder/components/OrderForm.vue

@@ -1,9 +1,59 @@
 <template>
-  <MForm ref="form" :items="formItems" v-model="formValues"></MForm>
+  <div>
+    <MForm ref="form" :items="formItems" v-model="formValues">
+      <template #tips>
+        <v-alert color="primary accent-4" elevation="1" colored-border border="left">
+          <span style="color: #777; font-size: 14px;">
+            在需求描述框内点击鼠标右键,选择“插入业务域”,即可插入业务域。(提示:鼠标右键仅在需求描述框内有效,插入位置为光标所在位置)
+          </span>
+        </v-alert>
+      </template>
+    </MForm>
+    <v-menu
+      v-model="contextMenuVisible"
+      attach
+      :position-x="contextMenuX"
+      :position-y="contextMenuY"
+      absolute
+      offset-y
+      nudge-top="8"
+    >
+      <v-list dense>
+        <v-list-item @click="handleInsertBusinessDomainClick">
+          <v-list-item-title>插入业务域</v-list-item-title>
+        </v-list-item>
+      </v-list>
+    </v-menu>
+    <v-dialog v-model="insertDialogVisible" max-width="400" persistent>
+      <v-card>
+        <v-card-title>选择要插入的业务域</v-card-title>
+        <v-card-text>
+          <v-autocomplete
+            v-model="insertDialogSelected"
+            :items="businessDomain"
+            item-text="name_zh"
+            item-value="name_zh"
+            label="请选择业务域"
+            outlined
+            dense
+            hide-details
+            clearable
+            @change="handleInsertDialogSelect"
+          />
+        </v-card-text>
+        <v-card-actions>
+          <v-spacer />
+          <v-btn text @click="insertDialogVisible = false">取消</v-btn>
+          <v-btn color="primary" text @click="handleInsertDialogConfirm">插入</v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
+  </div>
 </template>
 
 <script>
 import MForm from '@/components/MForm'
+import { api } from '@/api/dataGovernance'
 import { getDatasourceList } from '@/api/dataOrigin'
 
 export default {
@@ -28,7 +78,15 @@ export default {
         extracted_fields: [],
         extraction_purpose: ''
       },
-      dataSourceItems: []
+      dataSourceItems: [],
+      businessDomain: [],
+      descriptionCursor: { start: 0, end: 0 },
+      descriptionCursorTouched: false,
+      contextMenuVisible: false,
+      contextMenuX: 0,
+      contextMenuY: 0,
+      insertDialogVisible: false,
+      insertDialogSelected: null
     }
   },
   computed: {
@@ -58,6 +116,13 @@ export default {
           itemValue: 'id',
           items: this.dataSourceItems
         },
+        {
+          type: 'text',
+          key: 'created_by',
+          label: '创建人',
+          outlined: true,
+          dense: true
+        },
         {
           type: 'textarea',
           key: 'description',
@@ -71,21 +136,20 @@ export default {
           counter: true,
           outlined: true,
           rows: 8,
-          hideDetails: 'auto'
+          hideDetails: 'auto',
+          cursorRef: this.descriptionCursor,
+          onCursorSync: () => { this.descriptionCursorTouched = true },
+          onContextMenu: this.handleDescriptionContextMenu
         },
         {
-          type: 'text',
-          key: 'created_by',
-          label: '创建人',
-          outlined: true,
-          dense: true,
-          hideDetails: 'auto'
+          slotName: 'tips'
         }
       ]
     }
   },
   created () {
     this.init()
+    this.getBusinessDomainList()
     if (this.itemData && this.itemData.id) {
       this.formValues = {
         title: this.itemData.title || '',
@@ -106,6 +170,55 @@ export default {
         this.$snackbar.error(error)
       }
     },
+    // 获取业务域列表
+    async getBusinessDomainList () {
+      try {
+        const { data } = await api.getBusinessDomainList2()
+        if (!data || !data?.length) {
+          this.businessDomain = []
+          return
+        }
+        this.businessDomain = data
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    // 插入业务域
+    handleDescriptionContextMenu (item, event) {
+      if (!event) return
+      const rect = this.$el.getBoundingClientRect()
+      this.contextMenuX = event.clientX - rect.left
+      this.contextMenuY = event.clientY - rect.top + 110
+      this.contextMenuVisible = true
+    },
+    handleInsertBusinessDomainClick () {
+      this.contextMenuVisible = false
+      this.insertDialogSelected = null
+      this.insertDialogVisible = true
+    },
+    handleInsertDialogSelect (val) {
+      if (val != null && val !== '') {
+        this.handleBusinessDomainInsert(val)
+        this.insertDialogVisible = false
+        this.insertDialogSelected = null
+      }
+    },
+    handleInsertDialogConfirm () {
+      if (this.insertDialogSelected != null && this.insertDialogSelected !== '') {
+        this.handleBusinessDomainInsert(this.insertDialogSelected)
+      }
+      this.insertDialogVisible = false
+      this.insertDialogSelected = null
+    },
+    handleBusinessDomainInsert (val) {
+      const { start, end } = this.descriptionCursor
+      const desc = this.formValues.description || ''
+      const useAppend = !this.descriptionCursorTouched && desc.length > 0
+      const insertStart = useAppend ? desc.length : start
+      const insertEnd = useAppend ? desc.length : end
+      this.formValues.description = desc.slice(0, insertStart) + val + desc.slice(insertEnd)
+      this.descriptionCursor.start = this.descriptionCursor.end = insertStart + val.length
+    },
     getValue () {
       if (!this.$refs.form.validate()) {
         return