diff --git a/package.json b/package.json index ff2b380..a379a86 100644 --- a/package.json +++ b/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", diff --git a/packages/crud/index.d.ts b/packages/crud/index.d.ts index 0858442..baafb81 100644 --- a/packages/crud/index.d.ts +++ b/packages/crud/index.d.ts @@ -40,8 +40,8 @@ declare type obj = { declare type DeepPartial = T extends Function ? T : T extends object - ? { [P in keyof T]?: DeepPartial } - : T; + ? { [P in keyof T]?: DeepPartial } + : T; // 合并 declare type Merge = Omit & B; @@ -327,10 +327,17 @@ declare namespace ClTable { type: "index" | "selection" | "expand" | "op"; hidden: boolean | Vue.Ref; component: Render.Component; + search: { + isInput: boolean; + value: any; + component: Render.Component; + }; + searchComponent: Render.Component; dict: DictOptions | Vue.Ref; dictFormatter: (values: DictOptions) => string; dictColor: boolean; dictSeparator: string; + dictAllLevels: boolean; buttons: OpButton | ((options: { scope: obj }) => OpButton); align: "left" | "center" | "right"; label: string | Vue.Ref; diff --git a/packages/crud/package.json b/packages/crud/package.json index 6da4455..a4308f1 100644 --- a/packages/crud/package.json +++ b/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", diff --git a/packages/crud/src/components/table/helper/header.tsx b/packages/crud/src/components/table/helper/header.tsx new file mode 100644 index 0000000..a43a69b --- /dev/null +++ b/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 = ( +
+ + + + + {scope.column.label} +
+ ); + + // 输入框 + 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 ; +} diff --git a/packages/crud/src/components/table/helper/index.ts b/packages/crud/src/components/table/helper/index.ts index f1e21c7..c02929c 100644 --- a/packages/crud/src/components/table/helper/index.ts +++ b/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"; diff --git a/packages/crud/src/components/table/helper/render.tsx b/packages/crud/src/components/table/helper/render.tsx index 1a813c6..184c0e0 100644 --- a/packages/crud/src/components/table/helper/render.tsx +++ b/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) { diff --git a/packages/crud/src/static/index.scss b/packages/crud/src/static/index.scss index d2da187..5fff31e 100644 --- a/packages/crud/src/static/index.scss +++ b/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); } diff --git a/packages/crud/src/test/service.ts b/packages/crud/src/test/service.ts index 0c11aa8..c7948b8 100644 --- a/packages/crud/src/test/service.ts +++ b/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 keyWordLikeFields = ["phone", "name"]; + + // 等值查询 + const fieldEq = ["createTime", "occupation", "status"]; + + // 模糊查询 + const likeFields = ["phone", "name"]; // 过滤后的列表 - const list = orderBy(userList, order, sort).filter((e) => { - if (status !== undefined) { - return e.status == status; - } - - if (phone !== undefined) { - return String(e.phone).includes(phone); - } - - if (name !== undefined) { - return e.name.includes(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]; + } + } + }); - return true; + likeFields.forEach((k) => { + if (f) { + if (params[k] !== undefined) { + f = String(e[k]).includes(String(params[k])); + } + } + }); + + return f; }); return new Promise((resolve) => { diff --git a/packages/crud/src/utils/index.ts b/packages/crud/src/utils/index.ts index ecc93a3..c642977 100644 --- a/packages/crud/src/utils/index.ts +++ b/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 diff --git a/packages/crud/types/components/table/helper/header.d.ts b/packages/crud/types/components/table/helper/header.d.ts new file mode 100644 index 0000000..bb0ed39 --- /dev/null +++ b/packages/crud/types/components/table/helper/header.d.ts @@ -0,0 +1,2 @@ +/// +export declare function renderHeader(item: ClTable.Column, { scope, slots }: any): any; diff --git a/packages/crud/types/components/table/helper/index.d.ts b/packages/crud/types/components/table/helper/index.d.ts index 6b2f40a..e66d673 100644 --- a/packages/crud/types/components/table/helper/index.d.ts +++ b/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"; diff --git a/packages/crud/types/utils/index.d.ts b/packages/crud/types/utils/index.d.ts index f07c218..aa7f531 100644 --- a/packages/crud/types/utils/index.d.ts +++ b/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; diff --git a/src/modules/base/components/date/picker.vue b/src/modules/base/components/date/picker.vue index 662097e..e27cd5c 100644 --- a/src/modules/base/components/date/picker.vue +++ b/src/modules/base/components/date/picker.vue @@ -3,6 +3,7 @@ => { return { options: { + style: { + table: { + // contextMenu: [], 是否关闭表格右键菜单 + } + }, dict: { sort: { prop: "order", diff --git a/src/modules/demo/service/test.ts b/src/modules/demo/service/test.ts index b173af2..880a801 100644 --- a/src/modules/demo/service/test.ts +++ b/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 || {}; + + console.log("[test]", params); + + // 关键字查询 + const keyWordLikeFields = ["phone", "name"]; + + // 等值查询 + const fieldEq = ["createTime", "occupation", "status"]; + + // 模糊查询 + const likeFields = ["phone", "name"]; // 过滤后的列表 - const list = orderBy(userList, order, sort).filter((e) => { - if (status !== undefined) { - return e.status == status; - } - - if (phone !== undefined) { - return String(e.phone).includes(phone); - } - - if (name !== undefined) { - return e.name.includes(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]; + } + } + }); - return true; + likeFields.forEach((k) => { + if (f) { + if (params[k] !== undefined) { + f = String(e[k]).includes(String(params[k])); + } + } + }); + + return f; }); return new Promise((resolve) => { diff --git a/src/modules/demo/views/crud.vue b/src/modules/demo/views/crud.vue index ca0b677..ffb831e 100644 --- a/src/modules/demo/views/crud.vue +++ b/src/modules/demo/views/crud.vue @@ -36,17 +36,7 @@ - + @@ -77,6 +67,11 @@ + + + @@ -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: ( + + ) + } }, { type: "op", diff --git a/stats.html b/stats.html index 405f956..afefacb 100644 --- a/stats.html +++ b/stats.html @@ -4818,7 +4818,7 @@ var drawChart = (function (exports) {