Bläddra i källkod

feat: 流程间的实例迁移

wuwenyi 1 vecka sedan
förälder
incheckning
44e8084e2f
2 ändrade filer med 76 tillägg och 1 borttagningar
  1. 4 0
      src/api/flow/instance.ts
  2. 72 1
      src/views/flow/definition/index.vue

+ 4 - 0
src/api/flow/instance.ts

@@ -64,3 +64,7 @@ export function updateInstanceFormData(id: number, formData: Record<string, unkn
     formData: JSON.stringify(formData),
   })
 }
+
+export function migrateInstances(fromDefinitionId: number, toDefinitionId: number): Promise<{ migratedCount: number }> {
+  return request.post('/flow/instance/migrate-all', null, { params: { fromDefinitionId, toDefinitionId } })
+}

+ 72 - 1
src/views/flow/definition/index.vue

@@ -4,7 +4,10 @@
       <template #header>
         <div class="card-header">
           <span>流程管理</span>
-          <el-button type="primary" @click="handleAdd">新增流程</el-button>
+          <div>
+            <el-button type="warning" @click="openMigrateDialog" style="margin-right: 8px;">流程迁移</el-button>
+            <el-button type="primary" @click="handleAdd">新增流程</el-button>
+          </div>
         </div>
       </template>
 
@@ -219,6 +222,31 @@
         <el-button type="primary" @click="saveOptions">保存</el-button>
       </template>
     </el-dialog>
+
+    <!-- 流程迁移弹窗 -->
+    <el-dialog v-model="migrateDialogVisible" title="流程迁移" width="550px">
+      <el-form label-width="100px">
+        <el-form-item label="源流程版本">
+          <el-select v-model="migrateFromId" placeholder="请选择旧版本流程" style="width: 100%" @change="onMigrateFromChange">
+            <el-option v-for="def in migrateDefinitionList" :key="def.id" :label="`${def.name}(v${def.version})${def.status === 0 ? ' [设计]' : def.status === 1 ? ' [已发布]' : ' [已停用]'}`" :value="def.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="目标版本">
+          <el-select v-model="migrateToId" placeholder="请选择新版本流程" style="width: 100%" :disabled="!migrateFromId">
+            <el-option v-for="def in migrateTargetList" :key="def.id" :label="`${def.name}(v${def.version})${def.status === 0 ? ' [设计]' : def.status === 1 ? ' [已发布]' : ' [已停用]'}`" :value="def.id" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <el-alert type="warning" :closable="false" style="margin-bottom: 12px;">
+        <template #title>
+          迁移将把所有运行中的实例从源版本切换到目标版本,节点按名称匹配。不兼容的实例将被跳过。操作不可逆,请确认后执行。
+        </template>
+      </el-alert>
+      <template #footer>
+        <el-button @click="migrateDialogVisible = false">取消</el-button>
+        <el-button type="warning" :disabled="!migrateFromId || !migrateToId" :loading="migrating" @click="confirmMigrate">确认迁移</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -229,6 +257,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
 import { QuestionFilled } from '@element-plus/icons-vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import { listDefinition, getDefinition, updateDefinition, deleteDefinition, publishDefinition, stopDefinition, enableDefinition } from '@/api/flow/definition'
+import { migrateInstances } from '@/api/flow/instance'
 import type { FlowDefinition, FormField, FormFieldOption } from '@/types/flow'
 
 const router = useRouter()
@@ -495,6 +524,48 @@ async function handleDelete(row: FlowDefinition) {
   }
 }
 
+// 流程迁移
+const migrateDialogVisible = ref(false)
+const migrateFromId = ref<number | null>(null)
+const migrateToId = ref<number | null>(null)
+const migrateDefinitionList = ref<FlowDefinition[]>([])
+const migrateTargetList = ref<FlowDefinition[]>([])
+const migrating = ref(false)
+
+function openMigrateDialog() {
+  migrateFromId.value = null
+  migrateToId.value = null
+  migrateTargetList.value = []
+  migrateDefinitionList.value = [...tableData.value]
+  migrateDialogVisible.value = true
+}
+
+function onMigrateFromChange() {
+  migrateToId.value = null
+  const from = migrateDefinitionList.value.find(d => d.id === migrateFromId.value)
+  if (!from) { migrateTargetList.value = []; return }
+  migrateTargetList.value = migrateDefinitionList.value.filter(d => d.code === from.code && d.id !== from.id)
+}
+
+async function confirmMigrate() {
+  if (!migrateFromId.value || !migrateToId.value) return
+  try {
+    await ElMessageBox.confirm(
+      `确认将所有运行中的实例从源版本 v${migrateDefinitionList.value.find(d => d.id === migrateFromId.value)?.version} 迁移到目标版本 v${migrateTargetList.value.find(d => d.id === migrateToId.value)?.version} 吗?此操作不可逆。`,
+      '确认迁移', { type: 'warning' }
+    )
+  } catch { return }
+  migrating.value = true
+  try {
+    const res = await migrateInstances(migrateFromId.value, migrateToId.value)
+    ElMessage.success(`迁移完成,共迁移 ${res.migratedCount} 个实例`)
+    migrateDialogVisible.value = false
+    loadData()
+  } catch (e: any) {
+    ElMessage.error(e?.message || '迁移失败')
+  } finally { migrating.value = false }
+}
+
 onMounted(loadData)
 </script>