index.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <template>
  2. <div class="main-box">
  3. <div class="card filter">
  4. <el-tree
  5. class="scroller"
  6. :load="loadNode"
  7. :expand-on-click-node="true"
  8. :highlight-current="true"
  9. :render-content="renderContent"
  10. @node-click="handleNodeClick"
  11. :lazy="true"
  12. >
  13. </el-tree>
  14. </div>
  15. <div class="table-box card content-box-c">
  16. <div class="table-container">
  17. <span>当前表:{{ currentNode.schemaName }} / {{ currentNode.tableName }}</span>
  18. <el-tabs v-model="activeName" class="demo-tabs">
  19. <el-tab-pane label="基本信息" name="1">
  20. <el-descriptions title="元数据" size="small" :column="1" colon border>
  21. <el-descriptions-item label="表名称">{{ tableMeta.tableName }}</el-descriptions-item>
  22. <el-descriptions-item label="表类型">{{ tableMeta.type }}</el-descriptions-item>
  23. <el-descriptions-item label="模式名">{{ tableMeta.schemaName }}</el-descriptions-item>
  24. <el-descriptions-item label="表注释">
  25. <el-input type="textarea" :rows="2" v-model="tableMeta.remarks" auto-complete="off" :readonly="true"></el-input>
  26. </el-descriptions-item>
  27. <el-descriptions-item label="建表DDL">
  28. <el-input type="textarea" :rows="16" v-model="tableMeta.createSql" auto-complete="off" :readonly="true"></el-input>
  29. </el-descriptions-item>
  30. </el-descriptions>
  31. </el-tab-pane>
  32. <el-tab-pane label="字段信息" name="2">
  33. <ProTable :pagination="false" :is-show-search="false" :tool-button="false" :columns="fieldColumns" :data="tableMeta.columns">
  34. <!-- 表格操作 -->
  35. <template #empty>
  36. <span>单击左侧展开"数据源"来查看表的元数据记录</span>
  37. </template>
  38. </ProTable>
  39. </el-tab-pane>
  40. <el-tab-pane label="索引信息" name="3">
  41. <ProTable :pagination="false" :is-show-search="false" :tool-button="false" :columns="indexColumns" :data="tableMeta.indexes">
  42. <!-- 表格操作 -->
  43. <template #empty>
  44. <span>单击左侧展开"数据源"来查看表的元数据记录</span>
  45. </template>
  46. </ProTable>
  47. </el-tab-pane>
  48. <el-tab-pane label="取样数据" name="4">
  49. <el-table :data="sampleData?.rows" border>
  50. <template #empty>
  51. <span>单击左侧展开"数据源导航树"来查看表的数据记录</span>
  52. </template>
  53. <el-table-column v-for="(item, index) in sampleData?.columns" :prop="item" :label="item" :key="index" show-overflow-tooltip>
  54. </el-table-column>
  55. </el-table>
  56. </el-tab-pane>
  57. </el-tabs>
  58. </div>
  59. </div>
  60. </div>
  61. </template>
  62. <script setup lang="tsx" name="UserManage">
  63. import { ref, reactive } from 'vue'
  64. // import TreeFilter from '@/components/TreeFilter/index.vue'
  65. import { getNameListApi } from '@/api/modules/db/connection'
  66. import { getSchemasApi, getTablesApi, getTablesMetaApi, getTablesDataApi } from '@/api/modules/db/metadata'
  67. // import type { TabsPaneContext } from 'element-plus'
  68. import ProTable from '@/components/ProTable/index.vue'
  69. import { ColumnProps } from '@/components/ProTable/interface'
  70. const activeName = ref('1')
  71. const tableMeta = ref({
  72. tableName: '-',
  73. schemaName: '-',
  74. remarks: '',
  75. type: '-',
  76. createSql: '',
  77. primaryKeys: [],
  78. columns: [],
  79. indexes: []
  80. })
  81. const currentNode = ref({
  82. tableName: '-',
  83. schemaName: '-'
  84. })
  85. let sampleData = ref<any>()
  86. // const handleClick = (tab: TabsPaneContext, event: Event) => {
  87. // console.log(tab, event)
  88. // }
  89. const loadNode = (node, resolve) => {
  90. if (node.level === 0) {
  91. const rootNode = [{ label: '数据源导航树', value: 0, hasChild: true, children: 'child' }]
  92. return resolve(rootNode)
  93. }
  94. setTimeout(() => {
  95. if (node.level === 1) {
  96. getNameListApi().then(res => {
  97. res.data.forEach(item => {
  98. item['label'] = item.name
  99. item['parent'] = 0
  100. item['value'] = item.id
  101. item['hasChild'] = true
  102. item['children'] = 'child'
  103. })
  104. resolve(res.data)
  105. })
  106. } else if (node.level === 2) {
  107. getSchemasApi(node.data.value).then(res => {
  108. res.data.forEach(item => {
  109. item['label'] = item.schema
  110. item['parent'] = node.data.value
  111. item['value'] = item.connection
  112. item['hasChild'] = true
  113. item['children'] = 'child'
  114. })
  115. resolve(res.data)
  116. })
  117. } else if (node.level === 3) {
  118. getTablesApi(node.data.parent, { schema: node.data.label }).then(res => {
  119. res.data.forEach(item => {
  120. item['label'] = item.tableName
  121. item['parent'] = node.data.label
  122. item['id'] = node.data.parent
  123. item['value'] = item.type
  124. item['hasChild'] = false
  125. item['children'] = 'child'
  126. })
  127. resolve(res.data)
  128. })
  129. } else if (node.level == 4) {
  130. resolve([])
  131. } else {
  132. resolve([])
  133. }
  134. }, 500)
  135. }
  136. const handleNodeClick = data => {
  137. let id = data.id
  138. let schema = data.schemaName
  139. let table = data.tableName
  140. if (!data.hasChild && id && schema && table) {
  141. activeName.value = '1'
  142. getTablesMetaApi(id, { schema, table }).then(res => {
  143. tableMeta.value = res.data
  144. currentNode.value.tableName = table
  145. currentNode.value.schemaName = schema
  146. })
  147. getTablesDataApi(id, { schema, table }).then(res => {
  148. sampleData.value = res.data
  149. })
  150. }
  151. }
  152. const renderContent = (h, { node, data }) => {
  153. if (node.level === 1) {
  154. return (
  155. <div class="custom-tree-node">
  156. <i class="el-icon-takeaway-box"></i>
  157. <span>{data.label}</span>
  158. </div>
  159. )
  160. } else if (node.level === 2) {
  161. return (
  162. <div class="custom-tree-node">
  163. <i class="el-icon-folder-opened"></i>
  164. <span>{data.label}</span>
  165. </div>
  166. )
  167. } else if (node.level === 3) {
  168. return (
  169. <div class="custom-tree-node">
  170. <i class="iconfont icon-shujuku1"></i>
  171. <span>{data.label}</span>
  172. </div>
  173. )
  174. } else {
  175. let icon_pic = 'iconfont icon-shitu_biaoge'
  176. if (data.value === 'VIEW') {
  177. icon_pic = 'iconfont icon-viewList'
  178. }
  179. return (
  180. <div class="custom-tree-node">
  181. <i class={icon_pic}></i>
  182. <el-tooltip class="item" content={node.label} effect="light" placement="left">
  183. <span>{data.label}</span>
  184. </el-tooltip>
  185. </div>
  186. )
  187. }
  188. }
  189. // 表格配置项
  190. const fieldColumns = reactive<ColumnProps<any>[]>([
  191. { prop: 'fieldName', label: '名称' },
  192. { prop: 'typeName', label: '类型' },
  193. { prop: 'fieldType', label: 'jdbcType' },
  194. { prop: 'displaySize', label: '长度' },
  195. { prop: 'precision', label: '精度' },
  196. { prop: 'scale', label: '位数' },
  197. { prop: 'isPrimaryKey', label: '主键' },
  198. { prop: 'isAutoIncrement', label: '自增' },
  199. { prop: 'isNullable', label: '可空' },
  200. { prop: 'remarks', label: '注释' }
  201. ])
  202. const indexColumns = reactive<ColumnProps<any>[]>([
  203. { prop: 'indexType', label: '索引类型' },
  204. { prop: 'indexName', label: '索引名称' },
  205. { prop: 'indexFields', label: '索引字段' }
  206. ])
  207. </script>
  208. <style scoped lang="scss">
  209. @import './index.scss';
  210. </style>