Sfoglia il codice sorgente

备份恢复相关功能

zhengnaiwen_citu 2 mesi fa
parent
commit
ad0af417c7

+ 11 - 1
src/api/dataChart.js

@@ -181,6 +181,16 @@ export function getTasksLog (taskId, param) {
 }
 
 // PgVector 表备份
-export function getPgBackup (param) {
+export function addPgBackup (param) {
   return http.post('/vanna/v0/data_pipeline/vector/backup', param)
 }
+
+// PgVector 已备份
+export function getPgBackupList (param) {
+  return http.get('/vanna/v0/data_pipeline/vector/restore/list', param)
+}
+
+// PgVector 备份恢复
+export function pgBackupRestore (param) {
+  return http.post('/vanna/v0/data_pipeline/vector/restore', param)
+}

+ 181 - 0
src/components/MSearch/index.vue

@@ -0,0 +1,181 @@
+<template>
+    <v-card style="width:100%; min-height: 80px;" elevation="5">
+        <v-form v-model="valid" ref="form" @submit.prevent="search">
+          <div class="flex-horizon d-flex pa-2 flex-wrap">
+            <div v-for="(val, index) in items" :key="`filter_${val.label}_${index}`" class="pa-3" v-show="!val.hidden">
+              <v-text-field
+                  v-if="val.type === 'text'"
+                  v-model="query[val.key]"
+                  :placeholder="val.placeholder || `请输入${val.label}`"
+                  :style="`width: ${val.width || 250}px`"
+                  v-bind="val"
+                  outlined dense clearable hide-details @keyup.enter.native="search"
+              ></v-text-field>
+              <v-autocomplete
+                    v-if="val.type === 'autocomplete'"
+                    v-model="query[val.key]"
+                    :placeholder="val.placeholder || `请选择${val.label}`"
+                    :item-text="val.itemText || 'label'"
+                    :item-value="val.itemValue || 'value'"
+                    :style="`width: ${val.width || 250}px`"
+                    :search-input.sync="val.searchInput"
+                    outlined
+                    dense
+                    attach
+                    hide-details
+                    v-bind="val"
+                    @change="$event => val.change && val.change($event)"
+              ></v-autocomplete>
+              <v-select
+                v-if="val.type === 'select'"
+                v-model="query[val.key]"
+                :placeholder="val.placeholder || `请选择${val.label}`"
+                :item-text="val.itemText || 'label'"
+                :item-value="val.itemValue || 'value'"
+                :style="`width: ${val.width || 250}px`"
+                v-bind="val"
+                hide-details outlined dense attach
+                @change="$event => val.change && val.change($event)"
+              ></v-select>
+              <date-picker
+                  v-if="val.type === 'datePicker'"
+                  v-model="val.value"
+                  ref="picker"
+                  :option="{
+                    max: val.max,
+                    min: val.min,
+                    range: val.range,
+                    placeholder: val.placeholder || val.label,
+                    type: val.dateType || 'date',
+                    clearable: val.clearable || false
+                  }"
+                  @change="val.change && val.change($event), val.value = $event"
+              />
+              <m-data-picker
+                v-if="val.type === 'dataPicker'"
+                v-model="val.value"
+                :style="`width: ${val.width || 250}px`"
+                :label="val.label"
+                :placeholder="val.placeholder || val.label"
+                :item-children="val.itemChildren ?? 'children'"
+                :item-label="val.itemLabel ?? 'label'"
+                :item-value="val.itemValue ?? 'value'"
+                :items="val.items"
+                @change="$event => val.change && val.change($event)"
+              ></m-data-picker>
+              <template v-if="val.type === 'checkbox'">
+                <div class="d-flex">
+                  <v-checkbox
+                    v-model="val.value"
+                    v-for="k in val.items"
+                    :key="k.key"
+                    :label="k.label"
+                    :color="val.color"
+                    :value="k.value"
+                    hide-details
+                    :multiple="true"
+                    class="mr-3"
+                    style="margin-top: 0;padding-top: 6px;"
+                  ></v-checkbox>
+                </div>
+              </template>
+            </div>
+            <template>
+              <div class="pt-3 mr-3">
+                <v-btn color="primary" class="elevation-5 half-button" rounded @click="search">查 询</v-btn>
+              </div>
+              <div class="pt-3">
+                <v-btn color="#5cbbf6" rounded class="white--text elevation-5 half-button" @click="reset">重 置</v-btn>
+              </div>
+            </template>
+            <slot name="btn"></slot>
+          </div>
+          <slot></slot>
+        </v-form>
+        <slot name="footer"></slot>
+    </v-card>
+</template>
+
+<script>
+import DatePicker from '@/components/Form/datePicker.vue'
+import MDataPicker from '@/components/MDataPicker'
+export default {
+  name: 'MSearch',
+  components: { DatePicker, MDataPicker },
+  props: {
+    value: {
+      type: Object,
+      default: () => ({})
+    },
+    items: {
+      type: Array,
+      default: () => [],
+      required: true
+    }
+  },
+  data () {
+    return {
+      query: {
+        ...this.value
+      },
+      inputUpdateValue: '',
+      valid: false
+      // filter: {},
+      // resetData: {}
+    }
+  },
+  watch: {
+    value: {
+      handler (val) {
+        this.query = val
+      },
+      deep: true
+    },
+    query: {
+      handler (val) {
+        this.$emit('input', val)
+      },
+      deep: true
+    }
+  },
+  created () {
+    // this.option?.list.forEach(item => {
+    //   this.filter[item.key] = item.value
+    //   this.resetData[item.key] = item.value
+    // })
+  },
+  methods: {
+    search () {
+      const obj = this.option?.list.reduce((res, item) => {
+        res[item.key] = item.value
+        return res
+      }, {})
+      this.$emit('search', obj)
+    },
+    reset () {
+      if (this.$listeners.resetNoParams) {
+        this.$emit('resetNoParams')
+        return
+      }
+      this.$refs.form.reset()
+      if (this.$refs?.picker && this.$refs.picker.length) {
+        this.$refs.picker.forEach(e => {
+          e.reset()
+        })
+      }
+      const obj = this.option?.list.reduce((res, item) => {
+        res[item.key] = null
+        return res
+      }, {})
+      if (this.$listeners.customReset) {
+        this.$emit('customReset')
+        return
+      }
+      this.$emit('search', obj, true)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 24 - 24
src/router/routes.js

@@ -2477,29 +2477,6 @@ export default {
           name: 'model-train',
           alwaysShow: 0
         },
-        // {
-        //   hidden: 0,
-        //   icon: '',
-        //   type: 1,
-        //   title: 'PgVector表管理',
-        //   path: '/model-system/pg-vector',
-        //   children: [],
-        //   enName: 'pg vector',
-        //   redirect: '',
-        //   active: '',
-        //   label: 'PgVector表管理',
-        //   sort: 10,
-        //   component: 'modelSystem/pgVector',
-        //   meta: {
-        //     roles: [],
-        //     enName: 'pg vector',
-        //     icon: '',
-        //     title: 'PgVector表管理',
-        //     fullScreen: false
-        //   },
-        //   name: 'pg vector',
-        //   alwaysShow: 0
-        // },
         {
           hidden: 0,
           icon: '',
@@ -2522,6 +2499,29 @@ export default {
           },
           name: 'model-history',
           alwaysShow: 0
+        },
+        {
+          hidden: 0,
+          icon: '',
+          type: 1,
+          title: '备份与恢复',
+          path: '/model-system/backup',
+          children: [],
+          enName: 'backup',
+          redirect: '',
+          active: '',
+          label: '备份与恢复',
+          sort: 10,
+          component: 'modelSystem/backup',
+          meta: {
+            roles: [],
+            enName: 'backup',
+            icon: '',
+            title: '备份与恢复',
+            fullScreen: false
+          },
+          name: 'backup',
+          alwaysShow: 0
         }
       ],
       name: 'model-system',
@@ -2535,7 +2535,7 @@ export default {
         roles: [],
         enName: 'modelSystem',
         icon: 'mdi-cog-outline',
-        title: '模型微调',
+        title: '知识库管理',
         target: false,
         effectiveStatus: true
       },

+ 64 - 0
src/views/modelSystem/backup/backupCreate.vue

@@ -0,0 +1,64 @@
+<template>
+  <MDialog :visible.sync="show" title="创建备份" @submit="handleSubmit">
+    <MForm ref="form" :items="formItems" v-model="formValues" v-loading="loading"></MForm>
+  </MDialog>
+</template>
+
+<script>
+import MForm from '@/components/MForm'
+import MDialog from '@/components/Dialog'
+import {
+  addPgBackup
+} from '@/api/dataChart'
+export default {
+  name: 'backupCreate',
+  components: { MDialog, MForm },
+  data () {
+    return {
+      loading: false,
+      show: false,
+      formValues: {
+        db_connection: null
+      },
+      formItems: [
+        {
+          label: 'PostgreSQL连接字符串',
+          key: 'db_connection',
+          type: 'text',
+          placeholder: '请输入PostgreSQL连接字符串,不提供则从config.py获取'
+        }
+      ],
+      itemData: null
+    }
+  },
+  methods: {
+    open (item) {
+      this.itemData = item ?? null
+      this.show = true
+      this.formValues = {
+        db_connection: null
+      }
+    },
+    async handleSubmit () {
+      this.loading = true
+      try {
+        await addPgBackup({
+          ...this.formValues,
+          task_id: this.itemData?.task_id
+        })
+        this.$snackbar.success('创建备份成功')
+        this.$emit('success')
+        this.show = false
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 72 - 0
src/views/modelSystem/backup/backupDetails.vue

@@ -0,0 +1,72 @@
+<template>
+  <MDialog :visible.sync="show" title="文件列表" widthType="1">
+    <MTable
+      :loading="false"
+      elevation="0"
+      :headers="headers"
+      :items="items"
+      :total="total"
+      :page-info="pageInfo"
+      :show-select="false"
+      :is-tools="false"
+    >
+      <template #actions="{ item }">
+        <v-btn text color="primary" @click="handleRestore(item)">恢复备份</v-btn>
+      </template>
+    </MTable>
+    <BackupDetailsConfirm ref="backupDetailsConfirmRefs" @success="$emit('success')"></BackupDetailsConfirm>
+  </MDialog>
+</template>
+
+<script>
+import MDialog from '@/components/Dialog'
+import MTable from '@/components/List/table'
+import BackupDetailsConfirm from './backupDetailsConfirm.vue'
+// import {
+//   pgBackupRestore
+// } from '@/api/dataChart'
+export default {
+  name: 'backupDetails',
+  components: {
+    MDialog,
+    MTable,
+    BackupDetailsConfirm
+  },
+  data () {
+    return {
+      show: false,
+      headers: [
+        { text: 'collection文件', align: 'start', value: 'collection_file' },
+        { text: 'collection文件大小', align: 'center', value: 'collection_size' },
+        { text: 'embedding文件', align: 'start', value: 'embedding_file' },
+        { text: 'embedding文件大小', align: 'center', value: 'embedding_size' },
+        { text: '备份日期', align: 'start', value: 'backup_date' },
+        { text: '操作', value: 'actions' }
+      ],
+      itemData: {},
+      items: [],
+      orders: [],
+      pageInfo: {
+        size: 10,
+        current: 1
+      },
+      total: 0
+    }
+  },
+  methods: {
+    open (item) {
+      this.itemData = item
+      this.items = item.backups
+      this.total = item.backups.length
+      this.show = true
+    },
+    handleRestore (item) {
+      this.$refs.backupDetailsConfirmRefs.open(item, this.itemData)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 91 - 0
src/views/modelSystem/backup/backupDetailsConfirm.vue

@@ -0,0 +1,91 @@
+<template>
+  <MDialog :visible.sync="show" title="恢复确认" @submit="handleSubmit">
+    <MForm ref="form" :items="formItems" v-model="formValues" v-loading="loading"></MForm>
+  </MDialog>
+</template>
+
+<script>
+import MDialog from '@/components/Dialog'
+import MForm from '@/components/MForm'
+import {
+  pgBackupRestore
+} from '@/api/dataChart'
+export default {
+  name: 'backupDetailsConfirm',
+  components: {
+    MDialog,
+    MForm
+  },
+  data () {
+    return {
+      loading: false,
+      show: false,
+      formValues: {
+        tables: ['langchain_pg_collection', 'langchain_pg_embedding'],
+        db_connection: null,
+        truncate_before_restore: false
+      },
+      formItems: [
+        {
+          label: '恢复表名',
+          type: 'autocomplete',
+          key: 'tables',
+          multiple: true,
+          noAttach: true,
+          items: ['langchain_pg_collection', 'langchain_pg_embedding']
+        },
+        {
+          label: 'PostgreSQL连接字符串',
+          type: 'text',
+          key: 'db_connection'
+        },
+        {
+          label: '清空pgvecror目标表',
+          type: 'ifRadio',
+          key: 'truncate_before_restore',
+          width: '140',
+          items: [
+            { label: '是', value: true },
+            { label: '否', value: false }
+          ]
+        }
+      ],
+      itemData: {},
+      origin: {}
+    }
+  },
+  methods: {
+    open (item, origin) {
+      this.itemData = item
+      this.origin = origin
+      this.show = true
+      this.formValues = {
+        tables: ['langchain_pg_collection', 'langchain_pg_embedding'],
+        db_connection: null,
+        truncate_before_restore: false
+      }
+    },
+    async handleSubmit () {
+      this.loading = true
+      try {
+        await pgBackupRestore({
+          backup_path: this.origin.relative_path,
+          timestamp: this.itemData.timestamp,
+          ...this.formValues
+        })
+        this.$snackbar.success('恢复成功')
+        this.show = false
+        this.$emit('success')
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 135 - 0
src/views/modelSystem/backup/index.vue

@@ -0,0 +1,135 @@
+<template>
+  <div class="pa-3 white">
+    <m-search :items="searchItems" v-model="searchValues" @search="handleSearch" />
+    <m-table
+      class="mt-3"
+      :loading="loading"
+      :headers="headers"
+      :items="items"
+      :total="total"
+      :page-info="pageInfo"
+      :show-select="false"
+      :is-tools="false"
+      @pageHandleChange="pageHandleChange"
+      @sort="handleSort"
+    >
+      <template #navBtn>
+        <v-btn elevation="5" class="buttons" color="primary" rounded @click="handleAdd">
+          <v-icon left>mdi-plus</v-icon>
+          创建备份
+        </v-btn>
+      </template>
+      <template #actions="{ item }">
+        <v-btn text color="primary" @click="handleAdd(item)">创建备份</v-btn>
+        <v-btn text color="primary" @click="handleDetails(item)">查看备份文件</v-btn>
+      </template>
+    </m-table>
+    <BackupDetails ref="backupDetailsRefs" @success="init"></BackupDetails>
+    <BackupCreate ref="backupCreateRefs" @success="init"></BackupCreate>
+  </div>
+</template>
+
+<script>
+import MSearch from '@/components/MSearch'
+import MTable from '@/components/List/table.vue'
+import BackupDetails from './backupDetails.vue'
+import BackupCreate from './backupCreate.vue'
+import {
+  getPgBackupList
+} from '@/api/dataChart'
+export default {
+  name: 'pgBackup',
+  components: { MSearch, MTable, BackupDetails, BackupCreate },
+  data () {
+    return {
+      loading: false,
+      show: false,
+      searchValues: {},
+      searchItems: [
+        {
+          type: 'autocomplete',
+          label: '备份目录',
+          key: 'global_only',
+          items: [
+            { label: '仅查询全局备份目录', value: true },
+            { label: '不限备份目录', value: false }
+          ]
+        },
+        {
+          type: 'text',
+          label: '任务id',
+          key: 'task_id'
+        }
+      ],
+      queryData: {
+        name: null
+      },
+      headers: [
+        { text: '任务ID', align: 'start', value: 'task_id' },
+        { text: '类型', align: 'center', value: 'type' },
+        { text: '路径', align: 'start', value: 'relative_path' },
+        { text: '操作', value: 'actions' }
+      ],
+      items: [],
+      orders: [],
+      pageInfo: {
+        size: 10,
+        current: 1
+      },
+      total: 0
+    }
+  },
+  created () {
+    this.init()
+  },
+  methods: {
+    async init () {
+      this.loading = true
+      try {
+        const { data } = await getPgBackupList(this.searchValues)
+        this.items = data.backup_locations
+        this.total = data.summary.total_locations
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.loading = false
+      }
+    },
+    handleSearch (val) {
+      Object.assign(this.queryData, val)
+      this.pageInfo.current = 1
+      this.init()
+    },
+    handleAdd (item) {
+      this.$refs.backupCreateRefs.open(item)
+    },
+    handleDelete (ids) {
+      if (Array.isArray(ids) && !ids.length) return this.$snackbar.warning('请选择要删除的选项')
+      this.$confirm('提示', '是否删除该选项')
+        .then(async () => {
+          try {
+            this.$snackbar.success('删除成功')
+            this.init()
+          } catch (error) {
+            this.$snackbar.error('删除失败')
+          }
+        })
+    },
+    pageHandleChange (page) {
+      this.pageInfo.current = page
+      this.init()
+    },
+    handleSort (val) {
+      this.orders = val
+      this.init()
+    },
+    handleDetails (item) {
+      this.$refs.backupDetailsRefs.open(item)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 6 - 9
src/views/modelSystem/modelTrain/index.vue

@@ -20,7 +20,6 @@
       <template #actions="{ item }">
         <template v-if="item.directory_exists">
           <v-btn color="success" text @click="handleExecute(item.task_id)">执行</v-btn>
-          <!-- <v-btn color="success" text @click="handleExecuteStep(item.task_id)">分步执行</v-btn> -->
           <v-btn color="primary" text @click="handleFindExecute(item.task_id)">执行进度</v-btn>
           <v-btn color="primary" text @click="handleTable(item.task_id)">表名清单</v-btn>
           <v-btn color="primary" text @click="handleFiles(item.task_id)">任务文件</v-btn>
@@ -37,7 +36,7 @@
     <ModelTrainTable ref="modelTrainTableRefs"></ModelTrainTable>
     <ModelTrainLog ref="modelTrainLogRefs"></ModelTrainLog>
     <ModelTrainFiles ref="modelTrainFilesRefs"></ModelTrainFiles>
-    <ModelTrainStep ref="modelTrainStepRefs"></ModelTrainStep>
+    <!-- <ModelTrainStep ref="modelTrainStepRefs"></ModelTrainStep> -->
   </div>
 </template>
 
@@ -49,7 +48,6 @@ import ModelTrainStatus from './modelTrainStatus.vue'
 import ModelTrainTable from './modelTrainTable.vue'
 import ModelTrainLog from './modelTrainLog.vue'
 import ModelTrainFiles from './modelTrainFiles.vue'
-import ModelTrainStep from './modelTrainStep.vue'
 import {
   // createDataTasks,
   executeDataTasks,
@@ -65,8 +63,7 @@ export default {
     ModelTrainStatus,
     ModelTrainTable,
     ModelTrainLog,
-    ModelTrainFiles,
-    ModelTrainStep
+    ModelTrainFiles
   },
   data () {
     return {
@@ -81,6 +78,7 @@ export default {
         task_name: null
       },
       headers: [
+        { text: '任务ID', align: 'start', value: 'task_id' },
         { text: '任务名称', align: 'start', value: 'task_name' },
         { text: '数据库名称', align: 'start', value: 'db_name' },
         { text: '上下文', align: 'start', value: 'business_context' },
@@ -146,16 +144,15 @@ export default {
     async handleExecute (id) {
       try {
         const { data } = await executeDataTasks(id, {
-          execution_mode: 'complete'
+          execution_mode: 'complete',
+          backup_vector_tables: false, // 备份vector表数据
+          truncate_vector_tables: false // 清空vector表数据(自动启用备份)
         })
         this.$snackbar.success(data.response)
       } catch (error) {
         this.$snackbar.error(error)
       }
     },
-    handleExecuteStep (id) {
-      this.$refs.modelTrainStepRefs.open(id)
-    },
     handleDelete (ids) {
       if (Array.isArray(ids) && !ids.length) return this.$snackbar.warning('请选择要删除的选项')
       this.$confirm('提示', '是否删除该选项')

+ 15 - 3
src/views/modelSystem/modelTrain/modelTrainStatus.vue

@@ -27,7 +27,16 @@
                 <v-chip :color="statusList[status.value]">{{ status.value }}</v-chip>
               </div>
               <div class="text-center mt-3">
-                <v-btn color="primary" :loading="status.loading" @click="handleRun(status)">重新执行</v-btn>
+                <v-btn color="primary" :loading="status.loading" @click="handleRun(status)">
+                  <v-icon left>mdi-play</v-icon>
+                  重新执行
+                </v-btn>
+              </div>
+              <div class="text-center mt-3">
+                <v-btn :loading="status.loading" @click="handleRun(status, true)">
+                  <v-icon left>mdi-play</v-icon>
+                  测试执行
+                </v-btn>
               </div>
             </v-stepper-step>
 
@@ -84,12 +93,15 @@ export default {
         this.loading = false
       }
     },
-    async handleRun (item) {
+    async handleRun (item, test = false) {
       item.loading = true
       try {
         await executeDataTasks(this.id, {
           execution_mode: 'step',
-          step_name: item.key
+          step_name: item.key,
+          backup_vector_tables: true,
+          truncate_vector_tables: true,
+          skip_training: test
         })
         this.$snackbar.success('任务已启动')
         this.getStatus()

+ 0 - 90
src/views/modelSystem/modelTrain/modelTrainStep.vue

@@ -1,90 +0,0 @@
-<template>
-  <m-dialog title="分步执行" :visible.sync="show" :footer="false">
-    <v-stepper v-model="stepValue">
-      <v-stepper-header>
-        <template v-for="(step, index) in steps">
-          <v-stepper-step
-            :key="step.value"
-            :step="index"
-            editable
-          >
-            {{ step.text }}
-          </v-stepper-step>
-
-          <v-divider
-            v-if="steps.length - 1 !== index"
-            :key="step.value"
-          ></v-divider>
-        </template>
-      </v-stepper-header>
-
-      <v-stepper-items>
-        <v-stepper-content
-          v-for="(step, index) in steps"
-          :key="step.value"
-          :step="index"
-        >
-          <v-card
-            class="mb-12"
-            color="grey lighten-1"
-            height="200px"
-          ></v-card>
-
-          <div class="text-center">
-            <v-btn
-              color="success"
-              @click="handleRunStep(index)"
-            >
-              <v-icon left>mdi-send</v-icon>
-              开始执行
-            </v-btn>
-          </div>
-        </v-stepper-content>
-      </v-stepper-items>
-    </v-stepper>
-  </m-dialog>
-</template>
-
-<script>
-import MDialog from '@/components/Dialog'
-import {
-  executeDataTasks
-} from '@/api/dataChart'
-export default {
-  name: 'modelTrainStep',
-  components: {
-    MDialog
-  },
-  data () {
-    return {
-      show: false,
-      stepValue: 0,
-      steps: [
-        { text: 'DDL/MD文档生成', value: 'ddl_generation' },
-        { text: 'Question-SQL对生成', value: 'qa_generation' },
-        { text: 'SQL验证和修复', value: 'sql_validation' },
-        { text: '训练数据加载', value: 'training_load' }
-      ],
-      id: null
-    }
-  },
-  methods: {
-    open (id) {
-      this.id = id
-      this.show = true
-    },
-    async handleRunStep (index) {
-      try {
-        const { data } = await executeDataTasks(this.id, {})
-        console.log(data)
-      } catch (error) {
-        this.$snackbar.error(error)
-      }
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-
-</style>

+ 0 - 102
src/views/modelSystem/pgVector/index.vue

@@ -1,102 +0,0 @@
-<template>
-  <div class="pa-3 white">
-    <m-filter :option="filter" @search="handleSearch" />
-    <m-table
-      class="mt-3"
-      :loading="loading"
-      :headers="headers"
-      :items="items"
-      :total="total"
-      :page-info="pageInfo"
-      @add="handleAdd"
-      @edit="handleEdit"
-      @delete="handleDelete"
-      @pageHandleChange="pageHandleChange"
-      @sort="handleSort"
-    >
-    </m-table>
-  </div>
-</template>
-
-<script>
-import MFilter from '@/components/Filter'
-import MTable from '@/components/List/table.vue'
-export default {
-  name: 'pgVector',
-  components: { MFilter, MTable },
-  data () {
-    return {
-      loading: false,
-      show: false,
-      filter: {
-        list: [
-          { type: 'textField', value: '', label: '名称', key: 'name' },
-          { type: 'autocomplete', value: null, label: '选择', key: 'type', items: [] }
-        ]
-      },
-      queryData: {
-        name: null
-      },
-      headers: [
-        { text: '名称', align: 'start', value: 'name' }
-      ],
-      itemData: {},
-      items: [],
-      orders: [],
-      pageInfo: {
-        size: 10,
-        current: 1
-      },
-      total: 0
-    }
-  },
-  computed: {
-    title () {
-      return Object.keys(this.itemData).length ? '编辑' : '新增'
-    }
-  },
-  created () {
-    this.init()
-  },
-  methods: {
-    async init () {},
-    handleSearch (val) {
-      Object.assign(this.queryData, val)
-      this.pageInfo.current = 1
-      this.init()
-    },
-    handleAdd () {
-      this.itemData = {}
-      this.show = true
-    },
-    async handleEdit (item) {
-      this.itemData = item
-      this.show = true
-    },
-    handleDelete (ids) {
-      if (Array.isArray(ids) && !ids.length) return this.$snackbar.warning('请选择要删除的选项')
-      this.$confirm('提示', '是否删除该选项')
-        .then(async () => {
-          try {
-            this.$snackbar.success('删除成功')
-            this.init()
-          } catch (error) {
-            this.$snackbar.error('删除失败')
-          }
-        })
-    },
-    pageHandleChange (page) {
-      this.pageInfo.current = page
-      this.init()
-    },
-    handleSort (val) {
-      this.orders = val
-      this.init()
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-
-</style>