import { Plugin } from "vite"; import prettier from "prettier"; import fs from "fs"; import path from "path"; import { isFunction, isRegExp, isString } from "lodash"; import rules from "../config/rules"; // 根路径 const coolPath = path.join(__dirname, `../../src/cool`); // 格式化 function format(data: any) { return { label: data.label, prop: data.prop, ...data, component: data.component }; } // 颜色 const colors = [ "#409EFF", "#67C23A", "#E6A23C", "#F56C6C", "#909399", "#B0CFEB", "#FF9B91", "#E6A23C", "#BFAD6F", "#FB78F2" ]; // 组件处理器 const handler = { // 单选 dict({ comment }) { const [label, ...arr] = comment.split(" "); // 选择列表 const list = arr.map((e: string, i: number) => { const [value, label] = e.split("-"); const d: any = { label, value: isNaN(Number(value)) ? value : Number(value) }; if (i > 0 && colors[i]) { d.color = colors[i]; } return d; }); const d = { table: { label, dict: list }, form: { label, value: list[0]?.value, component: { name: "", options: list } } }; // 匹配组件 d.form.component.name = arr.length > 4 ? "el-select" : "el-radio-group"; return d; }, // 多选 dict_multiple({ comment }) { const { table, form }: any = handler.dict({ comment }); if (!form.component.props) { form.component.props = {}; } if (!form.value) { form.value = []; } switch (form.component.name) { case "el-select": form.component.props.multiple = true; form.component.props.filterable = true; break; case "el-radio-group": form.component.name = "el-checkbox-group"; break; } return { table, form }; } }; // 解析body function parseJson(req: any) { return new Promise((resolve, reject) => { let d = ""; req.on("data", function (chunk: Buffer) { d += chunk; }); req.on("end", function () { try { resolve(JSON.parse(d)); } catch (e) { reject(e); } }); }); } // 创建组件 function createComponent(item: any) { const { propertyName: prop, comment: label } = item; let d = null; rules.forEach((r: any) => { const s = r.test.find((e: any) => { if (isRegExp(e)) { return e.test(prop); } if (isFunction(e)) { return e(prop); } if (isString(e)) { const re = new RegExp(`${e}$`); return re.test(prop.toLocaleLowerCase()); } return false; }); if (s) { if (r.handler) { const fn = isString(r.handler) ? handler[r.handler] : r.handler; if (isFunction(fn)) { d = fn(item); } } else { d = { ...r, test: undefined }; } } }); function parse(v: any) { if (v?.name) { return { prop, label, component: v }; } else { return { prop, label, ...v }; } } return { column: parse(d?.table), item: parse(d?.form) }; } // 获取页面标识 function getPageName(router: string) { if (router.indexOf("/") === 0) { router = router.substr(1, router.length); } return router ? `name: "${router.replace("/", "-")}",` : ""; } // 时间合并 function datetimeMerge({ columns, item }: any) { if (["startTime", "startDate"].includes(item.prop)) { const key = item.prop.replace("start", ""); if (columns.find((e: any) => e.propertyName == "end" + key)) { item.label = key == "time" ? "时间范围" : "日期范围"; item.prop = key.toLocaleLowerCase(); item.hook = "datetimeRange"; item.component = { name: "el-date-picker", props: { type: key == "time" ? "datetimerange" : "daterange", valueFormat: "time" ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD 00:00:00", defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)] } }; } } } // 创建文件 function createVue({ router, columns, prefix, api, module, filename }: any): void { const upsert: any = { items: [] }; const table: any = { columns: [] }; // 遍历 columns.forEach((e: any) => { // 组件 const { item, column }: any = createComponent(e); // 验证规则 if (!e.nullable) { item.required = true; } // 忽略部分字段 if (!["createTime", "updateTime", "id", "endTime", "endDate"].includes(item.prop)) { datetimeMerge({ columns, item }); if (!item.component) { item.component = { name: "el-input" }; } upsert.items.push(format(item)); } table.columns.push(format(column)); }); // 服务 const service = prefix.replace("/admin", "service").replace(/\//g, "."); // 请求路径 const paths = api.map((e: any) => e.path); // 权限 const permission: any = { add: paths.includes("/add"), del: paths.includes("/delete"), update: paths.includes("/info") && paths.includes("/update"), page: paths.includes("/page"), upsert: true }; permission.upsert = permission.add || permission.update; // 是否有操作栏 if (permission.del || permission.upsert) { const d: any = { type: "op", buttons: [] }; if (permission.upsert) { d.buttons.push("edit"); } if (permission.del) { d.buttons.push("delete"); } table.columns.push(d); } // 是否多选、序号 if (permission.del) { table.columns.unshift({ type: "selection" }); } else { table.columns.unshift({ label: "#", type: "index" }); } // 代码模板 const temp = ` `; const content = prettier.format(temp, { parser: "vue", useTabs: true, tabWidth: 4, endOfLine: "lf", semi: true, jsxBracketSameLine: true, singleQuote: false, printWidth: 100, trailingComma: "none" }); // views 目录是否存在 const dir = path.join(coolPath, `modules/${module}/views`); if (!fs.existsSync(dir)) fs.mkdirSync(dir); // 创建文件 fs.createWriteStream(path.join(dir, `${filename}.vue`), { flags: "w" }).write(content); } export const cool = (): Plugin | null => { return { name: "vite-cool", configureServer(server) { server.middlewares.use(async (req, res, next) => { function done(data) { res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" }); res.end(JSON.stringify(data)); } if (req.url.includes("/__cool_createMenu")) { try { const body: any = await parseJson(req); await createVue(body); done({ code: 1000 }); } catch (e) { done({ code: 1001, message: e.message }); } } else if (req.url.includes("/__cool_modules")) { const dirs = fs.readdirSync(path.join(coolPath, "modules")); done({ code: 1000, data: dirs.filter((e) => !e.includes(".")) }); } else { next(); } }); } }; };