index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <template>
  2. <div>
  3. <!-- 从数据库直接获取字段 -->
  4. <el-collapse v-model="activeNames">
  5. <el-collapse-item name="1">
  6. <template slot="title">
  7. <div :style="{fontSize:'16px',paddingLeft:'20px'}">
  8. 点这里从现有数据库创建代码
  9. <i class="header-icon el-icon-thumb" />
  10. </div>
  11. </template>
  12. <el-form ref="getTableForm" :inline="true" :model="dbform" label-width="120px">
  13. <el-form-item label="数据库名" prop="structName">
  14. <el-select v-model="dbform.dbName" filterable placeholder="请选择数据库" @change="getTable">
  15. <el-option
  16. v-for="item in dbOptions"
  17. :key="item.database"
  18. :label="item.database"
  19. :value="item.database"
  20. />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item label="表名" prop="structName">
  24. <el-select
  25. v-model="dbform.tableName"
  26. :disabled="!dbform.dbName"
  27. filterable
  28. placeholder="请选择表"
  29. >
  30. <el-option
  31. v-for="item in tableOptions"
  32. :key="item.tableName"
  33. :label="item.tableName"
  34. :value="item.tableName"
  35. />
  36. </el-select>
  37. </el-form-item>
  38. <el-form-item>
  39. <el-button size="mini" type="primary" @click="getColumn">使用此表创建</el-button>
  40. </el-form-item>
  41. </el-form>
  42. </el-collapse-item>
  43. </el-collapse>
  44. <el-divider />
  45. <!-- 初始版本自动化代码工具 -->
  46. <el-form ref="autoCodeForm" :rules="rules" :model="form" label-width="120px" :inline="true">
  47. <el-form-item label="Struct名称" prop="structName">
  48. <el-input v-model="form.structName" placeholder="首字母自动转换大写" />
  49. </el-form-item>
  50. <el-form-item label="tableName" prop="tableName">
  51. <el-input v-model="form.tableName" placeholder="指定表名(非必填)" />
  52. </el-form-item>
  53. <el-form-item label="Struct简称" prop="abbreviation">
  54. <el-input v-model="form.abbreviation" placeholder="简称会作为入参对象名和路由group" />
  55. </el-form-item>
  56. <el-form-item label="Struct中文名称" prop="description">
  57. <el-input v-model="form.description" placeholder="中文描述作为自动api描述" />
  58. </el-form-item>
  59. <el-form-item label="文件名称" prop="packageName">
  60. <el-input v-model="form.packageName" placeholder="生成文件的默认名称(建议为驼峰格式,首字母小写,如sysXxxXxxx)" />
  61. </el-form-item>
  62. <el-form-item>
  63. <template slot="label">
  64. <el-tooltip content="注:把自动生成的API注册进数据库" placement="bottom" effect="light">
  65. <div> 自动创建API </div>
  66. </el-tooltip>
  67. </template>
  68. <el-checkbox v-model="form.autoCreateApiToSql" />
  69. </el-form-item>
  70. <el-form-item>
  71. <template slot="label">
  72. <el-tooltip content="注:自动迁移生成的文件到ymal配置的对应位置" placement="bottom" effect="light">
  73. <div> 自动移动文件 </div>
  74. </el-tooltip>
  75. </template>
  76. <el-checkbox v-model="form.autoMoveFile" />
  77. </el-form-item>
  78. </el-form>
  79. <!-- 组件列表 -->
  80. <div class="button-box clearflex">
  81. <el-button size="mini" type="primary" @click="editAndAddField()">新增Field</el-button>
  82. </div>
  83. <el-table :data="form.fields" border stripe>
  84. <el-table-column type="index" label="序列" width="100" />
  85. <el-table-column prop="fieldName" label="Field名" />
  86. <el-table-column prop="fieldDesc" label="中文名" />
  87. <el-table-column prop="fieldJson" label="FieldJson" />
  88. <el-table-column prop="fieldType" label="Field数据类型" width="130" />
  89. <el-table-column prop="dataType" label="数据库字段类型" width="130" />
  90. <el-table-column prop="dataTypeLong" label="数据库字段长度" width="130" />
  91. <el-table-column prop="columnName" label="数据库字段" width="130" />
  92. <el-table-column prop="comment" label="数据库字段描述" width="130" />
  93. <el-table-column prop="fieldSearchType" label="搜索条件" width="130" />
  94. <el-table-column prop="dictType" label="字典" width="130" />
  95. <el-table-column label="操作" width="300">
  96. <template slot-scope="scope">
  97. <el-button
  98. size="mini"
  99. type="primary"
  100. icon="el-icon-edit"
  101. @click="editAndAddField(scope.row)"
  102. >编辑</el-button>
  103. <el-button
  104. size="mini"
  105. type="text"
  106. :disabled="scope.$index === 0"
  107. @click="moveUpField(scope.$index)"
  108. >上移</el-button>
  109. <el-button
  110. size="mini"
  111. type="text"
  112. :disabled="(scope.$index + 1) === form.fields.length"
  113. @click="moveDownField(scope.$index)"
  114. >下移</el-button>
  115. <el-popover v-model="scope.row.visible" placement="top">
  116. <p>确定删除吗?</p>
  117. <div style="text-align: right; margin: 0">
  118. <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
  119. <el-button type="primary" size="mini" @click="deleteField(scope.$index)">确定</el-button>
  120. </div>
  121. <el-button slot="reference" size="mini" type="danger" icon="el-icon-delete">删除</el-button>
  122. </el-popover>
  123. </template>
  124. </el-table-column>
  125. </el-table>
  126. <el-tag type="danger">id , created_at , updated_at , deleted_at 会自动生成请勿重复创建</el-tag>
  127. <!-- 组件列表 -->
  128. <div class="button-box clearflex">
  129. <el-button size="mini" type="primary" @click="enterForm(true)">预览代码</el-button>
  130. <el-button size="mini" type="primary" @click="enterForm(false)">生成代码</el-button>
  131. </div>
  132. <!-- 组件弹窗 -->
  133. <el-dialog title="组件内容" :visible.sync="dialogFlag">
  134. <FieldDialog v-if="dialogFlag" ref="fieldDialog" :dialog-middle="dialogMiddle" />
  135. <div slot="footer" class="dialog-footer">
  136. <el-button size="mini" @click="closeDialog">取 消</el-button>
  137. <el-button size="mini" type="primary" @click="enterDialog">确 定</el-button>
  138. </div>
  139. </el-dialog>
  140. <el-dialog :visible.sync="previewFlag">
  141. <PreviewCodeDialg v-if="previewFlag" :preview-code="preViewCode" />
  142. <div slot="footer" class="dialog-footer">
  143. <el-button type="primary" @click="previewFlag = false">确 定</el-button>
  144. </div>
  145. </el-dialog>
  146. </div>
  147. </template>
  148. <script>
  149. const fieldTemplate = {
  150. fieldName: '',
  151. fieldDesc: '',
  152. fieldType: '',
  153. dataType: '',
  154. fieldJson: '',
  155. columnName: '',
  156. dataTypeLong: '',
  157. comment: '',
  158. fieldSearchType: '',
  159. dictType: ''
  160. }
  161. import FieldDialog from '@/view/systemTools/autoCode/component/fieldDialog.vue'
  162. import PreviewCodeDialg from '@/view/systemTools/autoCode/component/previewCodeDialg.vue'
  163. import { toUpperCase, toHump, toSQLLine } from '@/utils/stringFun'
  164. import { createTemp, getDB, getTable, getColumn, preview } from '@/api/autoCode'
  165. import { getDict } from '@/utils/dictionary'
  166. export default {
  167. name: 'AutoCode',
  168. components: {
  169. FieldDialog,
  170. PreviewCodeDialg
  171. },
  172. data() {
  173. return {
  174. activeNames: [''],
  175. preViewCode: {},
  176. dbform: {
  177. dbName: '',
  178. tableName: ''
  179. },
  180. dbOptions: [],
  181. tableOptions: [],
  182. addFlag: '',
  183. fdMap: {},
  184. form: {
  185. structName: '',
  186. tableName: '',
  187. packageName: '',
  188. abbreviation: '',
  189. description: '',
  190. autoCreateApiToSql: false,
  191. autoMoveFile: false,
  192. fields: []
  193. },
  194. rules: {
  195. structName: [
  196. { required: true, message: '请输入结构体名称', trigger: 'blur' }
  197. ],
  198. abbreviation: [
  199. { required: true, message: '请输入结构体简称', trigger: 'blur' }
  200. ],
  201. description: [
  202. { required: true, message: '请输入结构体描述', trigger: 'blur' }
  203. ],
  204. packageName: [
  205. {
  206. required: true,
  207. message: '文件名称:sysXxxxXxxx',
  208. trigger: 'blur'
  209. }
  210. ]
  211. },
  212. dialogMiddle: {},
  213. bk: {},
  214. dialogFlag: false,
  215. previewFlag: false
  216. }
  217. },
  218. created() {
  219. this.getDb()
  220. this.setFdMap()
  221. },
  222. methods: {
  223. editAndAddField(item) {
  224. this.dialogFlag = true
  225. if (item) {
  226. this.addFlag = 'edit'
  227. this.bk = JSON.parse(JSON.stringify(item))
  228. this.dialogMiddle = item
  229. } else {
  230. this.addFlag = 'add'
  231. this.dialogMiddle = JSON.parse(JSON.stringify(fieldTemplate))
  232. }
  233. },
  234. moveUpField(index) {
  235. if (index === 0) {
  236. return
  237. }
  238. const oldUpField = this.form.fields[index - 1]
  239. this.form.fields.splice(index - 1, 1)
  240. this.form.fields.splice(index, 0, oldUpField)
  241. },
  242. moveDownField(index) {
  243. const fCount = this.form.fields.length
  244. if (index === fCount - 1) {
  245. return
  246. }
  247. const oldDownField = this.form.fields[index + 1]
  248. this.form.fields.splice(index + 1, 1)
  249. this.form.fields.splice(index, 0, oldDownField)
  250. },
  251. enterDialog() {
  252. this.$refs.fieldDialog.$refs.fieldDialogFrom.validate(valid => {
  253. if (valid) {
  254. this.dialogMiddle.fieldName = toUpperCase(
  255. this.dialogMiddle.fieldName
  256. )
  257. if (this.addFlag === 'add') {
  258. this.form.fields.push(this.dialogMiddle)
  259. }
  260. this.dialogFlag = false
  261. } else {
  262. return false
  263. }
  264. })
  265. },
  266. closeDialog() {
  267. if (this.addFlag === 'edit') {
  268. this.dialogMiddle = this.bk
  269. }
  270. this.dialogFlag = false
  271. },
  272. deleteField(index) {
  273. this.form.fields.splice(index, 1)
  274. },
  275. async enterForm(isPreview) {
  276. if (this.form.fields.length <= 0) {
  277. this.$message({
  278. type: 'error',
  279. message: '请填写至少一个field'
  280. })
  281. return false
  282. }
  283. if (
  284. this.form.fields.some(item => item.fieldName === this.form.structName)
  285. ) {
  286. this.$message({
  287. type: 'error',
  288. message: '存在与结构体同名的字段'
  289. })
  290. return false
  291. }
  292. this.$refs.autoCodeForm.validate(async valid => {
  293. if (valid) {
  294. this.form.structName = toUpperCase(this.form.structName)
  295. if (this.form.structName === this.form.abbreviation) {
  296. this.$message({
  297. type: 'error',
  298. message: 'structName和struct简称不能相同'
  299. })
  300. return false
  301. }
  302. this.form.humpPackageName = toSQLLine(this.form.packageName)
  303. debugger
  304. if (isPreview) {
  305. const data = await preview(this.form)
  306. this.preViewCode = data.data.autoCode
  307. this.previewFlag = true
  308. } else {
  309. const data = await createTemp(this.form)
  310. if (data.headers?.success === 'false') {
  311. return
  312. } else {
  313. this.$message({
  314. type: 'success',
  315. message: '自动化代码创建成功,正在下载'
  316. })
  317. }
  318. const blob = new Blob([data])
  319. const fileName = 'ginvueadmin.zip'
  320. if ('download' in document.createElement('a')) {
  321. // 不是IE浏览器
  322. const url = window.URL.createObjectURL(blob)
  323. const link = document.createElement('a')
  324. link.style.display = 'none'
  325. link.href = url
  326. link.setAttribute('download', fileName)
  327. document.body.appendChild(link)
  328. link.click()
  329. document.body.removeChild(link) // 下载完成移除元素
  330. window.URL.revokeObjectURL(url) // 释放掉blob对象
  331. } else {
  332. // IE 10+
  333. window.navigator.msSaveBlob(blob, fileName)
  334. }
  335. }
  336. } else {
  337. return false
  338. }
  339. })
  340. },
  341. async getDb() {
  342. const res = await getDB()
  343. if (res.code === 0) {
  344. this.dbOptions = res.data.dbs
  345. }
  346. },
  347. async getTable() {
  348. const res = await getTable({ dbName: this.dbform.dbName })
  349. if (res.code === 0) {
  350. this.tableOptions = res.data.tables
  351. }
  352. this.dbform.tableName = ''
  353. },
  354. async getColumn() {
  355. const gormModelList = ['id', 'created_at', 'updated_at', 'deleted_at']
  356. const res = await getColumn(this.dbform)
  357. if (res.code === 0) {
  358. const tbHump = toHump(this.dbform.tableName)
  359. this.form.structName = toUpperCase(tbHump)
  360. this.form.tableName = this.dbform.tableName
  361. this.form.packageName = tbHump
  362. this.form.abbreviation = tbHump
  363. this.form.description = tbHump + '表'
  364. this.form.autoCreateApiToSql = true
  365. this.form.fields = []
  366. res.data.columns &&
  367. res.data.columns.map(item => {
  368. if (!gormModelList.some(gormfd => gormfd === item.columnName)) {
  369. const fbHump = toHump(item.columnName)
  370. this.form.fields.push({
  371. fieldName: toUpperCase(fbHump),
  372. fieldDesc: item.columnComment || fbHump + '字段',
  373. fieldType: this.fdMap[item.dataType],
  374. dataType: item.dataType,
  375. fieldJson: fbHump,
  376. dataTypeLong: item.dataTypeLong,
  377. columnName: item.columnName,
  378. comment: item.columnComment,
  379. fieldSearchType: '',
  380. dictType: ''
  381. })
  382. }
  383. })
  384. }
  385. },
  386. async setFdMap() {
  387. const fdTypes = ['string', 'int', 'bool', 'float64', 'time.Time']
  388. fdTypes.map(async fdtype => {
  389. const res = await getDict(fdtype)
  390. res && res.map(item => {
  391. this.fdMap[item.label] = fdtype
  392. })
  393. })
  394. }
  395. }
  396. }
  397. </script>
  398. <style scoped lang="scss">
  399. .button-box {
  400. padding: 10px 20px;
  401. .el-button {
  402. margin-right: 20px;
  403. float: right;
  404. }
  405. }
  406. </style>