editTable.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. <template>
  2. <cacp-page-layout>
  3. <el-card>
  4. <el-tabs v-model="state.activeName" type="border-card">
  5. <el-tab-pane label="基本信息" name="basic">
  6. <basic-info-form ref="basicInfoRef" :info="state.info" />
  7. </el-tab-pane>
  8. <el-tab-pane label="字段信息" name="columnInfo">
  9. <el-table
  10. ref="dragTableRef"
  11. :data="state.columns"
  12. row-key="columnId"
  13. :height="state.tableHeight"
  14. border
  15. stripe
  16. >
  17. <el-table-column
  18. label="序号"
  19. type="index"
  20. width="55"
  21. align="center"
  22. class-name="allowDrag"
  23. />
  24. <el-table-column
  25. label="字段列名"
  26. prop="columnName"
  27. min-width="120"
  28. :show-overflow-tooltip="true"
  29. align="center"
  30. />
  31. <el-table-column label="字段描述" min-width="120" align="center">
  32. <template #default="scope">
  33. <el-input
  34. v-model="scope.row.columnComment"
  35. placeholder="请输入字段描述"
  36. clearable
  37. />
  38. </template>
  39. </el-table-column>
  40. <el-table-column
  41. label="物理类型"
  42. prop="columnType"
  43. min-width="120"
  44. :show-overflow-tooltip="true"
  45. align="center"
  46. />
  47. <el-table-column label="Java类型" min-width="130" align="center">
  48. <template #default="scope">
  49. <el-select
  50. v-model="scope.row.javaType"
  51. placeholder="请选择"
  52. style="width: 100%"
  53. >
  54. <el-option label="Long" value="Long" />
  55. <el-option label="String" value="String" />
  56. <el-option label="Integer" value="Integer" />
  57. <el-option label="Double" value="Double" />
  58. <el-option label="BigDecimal" value="BigDecimal" />
  59. <el-option label="Date" value="Date" />
  60. <el-option label="Boolean" value="Boolean" />
  61. </el-select>
  62. </template>
  63. </el-table-column>
  64. <el-table-column label="java属性" min-width="120" align="center">
  65. <template #default="scope">
  66. <el-input
  67. v-model="scope.row.javaField"
  68. placeholder="请输入java属性"
  69. clearable
  70. @blur="handleJavaFieldBlur(scope.row)"
  71. />
  72. </template>
  73. </el-table-column>
  74. <el-table-column label="插入" width="70" align="center">
  75. <template #default="scope">
  76. <el-checkbox
  77. v-model="scope.row.isInsert"
  78. true-label="1"
  79. false-label="0"
  80. />
  81. </template>
  82. </el-table-column>
  83. <el-table-column label="编辑" width="70" align="center">
  84. <template #default="scope">
  85. <el-checkbox
  86. v-model="scope.row.isEdit"
  87. true-label="1"
  88. false-label="0"
  89. />
  90. </template>
  91. </el-table-column>
  92. <el-table-column label="列表" width="70" align="center">
  93. <template #default="scope">
  94. <el-checkbox
  95. v-model="scope.row.isList"
  96. true-label="1"
  97. false-label="0"
  98. />
  99. </template>
  100. </el-table-column>
  101. <el-table-column label="查询" width="70" align="center">
  102. <template #default="scope">
  103. <el-checkbox
  104. v-model="scope.row.isQuery"
  105. true-label="1"
  106. false-label="0"
  107. />
  108. </template>
  109. </el-table-column>
  110. <el-table-column label="查询方式" min-width="120" align="center">
  111. <template #default="scope">
  112. <el-select
  113. v-model="scope.row.queryType"
  114. placeholder="请选择"
  115. style="width: 100%"
  116. >
  117. <el-option label="=" value="EQ" />
  118. <el-option label="!=" value="NE" />
  119. <el-option label=">" value="GT" />
  120. <el-option label=">=" value="GTE" />
  121. <el-option label="<" value="LT" />
  122. <el-option label="<=" value="LTE" />
  123. <el-option label="LIKE" value="LIKE" />
  124. <el-option label="BETWEEN" value="BETWEEN" />
  125. </el-select>
  126. </template>
  127. </el-table-column>
  128. <el-table-column label="必填" width="70" align="center">
  129. <template #default="scope">
  130. <el-checkbox
  131. v-model="scope.row.isRequired"
  132. true-label="1"
  133. false-label="0"
  134. />
  135. </template>
  136. </el-table-column>
  137. <el-table-column label="显示类型" min-width="140" align="center">
  138. <template #default="scope">
  139. <el-select
  140. v-model="scope.row.htmlType"
  141. placeholder="请选择"
  142. style="width: 100%"
  143. >
  144. <el-option label="文本框" value="input" />
  145. <el-option label="文本域" value="textarea" />
  146. <el-option label="下拉框" value="select" />
  147. <el-option label="单选框" value="radio" />
  148. <el-option label="复选框" value="checkbox" />
  149. <el-option label="日期控件" value="datetime" />
  150. <el-option label="图片上传" value="imageUpload" />
  151. <el-option label="文件上传" value="fileUpload" />
  152. <el-option label="富文本控件" value="editor" />
  153. </el-select>
  154. </template>
  155. </el-table-column>
  156. <el-table-column label="字典类型" min-width="140" align="center">
  157. <template #default="scope">
  158. <el-select
  159. v-model="scope.row.dictType"
  160. placeholder="请选择字典类型"
  161. clearable
  162. filterable
  163. style="width: 100%"
  164. >
  165. <el-option
  166. v-for="dict in state.dictOptions"
  167. :key="dict.dictType"
  168. :label="dict.dictName"
  169. :value="dict.dictType"
  170. >
  171. <span style="float: left">{{ dict.dictName }}</span>
  172. <span style="float: right; color: #8492a6; font-size: 13px">
  173. {{ dict.dictType }}
  174. </span>
  175. </el-option>
  176. </el-select>
  177. </template>
  178. </el-table-column>
  179. </el-table>
  180. </el-tab-pane>
  181. <el-tab-pane label="生成信息" name="genInfo">
  182. <gen-info-form
  183. ref="genInfoRef"
  184. :info="state.info"
  185. :tables="state.tables"
  186. :menus="state.menus"
  187. />
  188. </el-tab-pane>
  189. </el-tabs>
  190. <div class="footer-action">
  191. <el-button type="primary" @click="onSubmit">提交</el-button>
  192. <el-button @click="onCancel">返回</el-button>
  193. </div>
  194. </el-card>
  195. </cacp-page-layout>
  196. </template>
  197. <script lang="ts" setup>
  198. import { reactive, ref, onMounted, nextTick } from 'vue'
  199. import { useRoute, useRouter } from 'vue-router'
  200. import { ElMessage } from 'element-plus'
  201. import Sortable from 'sortablejs'
  202. import type { GenTable } from '@/types/gen/GenTable'
  203. import type { GenTableColumn } from '@/types/gen/GenTableColumn'
  204. import { getDetail, update } from "@/apis/gen/gen"
  205. // import { optionselect as getDictOptionselect } from "@/api/system/dict/type"
  206. // import { listMenu as getMenuTreeselect } from "@/api/system/menu"
  207. import basicInfoForm from "./basicInfoForm.vue"
  208. import genInfoForm from "./genInfoForm.vue"
  209. import { SuccessResultCode } from '@cacp/ui'
  210. // 组件引用
  211. const basicInfoRef = ref<InstanceType<typeof basicInfoForm>>()
  212. const genInfoRef = ref<InstanceType<typeof genInfoForm>>()
  213. const dragTableRef = ref()
  214. // 路由
  215. const route = useRoute()
  216. const router = useRouter()
  217. // 工具函数:检查是否为驼峰命名
  218. const isCamelCase = (str: string): boolean => {
  219. if (!str) return false
  220. // 驼峰命名规则:以小写字母开头,后续可以包含大写字母
  221. return /^[a-z]+([A-Z][a-z]*)*$/.test(str)
  222. }
  223. // 工具函数:格式化java属性
  224. const formatJavaField = (field: string = ''): string => {
  225. if (!field) return ''
  226. // 如果是驼峰命名,保持原样
  227. if (isCamelCase(field)) {
  228. return field
  229. }
  230. // 其他情况全部转为小写
  231. return field.toLowerCase()
  232. }
  233. // 处理java属性输入框失去焦点事件 - 明确指定参数类型
  234. const handleJavaFieldBlur = (column: GenTableColumn) => {
  235. if (column.javaField) {
  236. const formatted = formatJavaField(column.javaField)
  237. if (column.javaField !== formatted) {
  238. column.javaField = formatted
  239. }
  240. }
  241. }
  242. // 响应式状态
  243. interface DictOption {
  244. dictType: string
  245. dictName: string
  246. }
  247. interface Menu {
  248. menuId: number
  249. [key: string]: any
  250. }
  251. interface State {
  252. activeName: string
  253. tableHeight: string
  254. info: Partial<GenTable>
  255. columns: GenTableColumn[]
  256. tables: any[]
  257. dictOptions: DictOption[]
  258. menus: Menu[]
  259. }
  260. const state = reactive<State>({
  261. activeName: 'columnInfo',
  262. tableHeight: `${document.documentElement.scrollHeight - 300}px`,
  263. info: {},
  264. columns: [],
  265. tables: [],
  266. dictOptions: [],
  267. menus: []
  268. })
  269. // 修改 onSubmit 方法
  270. const onSubmit = async () => {
  271. try {
  272. if (!basicInfoRef.value || !genInfoRef.value) {
  273. ElMessage.error('表单组件未正确加载')
  274. return
  275. }
  276. // 直接从子组件获取最新的表单数据
  277. let basicInfoData = {}
  278. let genInfoData = {}
  279. try {
  280. basicInfoData = await basicInfoRef.value.validate?.()
  281. } catch (error) {
  282. console.error('基础表单验证失败:', error)
  283. ElMessage.error('基本信息表单验证失败,请检查必填项')
  284. state.activeName = 'basic'
  285. return
  286. }
  287. try {
  288. genInfoData = await genInfoRef.value.validate?.()
  289. } catch (error) {
  290. console.error('生成信息表单验证失败:', error)
  291. ElMessage.error('生成信息表单验证失败,请检查必填项')
  292. state.activeName = 'genInfo'
  293. return
  294. }
  295. // 合并所有数据
  296. const mergedInfo: { [key: string]: any } = {
  297. ...state.info,
  298. ...basicInfoData,
  299. ...genInfoData
  300. }
  301. // 检查必填字段
  302. const requiredFields = [
  303. 'packageName', 'tableName', 'functionName', 'functionAuthor',
  304. 'businessName', 'moduleName', 'className', 'tableComment'
  305. ]
  306. const missingFields = requiredFields.filter(field => !mergedInfo[field])
  307. if (missingFields.length > 0) {
  308. ElMessage.error(`以下字段不能为空: ${missingFields.join(', ')}`)
  309. state.activeName = 'basic'
  310. return
  311. }
  312. // 准备提交的数据
  313. const genTable = {
  314. ...mergedInfo,
  315. columns: state.columns,
  316. params: {
  317. treeCode: mergedInfo.treeCode || '',
  318. treeName: mergedInfo.treeName || '',
  319. treeParentCode: mergedInfo.treeParentCode || '',
  320. parentMenuId: mergedInfo.parentMenuId || ''
  321. }
  322. }
  323. console.log('准备提交的数据:', JSON.stringify(genTable, null, 2))
  324. const res = await update(genTable)
  325. if (res.code === SuccessResultCode) {
  326. ElMessage.success(res.message || '提交成功')
  327. onCancel()
  328. } else {
  329. console.error('提交失败响应:', res)
  330. ElMessage.error(res.message || '提交失败')
  331. }
  332. } catch (error) {
  333. console.error('提交过程中发生错误:', error)
  334. ElMessage.error('提交失败,请检查表单数据')
  335. }
  336. }
  337. // 返回
  338. const onCancel = () => {
  339. router.push({
  340. path: '/gen-index',
  341. query: {
  342. t: Date.now(),
  343. pageNum: route.query.pageNum
  344. }
  345. })
  346. }
  347. // 初始化拖拽
  348. const initSortable = () => {
  349. nextTick(() => {
  350. if (!dragTableRef.value) return
  351. const el = dragTableRef.value.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
  352. if (!el) return
  353. Sortable.create(el, {
  354. handle: '.allowDrag',
  355. onEnd: (evt: { oldIndex?: number; newIndex?: number }) => {
  356. const { oldIndex, newIndex } = evt
  357. if (oldIndex === undefined || newIndex === undefined) return
  358. const targetRow = state.columns.splice(oldIndex, 1)[0]
  359. state.columns.splice(newIndex, 0, targetRow)
  360. // 更新排序 - 明确指定参数类型
  361. state.columns.forEach((column: GenTableColumn, index: number) => {
  362. column.sort = index + 1
  363. })
  364. }
  365. })
  366. })
  367. }
  368. // 初始化数据
  369. const initData = async () => {
  370. const tableId = route.params?.tableId as string
  371. if (!tableId) return
  372. try {
  373. const res = await getDetail(tableId)
  374. if (res.code === SuccessResultCode) {
  375. // 初始化时格式化所有 javaField,非驼峰命名的转为小写
  376. state.columns = (res.data.rows || []).map((column: GenTableColumn) => ({
  377. ...column,
  378. javaField: formatJavaField(column.javaField)
  379. }))
  380. state.info = res.data.info || {}
  381. state.tables = res.data.tables || []
  382. // 查询字典下拉列表
  383. // const dictRes = await getDictOptionselect()
  384. // state.dictOptions = dictRes.data || []
  385. // 查询菜单下拉列表
  386. // const menuRes = await getMenuTreeselect()
  387. // state.menus = handleTree(menuRes.data || [], 'menuId')
  388. // 初始化拖拽
  389. initSortable()
  390. } else {
  391. ElMessage.error(res.message || '获取数据失败')
  392. }
  393. } catch (error) {
  394. console.error('初始化数据失败:', error)
  395. ElMessage.error('初始化数据失败,请稍后重试')
  396. }
  397. }
  398. // 组件挂载
  399. onMounted(() => {
  400. initData()
  401. })
  402. // 监听窗口大小变化调整表格高度
  403. window.addEventListener('resize', () => {
  404. state.tableHeight = `${document.documentElement.scrollHeight - 300}px`
  405. })
  406. </script>
  407. <style scoped>
  408. .footer-action {
  409. display: flex;
  410. justify-content: center;
  411. margin-top: 20px;
  412. padding-top: 20px;
  413. border-top: 1px solid #ebeef5;
  414. }
  415. :deep(.el-tabs--border-card) {
  416. box-shadow: none;
  417. border: 1px solid #dcdfe6;
  418. }
  419. :deep(.el-table) {
  420. margin-top: 10px;
  421. }
  422. :deep(.el-tabs__content) {
  423. padding: 20px;
  424. }
  425. </style>