123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- <script lang="tsx">
- import { defineComponent, watch, ref, computed, PropType, CSSProperties } from 'vue'
- import { Refresh, Search } from '@element-plus/icons-vue'
- import { LeFormItem, ObjectOpts, FormConfigOpts, FormItemSlots, SlotOption } from '@/components/FormConfig/formConfig.types'
- import InputNumber from './InputNumber'
- import InputNumberRange from './InputNumberRange'
- import CustomRender from './CustomRender'
- import { useI18n } from 'vue-i18n'
- import { getOptions, renderOption, get_formSlots } from '@/components/FormConfig/utils.ts'
- import { OptionItemProps } from '@/components/Select/select.types.ts'
- const emits = ['update:searchData']
- export type SearchFormItem = LeFormItem
- export const SearchFormProps = {
- forms: {
- // SearchForm: 与FormConfig不同的是 change的第一个参数的params, 去掉了原来的value 选项
- type: Array as PropType<SearchFormItem[]>,
- required: true
- },
- // 后台传递的初始值 以及 双向绑定 对象
- searchData: {
- required: true,
- type: Object as PropType<ObjectOpts>
- },
- // item 修改后 自动触发搜索
- triggerSearchAuto: {
- type: Boolean,
- default: false
- // default: true
- },
- // 自动触发 Automatic trigger
- // formModal的配置项对象
- formConfig: {
- type: Object as PropType<FormConfigOpts>,
- default: () => ({})
- },
- loading: {
- type: Boolean,
- default: false
- },
- reset: {
- type: Function as PropType<((initSearchData: Record<string, any>) => any)>
- }
- }
- export const SearchForm = defineComponent({
- name: 'LeSearchForm',
- emits,
- props: SearchFormProps,
- setup(props, ctx) {
- const { t } = useI18n()
- const formRef = ref(/*formRef*/)
- let initSearchData = undefined
- watch(
- () => props.searchData,
- (newValue, oldValue) => {
- if (!initSearchData && newValue) {
- initSearchData = { ...newValue }
- }
- },
- {
- immediate: true
- }
- )
- // 重置搜索
- const local_resetHandler = () => {
- // 若有reset 将不会触发默认重置的操作
- const emitReset = props.reset
- const _initSearchData = { ...(initSearchData || {}) }
- if (emitReset) {
- emitReset(_initSearchData)
- } else {
- // formRef.value!.resetFields()
- // 撤回为初始化状态
- ctx.emit('update:searchData', _initSearchData)
- }
- }
- // 搜索
- const searchHandler = () => {
- ctx.emit('update:searchData', { ...props.searchData })
- }
- // 强行修改初始化数据(用于 重置方法进行数据重置)
- const forceUpdateInitParams = (searchData = props.searchData) => {
- initSearchData = { ...searchData }
- }
- ctx.expose({
- formRef,
- forceUpdateInitParams
- })
- const vSlots = ctx.slots
- const realForms = computed(() => {
- return (props.forms || []).map((form) => {
- return {
- ...form,
- le_slots: get_formSlots(vSlots, form.slots)
- }
- })
- })
- // const gutter = 8
- // const colStyle = computed(() => {
- // const styles: CSSProperties = {}
- // if (gutter) {
- // styles.paddingLeft = styles.paddingRight = `${gutter / 2}px`
- // }
- // return styles
- // })
- // render渲染
- return () => {
- const { searchData, formConfig = {}, triggerSearchAuto } = props
- let warpClass = 'le-search-form-container' // labelStyle
- const getItemStyle = (itemStyle, defaultWidth) => {
- return itemStyle + ((/width\:/g).test(itemStyle) ? '' : `;width:${defaultWidth}`)
- }
- const itemRender = (form, _label) => {
- // 申明: onChange 会导致(类input) change后触发两次(组件定义一次,原生change一次) 对组件定义进行过滤,仅留原生触发,组件触发onChange 用change 替代
- const { prop, itemType, itemWidth, options, change, onChange, itemStyle = '', placeholder, t_placeholder, le_slots, ...formOthers } = form
- const _options = options || []
- const _itemStyle = itemStyle + (itemWidth ? `;width: ${itemWidth}` : '')
- let disabled = form.disabled
- const _placeholder: string = (t_placeholder ? t(t_placeholder) : placeholder) || _label
- // 优化后的 change事件
- let formatterChange = async () => {
- if (typeof change === 'function') {
- return change(searchData[prop], _options, searchData)
- }
- }
- let bindInputEvents = {}
- let changeAndSearch = formatterChange
- if (triggerSearchAuto) {
- changeAndSearch = () => formatterChange().then(searchHandler)
- bindInputEvents = {
- onBlur: searchHandler,
- // 回车触发搜索
- onKeydown: (e: KeyboardEvent) => {
- // console.error(e, 'onKeyDown', e.key)
- if (e.key === 'Enter') {
- searchHandler()
- }
- }
- }
- }
- switch (itemType) {
- case 'leSelect':
- // leSelect: 基于 element-plus el-select-v2扩展
- const slots_leSelect = {
- default: le_slots.option as SlotOption<OptionItemProps>
- }
- return (
- <LeSelect
- {...formOthers}
- options={_options}
- v-model={searchData[prop]}
- // 通过teleport插入到body (popper-append-to-body popperAppendToBody已弃用)
- teleported={formOthers.teleported ?? true}
- // '@update:selected_label' todo
- onChange={changeAndSearch}
- disabled={disabled}
- placeholder={_placeholder}
- style={getItemStyle(_itemStyle, '200px')}
- v-slots={slots_leSelect}
- />
- )
- // 自定义render
- case 'render':
- return <CustomRender
- form={form}
- params={searchData}
- />
- // 下拉框
- case 'select':
- return (
- <el-select
- {...formOthers}
- v-model={searchData[prop]}
- onChange={changeAndSearch}
- style={getItemStyle(_itemStyle, '200px')}
- disabled={disabled}
- clearable={form.clearable ?? true}
- placeholder={_placeholder}
- >
- {getOptions(_options, form).map((option, i) => {
- const { label, value, disabled } = option
- return (
- <el-option key={`${value}_${i}`} value={value} label={label} disabled={disabled} title={label}>
- {/*renderOption(ctx.slots, form.slots?.option, option)*/}
- {renderOption(le_slots.option, option)}
- </el-option>
- )
- })}
- </el-select>
- )
- // 单选框
- case 'radio':
- return (
- <el-radio-group
- {...formOthers}
- v-model={searchData[prop]}
- disabled={disabled}
- onChange={changeAndSearch}
- style={getItemStyle(_itemStyle, 'auto')}
- >
- {getOptions(_options, form).map((option, i) => {
- const { label, value, disabled } = option
- return (
- <el-radio-button key={`${value}_${i}`} label={value} disabled={disabled} title={label}>
- {/*renderOption(ctx.slots, form.slots?.option, option)*/}
- {renderOption(le_slots.option, option)}
- </el-radio-button>
- )
- })}
- </el-radio-group>
- )
- // 级联
- case 'cascader':
- const slots_cascader = {
- default: le_slots.option as SlotOption<{ data: any; node: any }>
- }
- return (
- <el-cascader
- {...formOthers}
- v-model={searchData[prop]}
- onChange={changeAndSearch}
- style={getItemStyle(_itemStyle, '200px')}
- disabled={disabled}
- clearable={form.clearable ?? true}
- filterable={form.filterable ?? true}
- options={_options}
- placeholder={_placeholder}
- v-slots={slots_cascader}
- />
- )
- // 数字
- case 'inputNumber':
- return (
- <InputNumber
- class="rate100"
- {...bindInputEvents}
- {...formOthers}
- slots={le_slots}
- v-model={searchData[prop]}
- onChange={formatterChange}
- style={getItemStyle(_itemStyle, '130px')}
- disabled={disabled}
- placeholder={_placeholder}
- precision={form.precision || 0}
- />
- )
- // 数字区间
- case 'inputNumberRange':
- const numberChange = (e, propKey) => {
- change && change(searchData[propKey], _options, searchData, propKey)
- }
- return (
- <InputNumberRange
- {...bindInputEvents}
- prop={prop}
- {...formOthers}
- v-model={searchData[prop]}
- isValueArray
- // modelValue={searchData}
- onChange={numberChange}
- itemStyle={getItemStyle(_itemStyle, '230px')}
- disabled={disabled}
- placeholder={_placeholder}
- precision={form.precision || 0}
- v-slots={le_slots}
- />
- )
- // 日期选择 (单日期 || 日期区间 ...) year/month/date/datetime/ week/datetimerange/daterange
- case 'datePicker':
- let dateWidthDefault = '160px'
- let dateOpts: any = {}
- dateOpts.valueFormat = form.valueFormat || 'MM/DD/YYYY'
- dateOpts.format = form.format || dateOpts.valueFormat
- // 区间类型
- if (/range$/.test(form.type || '')) {
- dateWidthDefault = '220px'
- const startPlaceholder = form.t_startPlaceholder ? t(form.t_startPlaceholder) : form.startPlaceholder
- const endPlaceholder = form.t_endPlaceholder ? t(form.t_endPlaceholder) : form.endPlaceholder
- dateOpts = Object.assign(dateOpts, {
- startPlaceholder: startPlaceholder ?? t('le.filter.startDate'),
- endPlaceholder: endPlaceholder ?? t('le.filter.endDate'),
- unlinkPanels: form.unlinkPanels ?? true // 解除联动
- })
- } else {
- dateOpts.placeholder = _placeholder || t('le.filter.selectDate')
- }
- return (
- <el-date-picker
- {...formOthers}
- {...dateOpts}
- v-model={searchData[prop]}
- onChange={changeAndSearch}
- style={getItemStyle(_itemStyle, dateWidthDefault)}
- disabled={disabled}
- />
- )
- // switch
- case 'switch':
- return <el-switch
- {...formOthers}
- v-model={searchData[prop]}
- onChange={changeAndSearch}
- style={_itemStyle}
- disabled={disabled}
- />
- case 'input':
- default:
- return (
- <el-input
- {...bindInputEvents}
- {...formOthers}
- v-model={searchData[prop]}
- onChange={formatterChange}
- disabled={disabled}
- placeholder={_placeholder}
- style={getItemStyle(_itemStyle, '160px')}
- />
- )
- }
- }
- return (
- <div class={warpClass}>
- <div class="le-search-form-flex">
- <el-form ref={formRef} inline={true} size="default" class="le-search-form-flex-wrap" model={searchData} {...formConfig}>
- <el-row class="form_wrap" gutter={8}>
- {realForms.value.map((form: SearchFormItem & { le_slots: ObjectOpts }, idx) => {
- // 通过 form.visible 控制 是否展示
- const _label = form.t_label ? t(form.t_label) : form.label
- const slots = {
- label: form.le_slots.label
- }
- return (
- <el-col v-show={form.visible !== false} key={idx} span={form.span ?? 1024}>
- <el-form-item
- // class={`${form.showLabel === false ? 'hideLabel' : ''} el-col el-col-${form.span}`}
- // v-show={form.visible !== false}
- // style={colStyle.value}
- // key={idx}
- class={form.showLabel === false ? 'hideLabel' : ''}
- {...form}
- label={_label}
- v-slots={slots}
- >
- {itemRender(form, _label)}
- </el-form-item>
- </el-col>
- )
- })}
- </el-row>
- </el-form>
- <div class="action-wrap">
- <el-button
- size="default"
- plain
- icon={Refresh}
- disabled={props.loading}
- onClick={local_resetHandler}>
- { t('le.btn.reset') }
- </el-button>
- <el-button
- size="default"
- type="primary"
- loading={props.loading}
- icon={Search}
- onClick={searchHandler}>
- { t('le.btn.search') }
- </el-button>
- </div>
- </div>
- </div>
- )
- }
- }
- })
- export default SearchForm
- </script>
|