code.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. import { isEmpty, isFunction, isRegExp, isString } from "lodash-es";
  2. import { PropRules } from "../dict";
  3. import type { EpsColumn, EpsModule } from "../types";
  4. export function useCode() {
  5. // 特殊情况处理
  6. const handler = {
  7. // 单选
  8. dict({ comment }: EpsColumn) {
  9. const [label, ...arr] = comment.split(" ");
  10. // 选项列表
  11. const list: any[] = arr.map((e) => {
  12. const [value, label] = e.split("-");
  13. return {
  14. label,
  15. value: isNaN(Number(value)) ? value : Number(value)
  16. };
  17. });
  18. // boolean
  19. if (list.length == 2) {
  20. list.forEach((e) => {
  21. if (e.value == 1) {
  22. e.type = "success";
  23. } else {
  24. e.type = "danger";
  25. }
  26. });
  27. }
  28. const d = {
  29. table: {
  30. label,
  31. dict: list,
  32. minWidth: 120
  33. },
  34. form: {
  35. label,
  36. component: {
  37. name: "",
  38. options: list
  39. }
  40. } as ClForm.Item
  41. };
  42. // 默认值
  43. if (list[0]) {
  44. d.form.value = list[0].value;
  45. }
  46. // 匹配组件
  47. d.form.component!.name = arr.length > 4 ? "el-select" : "el-radio-group";
  48. return d;
  49. },
  50. // 多选
  51. dict_multiple(column: EpsColumn) {
  52. const { table, form } = handler.dict(column);
  53. if (!form.component?.props) {
  54. form.component!.props = {};
  55. }
  56. if (!form.value) {
  57. form.value = [];
  58. }
  59. switch (form.component?.name) {
  60. case "el-select":
  61. Object.assign(form.component.props, {
  62. multiple: true,
  63. filterable: true
  64. });
  65. break;
  66. case "el-radio-group":
  67. form.component.name = "el-checkbox-group";
  68. break;
  69. }
  70. return {
  71. table,
  72. form
  73. };
  74. }
  75. };
  76. // 创建组件
  77. function createComponent(column: EpsColumn, columns: EpsColumn[]) {
  78. const prop = column.propertyName;
  79. let label = column.comment || "";
  80. let d: any;
  81. let isHidden = false;
  82. // 根据规则匹配组件
  83. PropRules.find((r) => {
  84. let s = false;
  85. // 已知组件的情况下
  86. if (column.component) {
  87. if (column.component == r.value) {
  88. s = true;
  89. }
  90. } else {
  91. // 根据规则匹配
  92. if (r.test) {
  93. s = !!r.test.find((e) => {
  94. if (isRegExp(e)) {
  95. return e.test(prop);
  96. }
  97. if (isFunction(e)) {
  98. return e(prop);
  99. }
  100. if (isString(e)) {
  101. if (e == prop) {
  102. return true;
  103. }
  104. const re = new RegExp(`${e}$`);
  105. return re.test(prop.toLocaleLowerCase());
  106. }
  107. return false;
  108. });
  109. }
  110. }
  111. if (r.group) {
  112. if (
  113. r.group.includes(prop) &&
  114. r.group.some((e) => columns.find((c) => c.propertyName == e))
  115. ) {
  116. if (r.group[0] == prop) {
  117. s = true;
  118. } else {
  119. isHidden = true;
  120. }
  121. }
  122. }
  123. if (s) {
  124. if (r.handler) {
  125. // @ts-ignore
  126. const fn = isString(r.handler) ? handler[r.handler] : r.handler;
  127. if (isFunction(fn)) {
  128. d = fn(column);
  129. }
  130. } else {
  131. d = {
  132. ...r,
  133. test: undefined
  134. };
  135. }
  136. }
  137. return s;
  138. });
  139. // 没找到则默认input
  140. if (!d) {
  141. d = PropRules.find((e) => e.value == "input")?.render;
  142. }
  143. // 格式化标题
  144. label = label?.split?.(" ")?.[0] || column.propertyName;
  145. return {
  146. column: {
  147. label,
  148. prop,
  149. ...d?.table
  150. },
  151. item: {
  152. label,
  153. prop,
  154. ...d?.form
  155. },
  156. isHidden
  157. };
  158. }
  159. // 创建 vue 代码
  160. function createVue({
  161. router = "",
  162. columns = [],
  163. prefix = "",
  164. api = [],
  165. fieldEq = [],
  166. keyWordLikeFields = []
  167. }: EpsModule) {
  168. // 新增、编辑
  169. const upsert = {
  170. items: [] as DeepPartial<ClForm.Item>[]
  171. };
  172. // 表格
  173. const table = {
  174. columns: [] as DeepPartial<ClTable.Column>[]
  175. };
  176. // 选项
  177. const options = {};
  178. // 遍历
  179. columns.forEach((e) => {
  180. // 创建组件
  181. const { item, column, isHidden } = createComponent(e, columns);
  182. // 过滤隐藏
  183. if (isHidden) {
  184. return false;
  185. }
  186. // 验证规则
  187. if (!e.nullable) {
  188. item.required = true;
  189. }
  190. // 字典
  191. const dict = item.component?.options || column.dict;
  192. if (!isEmpty(dict)) {
  193. options[item.prop] = dict;
  194. const str = `$$options.${item.prop}$$`;
  195. if (!column.component) {
  196. column.dict = str;
  197. }
  198. item.component.options = str;
  199. }
  200. // 表单忽略
  201. if (!["createTime", "updateTime", "id", "endTime", "endDate"].includes(item.prop)) {
  202. upsert.items.push(item);
  203. }
  204. // 表格忽略
  205. if (!["id"].includes(item.prop)) {
  206. // 默认排序
  207. if (item.prop == "createTime") {
  208. column.sortable = "desc";
  209. }
  210. table.columns.push(column);
  211. }
  212. // 时间范围处理
  213. if (["startTime", "startDate"].includes(item.prop)) {
  214. const key = item.prop.replace("start", "");
  215. if (columns.find((e) => e.propertyName == "end" + key)) {
  216. item.prop = key.toLocaleLowerCase();
  217. const isTime = item.prop == "time";
  218. item.label = isTime ? "时间范围" : "日期范围";
  219. item.hook = "datetimeRange";
  220. item.component = {
  221. name: "el-date-picker",
  222. props: {
  223. type: isTime ? "datetimerange" : "daterange",
  224. valueFormat: isTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD 00:00:00",
  225. defaultTime: [
  226. new Date(2000, 1, 1, 0, 0, 0),
  227. new Date(2000, 1, 1, 23, 59, 59)
  228. ]
  229. }
  230. };
  231. }
  232. }
  233. });
  234. // 请求服务
  235. const service = prefix.replace("/admin", "service").split("/");
  236. // 请求地址
  237. const paths = api.map((e) => e.path);
  238. // 权限
  239. const perms = {
  240. add: paths.includes("/add"),
  241. del: paths.includes("/delete"),
  242. update: paths.includes("/info") && paths.includes("/update"),
  243. page: paths.includes("/page"),
  244. upsert: true
  245. };
  246. perms.upsert = perms.add || perms.update;
  247. // 是否有操作栏
  248. if (perms.del || perms.upsert) {
  249. const buttons: ClTable.OpButton = [];
  250. if (perms.upsert) {
  251. buttons.push("edit");
  252. }
  253. if (perms.del) {
  254. buttons.push("delete");
  255. }
  256. table.columns.push({
  257. type: "op",
  258. buttons
  259. });
  260. }
  261. // 是否多选、序号
  262. if (perms.del) {
  263. table.columns.unshift({
  264. type: "selection"
  265. });
  266. } else {
  267. table.columns.unshift({
  268. label: "#",
  269. type: "index"
  270. });
  271. }
  272. // 筛选
  273. const clFilter = fieldEq
  274. .map((field) => {
  275. if (isEmpty(options[field])) {
  276. return "";
  277. }
  278. const item = upsert.items.find((e) => e.prop == field);
  279. if (!item) {
  280. return "";
  281. }
  282. return `<!-- 筛选${item.label} -->\n<cl-filter label="${item.label}">\n<cl-select prop="${field}" :options="options.${field}" />\n</cl-filter>`;
  283. })
  284. .filter(Boolean)
  285. .join("\n");
  286. // 关键字搜索
  287. const clSearchKeyPlaceholder = keyWordLikeFields
  288. .map((field) => {
  289. return table.columns.find((e) => e.prop == field)?.label;
  290. })
  291. .filter((e) => !!e)
  292. .join("、");
  293. // 选项
  294. const ConstOptions = `${
  295. isEmpty(options)
  296. ? ""
  297. : "\n// 选项\nconst options = reactive(" + toCodeString(options) + ")\n"
  298. }`;
  299. // Vue 依赖
  300. let ImportVue = "";
  301. if (ConstOptions) {
  302. ImportVue = "import { reactive } from 'vue';\n";
  303. }
  304. // 代码模板
  305. const temp = `<template>
  306. <cl-crud ref="Crud">
  307. <cl-row>
  308. <!-- 刷新按钮 -->
  309. <cl-refresh-btn />
  310. ${perms.add ? "<!-- 新增按钮 -->\n <cl-add-btn />" : ""}
  311. ${perms.del ? "<!-- 删除按钮 -->\n <cl-multi-delete-btn />" : ""}
  312. ${clFilter}
  313. <cl-flex1 />
  314. <!-- 关键字搜索 -->
  315. <cl-search-key placeholder="搜索${clSearchKeyPlaceholder || "关键字"}" />
  316. </cl-row>
  317. <cl-row>
  318. <!-- 数据表格 -->
  319. <cl-table ref="Table" />
  320. </cl-row>
  321. <cl-row>
  322. <cl-flex1 />
  323. <!-- 分页控件 -->
  324. <cl-pagination />
  325. </cl-row>
  326. <!-- 新增、编辑 -->
  327. <cl-upsert ref="Upsert" />
  328. </cl-crud>
  329. </template>
  330. <script lang="ts" name="${router.replace(/^\//, "").replace(/\//g, "-")}" setup>
  331. import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
  332. import { useCool } from "/@/cool";
  333. ${ImportVue}
  334. const { service } = useCool();
  335. ${ConstOptions}
  336. // cl-upsert
  337. const Upsert = useUpsert(${toCodeString(upsert)});
  338. // cl-table
  339. const Table = useTable(${toCodeString(table)});
  340. // cl-crud
  341. const Crud = useCrud(
  342. {
  343. service: ${service.join(".")}
  344. },
  345. (app) => {
  346. app.refresh();
  347. }
  348. );
  349. // 刷新
  350. function refresh(params?: any) {
  351. Crud.value?.refresh(params);
  352. }
  353. </script>`;
  354. return temp.replace(/"\$\$|\$\$"/g, "");
  355. }
  356. // 转成代码字符串
  357. function toCodeString(data: any) {
  358. const arr: string[][] = [];
  359. let code = JSON.stringify(data, (key, value) => {
  360. if (isFunction(value)) {
  361. const str = value.toString();
  362. arr.push([JSON.stringify({ [key]: str }), str]);
  363. return str;
  364. } else {
  365. return value;
  366. }
  367. });
  368. arr.forEach((e) => {
  369. code = code.replace(e[0].substring(1, e[0].length - 1), e[1]);
  370. });
  371. return code;
  372. }
  373. return {
  374. handler,
  375. createVue,
  376. createComponent,
  377. toCodeString
  378. };
  379. }