houzhongjian
2024-08-08 820397e43a0b64d35c6d31d2a55475061438593b
提交 | 用户 | 时间
820397 1 <template>
H 2   <Dialog
3     v-model="dialogVisible"
4     align-center
5     class="app-infra-codegen-preview-container"
6     title="代码预览"
7     width="80%"
8   >
9     <div class="flex">
10       <!-- 代码目录树 -->
11       <el-card
12         v-loading="loading"
13         :gutter="12"
14         class="w-1/3"
15         element-loading-text="生成文件目录中..."
16         shadow="hover"
17       >
18         <el-scrollbar height="calc(100vh - 88px - 40px)">
19           <el-tree
20             ref="treeRef"
21             :data="preview.fileTree"
22             :expand-on-click-node="false"
23             default-expand-all
24             highlight-current
25             node-key="id"
26             @node-click="handleNodeClick"
27           />
28         </el-scrollbar>
29       </el-card>
30       <!-- 代码 -->
31       <el-card
32         v-loading="loading"
33         :gutter="12"
34         class="ml-3 w-2/3"
35         element-loading-text="加载代码中..."
36         shadow="hover"
37       >
38         <el-tabs v-model="preview.activeName">
39           <el-tab-pane
40             v-for="item in previewCodegen"
41             :key="item.filePath"
42             :label="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)"
43             :name="item.filePath"
44           >
45             <el-button class="float-right" text type="primary" @click="copy(item.code)">
46               {{ t('common.copy') }}
47             </el-button>
48             <el-scrollbar height="600px">
49               <pre><code v-dompurify-html="highlightedCode(item)" class="hljs"></code></pre>
50             </el-scrollbar>
51           </el-tab-pane>
52         </el-tabs>
53       </el-card>
54     </div>
55   </Dialog>
56 </template>
57 <script lang="ts" setup>
58 import { useClipboard } from '@vueuse/core'
59 import { handleTree2 } from '@/utils/tree'
60 import * as CodegenApi from '@/api/infra/codegen'
61
62 import hljs from 'highlight.js' // 导入代码高亮文件
63 import 'highlight.js/styles/github.css' // 导入代码高亮样式
64 import java from 'highlight.js/lib/languages/java'
65 import xml from 'highlight.js/lib/languages/java'
66 import javascript from 'highlight.js/lib/languages/javascript'
67 import sql from 'highlight.js/lib/languages/sql'
68 import typescript from 'highlight.js/lib/languages/typescript'
69
70 defineOptions({ name: 'InfraCodegenPreviewCode' })
71
72 const { t } = useI18n() // 国际化
73 const message = useMessage() // 消息弹窗
74
75 const dialogVisible = ref(false) // 弹窗的是否展示
76 const loading = ref(false) // 加载中的状态
77 const preview = reactive({
78   fileTree: [], // 文件树
79   activeName: '' // 激活的文件名
80 })
81 const previewCodegen = ref<CodegenApi.CodegenPreviewVO[]>()
82
83 /** 点击文件 */
84 const handleNodeClick = async (data, node) => {
85   if (node && !node.isLeaf) {
86     return false
87   }
88   preview.activeName = data.id
89 }
90
91 /** 生成 files 目录 **/
92 interface filesType {
93   id: string
94   label: string
95   parentId: string
96 }
97
98 /** 打开弹窗 */
99 const open = async (id: number) => {
100   dialogVisible.value = true
101   try {
102     loading.value = true
103     // 生成代码
104     const data = await CodegenApi.previewCodegen(id)
105     previewCodegen.value = data
106     // 处理文件
107     let file = handleFiles(data)
108     preview.fileTree = handleTree2(file, 'id', 'parentId', 'children', '/')
109     // 点击首个文件
110     preview.activeName = data[0].filePath
111   } finally {
112     loading.value = false
113   }
114 }
115 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
116
117 /** 处理文件 */
118 const handleFiles = (datas: CodegenApi.CodegenPreviewVO[]) => {
119   let exists = {} // key:file 的 id;value:true
120   let files: filesType[] = []
121   // 遍历每个元素
122   for (const data of datas) {
123     let paths = data.filePath.split('/')
124     let fullPath = '' // 从头开始的路径,用于生成 id
125     // 特殊处理 java 文件
126     if (paths[paths.length - 1].indexOf('.java') >= 0) {
127       let newPaths: string[] = []
128       for (let i = 0; i < paths.length; i++) {
129         let path = paths[i]
130         if (path !== 'java') {
131           newPaths.push(path)
132           continue
133         }
134         newPaths.push(path)
135         // 特殊处理中间的 package,进行合并
136         let tmp = ''
137         while (i < paths.length) {
138           path = paths[i + 1]
139           if (
140             path === 'controller' ||
141             path === 'convert' ||
142             path === 'dal' ||
143             path === 'enums' ||
144             path === 'service' ||
145             path === 'vo' || // 下面三个,主要是兜底。可能考虑到有人改了包结构
146             path === 'mysql' ||
147             path === 'dataobject'
148           ) {
149             break
150           }
151           tmp = tmp ? tmp + '.' + path : path
152           i++
153         }
154         if (tmp) {
155           newPaths.push(tmp)
156         }
157       }
158       paths = newPaths
159     }
160     // 遍历每个 path, 拼接成树
161     for (let i = 0; i < paths.length; i++) {
162       // 已经添加到 files 中,则跳过
163       let oldFullPath = fullPath
164       // 下面的 replaceAll 的原因,是因为上面包处理了,导致和 tabs 不匹配,所以 replaceAll 下
165       fullPath = fullPath.length === 0 ? paths[i] : fullPath.replaceAll('.', '/') + '/' + paths[i]
166       if (exists[fullPath]) {
167         continue
168       }
169       // 添加到 files 中
170       exists[fullPath] = true
171       files.push({
172         id: fullPath,
173         label: paths[i],
174         parentId: oldFullPath || '/' // "/" 为根节点
175       })
176     }
177   }
178   return files
179 }
180
181 /** 复制 **/
182 const copy = async (text: string) => {
183   const { copy, copied, isSupported } = useClipboard({ source: text })
184   if (!isSupported) {
185     message.error(t('common.copyError'))
186     return
187   }
188   await copy()
189   if (unref(copied)) {
190     message.success(t('common.copySuccess'))
191   }
192 }
193
194 /**
195  * 代码高亮
196  */
197 const highlightedCode = (item) => {
198   const language = item.filePath.substring(item.filePath.lastIndexOf('.') + 1)
199   const result = hljs.highlight(language, item.code || '', true)
200   return result.value || '&nbsp;'
201 }
202
203 /** 初始化 **/
204 onMounted(async () => {
205   // 注册代码高亮的各种语言
206   hljs.registerLanguage('java', java)
207   hljs.registerLanguage('xml', xml)
208   hljs.registerLanguage('html', xml)
209   hljs.registerLanguage('vue', xml)
210   hljs.registerLanguage('javascript', javascript)
211   hljs.registerLanguage('sql', sql)
212   hljs.registerLanguage('typescript', typescript)
213 })
214 </script>
215 <style lang="scss">
216 .app-infra-codegen-preview-container {
217   .el-scrollbar .el-scrollbar__wrap .el-scrollbar__view {
218     display: inline-block;
219     white-space: nowrap;
220   }
221 }
222 </style>