feat: ✨ pageSearch组件,添加卡片样式配置,自适应布局配置,级联操作数据示例
This commit is contained in:
@@ -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"
|
|
||||||
type="primary"
|
|
||||||
:underline="false"
|
|
||||||
@click="isExpand = !isExpand"
|
|
||||||
>
|
|
||||||
{{ isExpand ? "收起" : "展开" }}
|
{{ isExpand ? "收起" : "展开" }}
|
||||||
<component :is="isExpand ? ArrowUp : ArrowDown" class="w-4 h-4 ml-2" />
|
<component :is="isExpand ? ArrowUp : ArrowDown" class="w-4 h-4 ml-2" />
|
||||||
</el-link>
|
</el-link>
|
||||||
|
</template>
|
||||||
</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;
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user