554 lines
15 KiB
Vue
554 lines
15 KiB
Vue
<template>
|
|
<div v-loading="loadingPage" class="pageSearch searchBox main-container"
|
|
:style="`margin-bottom: ${marginBottom}px;background-color: ${backgroundColor}`">
|
|
<div class="filter-title">
|
|
<span class="filter-label"><el-icon>
|
|
<Filter />
|
|
</el-icon>筛选条件</span>
|
|
</div>
|
|
<div class="content-container">
|
|
<div class="box">
|
|
<div v-for="(item, index) in getArr" :key="index" class="item">
|
|
<div class="label" v-if="item.label">{{ item.label }}</div>
|
|
<!-- select -->
|
|
<el-select v-if="item.showType === 'select'" v-model="searchObj[item.prop]" :multiple="item.multiple"
|
|
:clearable="item.clearable" :filterable="item.filterable" :placeholder="item.placeholder" collapse-tags
|
|
collapse-tags-tooltip>
|
|
<el-option v-for="obj in getOptions[item.prop]" :key="obj.value" :label="obj.label || obj.lable"
|
|
:value="obj.value" />
|
|
</el-select>
|
|
<!-- input -->
|
|
<el-input v-else-if="item.showType === 'input'" class="input" v-model="searchObj[item.prop]"
|
|
:clearable="item.clearable" :placeholder="item.placeholder" />
|
|
<!-- input -->
|
|
<el-input v-else-if="item.showType === 'number'" class="input" v-model="searchObj[item.prop]"
|
|
:clearable="item.clearable" :placeholder="item.placeholder" type="number" />
|
|
<!-- 日期段选择器 -->
|
|
<el-date-picker v-else-if="item.showType === 'daterange'" v-model="searchObj[item.prop]" type="daterange"
|
|
unlink-panels :range-separator="item.rangeSeparator" :start-placeholder="item.startPlaceholder"
|
|
:end-placeholder="item.endPlaceholder" :shortcuts="item.shortcuts" :disabledDate="disabledDate"
|
|
value-format="YYYY-MM-DD" />
|
|
<el-date-picker v-else-if="item.showType === 'datetimerange'" v-model="searchObj[item.prop]"
|
|
type="datetimerange" unlink-panels :range-separator="item.rangeSeparator || '至'"
|
|
:start-placeholder="item.startPlaceholder || '开始日期'" :end-placeholder="item.endPlaceholder || '结束日期'"
|
|
:shortcuts="item.shortcuts" value-format="YYYY-MM-DD HH:mm:ss" />
|
|
<el-date-picker v-else-if="item.showType === 'date'" v-model="searchObj[item.prop]" type="date"
|
|
:placeholder="item.placeholder" :disabled-date="disabledDate" :shortcuts="item.shortcuts"
|
|
value-format="YYYY-MM-DD">
|
|
</el-date-picker>
|
|
<el-date-picker v-else-if="item.showType === 'datetime'" v-model="searchObj[item.prop]" type="datetime"
|
|
:placeholder="item.placeholder" value-format="YYYY-MM-DD HH:mm:ss">
|
|
</el-date-picker>
|
|
|
|
<!-- checkbox -->
|
|
<template v-else-if="item.showType === 'department'">
|
|
<MOSTY.Department clearable v-model="searchObj[item.prop]" />
|
|
</template>
|
|
<!-- checkbox -->
|
|
<template v-else-if="item.showType === 'checkbox'">
|
|
<el-checkbox v-if="item.showSelectAll" v-model="item.checkAll" :indeterminate="item.isIndeterminate"
|
|
@change="
|
|
(val) => {
|
|
handleCheckAllChange(val, item);
|
|
}
|
|
">全选</el-checkbox>
|
|
<el-checkbox-group v-model="searchObj[item.prop]" @change="
|
|
(val) => {
|
|
handleCheckedCitiesChange(val, item);
|
|
}
|
|
">
|
|
<el-checkbox v-for="obj in item.options" :key="obj.value" :label="obj.value">{{ obj.label }}</el-checkbox>
|
|
</el-checkbox-group>
|
|
</template>
|
|
<!-- radio -->
|
|
<el-radio-group v-else-if="item.showType === 'radio'" v-model="searchObj[item.prop]" @change="
|
|
(val) => {
|
|
handleRadioChange(val, item);
|
|
}
|
|
">
|
|
<el-radio v-for="obj in item.options" :key="obj.value" :label="obj.value">{{ obj.label }}</el-radio>
|
|
</el-radio-group>
|
|
<!-- 级联选择 -->
|
|
<el-cascader v-else-if="item.showType === 'cascader'" v-model="searchObj[item.prop]" :props="item.props"
|
|
:show-all-levels="item.showAllLevels" :clearable="item.clearable" :options="getOptions[item.prop]"
|
|
:placeholder="item.placeholder" />
|
|
<div v-if="item.showType === 'Slot'">
|
|
<slot :name="item.prop"></slot>
|
|
</div>
|
|
<div v-if="item.showType === 'defaultSlot'">
|
|
<slot name="defaultSlot"></slot>
|
|
</div>
|
|
<div v-if="item.showType === 'nameSlot'">
|
|
<slot name="nameSlot"></slot>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="button-container">
|
|
<div class="flex">
|
|
<el-button type="primary" @click="submit" size="small">确定</el-button>
|
|
<el-button type="" @click="reset" size="small">重置</el-button>
|
|
<slot> </slot>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
ref,
|
|
reactive,
|
|
watchEffect,
|
|
getCurrentInstance,
|
|
watch,
|
|
computed
|
|
} from "vue";
|
|
import * as MOSTY from "@/components/MyComponents/index";
|
|
const { proxy } = getCurrentInstance();
|
|
const props = defineProps({
|
|
searchArr: {
|
|
type: Array,
|
|
default: () => {
|
|
return [
|
|
{
|
|
showType: "select",
|
|
prop: "selectKey",
|
|
options: [
|
|
{
|
|
value: 1,
|
|
label: "选择1"
|
|
}
|
|
],
|
|
defaultVal: "",
|
|
label: "选择",
|
|
dict: "" // 字典编码
|
|
},
|
|
{
|
|
showType: "input",
|
|
prop: "inputKey",
|
|
defaultVal: "",
|
|
label: "输入"
|
|
},
|
|
{
|
|
showType: "department",
|
|
prop: "deptKey",
|
|
defaultVal: "",
|
|
label: "输入"
|
|
},
|
|
{
|
|
showType: "daterange",
|
|
prop: "daterangeKey",
|
|
defaultVal: "",
|
|
label: "输入"
|
|
},
|
|
{
|
|
showType: "date",
|
|
prop: "date",
|
|
defaultVal: ""
|
|
},
|
|
{
|
|
showType: "checkbox",
|
|
prop: "checkboxKey1",
|
|
options: [
|
|
{
|
|
value: 1,
|
|
label: "选择1"
|
|
}
|
|
],
|
|
defaultVal: ""
|
|
},
|
|
{
|
|
showType: "cascader",
|
|
prop: "cascaderKey",
|
|
label: "级联选择",
|
|
checkStrictly: false, //点击任意选中
|
|
defaultVal: ""
|
|
},
|
|
{
|
|
showType: "radio",
|
|
defaultVal: ""
|
|
},
|
|
{
|
|
showType: "defaultTime",
|
|
prop: "timeField",
|
|
options: []
|
|
}
|
|
];
|
|
}
|
|
},
|
|
marginBottom: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
backgroundColor: {
|
|
type: String,
|
|
default: "rgb(255, 255, 255, 1)"
|
|
},
|
|
bool: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
});
|
|
let loadingPage = ref(false);
|
|
const emit = defineEmits(["submit", "reset"]);
|
|
let searchObj = reactive({});
|
|
|
|
// select 的一些默认配置
|
|
const selectDefault = {
|
|
clearable: true, // 是否可以清空
|
|
filterable: true, // 是否可以筛选
|
|
multiple: false, // 是否多选
|
|
placeholder: "请选择"
|
|
};
|
|
// 重新定义下拉框的选项
|
|
let getOptions = reactive({});
|
|
// input 的一些默认配置
|
|
const inputDefault = {
|
|
clearable: true, // 是否可以清空
|
|
placeholder: "请输入"
|
|
};
|
|
const shortcuts = [
|
|
{
|
|
text: "今天",
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 0);
|
|
return [start, end];
|
|
}
|
|
},
|
|
{
|
|
text: "昨天",
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 1);
|
|
end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
|
|
return [start, end];
|
|
}
|
|
},
|
|
{
|
|
text: "最近7天",
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
|
return [start, end];
|
|
}
|
|
},
|
|
{
|
|
text: "最近30天",
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
|
return [start, end];
|
|
}
|
|
},
|
|
{
|
|
text: "最近90天",
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
|
return [start, end];
|
|
}
|
|
}
|
|
];
|
|
// daterange 的一些默认配置
|
|
const daterangeDefault = {
|
|
rangeSeparator: "至",
|
|
startPlaceholder: "开始日期",
|
|
endPlaceholder: "结束日期",
|
|
shortcuts: [], // 快捷选择
|
|
defaultShortcuts: true // 是否显示快捷选择 如果要自定义快捷选择传入一个shortcuts就可以了
|
|
};
|
|
// date 的一些默认配置
|
|
const defaultDate = {
|
|
clearable: true, // 是否可以清空
|
|
placeholder: "请输入",
|
|
shortcuts: [], // 快捷选择
|
|
defaultShortcuts: true // 是否显示快捷选择 如果要自定义快捷选择传入一个shortcuts就可以了
|
|
};
|
|
const dateShortcuts = [
|
|
{
|
|
text: "今天",
|
|
value: new Date()
|
|
},
|
|
{
|
|
text: "昨天",
|
|
value: () => {
|
|
const date = new Date();
|
|
date.setTime(date.getTime() - 3600 * 1000 * 24);
|
|
return date;
|
|
}
|
|
},
|
|
{
|
|
text: "7天前",
|
|
value: () => {
|
|
const date = new Date();
|
|
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
|
|
return date;
|
|
}
|
|
},
|
|
{
|
|
text: "30天前",
|
|
value: () => {
|
|
const date = new Date();
|
|
date.setTime(date.getTime() - 3600 * 1000 * 24 * 30);
|
|
return date;
|
|
}
|
|
},
|
|
{
|
|
text: "90天前",
|
|
value: () => {
|
|
const date = new Date();
|
|
date.setTime(date.getTime() - 3600 * 1000 * 24 * 90);
|
|
return date;
|
|
}
|
|
}
|
|
];
|
|
// 设置不可选的日期
|
|
const disabledDate = (time) => {
|
|
return time.getTime() > Date.now();
|
|
};
|
|
// checkbox 的一些默认配置
|
|
const defaultCheckbox = reactive({
|
|
defaultVal: [],
|
|
checkAll: false, // 全选的值
|
|
isIndeterminate: false, // 控制全选按钮样式
|
|
showSelectAll: true // 是否显示全选
|
|
});
|
|
// 全选复选框的选中与不选中
|
|
const handleCheckAllChange = (val, obj) => {
|
|
searchObj[obj.prop] = val ? obj.checkboxValueArr : [];
|
|
obj.isIndeterminate = false;
|
|
};
|
|
// 单个复选框的选中与不选中
|
|
const handleCheckedCitiesChange = (value, obj) => {
|
|
const checkedCount = value.length;
|
|
obj.checkAll = checkedCount === obj.checkboxValueArr.length;
|
|
obj.isIndeterminate =
|
|
checkedCount > 0 && checkedCount < obj.checkboxValueArr.length;
|
|
};
|
|
//单选
|
|
const handleRadioChange = (val, obj) => {
|
|
console.log(val, obj);
|
|
};
|
|
// cascader 的一些默认配置
|
|
let defaultCascader = {
|
|
filterable: true, // 是否可以搜索
|
|
clearable: true, // 是否可以清空
|
|
placeholder: "请选择",
|
|
checkStrictly: true, // 控制是否父子联动(是否可以选择任意节点)
|
|
showAllLevels: false, // 是否显示完整路径
|
|
lazy: false, // 是否懒加载 当设置为false时就要传入options
|
|
portUrl: "", // 这里必须写 接口地址
|
|
props: {
|
|
label: "label",
|
|
value: "value",
|
|
children: "children"
|
|
},
|
|
options: []
|
|
};
|
|
// 在懒加载状态下cascader 的props的一些配置
|
|
const cascaderLazyProps = reactive({
|
|
value: "value",
|
|
label: "label",
|
|
lazy: false,
|
|
lazyLoad(node, resolve) {
|
|
// 这里要根据实际情况修改
|
|
const { level } = node;
|
|
let options = [];
|
|
switch (level) {
|
|
case 0:
|
|
options = [
|
|
{
|
|
value: 1,
|
|
label: "选择1",
|
|
leaf: false // 表示有下一级 必须有这个表示 不然获取不到值
|
|
},
|
|
{
|
|
value: 2,
|
|
label: "选择2",
|
|
leaf: false // 表示有下一级
|
|
},
|
|
{
|
|
value: 3,
|
|
label: "选择3",
|
|
leaf: false // 表示有下一级
|
|
},
|
|
{
|
|
value: 4,
|
|
label: "选择4",
|
|
leaf: true // 表示没有下一级了
|
|
}
|
|
];
|
|
break;
|
|
case 1:
|
|
options = [
|
|
{
|
|
value: 11,
|
|
label: "选择1_1",
|
|
leaf: true // 表示没有下一级了
|
|
},
|
|
{
|
|
value: 21,
|
|
label: "选择2_1",
|
|
leaf: true // 表示没有下一级了
|
|
},
|
|
{
|
|
value: 31,
|
|
label: "选择3_1",
|
|
leaf: true // 表示没有下一级了
|
|
},
|
|
{
|
|
value: 41,
|
|
label: "选择4_1",
|
|
leaf: true // 表示没有下一级了
|
|
}
|
|
];
|
|
}
|
|
resolve(options);
|
|
}
|
|
});
|
|
// 获取到传过来的参数
|
|
let getArr = reactive([]);
|
|
const submit = () => {
|
|
emit("submit", searchObj);
|
|
};
|
|
const reset = () => {
|
|
getArr.forEach((item) => {
|
|
searchObj[item.prop] = item.defaultVal;
|
|
});
|
|
emit("reset", true);
|
|
emit("submit", searchObj);
|
|
};
|
|
|
|
// 暴露searchObj给父组件
|
|
defineExpose({
|
|
searchObj,
|
|
submit,
|
|
reset
|
|
});
|
|
watchEffect(() => {
|
|
loadingPage.value = true;
|
|
let arr = JSON.parse(JSON.stringify(props.searchArr));
|
|
getArr = arr.map((item) => {
|
|
switch (item.showType) {
|
|
case "select":
|
|
item = { ...selectDefault, ...item };
|
|
item.options = reactive(item.options);
|
|
getOptions[item.prop] = item.options;
|
|
break;
|
|
case "input":
|
|
item = { ...inputDefault, ...item };
|
|
break;
|
|
case "daterange":
|
|
item = { ...daterangeDefault, ...item };
|
|
if (item.defaultShortcuts) item.shortcuts = shortcuts;
|
|
break;
|
|
case "date":
|
|
item = { ...defaultDate, ...item };
|
|
if (item.defaultShortcuts) {
|
|
item.shortcuts = dateShortcuts;
|
|
}
|
|
break;
|
|
case "checkbox":
|
|
item = reactive({ ...defaultCheckbox, ...item });
|
|
item.checkboxValueArr = item.options.map((obj) => {
|
|
return obj.value;
|
|
});
|
|
break;
|
|
case "cascader":
|
|
item = { ...defaultCascader, ...item };
|
|
if (item.lazy) {
|
|
cascaderLazyProps.checkStrictly = item.checkStrictly;
|
|
item.props = { ...cascaderLazyProps, ...(item.props || {}) };
|
|
delete item.options;
|
|
} else {
|
|
item.props = {
|
|
...defaultCascader.props,
|
|
...(item.props || {}),
|
|
...{ checkStrictly: item.checkStrictly }
|
|
};
|
|
getOptions[item.prop] = reactive(item.options);
|
|
}
|
|
break;
|
|
}
|
|
loadingPage.value = false;
|
|
searchObj[item.prop] = item.defaultVal;
|
|
return item;
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.pageSearch {
|
|
.main-container {
|
|
padding: 0;
|
|
}
|
|
|
|
.content-container {
|
|
display: flex;
|
|
padding: 0 15px;
|
|
}
|
|
|
|
.button-container {
|
|
display: flex;
|
|
flex-direction: column-reverse;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.filter-title {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 10px;
|
|
width: 100%;
|
|
background: linear-gradient(to right, #9ed7ff, #e6f0f8);
|
|
padding: 5px 15px;
|
|
|
|
.filter-label {
|
|
display: flex;
|
|
align-items: center;
|
|
font-weight: bold;
|
|
color: #000;
|
|
margin-right: 10px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.filter-line {
|
|
flex: 1;
|
|
height: 1px;
|
|
background-color: #ccc;
|
|
}
|
|
}
|
|
|
|
.box {
|
|
flex: 1;
|
|
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
|
|
.item {
|
|
display: flex;
|
|
margin-right: 12px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.label {
|
|
margin: auto;
|
|
margin-right: 5px;
|
|
white-space: nowrap;
|
|
color: #000;
|
|
}
|
|
}
|
|
}
|
|
|
|
::v-deep .el-date-editor .el-range-separator {
|
|
color: #333;
|
|
}
|
|
</style>
|