table 表头添加搜索 search 参数

This commit is contained in:
神仙都没用 2023-12-14 21:53:40 +08:00
parent 7e6105d649
commit 8554990783
18 changed files with 309 additions and 84 deletions

View File

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

View File

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

View File

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

View File

@ -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>;
}

View File

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

View File

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

View File

@ -101,6 +101,31 @@
margin-bottom: 5px; 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 { .cl-filter {
@ -362,7 +387,9 @@
position: absolute; position: absolute;
bottom: -1px; bottom: -1px;
left: 0; 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); background-color: var(--el-color-primary);
} }

View File

@ -97,31 +97,42 @@ const userList = [
class TestService { class TestService {
// 分页列表 // 分页列表
async page(params: any) { 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) => { const list = orderBy(userList, order, sort).filter((e: any) => {
if (status !== undefined) { let f = true;
return e.status == status;
}
if (phone !== undefined) {
return String(e.phone).includes(phone);
}
if (name !== undefined) {
return e.name.includes(name);
}
if (keyWord !== undefined) { 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) { fieldEq.forEach((k) => {
return e.occupation == occupation; 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) => { return new Promise((resolve) => {

View File

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

View File

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

View File

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

View File

@ -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 addClass(el: Element, name: string): void;
export declare function removeClass(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 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; export declare function uuid(separator?: string): string;

View File

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

View File

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

View File

@ -45,31 +45,44 @@ const userList: User[] = data.list;
class Test { class Test {
// 分页列表 // 分页列表
async page(params: any) { 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) => { const list = orderBy(userList, order, sort).filter((e: any) => {
if (status !== undefined) { let f = true;
return e.status == status;
}
if (phone !== undefined) {
return String(e.phone).includes(phone);
}
if (name !== undefined) {
return e.name.includes(name);
}
if (keyWord !== undefined) { 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) { fieldEq.forEach((k) => {
return e.occupation == occupation; 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) => { return new Promise((resolve) => {

View File

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

File diff suppressed because one or more lines are too long

View File

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