55 <el-button icon =" ArrowLeft" link @click =" router.back()" >返回</el-button >
66 <h2 style =" display :inline ;margin-left :8px " >版本管理 - {{ flowKey }}</h2 >
77 </div >
8+ <el-button type =" warning" @click =" openDiffDialog" icon =" Switch" :disabled =" tableData.length < 2" >版本对比</el-button >
89 </div >
910 <el-card >
1011 <el-table :data =" tableData" stripe v-loading =" loading" >
2021 <el-table-column label =" 操作" width =" 260" fixed =" right" >
2122 <template #default =" { row } " >
2223 <el-button size =" small" type =" primary" link @click =" triggerTest(row)" >触发测试</el-button >
24+ <el-button size =" small" link @click =" viewContent(row)" >查看JSON</el-button >
2325 <el-tooltip content =" 复制此版本调用地址" >
2426 <el-button size =" small" link icon =" CopyDocument" @click =" copyVersionUrl(row)" />
2527 </el-tooltip >
3234 </el-table-column >
3335 </el-table >
3436 </el-card >
37+
38+ <!-- 版本对比对话框 -->
39+ <el-dialog v-model =" diffVisible" title =" 流程版本对比" width =" 90%" top =" 5vh" destroy-on-close >
40+ <div style =" display :flex ;gap :12px ;margin-bottom :16px ;align-items :center " >
41+ <el-select v-model =" diffLeft" placeholder =" 选择左侧版本" style =" flex :1 " size =" small" >
42+ <el-option v-for =" v in tableData" :key =" v.id" :value =" v.id" :label =" 'v' + v.version + ' (' + v.createdAt?.substring(0,16) + ')'" />
43+ </el-select >
44+ <el-icon style =" font-size :20px ;color :#999 " ><Switch /></el-icon >
45+ <el-select v-model =" diffRight" placeholder =" 选择右侧版本" style =" flex :1 " size =" small" >
46+ <el-option v-for =" v in tableData" :key =" v.id" :value =" v.id" :label =" 'v' + v.version + ' (' + v.createdAt?.substring(0,16) + ')'" />
47+ </el-select >
48+ <el-button type =" primary" size =" small" @click =" doDiff" :disabled =" diffLeft === diffRight || !diffLeft || !diffRight" >对比</el-button >
49+ </div >
50+ <div v-if =" diffResult" class =" diff-container" >
51+ <div class =" diff-summary" >
52+ <el-tag type =" success" size =" small" >新增 {{ diffResult.added }} 行</el-tag >
53+ <el-tag type =" danger" size =" small" >删除 {{ diffResult.removed }} 行</el-tag >
54+ <el-tag type =" warning" size =" small" >修改 {{ diffResult.changed }} 行</el-tag >
55+ </div >
56+ <div class =" diff-code" >
57+ <div v-for =" (line, i) in diffResult.lines" :key =" i" :class =" 'diff-line diff-line-' + line.type" >
58+ <span class =" diff-linenum" >{{ Number(i) + 1 }}</span >
59+ <span class =" diff-prefix" >{{ line.type === 'added' ? '+' : line.type === 'removed' ? '-' : ' ' }}</span >
60+ <span class =" diff-text" >{{ line.text }}</span >
61+ </div >
62+ </div >
63+ </div >
64+ </el-dialog >
65+
66+ <!-- 查看JSON对话框 -->
67+ <el-dialog v-model =" jsonVisible" title =" 流程节点 JSON" width =" 70%" destroy-on-close >
68+ <div style =" display :flex ;justify-content :flex-end ;margin-bottom :8px " >
69+ <el-button size =" small" @click =" formatJson" icon =" MagicStick" >格式化</el-button >
70+ <el-button size =" small" @click =" copyJson" icon =" CopyDocument" >复制</el-button >
71+ </div >
72+ <el-input v-model =" jsonContent" type =" textarea" :rows =" 20" readonly style =" font-family :monospace ;font-size :13px " />
73+ </el-dialog >
3574 </div >
3675</template >
3776
@@ -45,7 +84,17 @@ const route = useRoute()
4584const router = useRouter ()
4685const flowKey = route .params .flowKey as string
4786const loading = ref (false )
48- const tableData = ref ([])
87+ const tableData = ref <any []>([])
88+
89+ // 版本对比
90+ const diffVisible = ref (false )
91+ const diffLeft = ref <number >(0 )
92+ const diffRight = ref <number >(0 )
93+ const diffResult = ref <any >(null )
94+
95+ // 查看JSON
96+ const jsonVisible = ref (false )
97+ const jsonContent = ref (' ' )
4998
5099onMounted (loadData )
51100
@@ -87,9 +136,122 @@ function copyVersionUrl(row: any) {
87136 ElMessage .error (' 复制失败' )
88137 })
89138}
139+
140+ function openDiffDialog() {
141+ if (tableData .value .length >= 2 ) {
142+ diffLeft .value = tableData .value [1 ]?.id
143+ diffRight .value = tableData .value [0 ]?.id
144+ }
145+ diffResult .value = null
146+ diffVisible .value = true
147+ }
148+
149+ async function doDiff() {
150+ const leftRow = tableData .value .find ((v : any ) => v .id === diffLeft .value )
151+ const rightRow = tableData .value .find ((v : any ) => v .id === diffRight .value )
152+ if (! leftRow || ! rightRow ) return
153+
154+ try {
155+ // 从版本列表中获取 flowContent(可能需要重新加载)
156+ const res: any = await request .post (' /flow/version/page' , { pageNum: 1 , pageSize: 100 , flowKey })
157+ const allVersions = res .data ?.records || []
158+ const leftVer = allVersions .find ((v : any ) => v .id === diffLeft .value )
159+ const rightVer = allVersions .find ((v : any ) => v .id === diffRight .value )
160+
161+ const leftJson = leftVer ?.flowContent ? formatFlowJson (leftVer .flowContent ) : ' '
162+ const rightJson = rightVer ?.flowContent ? formatFlowJson (rightVer .flowContent ) : ' '
163+
164+ diffResult .value = computeDiff (leftJson , rightJson )
165+ } catch (e : any ) {
166+ ElMessage .error (' 对比失败: ' + (e .message || e ))
167+ }
168+ }
169+
170+ function formatFlowJson(content : string ): string {
171+ try {
172+ const nodes = JSON .parse (content )
173+ if (Array .isArray (nodes )) {
174+ // 格式化为每行一个节点的可读格式
175+ return nodes .map ((n : any ) => {
176+ const label = n .label || n .key
177+ const type = n .elementType
178+ const outgoings = n .outgoings ?.length ? ' → [' + n .outgoings .join (' ,' ) + ' ]' : ' '
179+ const incomings = n .incomings ?.length ? ' ← [' + n .incomings .join (' ,' ) + ' ]' : ' '
180+ return ` ${n .key } [${type }] "${label }"${incomings }${outgoings } `
181+ }).join (' \n ' )
182+ }
183+ return JSON .stringify (nodes , null , 2 )
184+ } catch {
185+ return content
186+ }
187+ }
188+
189+ function computeDiff(left : string , right : string ) {
190+ const leftLines = left .split (' \n ' )
191+ const rightLines = right .split (' \n ' )
192+
193+ // 简单 LCS diff 算法
194+ const lines: any [] = []
195+ const leftSet = new Set (leftLines )
196+ const rightSet = new Set (rightLines )
197+ let added = 0 , removed = 0 , changed = 0
198+
199+ // 构建统一的行列表
200+ // 逐行对比
201+ for (const line of leftLines ) {
202+ if (! rightSet .has (line )) {
203+ lines .push ({ type: ' removed' , text: line })
204+ removed ++
205+ } else {
206+ lines .push ({ type: ' unchanged' , text: line })
207+ }
208+ }
209+ for (const line of rightLines ) {
210+ if (! leftSet .has (line )) {
211+ lines .push ({ type: ' added' , text: line })
212+ added ++
213+ }
214+ }
215+
216+ return { lines , added , removed , changed }
217+ }
218+
219+ function viewContent(row : any ) {
220+ // 重新加载以获取完整 flowContent
221+ request .post (' /flow/version/page' , { pageNum: 1 , pageSize: 100 , flowKey }).then ((res : any ) => {
222+ const ver = res .data ?.records ?.find ((v : any ) => v .id === row .id )
223+ jsonContent .value = ver ?.flowContent ? JSON .stringify (JSON .parse (ver .flowContent ), null , 2 ) : ' (无内容)'
224+ jsonVisible .value = true
225+ })
226+ }
227+
228+ function formatJson() {
229+ try {
230+ jsonContent .value = JSON .stringify (JSON .parse (jsonContent .value ), null , 2 )
231+ } catch {
232+ ElMessage .warning (' JSON 格式错误,无法格式化' )
233+ }
234+ }
235+
236+ function copyJson() {
237+ navigator .clipboard .writeText (jsonContent .value ).then (() => {
238+ ElMessage .success (' 已复制到剪贴板' )
239+ })
240+ }
90241 </script >
91242
92243<style scoped>
93244.page-container { padding : 20px ; }
94245.page-header { display : flex ; justify-content : space-between ; align-items : center ; margin-bottom : 16px ; }
246+ .diff-summary { display : flex ; gap : 8px ; margin-bottom : 12px ; }
247+ .diff-code { background : #1e1e1e ; color : #d4d4d4 ; border-radius : 8px ; padding : 12px ; font-family : ' Consolas' , ' Monaco' , monospace ; font-size : 13px ; overflow : auto ; max-height : 60vh ; }
248+ .diff-line { display : flex ; gap : 8px ; line-height : 1.6 ; padding : 0 4px ; }
249+ .diff-line-added { background : #1e3a1e ; }
250+ .diff-line-removed { background : #3a1e1e ; }
251+ .diff-line-unchanged { color : #808080 ; }
252+ .diff-linenum { color : #6e7681 ; min-width : 36px ; text-align : right ; user-select : none ; }
253+ .diff-prefix { min-width : 16px ; font-weight : bold ; }
254+ .diff-line-added .diff-prefix { color : #4ec9b0 ; }
255+ .diff-line-removed .diff-prefix { color : #f44747 ; }
256+ .diff-text { white-space : pre-wrap ; word-break : break-all ; }
95257 </style >
0 commit comments