icssoa 1 سال پیش
والد
کامیت
c1b837a0bb
37فایلهای تغییر یافته به همراه431 افزوده شده و 340 حذف شده
  1. 1 1
      .gitignore
  2. 55 0
      build/cool/base.ts
  3. 88 133
      build/cool/eps/index.ts
  4. 4 71
      build/cool/index.ts
  5. 1 3
      build/cool/module/index.ts
  6. 32 0
      build/cool/types/index.d.ts
  7. 61 0
      build/cool/virtual.ts
  8. 1 1
      package.json
  9. 3 3
      packages/crud/index.d.ts
  10. 1 1
      packages/crud/package.json
  11. 2 1
      packages/crud/src/components/crud/helper.ts
  12. 7 4
      packages/crud/src/components/form/index.tsx
  13. 1 1
      packages/crud/src/components/table/helper/height.ts
  14. 1 1
      packages/crud/src/static/index.scss
  15. 12 11
      packages/crud/src/utils/parse.tsx
  16. 2 2
      packages/crud/types/components/adv/search.d.ts
  17. 2 2
      packages/crud/types/components/form-tabs/index.d.ts
  18. 12 12
      packages/crud/types/components/form/helper/index.d.ts
  19. 2 2
      packages/crud/types/components/search-key/index.d.ts
  20. 8 8
      packages/crud/types/components/table/helper/index.d.ts
  21. 1 1
      packages/crud/types/utils/parse.d.ts
  22. 14 22
      src/cool/utils/index.ts
  23. 2 2
      src/modules/base/components/select/index.tsx
  24. 52 0
      src/modules/base/components/view/head/index.vue
  25. 1 3
      src/modules/base/pages/login/index.vue
  26. 1 6
      src/modules/base/store/menu.ts
  27. 2 8
      src/modules/base/views/role.vue
  28. 3 0
      src/modules/demo/components/form-btn.vue
  29. 1 1
      src/modules/dict/store/dict.ts
  30. 4 3
      src/modules/dict/types/index.d.ts
  31. 11 5
      src/modules/extend/editor/monaco/index.vue
  32. 15 1
      src/modules/extend/editor/preview.vue
  33. 15 23
      src/modules/extend/editor/wang.vue
  34. 0 1
      src/modules/magic/hooks/menu.ts
  35. 3 2
      src/modules/upload/components/upload-item/index.vue
  36. 6 1
      src/modules/upload/components/upload.vue
  37. 4 4
      yarn.lock

+ 1 - 1
.gitignore

@@ -3,4 +3,4 @@ node_modules
 dist
 dist-ssr
 *.local
-pnpm-lock.yaml
+pnpm-lock.yaml

+ 55 - 0
build/cool/base.ts

@@ -0,0 +1,55 @@
+import type { Plugin } from "vite";
+import { createSvg } from "./svg";
+import { createTag } from "./tag";
+import { createEps } from "./eps";
+import { createMenu } from "./menu";
+import { parseJson } from "./utils";
+
+export function base(): Plugin {
+	return {
+		name: "vite-cool-base",
+		enforce: "pre",
+		configureServer(server) {
+			server.middlewares.use(async (req, res, next) => {
+				function done(data: any) {
+					res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
+					res.end(JSON.stringify(data));
+				}
+
+				if (req.url?.includes("__cool")) {
+					const body = await parseJson(req);
+
+					switch (req.url) {
+						// 快速创建菜单
+						case "/__cool_createMenu":
+							await createMenu(body);
+							break;
+
+						// 创建描述文件
+						case "/__cool_eps":
+							await createEps(body);
+							break;
+
+						default:
+							return done({
+								code: 1001,
+								message: "未知请求"
+							});
+					}
+
+					done({
+						code: 1000
+					});
+				} else {
+					next();
+				}
+			});
+		},
+		transform(code, id) {
+			return createTag(code, id);
+		},
+		transformIndexHtml(html) {
+			return createSvg(html);
+		}
+	};
+}

+ 88 - 133
build/cool/eps/index.ts

@@ -2,56 +2,27 @@ import { createDir, error, firstUpperCase, readFile, toCamel } from "../utils";
 import { join } from "path";
 import { Entity, DistPath } from "./config";
 import axios from "axios";
-import { isArray, isEmpty, last } from "lodash";
+import { isArray, isEmpty, last, merge } from "lodash";
 import { createWriteStream } from "fs";
 import prettier from "prettier";
 import { proxy } from "../../../src/config/proxy";
-
-// 实体类型
-type Entity = {
-	api: {
-		dts: {
-			parameters?: {
-				description: string;
-				name: string;
-				required: boolean;
-				schema: {
-					type: string;
-				};
-			}[];
-		};
-		name: string;
-		method: string;
-		path: string;
-		prefix: string;
-		summary: string;
-		tag: string;
-	}[];
-	columns: {
-		comment: string;
-		length: string;
-		nullable: boolean;
-		propertyName: string;
-		type: string;
-	}[];
-	module: string;
-	name: string;
-	prefix: string;
-};
+import type { Eps } from "../types";
 
 // 获取方法名
 function getNames(v: any) {
 	return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
 }
 
-// 获取数据
-async function getData(temps: any[]) {
-	let list: Entity[] = [];
+// 数据
+let service = {};
+let list: Eps.Entity[] = [];
 
+// 获取数据
+async function getData(temps?: Eps.Entity[]) {
 	// 本地文件
 	try {
 		list = JSON.parse(readFile(join(DistPath, "eps.json")) || "[]");
-	} catch (err) {
+	} catch (err: any) {
 		error(`[eps] ${join(DistPath, "eps.json")} 文件异常, ${err.message}`);
 	}
 
@@ -68,60 +39,55 @@ async function getData(temps: any[]) {
 
 			if (code === 1000) {
 				if (!isEmpty(data) && data) {
-					// @ts-ignore
-					list = Object.values(data).flat();
+					list = Object.values(data).flat() as Eps.Entity[];
 				}
 			} else {
 				error(`[eps] ${message}`);
 			}
 		})
 		.catch(() => {
-			error(`[eps] 获取失败, ${url} 无法访问!`);
+			error(`[eps] ${url} 服务未启动!!!`);
 		});
 
-	// 判断是否重复项
+	// 合并本地 service 数据
 	if (isArray(temps)) {
 		temps.forEach((e) => {
 			const d = list.find((a) => e.prefix === a.prefix);
 
 			if (d) {
-				Object.assign(d, e);
+				merge(d, e);
 			} else {
 				list.push(e);
 			}
 		});
 	}
-
-	return list;
 }
 
-// 创建数据文件
-function createJson(eps: Entity[]) {
-	createWriteStream(join(DistPath, "eps.json"), {
-		flags: "w"
-	}).write(
-		JSON.stringify(
-			eps.map((e) => {
+// 创建 json 文件
+function createJson() {
+	const d = list.map((e) => {
+		return {
+			prefix: e.prefix,
+			name: e.name || "",
+			api: e.api.map((e) => {
 				return {
-					prefix: e.prefix,
-					name: e.name || "",
-					api: e.api.map((e) => {
-						return {
-							name: e.name,
-							method: e.method,
-							path: e.path
-						};
-					})
+					name: e.name,
+					method: e.method,
+					path: e.path
 				};
 			})
-		)
-	);
+		};
+	});
+
+	createWriteStream(join(DistPath, "eps.json"), {
+		flags: "w"
+	}).write(JSON.stringify(d));
 }
 
 // 创建描述文件
-async function createDescribe({ list, service }: { list: Entity[]; service: any }) {
+async function createDescribe({ list, service }: { list: Eps.Entity[]; service: any }) {
 	// 获取类型
-	function getType({ propertyName, type }) {
+	function getType({ propertyName, type }: any) {
 		for (const map of Entity.mapping) {
 			if (map.custom) {
 				const resType = map.custom({ propertyName, type });
@@ -355,7 +321,7 @@ async function createDescribe({ list, service }: { list: Entity[]; service: any
 	`;
 
 	// 文本内容
-	const content = await prettier.format(text, {
+	const content = prettier.format(text, {
 		parser: "typescript",
 		useTabs: true,
 		tabWidth: 4,
@@ -372,93 +338,82 @@ async function createDescribe({ list, service }: { list: Entity[]; service: any
 	}).write(content);
 }
 
-// 创建服务
-function createService(data: Entity[]) {
-	const list: Entity[] = [];
-	const service = {};
-	const d = { data };
-
-	for (const i in d) {
-		if (isArray(d[i])) {
-			d[i].forEach((e: Entity) => {
-				// 分隔路径
-				const arr = e.prefix
-					.replace(/\//, "")
-					.replace("admin", "")
-					.split("/")
-					.filter(Boolean)
-					.map(toCamel);
-
-				// 遍历
-				function deep(d: any, i: number) {
-					const k = arr[i];
-
-					if (k) {
-						// 是否最后一个
-						if (arr[i + 1]) {
-							if (!d[k]) {
-								d[k] = {};
-							}
-
-							deep(d[k], i + 1);
-						} else {
-							// 本地不存在则创建实例
-							if (!d[k]) {
-								d[k] = {
-									namespace: e.prefix.substring(1, e.prefix.length),
-									permission: {}
-								};
-							}
-
-							// 创建方法
-							e.api.forEach((a) => {
-								// 方法名
-								const n = a.path.replace("/", "");
+// 创建 service
+function createService() {
+	list.forEach((e) => {
+		// 分隔路径
+		const arr = e.prefix
+			.replace(/\//, "")
+			.replace("admin", "")
+			.split("/")
+			.filter(Boolean)
+			.map(toCamel);
+
+		// 遍历
+		function deep(d: any, i: number) {
+			const k = arr[i];
+
+			if (k) {
+				// 是否最后一个
+				if (arr[i + 1]) {
+					if (!d[k]) {
+						d[k] = {};
+					}
 
-								if (n && !/[-:]/g.test(n)) {
-									d[k][n] = a;
-								}
-							});
+					deep(d[k], i + 1);
+				} else {
+					// 不存在则创建
+					if (!d[k]) {
+						d[k] = {
+							namespace: e.prefix.substring(1, e.prefix.length),
+							permission: {}
+						};
+					}
 
-							// 创建权限
-							getNames(d[k]).forEach((e) => {
-								d[k].permission[e] = `${d[k].namespace.replace(
-									"admin/",
-									""
-								)}/${e}`.replace(/\//g, ":");
-							});
+					// 创建方法
+					e.api.forEach((a) => {
+						// 方法名
+						const n = a.path.replace("/", "");
 
-							list.push(e);
+						if (n && !/[-:]/g.test(n)) {
+							d[k][n] = a;
 						}
-					}
+					});
+
+					// 创建权限
+					getNames(d[k]).forEach((e) => {
+						d[k].permission[e] = `${d[k].namespace.replace("admin/", "")}/${e}`.replace(
+							/\//g,
+							":"
+						);
+					});
 				}
-
-				deep(service, 0);
-			});
+			}
 		}
-	}
 
-	return { service, list };
+		deep(service, 0);
+	});
 }
 
 // 创建 eps
 export async function createEps(query?: { list: any[] }) {
 	// 获取数据
-	const data = await getData(query?.list || []);
+	await getData(query?.list || []);
 
-	// 生成数据
-	const { service, list } = createService(data);
+	// 创建 service
+	createService();
 
 	// 创建临时目录
 	createDir(DistPath);
 
-	// 创建数据文件
-	createJson(data);
+	// 创建 json 文件
+	createJson();
 
 	// 创建描述文件
 	createDescribe({ service, list });
 
-	return `
-	export const eps = ${JSON.stringify({ service, list })}
-	`;
+	return {
+		service,
+		list
+	};
 }

+ 4 - 71
build/cool/index.ts

@@ -1,73 +1,6 @@
-import { Plugin } from "vite";
-import { createSvg } from "./svg";
-import { createTag } from "./tag";
-import { createEps } from "./eps";
-import { createModule } from "./module";
-import { createMenu } from "./menu";
-import { parseJson } from "./utils";
+import { base } from "./base";
+import { virtual } from "./virtual";
 
-export function cool(): Plugin {
-	// 虚拟模块
-	const virtualModuleIds = ["virtual:eps", "virtual:module"];
-
-	return {
-		name: "vite-cool",
-		enforce: "pre",
-		configureServer(server) {
-			server.middlewares.use(async (req, res, next) => {
-				function done(data: any) {
-					res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
-					res.end(JSON.stringify(data));
-				}
-
-				if (req.url?.includes("__cool")) {
-					const body = await parseJson(req);
-
-					switch (req.url) {
-						// 快速创建菜单
-						case "/__cool_createMenu":
-							await createMenu(body);
-							break;
-
-						// 创建描述文件
-						case "/__cool_eps":
-							await createEps(body);
-							break;
-
-						default:
-							return done({
-								code: 1001,
-								message: "未知请求"
-							});
-					}
-
-					done({
-						code: 1000
-					});
-				} else {
-					next();
-				}
-			});
-		},
-		transform(code, id) {
-			return createTag(code, id);
-		},
-		transformIndexHtml(html) {
-			return createSvg(html);
-		},
-		resolveId(id) {
-			if (virtualModuleIds.includes(id)) {
-				return "\0" + id;
-			}
-		},
-		async load(id) {
-			if (id === "\0virtual:eps") {
-				return createEps();
-			}
-
-			if (id === "\0virtual:module") {
-				return createModule();
-			}
-		}
-	};
+export function cool() {
+	return [base(), virtual()];
 }

+ 1 - 3
build/cool/module/index.ts

@@ -8,7 +8,5 @@ export function createModule() {
 		dirs = dirs.filter((e) => !e.includes("."));
 	} catch (err) {}
 
-	return `
-		export const dirs = ${JSON.stringify(dirs)}
-	`;
+	return { dirs };
 }

+ 32 - 0
build/cool/types/index.d.ts

@@ -0,0 +1,32 @@
+export namespace Eps {
+	interface Entity {
+		api: {
+			dts: {
+				parameters?: {
+					description: string;
+					name: string;
+					required: boolean;
+					schema: {
+						type: string;
+					};
+				}[];
+			};
+			name: string;
+			method: string;
+			path: string;
+			prefix: string;
+			summary: string;
+			tag: string;
+		}[];
+		columns: {
+			comment: string;
+			length: string;
+			nullable: boolean;
+			propertyName: string;
+			type: string;
+		}[];
+		module: string;
+		name: string;
+		prefix: string;
+	}
+}

+ 61 - 0
build/cool/virtual.ts

@@ -0,0 +1,61 @@
+import type { Plugin, ViteDevServer } from "vite";
+import { createEps } from "./eps";
+import { createModule } from "./module";
+
+export function virtual(): Plugin {
+	const virtualModuleIds = ["virtual:eps", "virtual:module"];
+
+	// 使虚拟模块失效,重新加载
+	function buildEps(server: ViteDevServer) {
+		virtualModuleIds.forEach((vm) => {
+			const mod = server.moduleGraph.getModuleById(`\0${vm}`);
+
+			if (mod) {
+				server.moduleGraph.invalidateModule(mod);
+			}
+		});
+	}
+
+	return {
+		name: "vite-cool-virtual",
+		enforce: "pre",
+		configureServer(server) {
+			server.middlewares.use(async (req, res, next) => {
+				// 页面刷新时触发 eps 刷新
+				if (req.url == "/@vite/client") {
+					buildEps(server);
+				}
+
+				next();
+			});
+		},
+		handleHotUpdate({ file, server }) {
+			// 代码保存时触发 eps 刷新
+			if (!file.includes("build/cool/dist")) {
+				buildEps(server);
+			}
+		},
+		resolveId(id) {
+			if (virtualModuleIds.includes(id)) {
+				return "\0" + id;
+			}
+		},
+		async load(id) {
+			if (id === "\0virtual:eps") {
+				const { service, list } = await createEps();
+
+				return `
+					export const eps = ${JSON.stringify({ service, list })}
+				`;
+			}
+
+			if (id === "\0virtual:module") {
+				const { dirs } = createModule();
+
+				return `
+					export const dirs = ${JSON.stringify(dirs)}
+				`;
+			}
+		}
+	};
+}

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
 		"lint:eslint": "eslint \"{src}/**/*.{vue,ts,tsx}\" --fix"
 	},
 	"dependencies": {
-		"@cool-vue/crud": "^7.0.1-beta2",
+		"@cool-vue/crud": "^7.0.1-beta9",
 		"@element-plus/icons-vue": "^2.1.0",
 		"@vueuse/core": "^10.4.0",
 		"@wangeditor/editor": "^5.1.23",

+ 3 - 3
packages/crud/index.d.ts

@@ -56,8 +56,8 @@ declare type List<T> = Array<DeepPartial<T> | (() => DeepPartial<T>)>;
 
 // 字典选项
 declare type DictOptions = {
-	label: string;
-	value: any;
+	label?: string;
+	value?: any;
 	color?: string;
 	type?: string;
 	[key: string]: any;
@@ -703,7 +703,7 @@ declare interface Config {
 		size: ElementPlus.Size;
 		colors: string[];
 		form: {
-			labelPostion: ElementPlus.FormProps["labelPosition"];
+			labelPosition: ElementPlus.FormProps["labelPosition"];
 			labelWidth: ElementPlus.FormProps["labelWidth"];
 			span: number;
 		};

+ 1 - 1
packages/crud/package.json

@@ -1,6 +1,6 @@
 {
 	"name": "@cool-vue/crud",
-	"version": "7.0.1-beta2",
+	"version": "7.0.1-beta9",
 	"private": false,
 	"main": "./dist/index.umd.min.js",
 	"typings": "types/index.d.ts",

+ 2 - 1
packages/crud/src/components/crud/helper.ts

@@ -1,7 +1,8 @@
 import { ElMessageBox, ElMessage } from "element-plus";
 import { Mitt } from "../../utils/mitt";
 import { ref } from "vue";
-import { isArray, isFunction, merge } from "lodash-es";
+import { isArray, isFunction } from "lodash-es";
+import { merge } from "../../utils";
 
 interface Options {
 	mitt: Mitt;

+ 7 - 4
packages/crud/src/components/form/index.tsx

@@ -463,9 +463,6 @@ export default defineComponent({
 						<el-form
 							ref={Form}
 							size={style.size}
-							label-position={
-								browser.isMini && !props.inline ? "top" : style.form.labelPostion
-							}
 							label-width={style.form.labelWidth}
 							inline={props.inline}
 							disabled={saving.value}
@@ -476,7 +473,13 @@ export default defineComponent({
 								e.preventDefault();
 							}}
 						/>,
-						config.props,
+						{
+							...config.props,
+							labelPosition:
+								browser.isMini && !props.inline
+									? "top"
+									: config.props.labelPosition || style.form.labelPosition
+						},
 						{
 							default: () => {
 								return (

+ 1 - 1
packages/crud/src/components/table/helper/height.ts

@@ -15,7 +15,7 @@ export function useHeight({ config, Table }: { Table: Vue.Ref<any>; config: ClTa
 		let vm = Table.value;
 
 		if (vm) {
-			while (!vm.$parent?.$el.className.includes("cl-crud")) {
+			while (!vm.$parent?.$el.className?.includes("cl-crud")) {
 				vm = vm.$parent;
 			}
 

+ 1 - 1
packages/crud/src/static/index.scss

@@ -405,7 +405,7 @@
 		grid-template-rows: 0fr;
 
 		> .cl-form-item__children {
-			margin: 10px 10px 10px 0px;
+			margin: 10px;
 			min-height: 0;
 			overflow: hidden;
 		}

+ 12 - 11
packages/crud/src/utils/parse.tsx

@@ -29,15 +29,16 @@ export function parseTableDict(value: any, item: ClTable.Column) {
 	// 设置颜色
 	if (item.dictColor) {
 		options.forEach((e, i) => {
-			e.color = style.colors[i];
+			if (!e.color) {
+				e.color = style.colors[i];
+			}
 		});
 	}
 
-	// 格式化方法
-	const formatter = item.dictFormatter;
-
-	// 多个值
-	const values = isArray(value) ? value : [value];
+	// 绑定值
+	const values = (isArray(value) ? value : [value]).filter(
+		(e) => e !== undefined && e !== null && e !== ""
+	);
 
 	// 返回值
 	const list = values.map((v) => {
@@ -48,8 +49,8 @@ export function parseTableDict(value: any, item: ClTable.Column) {
 	});
 
 	// 是否格式化
-	if (formatter) {
-		return formatter(list);
+	if (item.dictFormatter) {
+		return item.dictFormatter(list);
 	} else {
 		return list.map((e) => {
 			return h(
@@ -66,14 +67,14 @@ export function parseTableDict(value: any, item: ClTable.Column) {
 /**
  * 解析 table.op.buttons
  */
-export function parseTableOpButtons(buttons: any, { scope }: any) {
+export function parseTableOpButtons(buttons: any[], { scope }: any) {
 	const { crud } = useCore();
 	const { style } = useConfig();
 	const slots = useSlots();
 
-	const list = getValue(buttons, { scope }) || ["edit", "delete"];
+	const list: any[] = getValue(buttons, { scope }) || ["edit", "delete"];
 
-	return list.map((vnode: any) => {
+	return list.map((vnode) => {
 		if (vnode === "info") {
 			return (
 				<el-button

+ 2 - 2
packages/crud/types/components/adv/search.d.ts

@@ -7,7 +7,7 @@ declare const _default: import("vue").DefineComponent<{
     };
     title: StringConstructor;
     size: {
-        type: (StringConstructor | NumberConstructor)[];
+        type: (NumberConstructor | StringConstructor)[];
         default: string;
     };
     op: {
@@ -22,7 +22,7 @@ declare const _default: import("vue").DefineComponent<{
     };
     title: StringConstructor;
     size: {
-        type: (StringConstructor | NumberConstructor)[];
+        type: (NumberConstructor | StringConstructor)[];
         default: string;
     };
     op: {

+ 2 - 2
packages/crud/types/components/form-tabs/index.d.ts

@@ -1,6 +1,6 @@
 import { PropType } from "vue";
 declare const _default: import("vue").DefineComponent<{
-    modelValue: (StringConstructor | NumberConstructor)[];
+    modelValue: (NumberConstructor | StringConstructor)[];
     labels: {
         type: ArrayConstructor;
         default: () => never[];
@@ -14,7 +14,7 @@ declare const _default: import("vue").DefineComponent<{
         default: string;
     };
 }, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue")[], "change" | "update:modelValue", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
-    modelValue: (StringConstructor | NumberConstructor)[];
+    modelValue: (NumberConstructor | StringConstructor)[];
     labels: {
         type: ArrayConstructor;
         default: () => never[];

+ 12 - 12
packages/crud/types/components/form/helper/index.d.ts

@@ -74,15 +74,15 @@ export declare function useForm(): {
                 name?: string | undefined;
                 options?: {
                     [x: string]: any;
-                    label: string;
-                    value: any;
+                    label?: string | undefined;
+                    value?: any;
                     color?: string | undefined;
                     type?: string | undefined;
                 }[] | {
                     value: {
                         [x: string]: any;
-                        label: string;
-                        value: any;
+                        label?: string | undefined;
+                        value?: any;
                         color?: string | undefined;
                         type?: string | undefined;
                     }[];
@@ -105,15 +105,15 @@ export declare function useForm(): {
                 name?: string | undefined;
                 options?: {
                     [x: string]: any;
-                    label: string;
-                    value: any;
+                    label?: string | undefined;
+                    value?: any;
                     color?: string | undefined;
                     type?: string | undefined;
                 }[] | {
                     value: {
                         [x: string]: any;
-                        label: string;
-                        value: any;
+                        label?: string | undefined;
+                        value?: any;
                         color?: string | undefined;
                         type?: string | undefined;
                     }[];
@@ -136,15 +136,15 @@ export declare function useForm(): {
                 name?: string | undefined;
                 options?: {
                     [x: string]: any;
-                    label: string;
-                    value: any;
+                    label?: string | undefined;
+                    value?: any;
                     color?: string | undefined;
                     type?: string | undefined;
                 }[] | {
                     value: {
                         [x: string]: any;
-                        label: string;
-                        value: any;
+                        label?: string | undefined;
+                        value?: any;
                         color?: string | undefined;
                         type?: string | undefined;
                     }[];

+ 2 - 2
packages/crud/types/components/search-key/index.d.ts

@@ -15,7 +15,7 @@ declare const _default: import("vue").DefineComponent<{
     onSearch: FunctionConstructor;
     placeholder: StringConstructor;
     width: {
-        type: (StringConstructor | NumberConstructor)[];
+        type: (NumberConstructor | StringConstructor)[];
         default: number;
     };
 }, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue" | "field-change")[], "change" | "update:modelValue" | "field-change", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
@@ -34,7 +34,7 @@ declare const _default: import("vue").DefineComponent<{
     onSearch: FunctionConstructor;
     placeholder: StringConstructor;
     width: {
-        type: (StringConstructor | NumberConstructor)[];
+        type: (NumberConstructor | StringConstructor)[];
         default: number;
     };
 }>> & {

+ 8 - 8
packages/crud/types/components/table/helper/index.d.ts

@@ -13,15 +13,15 @@ export declare function useTable(props: any): {
                 name?: string | undefined;
                 options?: {
                     [x: string]: any;
-                    label: string;
-                    value: any;
+                    label?: string | undefined;
+                    value?: any;
                     color?: string | undefined;
                     type?: string | undefined;
                 }[] | {
                     value: {
                         [x: string]: any;
-                        label: string;
-                        value: any;
+                        label?: string | undefined;
+                        value?: any;
                         color?: string | undefined;
                         type?: string | undefined;
                     }[];
@@ -41,15 +41,15 @@ export declare function useTable(props: any): {
             };
             dict: {
                 [x: string]: any;
-                label: string;
-                value: any;
+                label?: string | undefined;
+                value?: any;
                 color?: string | undefined;
                 type?: string | undefined;
             }[] | {
                 value: {
                     [x: string]: any;
-                    label: string;
-                    value: any;
+                    label?: string | undefined;
+                    value?: any;
                     color?: string | undefined;
                     type?: string | undefined;
                 }[];

+ 1 - 1
packages/crud/types/utils/parse.d.ts

@@ -12,7 +12,7 @@ export declare function parseTableDict(value: any, item: ClTable.Column): string
 /**
  * 解析 table.op.buttons
  */
-export declare function parseTableOpButtons(buttons: any, { scope }: any): any;
+export declare function parseTableOpButtons(buttons: any[], { scope }: any): any[];
 /**
  * 解析扩展组件
  */

+ 14 - 22
src/cool/utils/index.ts

@@ -200,34 +200,26 @@ export function deepPaths(paths: string[], splitor?: string) {
 }
 
 // 列表转树形
-export function deepTree(list: any[], order?: "asc" | "desc"): any[] {
+export function deepTree(list: any[], sort?: "desc" | "asc"): any[] {
 	const newList: any[] = [];
 	const map: any = {};
 
-	list.forEach((e) => (map[e.id] = e));
-
-	list.forEach((e) => {
-		const parent = map[e.parentId];
-
-		if (parent) {
-			(parent.children || (parent.children = [])).push(e);
-		} else {
-			newList.push(e);
-		}
-	});
-
-	const fn = (list: Array<any>) => {
-		list.map((e) => {
-			if (isArray(e.children)) {
-				e.children = orderBy(e.children, "orderNum", order);
-				fn(e.children);
+	orderBy(list, "orderNum", sort)
+		.map((e) => {
+			map[e.id] = e;
+			return e;
+		})
+		.forEach((e) => {
+			const parent = map[e.parentId];
+
+			if (parent) {
+				(parent.children || (parent.children = [])).push(e);
+			} else {
+				newList.push(e);
 			}
 		});
-	};
-
-	fn(newList);
 
-	return orderBy(newList, "orderNum", order);
+	return newList;
 }
 
 // 树形转列表

+ 2 - 2
src/modules/base/components/select/index.tsx

@@ -78,8 +78,8 @@ export default defineComponent({
 						) : (
 							<el-option
 								disabled={e.disabled}
-								label={e[props.labelKey] || ""}
-								value={e[props.valueKey] || ""}
+								label={e[props.labelKey]}
+								value={e[props.valueKey]}
 							/>
 						);
 					})}

+ 52 - 0
src/modules/base/components/view/head/index.vue

@@ -0,0 +1,52 @@
+<template>
+	<div class="cl-view-head">
+		<el-icon class="cl-view-head__back" @click="router.back()">
+			<ArrowLeft />
+		</el-icon>
+
+		<span class="cl-view-head__title">{{ title }}</span>
+	</div>
+</template>
+
+<script setup lang="ts" name="cl-view-head">
+import { computed } from "vue";
+import { useCool } from "/@/cool";
+import { ArrowLeft } from "@element-plus/icons-vue";
+
+const props = defineProps({
+	title: String
+});
+
+const { route, router } = useCool();
+
+// 标题
+const title = computed(() => props.title || route.query.title);
+</script>
+
+<style lang="scss" scoped>
+.cl-view-head {
+	display: flex;
+	align-items: center;
+	margin-bottom: 10px;
+	height: 30px;
+
+	&__back {
+		cursor: pointer;
+		height: 30px;
+		width: 30px;
+		font-size: 16px;
+		border-radius: 4px;
+		margin-right: 10px;
+		background-color: var(--el-fill-color-lighter);
+
+		&:hover {
+			color: var(--color-primary);
+		}
+	}
+
+	&__title {
+		font-size: 14px;
+		line-height: 1;
+	}
+}
+</style>

+ 1 - 3
src/modules/base/pages/login/index.vue

@@ -68,9 +68,7 @@
 			<cl-svg name="bg"></cl-svg>
 		</div>
 
-		<a href="https://beian.miit.gov.cn" class="copyright">
-			Copyright © 2023 COOL | 粤 ICP 备 2022012608 号
-		</a>
+		<a href="https://cool-js.com" class="copyright"> Copyright © COOL </a>
 	</div>
 </template>
 

+ 1 - 6
src/modules/base/store/menu.ts

@@ -143,12 +143,7 @@ export const useMenuStore = defineStore("menu", function () {
 			});
 		} else {
 			// 动态菜单
-			await service.base.comm
-				.permmenu()
-				.then(next)
-				.catch(() => {
-					ElMessage.error("菜单加载异常!");
-				});
+			await service.base.comm.permmenu().then(next);
 		}
 	}
 

+ 2 - 8
src/modules/base/views/role.vue

@@ -9,13 +9,7 @@
 		</cl-row>
 
 		<cl-row>
-			<cl-table
-				ref="Table"
-				:default-sort="{
-					prop: 'createTime',
-					order: 'descending'
-				}"
-			/>
+			<cl-table ref="Table" />
 		</cl-row>
 
 		<cl-row>
@@ -154,7 +148,7 @@ const Table = useTable({
 		{
 			prop: "createTime",
 			label: "创建时间",
-			sortable: "custom",
+			sortable: "desc",
 			minWidth: 160
 		},
 		{

+ 3 - 0
src/modules/demo/components/form-btn.vue

@@ -129,6 +129,9 @@ function open() {
 				}
 			}
 		],
+		op: {
+			// buttons: ["save"]
+		},
 		on: {
 			open() {
 				refs.name.focus();

+ 1 - 1
src/modules/dict/store/dict.ts

@@ -13,7 +13,7 @@ const useDictStore = defineStore("dict", () => {
 
 	// 获取
 	function get(name: string) {
-		return computed(() => data[name]);
+		return computed(() => data[name] || []);
 	}
 
 	// 查找

+ 4 - 3
src/modules/dict/types/index.d.ts

@@ -1,12 +1,13 @@
 export namespace Dict {
-	type List = {
+	interface Item {
 		id: string;
 		label: string;
 		value: any;
+		children?: Item[];
 		[key: string]: any;
-	}[];
+	}
 
 	interface Data {
-		[key: string]: List;
+		[key: string]: Item[];
 	}
 }

+ 11 - 5
src/modules/extend/editor/monaco/index.vue

@@ -19,7 +19,7 @@ import { useFormat } from "./format";
 import { parsePx } from "/@/cool/utils";
 import { useTypes } from "./types";
 import { useCool } from "/@/cool";
-import { merge } from "lodash-es";
+import { isObject, merge } from "lodash-es";
 
 const props = defineProps({
 	modelValue: String,
@@ -61,9 +61,15 @@ function getContent() {
 }
 
 // 设置内容
-function setContent(value?: string) {
+function setContent(value: string = "") {
+	if (isObject(value)) {
+		value = JSON.stringify(value);
+	} else {
+		value = value.toString();
+	}
+
 	if (value != getContent()) {
-		editor?.setValue(value || "");
+		editor?.setValue(value);
 	}
 }
 
@@ -74,7 +80,7 @@ async function formatCode() {
 }
 
 // 创建编辑器
-function init() {
+function create() {
 	const options = merge(
 		{
 			theme: "default",
@@ -148,7 +154,7 @@ watch(
 );
 
 onMounted(() => {
-	init();
+	create();
 });
 
 onUnmounted(() => {

+ 15 - 1
src/modules/extend/editor/preview.vue

@@ -1,10 +1,15 @@
 <template>
 	<div>
+		<slot>
+			<el-button @click="open()">{{ text }}</el-button>
+		</slot>
+
 		<cl-dialog width="1000px" :title="title" append-to-body v-model="visible">
 			<cl-editor
 				:name="`cl-editor-${name}`"
 				:ref="setRefs('editor')"
 				:height="600"
+				preview
 				v-bind="props.props"
 				v-model="text"
 			/>
@@ -25,6 +30,7 @@ import { nextTick, PropType, ref } from "vue";
 import { useCool } from "/@/cool";
 
 const props = defineProps({
+	modelValue: String,
 	title: {
 		type: String,
 		default: "文本预览"
@@ -33,6 +39,10 @@ const props = defineProps({
 		type: String as PropType<"monaco" | "quill" | "wang">,
 		required: true
 	},
+	text: {
+		type: String,
+		default: "点击查看"
+	},
 	props: Object
 });
 
@@ -45,7 +55,11 @@ const visible = ref(false);
 // 文本
 const text = ref("");
 
-async function open(data: string) {
+async function open(data?: string) {
+	if (!data) {
+		data = props.modelValue;
+	}
+
 	if (isString(data)) {
 		text.value = data;
 	}

+ 15 - 23
src/modules/extend/editor/wang.vue

@@ -1,13 +1,16 @@
 <template>
 	<div class="cl-editor-wang" :class="{ disabled }" :ref="setRefs('editor')">
 		<!-- 工具栏 -->
-		<toolbar :editor="Editor" :mode="mode" />
+		<toolbar :editor="Editor" :mode="mode" v-if="!preview" />
 
 		<!-- 编辑框 -->
 		<editor
 			v-model="value"
 			:defaultConfig="editorConfig"
 			:mode="mode"
+			:style="{
+				height: parsePx(height)
+			}"
 			@onCreated="onCreated"
 			@onFocus="onFocus"
 			@onBlur="onBlur"
@@ -58,8 +61,8 @@ export default defineComponent({
 			type: [String, Number],
 			default: 400
 		},
-		maxHeight: [String, Number],
-		disabled: Boolean
+		disabled: Boolean,
+		preview: Boolean
 	},
 
 	emits: ["update:modelValue", "change", "focus", "blur"],
@@ -106,20 +109,23 @@ export default defineComponent({
 			}
 		};
 
+		// 创建后
 		function onCreated(editor: any) {
 			Editor.value = editor;
 			onDisabled();
-			onHeight();
 		}
 
+		// 聚焦
 		function onFocus(editor: any) {
 			emit("focus", editor);
 		}
 
+		// 失焦
 		function onBlur(editor: any) {
 			emit("blur", editor);
 		}
 
+		// 值改变
 		function onChange() {
 			if (value.value == "<p><br></p>") {
 				value.value = "";
@@ -129,6 +135,7 @@ export default defineComponent({
 			emit("change", value.value);
 		}
 
+		// 文件选择
 		function onFileConfirm(files: any[]) {
 			if (files.length > 0) {
 				files.forEach((file) => {
@@ -141,30 +148,14 @@ export default defineComponent({
 
 		// 禁用
 		function onDisabled() {
-			if (props.disabled) {
+			if (props.disabled || props.preview) {
 				Editor.value?.disable();
 			} else {
 				Editor.value?.enable();
 			}
 		}
 
-		watch(() => props.disabled, onDisabled);
-
-		// 设置高度
-		function onHeight() {
-			const scroll = refs.editor.querySelector(".w-e-scroll");
-			scroll.style.maxHeight = parsePx(props.maxHeight || "auto");
-			scroll.style.height = parsePx(props.height);
-		}
-
-		watch(
-			() => {
-				return [props.height, props.maxHeight];
-			},
-			() => {
-				onHeight();
-			}
-		);
+		watch(() => [props.disabled, props.preview], onDisabled);
 
 		onBeforeUnmount(() => {
 			const editor = Editor.value;
@@ -182,7 +173,8 @@ export default defineComponent({
 			onBlur,
 			onChange,
 			editorConfig,
-			onFileConfirm
+			onFileConfirm,
+			parsePx
 		};
 	}
 });

+ 0 - 1
src/modules/magic/hooks/menu.ts

@@ -71,7 +71,6 @@ export function useCode() {
 
 			// 编辑器处理
 			if (item.component?.name?.includes("cl-editor-")) {
-				table.columns.push(column);
 				column.component = {
 					name: "cl-editor-preview",
 					props: {

+ 3 - 2
src/modules/upload/components/upload-item/index.vue

@@ -92,7 +92,7 @@
 							</el-icon>
 						</template>
 
-						<el-icon @click.stop="remove">
+						<el-icon @click.stop="remove" v-if="!disabled">
 							<delete />
 						</el-icon>
 					</div>
@@ -125,7 +125,8 @@ const props = defineProps({
 	list: {
 		type: Array as PropType<Upload.Item[]>,
 		default: () => []
-	}
+	},
+	disabled: Boolean
 });
 
 const emit = defineEmits(["remove"]);

+ 6 - 1
src/modules/upload/components/upload.vue

@@ -111,7 +111,12 @@
 					>
 						<slot name="item" :item="item" :index="index">
 							<div class="cl-upload__item">
-								<upload-item :item="item" :list="list" @remove="remove(index)" />
+								<upload-item
+									:item="item"
+									:list="list"
+									:disabled="disabled"
+									@remove="remove(index)"
+								/>
 							</div>
 						</slot>
 					</el-upload>

+ 4 - 4
yarn.lock

@@ -282,10 +282,10 @@
     "@babel/helper-validator-identifier" "^7.22.20"
     to-fast-properties "^2.0.0"
 
-"@cool-vue/crud@^7.0.1-beta2":
-  version "7.0.1-beta2"
-  resolved "https://registry.yarnpkg.com/@cool-vue/crud/-/crud-7.0.1-beta2.tgz#1c43ad0d5af3fe009bfcfa312c71d8b99a676213"
-  integrity sha512-CN9hlfsWKOU/qf7E1COjgLqEqJBOd3v8v8bHeivFt7BHdDmwxrgqGZWXvZ6zp4LXSBq7dacF7dqn1FOe7TYzCQ==
+"@cool-vue/crud@^7.0.1-beta9":
+  version "7.0.1-beta9"
+  resolved "https://registry.yarnpkg.com/@cool-vue/crud/-/crud-7.0.1-beta9.tgz#caacb1eab03f48bc6789209242b198ebd06435f5"
+  integrity sha512-PnukV6Q1sPsXZS2li2BrmCDmYKJdqFumE/wA2kaSTOcrIgmjYmrS/oa2c3C5BKXrwDD9561kq0q8vsKBTpGQxQ==
   dependencies:
     array.prototype.flat "^1.2.4"
     core-js "^3.21.1"