| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="search-panel-container"> |
| | | <el-form |
| | | ref="formRef" |
| | | :model="modelValue" |
| | | class="search-form" |
| | | label-width="0" |
| | | > |
| | | <el-row :gutter="10" class="form-row"> |
| | | <!-- 渲æè¡¨å项 --> |
| | | <el-col |
| | | v-for="(item, index) in visibleSchema" |
| | | :key="item.prop || index" |
| | | :xs="24" |
| | | :sm="12" |
| | | :md="8" |
| | | :lg="4" |
| | | :xl="4" |
| | | class="search-col" |
| | | > |
| | | <el-form-item :prop="item.prop" :rules="item.rules" class="search-form-item"> |
| | | <!-- èªå®ä¹ææ§½ --> |
| | | <slot v-if="item.slot" :name="item.slot" :item="item"></slot> |
| | | <!-- é»è®¤æ¸²æç±»å --> |
| | | <template v-else> |
| | | <!-- è¾å
¥æ¡ --> |
| | | <el-input |
| | | v-if="item.type === 'input'" |
| | | v-model="modelValue[item.prop]" |
| | | :placeholder="item.placeholder || '请è¾å
¥'" |
| | | clearable |
| | | class="full-width" |
| | | v-bind="item.props" |
| | | @keyup.enter="handleSearch" |
| | | /> |
| | | |
| | | <!-- ä¸ææ¡ --> |
| | | <el-select |
| | | v-else-if="item.type === 'select'" |
| | | v-model="modelValue[item.prop]" |
| | | :placeholder="item.placeholder || 'è¯·éæ©'" |
| | | clearable |
| | | class="full-width" |
| | | v-bind="item.props" |
| | | > |
| | | {{ item || 'è¯·éæ©' }} |
| | | <!-- <el-option |
| | | v-for="(opt,idx) in getOptions(item)" |
| | | :key="idx" |
| | | :label="opt.label" |
| | | :value="opt.value" |
| | | /> --> |
| | | </el-select> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <el-date-picker |
| | | v-else-if="item.type === 'date'" |
| | | v-model="modelValue[item.prop]" |
| | | type="date" |
| | | :placeholder="item.placeholder || 'éæ©æ¥æ'" |
| | | style="width: 100%" |
| | | value-format="YYYY-MM-DD" |
| | | class="full-width" |
| | | v-bind="item.props" |
| | | /> |
| | | |
| | | <!-- æ¥æèå´éæ©å¨ --> |
| | | <el-date-picker |
| | | v-else-if="item.type === 'daterange'" |
| | | v-model="modelValue[item.prop]" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | class="full-width" |
| | | v-bind="item.props" |
| | | /> |
| | | </template> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <!-- æé®åºå --> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="4" :xl="4" class="search-actions-col"> |
| | | <el-form-item class="search-actions"> |
| | | <el-button style="background: #002FA7; color: white;" icon="Search" @click="handleSearch">æç´¢</el-button> |
| | | <el-button icon="Refresh" @click="handleReset">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- å±å¼/æ¶èµ·æé® --> |
| | | <div v-if="schema.length > 5" class="expand-toggle" @click="toggleExpand"> |
| | | <span>{{ isExpanded ? 'æ¶èµ·' : 'å±å¼' }}</span> |
| | | <el-icon :class="{ 'is-reverse': isExpanded }"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup name="SearchPanel"> |
| | | import { ref, reactive, computed, getCurrentInstance, onMounted } from 'vue'; |
| | | import { ArrowDown, Search, Refresh } from '@element-plus/icons-vue'; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const props = defineProps({ |
| | | // è¡¨åæ°æ®å¯¹è±¡ |
| | | modelValue: { |
| | | type: Object, |
| | | required: true |
| | | }, |
| | | // 表åé
置项 |
| | | schema: { |
| | | type: Array, |
| | | default: () => [] |
| | | } |
| | | }); |
| | | |
| | | const emit = defineEmits(['update:modelValue', 'search', 'reset']); |
| | | |
| | | // æ¯å¦å±å¼ |
| | | const isExpanded = ref(false); |
| | | const formRef = ref(null); |
| | | const dictMap = reactive({}); |
| | | |
| | | // 计ç®å¯è§ç schema 项 |
| | | const visibleSchema = computed(() => { |
| | | if (isExpanded.value || props.schema.length <= 5) { |
| | | return props.schema; |
| | | } |
| | | return props.schema.slice(0, 5); |
| | | }); |
| | | |
| | | // åå§ååå
¸æ°æ® |
| | | onMounted(() => { |
| | | const dicts = props.schema.filter(item => item.dict).map(item => item.dict); |
| | | if (dicts.length > 0 && proxy.useDict) { |
| | | const dictData = proxy.useDict(...dicts); |
| | | Object.keys(dictData).forEach(key => { |
| | | dictMap[key] = dictData[key]; |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | // è·å䏿é项 (æ¯æéæ options å åå
¸ dict) |
| | | function getOptions(item) { |
| | | if (item.options) return item.options; |
| | | if (item.dict && dictMap[item.dict]) { |
| | | return dictMap[item.dict].value || []; |
| | | } |
| | | return []; |
| | | } |
| | | |
| | | // æç´¢ |
| | | function handleSearch() { |
| | | emit('search', props.modelValue); |
| | | } |
| | | |
| | | // éç½® |
| | | function handleReset() { |
| | | if (formRef.value) { |
| | | formRef.value.resetFields(); |
| | | } |
| | | const keys = props.schema.map(item => item.prop).filter(Boolean); |
| | | keys.forEach(key => { |
| | | props.modelValue[key] = undefined; |
| | | }); |
| | | emit('update:modelValue', props.modelValue); |
| | | emit('reset'); |
| | | } |
| | | |
| | | // 忢å±å¼/æ¶èµ· |
| | | function toggleExpand() { |
| | | isExpanded.value = !isExpanded.value; |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .search-panel-container { |
| | | background: #fff; |
| | | padding: 15px 15px 5px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | margin-bottom: 15px; |
| | | |
| | | .search-form { |
| | | .form-row { |
| | | width: 100%; |
| | | } |
| | | |
| | | .search-col { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .search-form-item { |
| | | margin-right: 0; |
| | | margin-bottom: 0; |
| | | width: 100%; |
| | | |
| | | :deep(.el-form-item__content) { |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | .full-width { |
| | | width: 100% !important; |
| | | } |
| | | |
| | | .search-actions-col { |
| | | margin-left: auto; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .search-actions { |
| | | margin-bottom: 0; |
| | | margin-right: 0; |
| | | |
| | | :deep(.el-button--primary) { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | | } |
| | | } |
| | | |
| | | .expand-toggle { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 4px; |
| | | font-size: 13px; |
| | | color: #909399; |
| | | cursor: pointer; |
| | | padding: 5px 0; |
| | | user-select: none; |
| | | width: 100%; |
| | | border-top: 1px solid #f0f2f5; |
| | | margin-top: 5px; |
| | | |
| | | &:hover { |
| | | color: #409eff; |
| | | } |
| | | |
| | | .el-icon { |
| | | transition: transform 0.3s; |
| | | &.is-reverse { |
| | | transform: rotate(180deg); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | |