Quellcode durchsuchen

更新数据订单API,新增更新和设置就绪状态功能;优化订单详情和创建对话框,支持编辑和状态判断;调整数据产品预览对话框,增加全选功能

Xiao_123 vor 3 Wochen
Ursprung
Commit
debd1850cc

+ 18 - 6
src/api/dataOrder.js

@@ -8,7 +8,7 @@ const dataOrder = {
 
   // 获取数据订单详情
   getOrderDetail (orderId) {
-    return http.get(`/dataservice/orders/${orderId}`)
+    return http.get(`/dataservice/orders/${orderId}/detail`)
   },
 
   // 创建数据订单
@@ -16,6 +16,11 @@ const dataOrder = {
     return http.post('/dataservice/neworder', data)
   },
 
+  // 更新数据订单
+  updateOrder (orderId, data) {
+    return http.put(`/dataservice/orders/${orderId}/update`, data)
+  },
+
   // 分析数据订单
   analyzeOrder (orderId) {
     return http.post(`/dataservice/orders/${orderId}/analyze`)
@@ -36,18 +41,25 @@ const dataOrder = {
     })
   },
 
-  // 完成订单
-  completeOrder (orderId, options = {}) {
-    return http.post(`/dataservice/orders/${orderId}/complete`, {
+  // 设置数据产品就绪状态(数据工厂回调)
+  setOrderOnboard (orderId, options = {}) {
+    return http.post(`/dataservice/orders/${orderId}/onboard`, {
       product_id: options.productId,
       dataflow_id: options.dataflowId,
-      processed_by: options.processedBy || 'system'
+      processed_by: options.processedBy || 'n8n-workflow'
+    })
+  },
+
+  // 完成订单
+  completeOrder (orderId, processedBy = 'user') {
+    return http.post(`/dataservice/orders/${orderId}/complete`, {
+      processed_by: processedBy
     })
   },
 
   // 删除订单
   deleteOrder (orderId) {
-    return http.del(`/dataservice/orders/${orderId}`)
+    return http.put(`/dataservice/orders/${orderId}/delete`)
   }
 }
 

+ 10 - 0
src/views/dataGovernance/dataProcess/index.vue

@@ -19,6 +19,12 @@
           {{ item.status === 'active' ? '启用' : '禁用' }}
         </v-chip>
       </template>
+      <template #created_at="{ item }">
+        <span>{{ formatDateTime(item.created_at) }}</span>
+      </template>
+      <template #updated_at="{ item }">
+        <span>{{ formatDateTime(item.updated_at) }}</span>
+      </template>
       <template #actions="{ item }">
         <v-btn color="primary" text class="mr-2" @click="onEdit(item)">编辑</v-btn>
         <!-- <v-btn color="success" text class="mr-2"  @click="onRun(item)">运行</v-btn> -->
@@ -37,6 +43,7 @@ import MTable from '@/components/List/table.vue'
 import MDialog from '@/components/Dialog'
 import EditPage from './components/edit'
 import { api } from '@/api/dataGovernance'
+import { formatDate } from '@/utils/date'
 export default {
   name: 'dataProcess',
   components: { MFilter, MTable, MDialog, EditPage },
@@ -139,6 +146,9 @@ export default {
     handleSort (val) {
       this.orders = val
       this.init()
+    },
+    formatDateTime (dateStr) {
+      return formatDate(dateStr)
     }
   }
 }

+ 1 - 1
src/views/dataGovernance/metadata/components/edit.vue

@@ -46,7 +46,7 @@ export default {
       formValues: {
         name_zh: null,
         name_en: null,
-        category: null,
+        category: 'DataOps',
         alias: null,
         affiliation: null,
         data_type: null,

+ 101 - 80
src/views/dataService/dataOrder/components/OrderDetailDialog.vue

@@ -1,15 +1,14 @@
 <template>
-  <MDialog
-    :visible="visible"
-    title="订单详情"
-    widthType="1"
-    :footer="false"
-    @update:visible="handleVisibleChange"
-  >
     <div v-loading="detailLoading">
       <v-card v-if="order" outlined>
         <v-card-text>
           <v-row>
+            <v-col cols="12" md="6">
+              <div class="detail-item">
+                <span class="label">标题:</span>
+                <span class="value">{{ order.title }}</span>
+              </div>
+            </v-col>
             <v-col cols="12" md="6">
               <div class="detail-item">
                 <span class="label">订单编号:</span>
@@ -22,16 +21,28 @@
                 <v-chip :color="getStatusColor(order.status)" small>{{ order.status_label }}</v-chip>
               </div>
             </v-col>
-            <v-col cols="12">
+            <v-col cols="12" md="6">
               <div class="detail-item">
-                <span class="label">标题:</span>
-                <span class="value">{{ order.title }}</span>
+                <span class="label">需求描述:</span>
+                <div class="value description-text">{{ order.description }}</div>
               </div>
             </v-col>
-            <v-col cols="12">
+
+            <!-- 连通性分析 -->
+            <v-col cols="12" md="6">
               <div class="detail-item">
-                <span class="label">需求描述:</span>
-                <div class="value description-text">{{ order.description }}</div>
+                <span class="label">图谱连通性:</span>
+                <span class="value">
+                  <v-chip v-if="order.can_connect === true" color="success" small>可连通</v-chip>
+                  <v-chip v-else-if="order.can_connect === false" color="error" small>不可连通</v-chip>
+                  <span v-else>未分析</span>
+                </span>
+              </div>
+            </v-col>
+            <v-col v-if="order.extraction_purpose" cols="12" md="6">
+              <div class="detail-item">
+                <span class="label">数据用途:</span>
+                <span class="value">{{ order.extraction_purpose }}</span>
               </div>
             </v-col>
 
@@ -39,7 +50,7 @@
             <v-col v-if="order.extracted_domains && order.extracted_domains.length" cols="12">
               <div class="detail-item">
                 <span class="label">提取的业务领域:</span>
-                <div class="value">
+                <span class="value">
                   <v-chip
                     v-for="domain in order.extracted_domains"
                     :key="domain"
@@ -48,40 +59,21 @@
                   >
                     {{ domain }}
                   </v-chip>
-                </div>
+                </span>
               </div>
             </v-col>
             <v-col v-if="order.extracted_fields && order.extracted_fields.length" cols="12">
               <div class="detail-item">
                 <span class="label">提取的数据字段:</span>
-                <div class="value">
+                <span class="value">
                   <v-chip
                     v-for="field in order.extracted_fields"
                     :key="field"
                     small
-                    color="info"
                     class="mr-2 mb-2"
                   >
                     {{ field }}
                   </v-chip>
-                </div>
-              </div>
-            </v-col>
-            <v-col v-if="order.extraction_purpose" cols="12">
-              <div class="detail-item">
-                <span class="label">数据用途:</span>
-                <span class="value">{{ order.extraction_purpose }}</span>
-              </div>
-            </v-col>
-
-            <!-- 连通性分析 -->
-            <v-col cols="12">
-              <div class="detail-item">
-                <span class="label">图谱连通性:</span>
-                <span class="value">
-                  <v-chip v-if="order.can_connect === true" color="success" small>可连通</v-chip>
-                  <v-chip v-else-if="order.can_connect === false" color="error" small>不可连通</v-chip>
-                  <span v-else>未分析</span>
                 </span>
               </div>
             </v-col>
@@ -107,14 +99,14 @@
             </v-col>
             <v-col v-if="order.result_dataflow_id" cols="12" md="6">
               <div class="detail-item">
-                <span class="label">生成的数据流:</span>
+                <span class="label">生成的数据流:</span>
                 <v-btn
                   text
                   color="primary"
                   small
                   @click="goToDataflow(order.result_dataflow_id)"
                 >
-                  查看数据流 #{{ order.result_dataflow_id }}
+                  查看数据流 #{{ order.result_dataflow_id }}
                 </v-btn>
               </div>
             </v-col>
@@ -150,60 +142,43 @@
 
       <!-- 操作按钮 -->
       <div v-if="order" class="mt-4 d-flex justify-end">
-        <v-btn v-if="order.status === 'pending'" color="primary" @click="handleAnalyze">开始分析</v-btn>
-        <v-btn v-if="order.status === 'manual_review'" color="success" class="ml-2" @click="handleApprove">审批通过</v-btn>
-        <v-btn v-if="order.status === 'manual_review'" color="error" class="ml-2" @click="handleReject">驳回</v-btn>
+        <!-- 待处理/待人工处理/待补充:可编辑和分析 -->
+        <v-btn v-if="canAnalyze(order.status)" color="primary" :loading="analyzeLoading" class="ml-2 half-button" @click="handleAnalyze">分析</v-btn>
+        <!-- 待审批/待人工处理:可审批和驳回 -->
+        <v-btn v-if="canApprove(order.status)" color="success" :loading="approveLoading" class="ml-2 half-button" @click="handleApprove">审批通过</v-btn>
+        <v-btn v-if="canApprove(order.status)" color="error" :loading="rejectLoading" class="ml-2 half-button" @click="handleReject">驳回</v-btn>
+        <!-- 数据产品就绪:可完成 -->
+        <v-btn v-if="order.status === 'onboard'" color="success" :loading="completeLoading" class="ml-2 half-button" @click="handleComplete">完成</v-btn>
       </div>
     </div>
-  </MDialog>
 </template>
 
 <script>
-import MDialog from '@/components/Dialog'
 import { api } from '@/api/dataOrder'
 import { formatDate } from '@/utils/date'
 
 export default {
   name: 'OrderDetailDialog',
-  components: {
-    MDialog
-  },
   props: {
-    visible: {
-      type: Boolean,
-      default: false
-    },
     orderId: {
       type: Number,
       default: null
-    },
-    loading: {
-      type: Boolean,
-      default: false
     }
   },
   data () {
     return {
       order: null,
-      detailLoading: false
+      detailLoading: false,
+      analyzeLoading: false,
+      approveLoading: false,
+      rejectLoading: false,
+      completeLoading: false
     }
   },
-  watch: {
-    visible (val) {
-      if (val && this.orderId) {
-        this.fetchDetail()
-      }
-    },
-    orderId (val) {
-      if (val && this.visible) {
-        this.fetchDetail()
-      }
-    }
+  created () {
+    this.fetchDetail()
   },
   methods: {
-    handleVisibleChange (val) {
-      this.$emit('update:visible', val)
-    },
     async fetchDetail () {
       if (!this.orderId) return
       this.detailLoading = true
@@ -216,34 +191,80 @@ export default {
         this.detailLoading = false
       }
     },
-    handleAnalyze () {
-      this.$emit('analyze', this.orderId)
-      this.handleVisibleChange(false)
+    // 分析
+    async handleAnalyze () {
+      this.analyzeLoading = true
+      try {
+        const { data } = await api.analyzeOrder(this.orderId)
+        if (data.can_connect) {
+          this.$snackbar.success('分析完成,实体可连通!')
+        } else {
+          this.$snackbar.warning('分析完成,部分实体无法连通')
+        }
+        this.$emit('refresh')
+        this.$emit('close')
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.analyzeLoading = false
+      }
     },
-    handleApprove () {
-      this.$emit('approve', this.orderId)
+    // 审批通过
+    async handleApprove () {
+      this.approveLoading = true
+      try {
+        await api.approveOrder(this.orderId)
+        this.$snackbar.success('审批通过')
+        this.$emit('refresh')
+        this.$emit('close')
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.approveLoading = false
+      }
     },
-    handleReject () {
+    // 驳回
+    async handleReject () {
       this.$emit('reject', this.orderId)
     },
+    // 完成
+    async handleComplete () {
+      this.completeLoading = true
+      try {
+        await api.completeOrder(this.orderId)
+        this.$snackbar.success('完成成功')
+        this.$emit('refresh')
+        this.$emit('close')
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.completeLoading = false
+      }
+    },
+    // 状态判断函数
+    canAnalyze (status) {
+      return ['pending', 'manual_review', 'need_supplement'].includes(status)
+    },
+    canApprove (status) {
+      return ['pending_approval', 'manual_review'].includes(status)
+    },
     goToProduct (productId) {
-      // TODO: 跳转到数据产品详情页
-      this.$snackbar.info('跳转到数据产品详情页')
+      this.$router.push('/dataService/dataProduct')
     },
     goToDataflow (dataflowId) {
-      // TODO: 跳转到数据流详情页
-      this.$snackbar.info('跳转到数据流详情页')
+      this.$router.push('/data-governance/data-process')
     },
     getStatusColor (status) {
       const colors = {
         pending: 'info',
         analyzing: 'warning',
+        pending_approval: 'primary',
         processing: 'primary',
+        onboard: 'success',
         completed: 'success',
         rejected: 'error',
         need_supplement: 'warning',
-        manual_review: 'warning',
-        updated: 'info'
+        manual_review: 'warning'
       }
       return colors[status] || 'info'
     },

+ 22 - 50
src/views/dataService/dataOrder/components/CreateOrderDialog.vue → src/views/dataService/dataOrder/components/OrderForm.vue

@@ -1,30 +1,19 @@
 <template>
-  <MDialog
-    :visible="visible"
-    title="创建数据订单"
-    widthType="0"
-    @update:visible="handleVisibleChange"
-    @submit="handleSubmit"
-  >
-    <MForm ref="form" :items="formItems" v-model="formValues"></MForm>
-  </MDialog>
+  <MForm ref="form" :items="formItems" v-model="formValues"></MForm>
 </template>
 
 <script>
-import MDialog from '@/components/Dialog'
 import MForm from '@/components/MForm'
-import { api } from '@/api/dataOrder'
 
 export default {
   name: 'CreateOrderDialog',
   components: {
-    MDialog,
     MForm
   },
   props: {
-    visible: {
-      type: Boolean,
-      default: false
+    itemData: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -32,7 +21,10 @@ export default {
       formValues: {
         title: '',
         description: '',
-        created_by: ''
+        created_by: '',
+        extracted_domains: [],
+        extracted_fields: [],
+        extraction_purpose: ''
       }
     }
   },
@@ -80,48 +72,28 @@ export default {
       ]
     }
   },
-  watch: {
-    visible (val) {
-      if (val) {
-        this.resetForm()
+  created () {
+    if (this.itemData && this.itemData.id) {
+      this.formValues = {
+        title: this.itemData.title || '',
+        description: this.itemData.description || '',
+        extracted_domains: this.itemData.extracted_domains || [],
+        extracted_fields: this.itemData.extracted_fields || [],
+        extraction_purpose: this.itemData.extraction_purpose || ''
       }
     }
   },
   methods: {
-    handleVisibleChange (val) {
-      this.$emit('update:visible', val)
-    },
-    resetForm () {
-      this.formValues = {
-        title: '',
-        description: '',
-        created_by: ''
-      }
-      this.$nextTick(() => {
-        if (this.$refs.form) {
-          this.$refs.form.resetValidation()
-        }
-      })
-    },
-    async handleSubmit () {
+    getValue () {
       if (!this.$refs.form.validate()) {
         return
       }
-      try {
-        const data = {
-          title: this.formValues.title,
-          description: this.formValues.description
-        }
-        if (this.formValues.created_by) {
-          data.created_by = this.formValues.created_by
-        }
-        await api.createOrder(data)
-        this.$snackbar.success('订单创建成功')
-        this.$emit('update:visible', false)
-        this.$emit('success')
-      } catch (error) {
-        this.$snackbar.error(error)
+      const params = {
+        title: this.formValues.title,
+        description: this.formValues.description,
+        created_by: this.formValues.created_by
       }
+      return this.itemData.id ? this.formValues : params
     }
   }
 }

+ 127 - 70
src/views/dataService/dataOrder/index.vue

@@ -29,36 +29,59 @@
       </template>
       <template #actions="{ item }">
         <v-btn text color="primary" @click="handleDetail(item)">详情</v-btn>
-        <v-btn v-if="item.status === 'processing'" text color="primary" @click="handleComplete(item)">完成</v-btn>
-        <v-btn v-if="item.status === 'pending'" text color="success" @click="handleAnalyze(item)">分析</v-btn>
-        <v-btn v-if="item.status === 'manual_review'" text color="success" @click="handleApprove(item)">审批</v-btn>
-        <v-btn v-if="item.status === 'manual_review'" text color="warning" @click="handleReject(item)">驳回</v-btn>
+        <v-btn
+          v-if="canEdit(item.status)"
+          text
+          color="primary"
+          @click="handleAdd(item)"
+        >
+          编辑
+        </v-btn>
         <v-btn v-if="canDelete(item.status)" text color="error" @click="handleDelete(item)">删除</v-btn>
       </template>
     </table-list>
 
-    <!-- 创建订单对话框 -->
-    <create-order-dialog :visible.sync="createDialog.show" @success="handleCreateSuccess" />
+    <!-- 创建、编辑订单对话框 -->
+    <edit-dialog
+      :visible.sync="createDialog.show"
+      :title="createDialog.itemData.id ? '编辑数据订单' : '创建数据订单'"
+      :footer="true"
+      @submit="handleCreateSuccess"
+      @close="handleCloseCreateDialog"
+    >
+      <OrderForm v-if="createDialog.show" ref="editFormRef" :item-data="createDialog.itemData" />
+    </edit-dialog>
 
     <!-- 订单详情对话框 -->
-    <order-detail-dialog
+    <edit-dialog
       :visible.sync="detailDialog.show"
-      :order-id="detailDialog.orderId"
-      @refresh="init"
-      @analyze="handleAnalyzeFromDetail"
-      @approve="handleApproveFromDetail"
-      @reject="handleRejectFromDetail"
-    />
+      title="订单详情"
+      :footer="false"
+      @close="detailDialog.show = false"
+    >
+      <OrderDetailDialog
+        v-if="detailDialog.show"
+        :order-id="detailDialog.orderId"
+        @refresh="init"
+        @reject="handleRejectFromDetail"
+        @close="detailDialog.show = false"
+      />
+    </edit-dialog>
 
     <!-- 驳回订单对话框 -->
-    <reject-order-dialog :visible.sync="rejectDialog.show" :order-id="rejectDialog.orderId" @success="init" />
+    <reject-order-dialog
+      :visible.sync="rejectDialog.show"
+      :order-id="rejectDialog.orderId"
+      @success="handleRejectSuccess"
+    />
   </div>
 </template>
 
 <script>
+import EditDialog from '@/components/Dialog'
 import MFilter from '@/components/Filter'
 import TableList from '@/components/List/table'
-import CreateOrderDialog from './components/CreateOrderDialog'
+import OrderForm from './components/OrderForm'
 import OrderDetailDialog from './components/OrderDetailDialog'
 import RejectOrderDialog from './components/RejectOrderDialog'
 import { api } from '@/api/dataOrder'
@@ -69,9 +92,10 @@ export default {
   components: {
     MFilter,
     TableList,
-    CreateOrderDialog,
+    OrderForm,
     OrderDetailDialog,
-    RejectOrderDialog
+    RejectOrderDialog,
+    EditDialog
   },
   data () {
     return {
@@ -89,12 +113,13 @@ export default {
               { label: '全部', value: null },
               { label: '待处理', value: 'pending' },
               { label: '分析中', value: 'analyzing' },
+              { label: '待审批', value: 'pending_approval' },
               { label: '加工中', value: 'processing' },
+              { label: '数据产品就绪', value: 'onboard' },
               { label: '已完成', value: 'completed' },
               { label: '已驳回', value: 'rejected' },
               { label: '待补充', value: 'need_supplement' },
-              { label: '待人工处理', value: 'manual_review' },
-              { label: '已更新', value: 'updated' }
+              { label: '待人工处理', value: 'manual_review' }
             ],
             itemText: 'label',
             itemValue: 'value'
@@ -118,7 +143,8 @@ export default {
       },
       query: {},
       createDialog: {
-        show: false
+        show: false,
+        itemData: {}
       },
       detailDialog: {
         show: false,
@@ -166,9 +192,6 @@ export default {
       this.pageInfo.current = index
       this.init()
     },
-    handleAdd () {
-      this.createDialog.show = true
-    },
     // 订单详情
     handleDetail (item) {
       this.detailDialog.orderId = item.id
@@ -176,8 +199,6 @@ export default {
     },
     // 分析订单
     async handleAnalyze (item) {
-      const analyzingItem = item
-      analyzingItem.analyzing = true
       try {
         const { data } = await api.analyzeOrder(item.id)
         if (data.can_connect) {
@@ -185,31 +206,34 @@ export default {
         } else {
           this.$snackbar.warning('分析完成,部分实体无法连通')
         }
+        this.detailDialog.show = false
         this.init()
       } catch (error) {
         this.$snackbar.error(error)
-      } finally {
-        analyzingItem.analyzing = false
       }
     },
     // 审批订单
-    async handleApprove (item) {
-      try {
-        await this.$confirm('审批确认', `确定要审批通过订单 "${item.title}" 吗?`)
-        await api.approveOrder(item.id)
-        this.$snackbar.success('审批通过')
-        this.init()
-      } catch (error) {
-        if (error !== 'cancel') {
-          this.$snackbar.error(error)
-        }
-      }
-    },
+    // async handleApprove (item) {
+    //   try {
+    //     await api.approveOrder(item.id)
+    //     this.$snackbar.success('审批通过')
+    //     this.detailDialog.show = false
+    //     this.init()
+    //   } catch (error) {
+    //     if (error !== 'cancel') {
+    //       this.$snackbar.error(error)
+    //     }
+    //   }
+    // },
     // 驳回订单
     handleReject (item) {
       this.rejectDialog.orderId = item.id
       this.rejectDialog.show = true
     },
+    handleRejectSuccess () {
+      this.init()
+      this.detailDialog.show = false
+    },
     // 删除订单
     async handleDelete (item) {
       try {
@@ -223,24 +247,45 @@ export default {
         }
       }
     },
-    // 创建订单成功
-    handleCreateSuccess () {
-      this.init()
-    },
-    // 分析订单
-    handleAnalyzeFromDetail (orderId) {
-      const item = this.items.find(i => i.id === orderId)
-      if (item) {
-        this.handleAnalyze(item)
+    // 创建、编辑订单
+    async handleCreateSuccess () {
+      const params = this.$refs.editFormRef.getValue()
+      if (!params) {
+        this.$snackbar.warning('请填写完整订单信息')
+        return
       }
-    },
-    // 审批订单
-    handleApproveFromDetail (orderId) {
-      const item = this.items.find(i => i.id === orderId)
-      if (item) {
-        this.handleApprove(item)
+      try {
+        if (this.createDialog.itemData.id) {
+          await api.updateOrder(this.createDialog.itemData.id, params)
+          this.$snackbar.success('订单更新成功')
+        } else {
+          await api.createOrder(params)
+          this.$snackbar.success('订单创建成功')
+        }
+        this.handleCloseCreateDialog()
+        this.init()
+      } catch (error) {
+        this.$snackbar.error(error)
       }
     },
+    handleCloseCreateDialog () {
+      this.createDialog.show = false
+      this.createDialog.itemData = {}
+    },
+    // 分析订单
+    // handleAnalyzeFromDetail (orderId) {
+    //   const item = this.items.find(i => i.id === orderId)
+    //   if (item) {
+    //     this.handleAnalyze(item)
+    //   }
+    // },
+    // 审批订单
+    // handleApproveFromDetail (orderId) {
+    //   const item = this.items.find(i => i.id === orderId)
+    //   if (item) {
+    //     this.handleApprove(item)
+    //   }
+    // },
     // 驳回订单
     handleRejectFromDetail (orderId) {
       const item = this.items.find(i => i.id === orderId)
@@ -249,34 +294,46 @@ export default {
       }
     },
     // 完成订单
-    async handleComplete (item) {
-      try {
-        await api.completeOrder(item.id, {
-          productId: item.result_product_id,
-          dataflowId: item.result_dataflow_id,
-          processedBy: item.created_by
-        })
-        this.$snackbar.success('操作成功')
-        this.init()
-      } catch (error) {
-        if (error !== 'cancel') {
-          this.$snackbar.error(error)
-        }
-      }
+    // handleCompleteFromDetail (orderId) {
+    //   const item = this.items.find(i => i.id === orderId)
+    //   if (item) {
+    //     this.handleComplete(item)
+    //   }
+    // },
+    handleAdd (item = {}) {
+      this.createDialog.show = true
+      this.createDialog.itemData = item
     },
+    // 完成订单
+    // async handleComplete (item) {
+    //   try {
+    //     await api.completeOrder(item.id)
+    //     this.$snackbar.success('订单已完成')
+    //     this.init()
+    //   } catch (error) {
+    //     if (error !== 'cancel') {
+    //       this.$snackbar.error(error)
+    //     }
+    //   }
+    // },
     getStatusColor (status) {
       const colors = {
         pending: 'info',
         analyzing: 'warning',
+        pending_approval: 'primary',
         processing: 'primary',
+        onboard: 'success',
         completed: 'success',
         rejected: 'error',
         need_supplement: 'warning',
-        manual_review: 'warning',
-        updated: 'info'
+        manual_review: 'warning'
       }
       return colors[status] || 'info'
     },
+    // 状态判断函数
+    canEdit (status) {
+      return ['pending', 'manual_review', 'need_supplement'].includes(status)
+    },
     canDelete (status) {
       return ['pending', 'completed', 'rejected'].includes(status)
     },

+ 78 - 26
src/views/dataService/dataProduct/components/PreviewDialog.vue

@@ -43,36 +43,48 @@
               (+{{ selectedColumns.length - 1 }} others)
             </span>
           </template>
+          <template v-slot:prepend-item>
+            <v-list-item
+              ripple
+              @mousedown.prevent
+              @click="handleSelectAll"
+            >
+              <v-list-item-action>
+                <v-icon :color="selectedColumns.length > 0 ? 'primary' : ''">
+                  {{ icon }}
+                </v-icon>
+              </v-list-item-action>
+              <v-list-item-content>
+                <v-list-item-title>
+                  全选
+                </v-list-item-title>
+              </v-list-item-content>
+            </v-list-item>
+            <v-divider class="mt-2"></v-divider>
+          </template>
         </v-select>
       </div>
-      <v-simple-table v-if="displayColumns && displayColumns.length" fixed-header height="400px">
-        <template v-slot:default>
-          <thead>
-            <tr>
-              <th v-for="col in displayColumns" :key="col.name" class="text-left">
-                <div>
-                  <div>{{ col.name }}</div>
-                </div>
-              </th>
-              <th class="text-center" width="100">操作</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr v-for="(row, index) in previewData.data" :key="index">
-              <td v-for="col in displayColumns" :key="col.name">
-                {{ row[col.name] }}
-              </td>
-              <td class="text-center">
-                <v-btn text color="primary" @click="handleVisualize(row)">可视化</v-btn>
-              </td>
-            </tr>
-          </tbody>
+      <TableList
+        v-if="displayColumns && displayColumns.length"
+        :headers="tableHeaders"
+        :items="previewData.data"
+        :height="400"
+        :show-select="false"
+        :is-tools="false"
+        :show-page="false"
+        elevation="0"
+        :disable-sort="true"
+        :items-per-page="-1"
+        :loading="false"
+      >
+        <template #actions="{ item }">
+          <v-btn text color="primary" @click="handleVisualize(item)">可视化</v-btn>
         </template>
-      </v-simple-table>
+      </TableList>
       <div v-else class="text-center pa-10 text--secondary">
         暂无数据
       </div>
-      <div v-if="previewData.preview_count > 0" class="text-end text--secondary">
+      <div v-if="previewData.preview_count > 0 && selectedColumns.length > 0" class="text-end text--secondary">
         <span>已加载 {{ previewData.preview_count || 0 }} / {{ previewData.total_count || 0 }} 条</span>
       </div>
     </div>
@@ -112,13 +124,15 @@
 <script>
 import MDialog from '@/components/Dialog'
 import mGraph from './mGraph.vue'
+import TableList from '@/components/List/table.vue'
 import { api } from '@/api/dataService'
 
 export default {
   name: 'PreviewDialog',
   components: {
     MDialog,
-    mGraph
+    mGraph,
+    TableList
   },
   props: {
     visible: {
@@ -165,6 +179,34 @@ export default {
         return []
       }
       return this.previewData.columns.filter(col => this.selectedColumns.includes(col.name))
+    },
+    tableHeaders () {
+      const headers = this.displayColumns.map(col => ({
+        text: col.name,
+        value: col.name,
+        align: 'left',
+        sortable: false
+      }))
+      // 添加操作列
+      headers.push({
+        text: '操作',
+        value: 'actions',
+        align: 'center',
+        sortable: false,
+        width: '100px'
+      })
+      return headers
+    },
+    likesAllFruit () {
+      return this.selectedColumns.length === this.previewData.columns.length
+    },
+    likesSomeFruit () {
+      return this.selectedColumns.length > 0 && !this.likesAllFruit
+    },
+    icon () {
+      if (this.likesAllFruit) return 'mdi-close-box'
+      if (this.likesSomeFruit) return 'mdi-minus-box'
+      return 'mdi-checkbox-blank-outline'
     }
   },
   watch: {
@@ -251,9 +293,19 @@ export default {
       // this.graphDialogVisible = true
     },
     handleColumnChange () {
-      // 列选择变化时的处理
       this.$emit('column-change', this.selectedColumns)
     },
+    // 表头项全选/取消全选
+    handleSelectAll () {
+      this.$nextTick(() => {
+        if (this.likesAllFruit) {
+          this.selectedColumns = []
+        } else {
+          this.selectedColumns = this.previewData.columns.map(col => col.name)
+        }
+        this.handleColumnChange()
+      })
+    },
     handleLoadMore () {
       this.$emit('load-more')
     },