feat: pageSearch组件,添加卡片样式配置,自适应布局配置,级联操作数据示例

This commit is contained in:
超凡
2025-04-10 23:20:13 +08:00
parent 0146ca7d86
commit ac609c1282
4 changed files with 64 additions and 49 deletions

View File

@@ -1,34 +1,28 @@
<template> <template>
<el-card <el-card v-show="visible" v-bind="cardAttrs">
v-show="visible" <el-form ref="queryFormRef" :model="queryParams" :inline="true" :class="isGrid">
v-hasPerm="[`${searchConfig.pageName}:query`]"
shadow="never"
class="mb-2.5"
>
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<template v-for="(item, index) in formItems" :key="item.prop"> <template v-for="(item, index) in formItems" :key="item.prop">
<el-form-item <el-form-item
v-show="isExpand ? true : index < showNumber" v-show="isExpand ? true : index < showNumber"
:label="item.label" :label="item?.label"
:prop="item.prop" :prop="item.prop"
> >
<!-- Label --> <!-- Label -->
<template v-if="item.tips" #label> <template v-if="item?.tips" #label>
<span class="flex-y-center"> <span class="flex-y-center">
{{ item.label }} {{ item.label }}
<el-tooltip v-bind="getTooltipProps(item.tips)"> <el-tooltip v-bind="getTooltipProps(item.tips)">
<QuestionFilled class="w-4 h-4 mx-1" /> <QuestionFilled class="w-4 h-4 mx-1" />
</el-tooltip> </el-tooltip>
{{ searchConfig.colon === false ? "" : ":" }} {{ searchConfig.colon ? ":" : "" }}
</span> </span>
</template> </template>
<template v-else #label> <template v-else #label>{{ item.label }} {{ searchConfig.colon ? ":" : "" }}</template>
{{ item.label }} {{ searchConfig.colon === false ? "" : ":" }}
</template>
<component <component
:is="componentMap[item?.type ? item?.type : 'input']" :is="componentMap[item?.type ? item?.type : 'input']"
v-model.trim="queryParams[item.prop]" v-model.trim="queryParams[item.prop]"
style="width: 100%"
v-bind="item.attrs" v-bind="item.attrs"
:config="item.attrs" :config="item.attrs"
v-on="item.events || {}" v-on="item.events || {}"
@@ -45,16 +39,12 @@
<el-button icon="search" type="primary" @click="handleQuery">搜索</el-button> <el-button icon="search" type="primary" @click="handleQuery">搜索</el-button>
<el-button icon="refresh" @click="handleReset">重置</el-button> <el-button icon="refresh" @click="handleReset">重置</el-button>
<!-- 展开/收起 --> <!-- 展开/收起 -->
<el-link <template v-if="isExpandable && formItems.length > showNumber">
v-if="isExpandable && formItems.length > showNumber" <el-link class="ml-3" type="primary" :underline="false" @click="isExpand = !isExpand">
class="ml-3" {{ isExpand ? "收起" : "展开" }}
type="primary" <component :is="isExpand ? ArrowUp : ArrowDown" class="w-4 h-4 ml-2" />
:underline="false" </el-link>
@click="isExpand = !isExpand" </template>
>
{{ isExpand ? "收起" : "展开" }}
<component :is="isExpand ? ArrowUp : ArrowDown" class="w-4 h-4 ml-2" />
</el-link>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@@ -93,14 +83,29 @@ const queryFormRef = ref<FormInstance>();
// 是否显示 // 是否显示
const visible = ref(true); const visible = ref(true);
// 响应式的formItems // 响应式的formItems
const formItems = reactive(props.searchConfig.formItems); const formItems = reactive(props.searchConfig?.formItems ?? []);
// 是否可展开/收缩 // 是否可展开/收缩
const isExpandable = ref(props.searchConfig.isExpandable ?? true); const isExpandable = ref(props.searchConfig?.isExpandable ?? true);
// 是否已展开 // 是否已展开
const isExpand = ref(false); const isExpand = ref(false);
// 表单项展示数量,若可展开,超出展示数量的表单项隐藏 // 表单项展示数量,若可展开,超出展示数量的表单项隐藏
const showNumber = computed(() => const showNumber = computed(() =>
isExpandable.value ? (props.searchConfig.showNumber ?? 3) : formItems.length isExpandable.value ? (props.searchConfig?.showNumber ?? 3) : formItems.length
);
// 卡片组件自定义属性(累名、阴影、权限等)
const cardAttrs = computed<IObject>(() => {
let auth = props.searchConfig?.pageName
? { "v-hasPerm": [`${props.searchConfig.pageName}:query`] }
: {};
return props.searchConfig?.cardAttrs && props.searchConfig.cardAttrs instanceof Object
? { class: "mb-2.5", shadow: "never", ...auth, ...props.searchConfig.cardAttrs }
: { class: "mb-2.5", shadow: "never", ...auth };
});
// 是否使用自适应网格布局
const isGrid = computed(() =>
props.searchConfig?.grid
? "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 4xl:grid-cols-6 gap-5"
: "flex flex-wrap gap-x-8 gap-y-4"
); );
// 搜索表单数据 // 搜索表单数据
const queryParams = reactive<IObject>({}); const queryParams = reactive<IObject>({});
@@ -110,10 +115,10 @@ const getTooltipProps = (tips: any) => {
}; };
onMounted(() => { onMounted(() => {
formItems.map((item) => { formItems.forEach((item) => {
item.initFn && item.initFn(item); item.initFn && item.initFn(item);
if (item.type === "input-tag" || item.type === "custom-tag") { if (item.type === "input-tag" || item.type === "custom-tag") {
queryParams[item.prop] = item.initialValue ?? []; queryParams[item.prop] = Array.isArray(item.initialValue) ? item.initialValue : [];
} else if (item.type === "input-number") { } else if (item.type === "input-number") {
queryParams[item.prop] = item.initialValue ?? null; queryParams[item.prop] = item.initialValue ?? null;
} else { } else {
@@ -142,12 +147,6 @@ defineExpose({
:deep(.el-input-number .el-input__inner) { :deep(.el-input-number .el-input__inner) {
text-align: left; text-align: left;
} }
.el-form {
display: flex;
flex-wrap: wrap;
row-gap: 1rem;
column-gap: 2rem;
}
.el-form-item { .el-form-item {
margin-right: 0; margin-right: 0;
margin-bottom: 0; margin-bottom: 0;

View File

@@ -40,15 +40,15 @@ export type ComponentType =
export interface ISearchConfig { export interface ISearchConfig {
// 页面名称(参与组成权限标识,如sys:user:xxx) // 页面名称(参与组成权限标识,如sys:user:xxx)
pageName: string; pageName?: string;
// 标签冒号 // 标签冒号(默认false)
colon?: boolean; colon?: boolean;
// 表单项 // 表单项(默认:[])
formItems: Array<{ formItems?: Array<{
// 组件类型(如input,select等) // 组件类型(如input,select等)
type?: ComponentType; type?: ComponentType;
// 标签文本 // 标签文本
label: string; label?: string;
// 标签提示 // 标签提示
tips?: string | IObject; tips?: string | IObject;
// 键名 // 键名
@@ -64,10 +64,14 @@ export interface ISearchConfig {
// 初始化数据函数扩展 // 初始化数据函数扩展
initFn?: (formItem: IObject) => void; initFn?: (formItem: IObject) => void;
}>; }>;
// 是否开启展开和收缩 // 是否开启展开和收缩(默认true)
isExpandable?: boolean; isExpandable?: boolean;
// 默认展示的表单项数量 // 默认展示的表单项数量(默认3)
showNumber?: number; showNumber?: number;
// 卡片属性
cardAttrs?: IObject;
// 自适应网格布局(使用时表单不要添加 style: { width: "200px" })
grid?: boolean;
} }
export interface IContentConfig<T = any> { export interface IContentConfig<T = any> {

View File

@@ -1,6 +1,11 @@
import DeptAPI from "@/api/system/dept.api"; import DeptAPI from "@/api/system/dept.api";
import type { ISearchConfig } from "@/components/CURD/types"; import type { ISearchConfig } from "@/components/CURD/types";
const selectOptions = reactive([
{ label: "启用", value: 1 },
{ label: "禁用", value: 0 },
]);
const searchConfig: ISearchConfig = { const searchConfig: ISearchConfig = {
pageName: "sys:user", pageName: "sys:user",
colon: false, colon: false,
@@ -16,9 +21,11 @@ const searchConfig: ISearchConfig = {
style: { width: "200px" }, style: { width: "200px" },
}, },
events: { events: {
// 监听输入框输入事件 change: (e) => {
blur: (e) => console.log("失去焦点: ", e), console.log("输入框的值: ", e);
focus: (e) => console.log("获得焦点: ", e), // 级联操作示例需要使用reactive提前定义数组
// selectOptions.push({ label: e, value: e });
},
}, },
}, },
{ {
@@ -50,12 +57,11 @@ const searchConfig: ISearchConfig = {
clearable: true, clearable: true,
style: { width: "200px" }, style: { width: "200px" },
}, },
options: [ options: selectOptions,
{ label: "启用", value: 1 },
{ label: "禁用", value: 0 },
],
events: { events: {
change: (e) => console.log("发生变化: ", e), change: function (e) {
console.log("选中的值: ", e);
},
}, },
}, },
{ {

View File

@@ -45,6 +45,12 @@ export default defineConfig({
primary: "var(--el-color-primary)", primary: "var(--el-color-primary)",
primary_dark: "var(--el-color-primary-light-5)", primary_dark: "var(--el-color-primary-light-5)",
}, },
breakpoints: Object.fromEntries(
[640, 768, 1024, 1280, 1536, 1920, 2048].map((size, index) => [
["sm", "md", "lg", "xl", "2xl", "3xl", "4xl"][index],
`${size}px`,
])
),
}, },
presets: [ presets: [
presetUno(), presetUno(),