2024-08-05 14:26:38 +08:00

455 lines
8.5 KiB
TypeScript

import { isEmpty, isFunction, isRegExp, isString } from "lodash-es";
import { PropRules } from "../dict";
import type { EpsColumn, EpsModule } from "../types";
export function useCode() {
// 特殊情况处理
const handler = {
// 单选
dict({ comment }: EpsColumn) {
const [label, ...arr] = comment.split(" ");
// 选项列表
const list: any[] = arr.map((e) => {
const [value, label] = e.split("-");
return {
label,
value: isNaN(Number(value)) ? value : Number(value)
};
});
// boolean
if (list.length == 2) {
list.forEach((e) => {
if (e.value == 1) {
e.type = "success";
} else {
e.type = "danger";
}
});
}
const d = {
table: {
label,
dict: list,
dictColor: true,
minWidth: 120
},
form: {
label,
component: {
name: "",
options: list
}
} as ClForm.Item
};
// 默认值
if (list[0]) {
d.form.value = list[0].value;
}
// 匹配组件
d.form.component!.name = arr.length > 4 ? "el-select" : "el-radio-group";
return d;
},
// 多选
dict_multiple(column: EpsColumn) {
const { table, form } = handler.dict(column);
if (!form.component?.props) {
form.component!.props = {};
}
if (!form.value) {
form.value = [];
}
switch (form.component?.name) {
case "el-select":
Object.assign(form.component.props, {
multiple: true,
filterable: true
});
break;
case "el-radio-group":
form.component.name = "el-checkbox-group";
break;
}
return {
table,
form
};
}
};
// 创建组件
function createComponent(column: EpsColumn, columns: EpsColumn[]) {
const prop = column.propertyName;
let label = column.comment || "";
let d: any;
let isHidden = false;
// 根据规则匹配组件
PropRules.find((r) => {
let s = false;
// 已知组件的情况下
if (column.component) {
if (column.component == r.value) {
s = true;
}
} else {
// 根据规则匹配
if (r.test) {
s = !!r.test.find((e) => {
if (isRegExp(e)) {
return e.test(prop);
}
if (isFunction(e)) {
return e(prop);
}
if (isString(e)) {
if (e == prop) {
return true;
}
const re = new RegExp(`${e}$`);
return re.test(prop.toLocaleLowerCase());
}
return false;
});
}
}
if (r.group) {
if (
r.group.includes(prop) &&
r.group.some((e) => columns.find((c) => c.propertyName == e))
) {
if (r.group[0] == prop) {
s = true;
} else {
isHidden = true;
}
}
}
if (s) {
if (r.handler) {
// @ts-ignore
const fn = isString(r.handler) ? handler[r.handler] : r.handler;
if (isFunction(fn)) {
d = fn(column);
}
} else {
d = {
...r,
test: undefined
};
}
}
return s;
});
// 没找到则默认input
if (!d) {
d = PropRules.find((e) => e.value == "input")?.render;
}
// 格式化标题
label = label.split(" ")[0];
return {
column: {
label,
prop,
...d?.table
},
item: {
label,
prop,
...d?.form
},
isHidden
};
}
// 创建 vue 代码
function createVue({
router = "",
columns = [],
prefix = "",
api = [],
fieldEq = [],
keyWordLikeFields = []
}: EpsModule) {
// 新增、编辑
const upsert = {
items: [] as DeepPartial<ClForm.Item>[]
};
// 表格
const table = {
columns: [] as DeepPartial<ClTable.Column>[]
};
// 选项
const options = {};
// 遍历
columns.forEach((e) => {
// 创建组件
const { item, column, isHidden } = createComponent(e, columns);
// 过滤隐藏
if (isHidden) {
return false;
}
// 验证规则
if (!e.nullable) {
item.required = true;
}
// 字典
const dict = item.component?.options || column.dict;
if (!isEmpty(dict)) {
options[item.prop] = dict;
const str = `$$options.${item.prop}$$`;
if (!column.component) {
column.dict = str;
}
item.component.options = str;
}
// 表单忽略
if (!["createTime", "updateTime", "id", "endTime", "endDate"].includes(item.prop)) {
upsert.items.push(item);
}
// 表格忽略
if (!["id"].includes(item.prop)) {
// 默认排序
if (item.prop == "createTime") {
column.sortable = "desc";
}
table.columns.push(column);
}
// 时间范围处理
if (["startTime", "startDate"].includes(item.prop)) {
const key = item.prop.replace("start", "");
if (columns.find((e) => e.propertyName == "end" + key)) {
item.prop = key.toLocaleLowerCase();
const isTime = item.prop == "time";
item.label = isTime ? "时间范围" : "日期范围";
item.hook = "datetimeRange";
item.component = {
name: "el-date-picker",
props: {
type: isTime ? "datetimerange" : "daterange",
valueFormat: isTime ? "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)
]
}
};
}
}
});
// 请求服务
const service = prefix.replace("/admin", "service").split("/");
// 请求地址
const paths = api.map((e) => e.path);
// 权限
const perms = {
add: paths.includes("/add"),
del: paths.includes("/delete"),
update: paths.includes("/info") && paths.includes("/update"),
page: paths.includes("/page"),
upsert: true
};
perms.upsert = perms.add || perms.update;
// 是否有操作栏
if (perms.del || perms.upsert) {
const buttons: ClTable.OpButton = [];
if (perms.upsert) {
buttons.push("edit");
}
if (perms.del) {
buttons.push("delete");
}
table.columns.push({
type: "op",
buttons
});
}
// 是否多选、序号
if (perms.del) {
table.columns.unshift({
type: "selection"
});
} else {
table.columns.unshift({
label: "#",
type: "index"
});
}
// 筛选
const clFilter = fieldEq
.map((field) => {
if (isEmpty(options[field])) {
return "";
}
const item = upsert.items.find((e) => e.prop == field);
if (!item) {
return "";
}
return `<!-- 筛选${item.label} -->\n<cl-filter label="${item.label}">\n<cl-select prop="${field}" :options="options.${field}" />\n</cl-filter>`;
})
.filter(Boolean)
.join("\n");
// 关键字搜索
const clSearchKeyPlaceholder = keyWordLikeFields
.map((field) => {
return table.columns.find((e) => e.prop == field)?.label;
})
.filter((e) => !!e)
.join("、");
// 选项
const ConstOptions = `${
isEmpty(options)
? ""
: "\n// 选项\nconst options = reactive(" + toCodeString(options) + ")\n"
}`;
// Vue 依赖
let ImportVue = "";
if (ConstOptions) {
ImportVue = "import { reactive } from 'vue';\n";
}
// 代码模板
const temp = `<template>
<cl-crud ref="Crud">
<cl-row>
<!-- 刷新按钮 -->
<cl-refresh-btn />
${perms.add ? "<!-- 新增按钮 -->\n <cl-add-btn />" : ""}
${perms.del ? "<!-- 删除按钮 -->\n <cl-multi-delete-btn />" : ""}
${clFilter}
<cl-flex1 />
<!-- 关键字搜索 -->
<cl-search-key placeholder="搜索${clSearchKeyPlaceholder || "关键字"}" />
</cl-row>
<cl-row>
<!-- 数据表格 -->
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<!-- 分页控件 -->
<cl-pagination />
</cl-row>
<!-- 新增、编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</template>
<script lang="ts" name="${router.replace(/^\//, "").replace(/\//g, "-")}" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
${ImportVue}
const { service } = useCool();
${ConstOptions}
// cl-upsert
const Upsert = useUpsert(${toCodeString(upsert)});
// cl-table
const Table = useTable(${toCodeString(table)});
// cl-crud
const Crud = useCrud(
{
service: ${service.join(".")}
},
(app) => {
app.refresh();
}
);
// 刷新
function refresh(params?: any) {
Crud.value?.refresh(params);
}
</script>`;
return temp.replace(/"\$\$|\$\$"/g, "");
}
// 转成代码字符串
function toCodeString(data: any) {
const arr: string[][] = [];
let code = JSON.stringify(data, (key, value) => {
if (isFunction(value)) {
const str = value.toString();
arr.push([JSON.stringify({ [key]: str }), str]);
return str;
} else {
return value;
}
});
arr.forEach((e) => {
code = code.replace(e[0].substring(1, e[0].length - 1), e[1]);
});
return code;
}
return {
handler,
createVue,
createComponent,
toCodeString
};
}