search表单

This commit is contained in:
Gary Fu
2024-01-06 18:11:40 +08:00
parent a32d6b2eac
commit 62f7ad4b9c
5 changed files with 297 additions and 237 deletions

View File

@@ -97,6 +97,10 @@ html, body, #app, .index-container {
padding-top: 20px; padding-top: 20px;
} }
.common-form.el-form--inline .el-input{
--el-input-width: 220px;
}
.form-edit-width-70 { .form-edit-width-70 {
width:70% width:70%
} }

View File

@@ -29,6 +29,7 @@
* @property {string} labelKey label字段名 * @property {string} labelKey label字段名
* @property {number} debounceTime 防抖时间 * @property {number} debounceTime 防抖时间
* @property {string} autocompleteWidth 宽度 * @property {string} autocompleteWidth 宽度
* @property {string} inputWidth input宽度
* @property {CommonSelectPageOption} selectPageConfig 分页 * @property {CommonSelectPageOption} selectPageConfig 分页
* @property {Number} colSize 显示几列 * @property {Number} colSize 显示几列
* @property {string} loadingText 加载提示loading * @property {string} loadingText 加载提示loading
@@ -36,7 +37,7 @@
* @property {Object} inputAttrs 输入框配置项 * @property {Object} inputAttrs 输入框配置项
*/ */
import { computed, nextTick, onMounted, ref, watch } from 'vue' import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { debounce } from 'lodash' import { debounce, isObject } from 'lodash'
import { onClickOutside, onKeyStroke, useVModel } from '@vueuse/core' import { onClickOutside, onKeyStroke, useVModel } from '@vueuse/core'
import chunk from 'lodash/chunk' import chunk from 'lodash/chunk'
@@ -80,6 +81,10 @@ const props = defineProps({
type: String, type: String,
default: '500px' default: '500px'
}, },
inputWidth: {
type: String,
default: '220px'
},
autocompleteConfig: { autocompleteConfig: {
type: Object, type: Object,
required: true required: true
@@ -122,7 +127,7 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['update:modelValue', 'onSelectData', 'update:defaultLabel']) const emit = defineEmits(['update:modelValue', 'change', 'update:defaultLabel'])
// 关键字搜索 // 关键字搜索
const keywords = ref(props.defaultLabel) const keywords = ref(props.defaultLabel)
// 上次搜索记录 // 上次搜索记录
@@ -214,15 +219,32 @@ watch(() => popoverVisible.value, (val) => {
} }
}) })
watch(() => props.modelValue, (value) => {
console.info('=====================value', value)
if (!props.useIdModel) {
setAutocompleteLabel(value && isObject(value) ? value[labelProp.value] : '')
}
vModel.value = value
})
watch(() => props.defaultLabel, (label) => {
setAutocompleteLabel(label)
})
//* ********************数据选择********************* //* ********************数据选择*********************
const vModel = useVModel(props, 'modelValue', emit) const vModel = useVModel(props, 'modelValue', emit)
const vAutocompleteLabel = useVModel(props, 'defaultLabel', emit) const vDefaultLabel = useVModel(props, 'defaultLabel', emit)
const setAutocompleteLabel = label => {
keywords.value = label
vDefaultLabel.value = label
lastAutocompleteLabel.value = label
}
const onSelectData = (row) => { const onSelectData = (row) => {
popoverVisible.value = false popoverVisible.value = false
if (!vModel.value && !row) { if (!vModel.value && !row) {
console.info('==================', row)
return return
} }
let label = '' let label = ''
@@ -231,11 +253,9 @@ const onSelectData = (row) => {
label = row[labelProp.value] label = row[labelProp.value]
value = props.useIdModel ? row[idProp.value] : row value = props.useIdModel ? row[idProp.value] : row
} }
keywords.value = label setAutocompleteLabel(label)
vAutocompleteLabel.value = label
lastAutocompleteLabel.value = label
vModel.value = value vModel.value = value
emit('onSelectData', row) emit('change', row)
} }
// =======================按键处理=================== // =======================按键处理===================
@@ -316,114 +336,113 @@ const selectPagePaginationChange = (tab, pageNumber) => {
</script> </script>
<template> <template>
<div> <el-popover
<el-popover ref="autocompletePopover"
ref="autocompletePopover" :visible="popoverVisible"
:visible="popoverVisible" popper-class="common-autocomplete"
popper-class="common-autocomplete" placement="bottom-start"
placement="bottom-start" :width="autocompleteWidth"
:width="autocompleteWidth" :title="title||placeholder"
:title="title||placeholder" >
> <template #reference>
<template #reference> <el-input
<el-input v-model="keywords"
v-model="keywords" :clearable="clearable"
:clearable="clearable" :placeholder="placeholder||title"
:placeholder="placeholder||title" :disabled="disabled"
:disabled="disabled" :readonly="readonly"
:readonly="readonly" :style="{width: inputWidth}"
v-bind="inputAttrs" v-bind="inputAttrs"
@input="onInputKeywords(true)" @input="onInputKeywords(true)"
@click="onInputKeywords(false)" @click="onInputKeywords(false)"
/> />
</template> </template>
<template #default> <template #default>
<div v-if="showSelectPage"> <div v-if="showSelectPage">
<el-tabs <el-tabs
v-model="selectPageTab" v-model="selectPageTab"
class="common-select-page" class="common-select-page"
type="border-card" type="border-card"
@tab-click="onInputKeywords(false)" @tab-click="onInputKeywords(false)"
>
<el-tab-pane
v-for="tab in selectPageConfig.tabs"
:key="tab.id"
:name="tab.id"
>
<template #label>
<span>{{ tab.label }}</span>
</template>
<template #default>
<div
v-loading="loadingData"
:element-loading-text="loadingText"
class="select-page-content"
:style="{minHeight}"
>
<template
v-for="(rowData, index) in parsedSelectPageData[tab.id]"
:key="index"
>
<el-row>
<el-col
v-for="(colData, idx) in rowData"
:key="idx"
:span="24/colSize"
>
<el-button
plain
class="common-select-page-btn is-text"
@click="onSelectData(colData)"
>
{{ colData[labelProp] }}
</el-button>
</el-col>
</el-row>
</template>
<el-pagination
v-if="selectPagePagination(tab)"
:style="{'justify-content':'center'}"
v-bind="selectPageAttrs"
:total="selectPagePageConfig[tab.id].totalCount"
:page-size="selectPagePageConfig[tab.id].pageSize"
:current-page="selectPagePageConfig[tab.id].pageNumber"
@current-change="selectPagePaginationChange(tab, $event)"
/>
</div>
</template>
</el-tab-pane>
</el-tabs>
</div>
<!--自动完成内容-->
<common-table
v-else
ref="tableRef"
v-model:page="autoPage"
:loading="loadingData"
:loading-text="loadingText"
class="autocomplete-table"
:columns="autocompleteConfig.columns"
:empty-text="autocompleteConfig.emptyMessage"
:data="dataList"
:page-attrs="pageAttrs"
@row-click="onSelectData($event)"
@current-page-change="onInputKeywords(false)"
> >
<template <el-tab-pane
v-for="column in autocompleteConfig.columns" v-for="tab in selectPageConfig.tabs"
#[column.slot]="scope" :key="tab.id"
:name="tab.id"
> >
<slot <template #label>
v-if="column.slot" <span>{{ tab.label }}</span>
:item="scope.item" </template>
:column-conf="scope.columnConf" <template #default>
:name="column.slot" <div
/> v-loading="loadingData"
</template> :element-loading-text="loadingText"
</common-table> class="select-page-content"
</template> :style="{minHeight}"
</el-popover> >
</div> <template
v-for="(rowData, index) in parsedSelectPageData[tab.id]"
:key="index"
>
<el-row>
<el-col
v-for="(colData, idx) in rowData"
:key="idx"
:span="24/colSize"
>
<el-button
plain
class="common-select-page-btn is-text"
@click="onSelectData(colData)"
>
{{ colData[labelProp] }}
</el-button>
</el-col>
</el-row>
</template>
<el-pagination
v-if="selectPagePagination(tab)"
:style="{'justify-content':'center'}"
v-bind="selectPageAttrs"
:total="selectPagePageConfig[tab.id].totalCount"
:page-size="selectPagePageConfig[tab.id].pageSize"
:current-page="selectPagePageConfig[tab.id].pageNumber"
@current-change="selectPagePaginationChange(tab, $event)"
/>
</div>
</template>
</el-tab-pane>
</el-tabs>
</div>
<!--自动完成内容-->
<common-table
v-else
ref="tableRef"
v-model:page="autoPage"
:loading="loadingData"
:loading-text="loadingText"
class="autocomplete-table"
:columns="autocompleteConfig.columns"
:empty-text="autocompleteConfig.emptyMessage"
:data="dataList"
:page-attrs="pageAttrs"
@row-click="onSelectData($event)"
@current-page-change="onInputKeywords(false)"
>
<template
v-for="column in autocompleteConfig.columns"
#[column.slot]="scope"
>
<slot
v-if="column.slot"
:item="scope.item"
:column-conf="scope.columnConf"
:name="column.slot"
/>
</template>
</common-table>
</template>
</el-popover>
</template> </template>
<style scoped> <style scoped>

View File

@@ -31,6 +31,10 @@ const props = defineProps({
type: Object, type: Object,
default: null default: null
}, },
validateOnRuleChange: {
type: Boolean,
default: false
},
showButtons: { showButtons: {
type: Boolean, type: Boolean,
default: true default: true
@@ -99,10 +103,9 @@ const emit = defineEmits(['submitForm', 'update:model'])
const formModel = useVModel(props, 'model', emit) const formModel = useVModel(props, 'model', emit)
watch(() => props.options, (options) => { watch(() => props.options, (options) => {
console.info('=================options', options)
options.forEach(option => { options.forEach(option => {
if (formModel.value && option.value) { if (formModel.value) {
formModel.value[option.prop] = option.value formModel.value[option.prop] = option.value || undefined
} }
}) })
rules.value = initRules() rules.value = initRules()
@@ -121,10 +124,12 @@ defineExpose({
<template> <template>
<el-form <el-form
ref="form" ref="form"
class="common-form"
:model="formModel" :model="formModel"
:rules="rules" :rules="rules"
:label-width="labelWidth" :label-width="labelWidth"
v-bind="$attrs" v-bind="$attrs"
:validate-on-rule-change="validateOnRuleChange"
> >
<common-form-control <common-form-control
v-for="(option,index) in options" v-for="(option,index) in options"

View File

@@ -1,125 +1,154 @@
<script setup> <script setup>
import { ref } from 'vue' import { computed, ref } from 'vue'
import { useCityAutocompleteConfig, useCitySelectPageConfig } from '@/services/city/CityService' import { useCityAutocompleteConfig, useCitySelectPageConfig } from '@/services/city/CityService'
import { $i18nMsg } from '@/messages'
const defaultCity = ref({})
setTimeout(() => {
defaultCity.value = {
code: 'SHA',
nameCn: '上海',
nameEn: 'Shanghai'
}
}, 1000)
/** /**
* @type {[CommonFormOption]} * @type {[CommonFormOption]}
*/ */
const formOptions = ref([{ const formOptions = computed(() => {
label: '用户名', return [{
prop: 'userName', label: '用户名',
value: '', prop: 'userName',
placeholder: '请输入用户名', value: '',
required: true, placeholder: '请输入用户名',
rules: [ required: true,
{ rules: [
min: 2, {
max: 6, min: 2,
message: '用户名在2-6位之间' max: 6,
message: '用户名在2-6位之间'
}
]
}, {
label: '密码',
prop: 'userPassword',
value: '',
placeholder: '请输入密码',
required: true,
pattern: /.{2,6}/,
attrs: {
showPassword: true
} }
] }, {
}, { label: '出生日期',
label: '密码', type: 'date-picker',
prop: 'userPassword', prop: 'birthday',
value: '', value: '',
placeholder: '请输入密码', placeholder: '选择出生日期',
required: true, required: true
pattern: /.{2,6}/, }, {
attrs: { label: '兴趣爱好',
showPassword: true type: 'checkbox-group',
} prop: 'hobby',
}, { value: '',
label: '出生日期', required: true,
type: 'date-picker', children: [
prop: 'birthday', {
value: '', label: '编程',
placeholder: '选择出生日期', value: 'program'
required: true },
}, { {
label: '兴趣爱好', label: '吃饭',
type: 'checkbox-group', value: 'eat'
prop: 'hobby', },
value: '', {
required: true, label: '睡觉',
children: [ value: 'sleep'
{ }
label: '编程', ]
value: 'program' }, {
label: '职业',
type: 'select',
prop: 'career',
value: '',
required: true,
children: [
{
label: '程序员',
value: 'programer'
},
{
label: '无业游民',
value: 'none'
},
{
label: '教师',
value: 'teacher'
}
]
}, {
label: '性别',
type: 'radio-group',
prop: 'gender',
value: '',
required: true,
children: [
{
label: '男',
value: 'male'
},
{
label: '女',
value: 'female'
},
{
label: '保密',
value: 'unknown'
}
]
}, {
label: '图标',
prop: 'icon',
value: '',
type: 'common-icon-select',
required: true
}, {
label: '城市',
prop: 'city',
value: defaultCity.value?.code,
type: 'common-autocomplete',
required: true,
placeholder: '请选择城市',
change: (city) => {
defaultCity.value = city
}, },
{ attrs: {
label: '吃饭', defaultLabel: $i18nMsg(defaultCity.value?.nameCn, defaultCity.value?.nameEn),
value: 'eat' autocompleteConfig: useCityAutocompleteConfig(),
}, selectPageConfig: useCitySelectPageConfig()
{
label: '睡觉',
value: 'sleep'
} }
] }, {
}, { label: '城市1',
label: '职业', prop: 'city1',
type: 'select', value: defaultCity,
prop: 'career', type: 'common-autocomplete',
value: '', required: true,
required: true, placeholder: '请选择城市1',
children: [ attrs: {
{ useIdModel: false,
label: '程序员', autocompleteConfig: useCityAutocompleteConfig(),
value: 'programer' selectPageConfig: useCitySelectPageConfig()
},
{
label: '无业游民',
value: 'none'
},
{
label: '教师',
value: 'teacher'
} }
] }, {
}, { label: '地址',
label: '性别', prop: 'address',
type: 'radio-group', value: '',
prop: 'gender', attrs: {
value: '', type: 'textarea'
required: true,
children: [
{
label: '男',
value: 'male'
},
{
label: '女',
value: 'female'
},
{
label: '保密',
value: 'unknown'
} }
] }]
}, { })
label: '图标',
prop: 'icon',
value: '',
type: 'common-icon-select',
required: true
}, {
label: '城市',
prop: 'city',
value: 'SHA',
type: 'common-autocomplete',
required: true,
placeholder: '请选择城市',
attrs: {
defaultLabel: '上海市',
autocompleteConfig: useCityAutocompleteConfig(),
selectPageConfig: useCitySelectPageConfig()
}
}, {
label: '地址',
prop: 'address',
value: '',
attrs: {
type: 'textarea'
}
}])
const userDto = ref({ const userDto = ref({
userName: '', userName: '',
userPassword: '' userPassword: ''

View File

@@ -82,20 +82,22 @@ const searchFormOptions = computed(() => {
{ {
label: '用户名', label: '用户名',
prop: 'nameCn' prop: 'nameCn'
},
{
label: '关键字',
prop: 'keywords'
},
{
label: '地址',
prop: 'address'
}, { }, {
label: $i18nMsg('性别', 'Gender'), label: $i18nMsg('性别', 'Gender'),
type: 'select', type: 'select',
prop: 'gender', prop: 'gender',
children: [{ children: [{
type: 'option',
value: '',
label: '请选择'
}, {
type: 'option',
value: 'male', value: 'male',
label: $i18nMsg('男', 'Male') label: $i18nMsg('男', 'Male')
}, { }, {
type: 'option',
value: 'female', value: 'female',
label: $i18nMsg('女', 'Female') label: $i18nMsg('女', 'Female')
}] }]
@@ -106,9 +108,9 @@ const doSearch = form => {
console.info('=================searchParam', searchParam.value) console.info('=================searchParam', searchParam.value)
} }
/** *************用户编辑**************/ /** *************用户编辑**************/
const currentUser = ref({}) const currentUser = ref(null)
const showEdit = ref(false) const showEdit = ref(false)
const userFormOptions = ref(useUserFormOptions()) const userFormOptions = computed(() => useUserFormOptions())
const toEditUser = user => { const toEditUser = user => {
currentUser.value = { ...user } currentUser.value = { ...user }
showEdit.value = true showEdit.value = true
@@ -171,6 +173,7 @@ const submitForm = () => {
title="用户编辑" title="用户编辑"
> >
<common-form <common-form
v-if="currentUser"
ref="formRef" ref="formRef"
class="form-edit-width-100" class="form-edit-width-100"
:model="currentUser" :model="currentUser"