Przeglądaj źródła

table 表头添加搜索 search 参数

神仙都没用 1 rok temu
rodzic
commit
8554990783

+ 1 - 1
package.json

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

+ 9 - 2
packages/crud/index.d.ts

@@ -40,8 +40,8 @@ declare type obj = {
 declare type DeepPartial<T> = T extends Function
 	? T
 	: T extends object
-	? { [P in keyof T]?: DeepPartial<T[P]> }
-	: T;
+	  ? { [P in keyof T]?: DeepPartial<T[P]> }
+	  : T;
 
 // 合并
 declare type Merge<A, B> = Omit<A, keyof B> & B;
@@ -327,10 +327,17 @@ declare namespace ClTable {
 		type: "index" | "selection" | "expand" | "op";
 		hidden: boolean | Vue.Ref<boolean>;
 		component: Render.Component;
+		search: {
+			isInput: boolean;
+			value: any;
+			component: Render.Component;
+		};
+		searchComponent: Render.Component;
 		dict: DictOptions | Vue.Ref<DictOptions>;
 		dictFormatter: (values: DictOptions) => string;
 		dictColor: boolean;
 		dictSeparator: string;
+		dictAllLevels: boolean;
 		buttons: OpButton | ((options: { scope: obj }) => OpButton);
 		align: "left" | "center" | "right";
 		label: string | Vue.Ref<string>;

+ 1 - 1
packages/crud/package.json

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

+ 58 - 0
packages/crud/src/components/table/helper/header.tsx

@@ -0,0 +1,58 @@
+import { Search } from "@element-plus/icons-vue";
+import { h } from "vue";
+import { useCrud } from "../../../hooks";
+import { renderNode } from "../../../utils/vnode";
+
+export function renderHeader(item: ClTable.Column, { scope, slots }: any) {
+	const crud = useCrud();
+
+	const slot = slots[`header-${item.prop}`];
+
+	if (slot) {
+		return slot({
+			scope
+		});
+	}
+
+	if (!item.search || !item.search.component) {
+		return scope.column.label;
+	}
+
+	// 显示输入框
+	function show(e: MouseEvent) {
+		item.search.isInput = true;
+		e.stopPropagation();
+	}
+
+	// 文字
+	const text = (
+		<div onClick={show}>
+			<el-icon class="icon">
+				<Search />
+			</el-icon>
+
+			<span>{scope.column.label}</span>
+		</div>
+	);
+
+	// 输入框
+	const input = h(renderNode(item.search.component, { prop: item.prop }), {
+		clearable: true,
+		modelValue: item.search.value,
+		onVnodeMounted(vn) {
+			// 默认聚焦
+			vn.component?.exposed?.focus?.();
+		},
+		onInput(val: any) {
+			item.search.value = val;
+		},
+		onChange(val: any) {
+			crud.value?.refresh({
+				page: 1,
+				[item.prop]: val === "" ? undefined : val
+			});
+		}
+	});
+
+	return <div class="cl-table-header__search">{item.search.isInput ? input : text}</div>;
+}

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

@@ -29,3 +29,4 @@ export * from "./render";
 export * from "./row";
 export * from "./selection";
 export * from "./sort";
+export * from "./header";

+ 2 - 9
packages/crud/src/components/table/helper/render.tsx

@@ -4,6 +4,7 @@ import { cloneDeep, isEmpty, orderBy } from "lodash-es";
 import { getValue } from "../../../utils";
 import { parseTableDict, parseTableOpButtons } from "../../../utils/parse";
 import { renderNode } from "../../../utils/vnode";
+import { renderHeader } from "./header";
 
 // 渲染
 export function useRender() {
@@ -78,15 +79,7 @@ export function useRender() {
 
 						return h(ElTableColumn, props, {
 							header(scope: any) {
-								const slot = slots[`header-${item.prop}`];
-
-								if (slot) {
-									return slot({
-										scope
-									});
-								} else {
-									return scope.column.label;
-								}
+								return renderHeader(item, { scope, slots });
 							},
 							default(scope: any) {
 								if (item.children) {

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

@@ -101,6 +101,31 @@
 			margin-bottom: 5px;
 		}
 	}
+
+	.cl-table-header__search {
+		display: inline-block;
+		width: 100%;
+		cursor: pointer;
+
+		.icon {
+			margin-right: 5px;
+			position: relative;
+			top: 2px;
+			font-size: 14px;
+		}
+
+		&:hover {
+			color: var(--el-color-primary);
+		}
+
+		& > div {
+			width: 100%;
+
+			.el-date-editor {
+				margin-right: 0;
+			}
+		}
+	}
 }
 
 .cl-filter {
@@ -362,7 +387,9 @@
 		position: absolute;
 		bottom: -1px;
 		left: 0;
-		transition: transform 0.3s ease-in-out, width 0.2s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
+		transition:
+			transform 0.3s ease-in-out,
+			width 0.2s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
 		background-color: var(--el-color-primary);
 	}
 

+ 28 - 17
packages/crud/src/test/service.ts

@@ -97,31 +97,42 @@ const userList = [
 class TestService {
 	// 分页列表
 	async page(params: any) {
-		const { status, occupation, keyWord, page, size, phone, name, sort, order } = params || {};
+		const { keyWord, page, size, sort, order } = params || {};
 
-		// 过滤后的列表
-		const list = orderBy(userList, order, sort).filter((e) => {
-			if (status !== undefined) {
-				return e.status == status;
-			}
+		// 关键字查询
+		const keyWordLikeFields = ["phone", "name"];
 
-			if (phone !== undefined) {
-				return String(e.phone).includes(phone);
-			}
+		// 等值查询
+		const fieldEq = ["createTime", "occupation", "status"];
 
-			if (name !== undefined) {
-				return e.name.includes(name);
-			}
+		// 模糊查询
+		const likeFields = ["phone", "name"];
+
+		// 过滤后的列表
+		const list = orderBy(userList, order, sort).filter((e: any) => {
+			let f = true;
 
 			if (keyWord !== undefined) {
-				return e.name.includes(keyWord) || String(e.phone).includes(keyWord);
+				f = !!keyWordLikeFields.find((k) => String(e[k]).includes(String(params.keyWord)));
 			}
 
-			if (occupation !== undefined) {
-				return e.occupation == occupation;
-			}
+			fieldEq.forEach((k) => {
+				if (f) {
+					if (params[k] !== undefined) {
+						f = e[k] == params[k];
+					}
+				}
+			});
+
+			likeFields.forEach((k) => {
+				if (f) {
+					if (params[k] !== undefined) {
+						f = String(e[k]).includes(String(params[k]));
+					}
+				}
+			});
 
-			return true;
+			return f;
 		});
 
 		return new Promise((resolve) => {

+ 15 - 5
packages/crud/src/utils/index.ts

@@ -115,13 +115,23 @@ export function getValue(data: any, params?: any) {
 }
 
 // 深度查找
-export function deepFind(value: any, list: any[]) {
-	function deep(arr: any[]): any | undefined {
+export function deepFind(value: any, list: any[], options?: { allLevels: boolean }) {
+	const { allLevels = true } = options || {};
+
+	function deep(arr: any[], name: string[]): any | undefined {
 		for (const e of arr) {
 			if (e.value === value) {
-				return e;
+				if (allLevels) {
+					return {
+						...e,
+						label: [...name, e.label].join(" / ")
+					};
+				} else {
+					return e;
+				}
 			} else if (e.children) {
-				const d = deep(e.children);
+				const d = deep(e.children, [...name, e.label]);
+
 				if (d !== undefined) {
 					return d;
 				}
@@ -130,7 +140,7 @@ export function deepFind(value: any, list: any[]) {
 		return undefined;
 	}
 
-	return deep(list);
+	return deep(list, []);
 }
 
 // uuid

+ 2 - 0
packages/crud/types/components/table/helper/header.d.ts

@@ -0,0 +1,2 @@
+/// <reference types="../index" />
+export declare function renderHeader(item: ClTable.Column, { scope, slots }: any): any;

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

@@ -39,6 +39,72 @@ export declare function useTable(props: any): {
                 functionSlot?: boolean | undefined;
                 vm?: any;
             };
+            search: {
+                isInput: boolean;
+                value: any;
+                component: {
+                    [x: string]: any;
+                    name?: string | undefined;
+                    options?: {
+                        [x: string]: any;
+                        label?: string | undefined;
+                        value?: any;
+                        color?: string | undefined;
+                        type?: string | undefined;
+                    }[] | {
+                        value: {
+                            [x: string]: any;
+                            label?: string | undefined;
+                            value?: any;
+                            color?: string | undefined;
+                            type?: string | undefined;
+                        }[];
+                    } | undefined;
+                    props?: {
+                        [x: string]: any;
+                        onChange?: ((value: any) => void) | undefined;
+                    } | {
+                        value: {
+                            [x: string]: any;
+                            onChange?: ((value: any) => void) | undefined;
+                        };
+                    } | undefined;
+                    style?: obj | undefined;
+                    functionSlot?: boolean | undefined;
+                    vm?: any;
+                };
+            };
+            searchComponent: {
+                [x: string]: any;
+                name?: string | undefined;
+                options?: {
+                    [x: string]: any;
+                    label?: string | undefined;
+                    value?: any;
+                    color?: string | undefined;
+                    type?: string | undefined;
+                }[] | {
+                    value: {
+                        [x: string]: any;
+                        label?: string | undefined;
+                        value?: any;
+                        color?: string | undefined;
+                        type?: string | undefined;
+                    }[];
+                } | undefined;
+                props?: {
+                    [x: string]: any;
+                    onChange?: ((value: any) => void) | undefined;
+                } | {
+                    value: {
+                        [x: string]: any;
+                        onChange?: ((value: any) => void) | undefined;
+                    };
+                } | undefined;
+                style?: obj | undefined;
+                functionSlot?: boolean | undefined;
+                vm?: any;
+            };
             dict: {
                 [x: string]: any;
                 label?: string | undefined;
@@ -57,6 +123,7 @@ export declare function useTable(props: any): {
             dictFormatter: (values: DictOptions) => string;
             dictColor: boolean;
             dictSeparator: string;
+            dictAllLevels: boolean;
             buttons: ((options: {
                 scope: obj;
             }) => ClTable.OpButton) | ("info" | "delete" | "edit" | `slot-${string}` | {
@@ -133,3 +200,4 @@ export * from "./render";
 export * from "./row";
 export * from "./selection";
 export * from "./sort";
+export * from "./header";

+ 3 - 1
packages/crud/types/utils/index.d.ts

@@ -7,5 +7,7 @@ export declare function merge(d1: any, d2: any): any;
 export declare function addClass(el: Element, name: string): void;
 export declare function removeClass(el: Element, name: string): void;
 export declare function getValue(data: any, params?: any): any;
-export declare function deepFind(value: any, list: any[]): any;
+export declare function deepFind(value: any, list: any[], options?: {
+    allLevels: boolean;
+}): any;
 export declare function uuid(separator?: string): string;

+ 12 - 5
src/modules/base/components/date/picker.vue

@@ -3,6 +3,7 @@
 		<el-date-picker
 			v-model="date"
 			:type="type"
+			:editable="false"
 			:default-time="defaultTime"
 			:value-format="valueFormat"
 			:style="{ width }"
@@ -77,13 +78,17 @@ const date = ref();
 const quickType = ref(props.defaultQuickType);
 
 // 日期改变
-function onChange(value: any[]) {
+function onChange(value: any) {
 	// 重置按钮类型
 	quickType.value = "";
 
 	// 参数
 	let params = {};
 
+	if (value === null) {
+		value = undefined;
+	}
+
 	if (isRange.value) {
 		let [startTime, endTime] = value || [];
 
@@ -103,10 +108,12 @@ function onChange(value: any[]) {
 		};
 	}
 
-	Crud.value?.refresh({
-		...params,
-		page: 1
-	});
+	if (props.prop) {
+		Crud.value?.refresh({
+			...params,
+			page: 1
+		});
+	}
 
 	emit("update:modelValue", value);
 	emit("change", value);

+ 5 - 0
src/modules/crud/config.ts

@@ -11,6 +11,11 @@ import "@cool-vue/crud/dist/index.css";
 export default (): Merge<ModuleConfig, CrudOptions> => {
 	return {
 		options: {
+			style: {
+				table: {
+					// contextMenu: [], 是否关闭表格右键菜单
+				}
+			},
 			dict: {
 				sort: {
 					prop: "order",

+ 31 - 18
src/modules/demo/service/test.ts

@@ -45,31 +45,44 @@ const userList: User[] = data.list;
 class Test {
 	// 分页列表
 	async page(params: any) {
-		const { status, occupation, keyWord, page, size, phone, name, sort, order } = params || {};
+		const { keyWord, page, size, sort, order } = params || {};
 
-		// 过滤后的列表
-		const list = orderBy(userList, order, sort).filter((e) => {
-			if (status !== undefined) {
-				return e.status == status;
-			}
+		console.log("[test]", params);
 
-			if (phone !== undefined) {
-				return String(e.phone).includes(phone);
-			}
+		// 关键字查询
+		const keyWordLikeFields = ["phone", "name"];
 
-			if (name !== undefined) {
-				return e.name.includes(name);
-			}
+		// 等值查询
+		const fieldEq = ["createTime", "occupation", "status"];
 
-			if (keyWord !== undefined) {
-				return e.name.includes(keyWord) || String(e.phone).includes(keyWord);
-			}
+		// 模糊查询
+		const likeFields = ["phone", "name"];
+
+		// 过滤后的列表
+		const list = orderBy(userList, order, sort).filter((e: any) => {
+			let f = true;
 
-			if (occupation !== undefined) {
-				return e.occupation == occupation;
+			if (keyWord !== undefined) {
+				f = !!keyWordLikeFields.find((k) => String(e[k]).includes(String(params.keyWord)));
 			}
 
-			return true;
+			fieldEq.forEach((k) => {
+				if (f) {
+					if (params[k] !== undefined) {
+						f = e[k] == params[k];
+					}
+				}
+			});
+
+			likeFields.forEach((k) => {
+				if (f) {
+					if (params[k] !== undefined) {
+						f = String(e[k]).includes(String(params[k]));
+					}
+				}
+			});
+
+			return f;
 		});
 
 		return new Promise((resolve) => {

+ 39 - 18
src/modules/demo/views/crud.vue

@@ -36,17 +36,7 @@
 			<cl-column-custom :columns="Table?.columns" :ref="setRefs('columnCustom')" />
 
 			<!-- 关键字搜索 -->
-			<cl-search-key
-				field="name"
-				:field-list="[
-					{
-						label: '姓名',
-						value: 'name'
-					},
-					{ label: '手机号', value: 'phone' }
-				]"
-				:width="250"
-			/>
+			<cl-search-key placeholder="搜索姓名、手机号" :width="250" />
 
 			<!-- 高级搜索按钮 -->
 			<cl-adv-btn />
@@ -77,6 +67,11 @@
 						</el-descriptions>
 					</div>
 				</template>
+
+				<!-- 自定义列 -->
+				<template #column-wages="{ scope }">
+					<span>{{ scope.row.wages }}🤑</span>
+				</template>
 			</cl-table>
 		</cl-row>
 
@@ -394,22 +389,33 @@ const Table = useTable({
 		{
 			label: "姓名",
 			prop: "name",
-			minWidth: 150
+			minWidth: 120
 		},
 		{
 			label: "手机号",
 			prop: "phone",
-			minWidth: 140
+			minWidth: 140,
+
+			// 带搜索组件
+			search: {
+				// cool渲染方式
+				component: {
+					name: "el-input",
+					props: {
+						placeholder: "搜索手机号"
+					}
+				}
+			}
 		},
 		{
 			label: "账号",
 			prop: "account",
-			minWidth: 140
+			minWidth: 150
 		},
 		{
 			label: "存款(元)",
 			prop: "wages",
-			minWidth: 120,
+			minWidth: 150,
 			sortable: "desc" // 默认倒序
 		},
 		{
@@ -417,7 +423,18 @@ const Table = useTable({
 			prop: "occupation",
 			dict: dict.get("occupation"),
 			dictColor: true,
-			minWidth: 120
+			minWidth: 150,
+
+			// 带搜索组件
+			search: {
+				// jsx方式
+				component: {
+					name: "cl-select",
+					props: {
+						options: dict.get("occupation")
+					}
+				}
+			}
 		},
 		{
 			label: "状态",
@@ -431,9 +448,13 @@ const Table = useTable({
 		{
 			label: "出生年月",
 			orderNum: 1,
-			minWidth: 140,
+			minWidth: 165,
 			prop: "createTime",
-			sortable: "custom"
+			search: {
+				component: (
+					<cl-date-picker type="date" value-format="YYYY-MM-DD" placeholder="搜索日期" />
+				)
+			}
 		},
 		{
 			type: "op",

Plik diff jest za duży
+ 0 - 0
stats.html


+ 4 - 4
yarn.lock

@@ -344,10 +344,10 @@
     "@babel/helper-validator-identifier" "^7.22.20"
     to-fast-properties "^2.0.0"
 
-"@cool-vue/crud@^7.0.8":
-  version "7.0.8"
-  resolved "https://registry.yarnpkg.com/@cool-vue/crud/-/crud-7.0.8.tgz#14855d5a326b5f91bb35b5791954341e5a61db23"
-  integrity sha512-hI8cyoRnRrwPNsyoVtb6nVCKVFFbsqhFH6HR1B2/NVRqfn87wvDoQ2kkm76K7kafO/bZM9WJu8tetYuqkaSoOw==
+"@cool-vue/crud@^7.0.9":
+  version "7.0.9"
+  resolved "https://registry.yarnpkg.com/@cool-vue/crud/-/crud-7.0.9.tgz#06c169380c1536385877154b98fd86eea8ce635d"
+  integrity sha512-Saaqq3A6Hunctkfnyv+TFhpOyPr8XY5kyMdOflLgPKD8lhgsgu2/cN9wRM9q0rx7uar/D/oHYjN7/Vfn1Hfs9w==
   dependencies:
     array.prototype.flat "^1.2.4"
     core-js "^3.21.1"

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików