Files
sgxt_web/src/views/backOfficeSystem/ces/components/QueryFormPanel.vue

380 lines
8.6 KiB
Vue
Raw Normal View History

2026-03-14 19:46:21 +08:00
<template>
<section class="query-wrap">
<div class="query-title">{{ title }}</div>
<div class="query-grid">
<div v-for="field in renderFields" :key="field.key" class="query-cell">
<div class="cell-label">{{ field.label }}</div>
2026-03-29 19:46:50 +08:00
<div
class="cell-control"
:class="{ 'is-checkbox': field.type === 'checkbox' }"
>
<el-input
clearable
v-if="field.type === 'input'"
v-model="formState[field.key]"
class="control-input"
:placeholder="field.placeholder || ''"
/>
<el-input
clearable
v-else-if="field.type === 'number'"
v-model="formState[field.key]"
class="control-input"
type="number"
:placeholder="field.placeholder || ''"
/>
<el-select
clearable
v-else-if="field.type === 'select'"
v-model="formState[field.key]"
class="control-select"
:placeholder="field.placeholder || '请选择'"
:multiple="field.multiple || false"
collapse-tags
collapse-tags-tooltip
>
<el-option
v-for="item in field.options || []"
:key="item.value ?? item"
:label="item.label ?? item"
:value="item.value ?? item"
/>
2026-03-14 19:46:21 +08:00
</el-select>
2026-03-29 19:46:50 +08:00
<el-date-picker
clearable
v-else-if="field.type === 'date'"
v-model="formState[field.key]"
class="control-date"
type="date"
:placeholder="field.placeholder || '请选择日期'"
value-format="YYYY-MM-DD"
/>
<el-date-picker
clearable
v-else-if="field.type === 'datetime'"
v-model="formState[field.key]"
class="control-date"
type="datetime"
:placeholder="field.placeholder || '请选择时间'"
value-format="YYYY-MM-DD HH:mm:ss"
/>
<el-date-picker
clearable
v-else-if="field.type === 'daterange'"
v-model="formState[field.key]"
class="control-date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
/>
<el-date-picker
clearable
v-else-if="field.type === 'datetimerange'"
v-model="formState[field.key]"
class="control-date"
type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="YYYY-MM-DD HH:mm:ss"
/>
2026-03-14 19:46:21 +08:00
<template v-else-if="field.type === 'department'">
2026-03-29 19:46:50 +08:00
<MOSTY.Department
clearable
v-model="formState[field.key]"
class="control-select"
/>
2026-03-14 19:46:21 +08:00
</template>
2026-03-29 19:46:50 +08:00
<div v-else-if="field.type === 'checkbox'" class="checkbox-wrap">
2026-03-14 19:46:21 +08:00
<el-checkbox v-model="formState[field.key]" />
</div>
2026-03-29 19:46:50 +08:00
<div v-else-if="field.type === 'slot'" class="checkbox-wrap">
<slot :name="field.key" />
</div>
2026-03-14 19:46:21 +08:00
</div>
</div>
</div>
<div class="query-action">
<div>
<slot name="but"></slot>
</div>
<div>
2026-03-29 19:46:50 +08:00
<el-button size="small" type="primary" @click="handleSearch">{{
searchText
}}</el-button>
<el-button size="small" type="button" @click="handleReset"
>重置
</el-button>
2026-03-14 19:46:21 +08:00
</div>
</div>
</section>
</template>
<script setup>
2026-03-29 19:46:50 +08:00
import { computed, reactive, watch } from "vue";
2026-03-14 19:46:21 +08:00
import * as MOSTY from "@/components/MyComponents/index";
const props = defineProps({
title: {
type: String,
2026-03-29 19:46:50 +08:00
default: "查询条件"
2026-03-14 19:46:21 +08:00
},
searchText: {
type: String,
2026-03-29 19:46:50 +08:00
default: "查询"
2026-03-14 19:46:21 +08:00
},
fields: {
type: Array,
default: () => []
},
searchArr: {
type: Array,
default: () => []
}
2026-03-29 19:46:50 +08:00
});
2026-03-14 19:46:21 +08:00
2026-03-29 19:46:50 +08:00
const emit = defineEmits(["search", "submit", "reset"]);
const formState = reactive({});
2026-03-14 19:46:21 +08:00
const renderFields = computed(() => {
2026-03-29 19:46:50 +08:00
const source = props.searchArr.length ? props.searchArr : props.fields;
return source
.map((field) => ({
...field,
key: field.key || field.prop,
type: field.type || field.showType || "input"
}))
.filter((field) => field.key);
});
2026-03-14 19:46:21 +08:00
const getResetValue = (field) => {
if (field.defaultVal !== undefined) {
2026-03-29 19:46:50 +08:00
return Array.isArray(field.defaultVal)
? [...field.defaultVal]
: field.defaultVal;
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
if (field.type === "checkbox") {
return false;
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
if (field.type === "daterange" || field.type === "datetimerange") {
return [];
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
if (field.type === "select") {
2026-03-14 19:46:21 +08:00
if (field.multiple) {
2026-03-29 19:46:50 +08:00
return [];
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
return null;
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
return "";
};
2026-03-14 19:46:21 +08:00
const buildResetPayload = () => {
2026-03-29 19:46:50 +08:00
const payload = {};
2026-03-14 19:46:21 +08:00
renderFields.value.forEach((field) => {
2026-03-29 19:46:50 +08:00
payload[field.key] = getResetValue(field);
});
return payload;
};
2026-03-14 19:46:21 +08:00
const setFormState = (value = {}) => {
Object.keys(formState).forEach((key) => {
2026-03-29 19:46:50 +08:00
delete formState[key];
});
2026-03-14 19:46:21 +08:00
renderFields.value.forEach((field) => {
if (value[field.key] !== undefined) {
2026-03-29 19:46:50 +08:00
formState[field.key] = Array.isArray(value[field.key])
? [...value[field.key]]
: value[field.key];
return;
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
formState[field.key] = getResetValue(field);
});
};
2026-03-14 19:46:21 +08:00
const getFormSnapshot = () => {
2026-03-29 19:46:50 +08:00
const snapshot = {};
2026-03-14 19:46:21 +08:00
renderFields.value.forEach((field) => {
2026-03-29 19:46:50 +08:00
const value = formState[field.key];
2026-03-14 19:46:21 +08:00
if (Array.isArray(value)) {
2026-03-29 19:46:50 +08:00
snapshot[field.key] = [...value];
return;
2026-03-14 19:46:21 +08:00
}
2026-03-29 19:46:50 +08:00
snapshot[field.key] = value;
});
return snapshot;
};
2026-03-14 19:46:21 +08:00
const handleSearch = () => {
2026-03-29 19:46:50 +08:00
const payload = getFormSnapshot();
emit("search", payload);
emit("submit", payload);
};
2026-03-14 19:46:21 +08:00
const handleReset = () => {
2026-03-29 19:46:50 +08:00
const payload = buildResetPayload();
setFormState(payload);
emit("reset", true);
emit("search", payload);
emit("submit", payload);
};
2026-03-14 19:46:21 +08:00
watch(
renderFields,
() => {
2026-03-29 19:46:50 +08:00
setFormState(buildResetPayload());
2026-03-14 19:46:21 +08:00
},
{ immediate: true }
2026-03-29 19:46:50 +08:00
);
2026-03-14 19:46:21 +08:00
defineExpose({
formState,
handleSearch,
handleReset
2026-03-29 19:46:50 +08:00
});
2026-03-14 19:46:21 +08:00
</script>
<style scoped lang="scss">
.query-wrap {
border: 1px solid #b8d3ff;
background: #fff;
}
.query-title {
height: 32px;
background: linear-gradient(to right, #9ed7ff, #e6f0f8);
line-height: 32px;
padding-left: 10px;
font-size: 16px;
font-weight: 700;
color: #0d2148;
border-bottom: 1px solid #b8d3ff;
}
.query-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.query-cell {
display: flex;
min-height: 40px;
border-right: 1px solid #b8d3ff;
border-bottom: 1px solid #b8d3ff;
}
.query-cell:nth-child(4n) {
border-right: 0;
}
.cell-label {
width: 130px;
flex-shrink: 0;
border-right: 1px solid #b8d3ff;
font-size: 14px;
color: #0d2148;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}
.cell-control {
flex: 1;
min-width: 0;
padding: 4px 8px;
display: flex;
align-items: center;
overflow: hidden;
}
.cell-control.is-checkbox {
justify-content: center;
padding: 0;
}
.control-input,
.control-select,
.control-date {
width: 100%;
max-width: 100%;
min-width: 0;
}
:deep(.control-input .el-input__wrapper),
:deep(.control-select .el-select__wrapper),
:deep(.control-date .el-input__wrapper) {
width: 100%;
max-width: 100%;
min-height: 28px;
border-radius: 0;
box-shadow: 0 0 0 1px #b8d3ff inset;
}
:deep(.control-date.el-date-editor) {
width: 100%;
max-width: 100%;
min-width: 0;
}
:deep(.control-date.el-date-editor .el-range-input) {
min-width: 0;
}
:deep(.control-input .el-input__inner),
:deep(.control-select .el-select__placeholder),
:deep(.control-date .el-input__inner) {
font-size: 14px;
color: #0d2148;
}
:deep(.control-date .el-range-input) {
font-size: 14px;
color: #0d2148;
}
:deep(.control-date .el-range__icon),
:deep(.control-date .el-range__close-icon) {
line-height: 1;
}
.checkbox-wrap {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
:deep(.checkbox-wrap .el-checkbox) {
margin-right: 0;
height: 100%;
display: flex;
align-items: center;
}
:deep(.checkbox-wrap .el-checkbox__inner) {
width: 14px;
height: 14px;
}
.query-action {
height: 36px;
display: flex;
// justify-content: flex-end;
justify-content: space-between;
align-items: center;
padding: 0 8px;
}
.search-btn {
height: 26px;
min-width: 70px;
border: 1px solid #0f5bbd;
background: #0f5bbd;
color: #fff;
font-size: 14px;
cursor: pointer;
}
</style>