From 928c61ccddebc8d2c697b86ee9bee0c207330a8c Mon Sep 17 00:00:00 2001 From: schangxiang@126.com <schangxiang@126.com> Date: 周二, 06 5月 2025 07:18:22 +0800 Subject: [PATCH] 222 --- HIAWms/web/src/components/TableFilter/TableFilter.tsx | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 337 insertions(+), 0 deletions(-) diff --git a/HIAWms/web/src/components/TableFilter/TableFilter.tsx b/HIAWms/web/src/components/TableFilter/TableFilter.tsx new file mode 100644 index 0000000..588edec --- /dev/null +++ b/HIAWms/web/src/components/TableFilter/TableFilter.tsx @@ -0,0 +1,337 @@ +import { + defineComponent, + ref, + computed, + onMounted, + watch, + onUnmounted, + Component, +} from 'vue' +import styles from './TableFilter.module.scss' +import { CaretBottom } from '@element-plus/icons-vue' +import IconButton from '@/components/IconButton/IconButton' + +import ElInput from 'element-plus/es/components/input/index' +import ElSelect from 'element-plus/es/components/select/index' +import Select from '@/components/Select/Select' +import Option from '@/components/Select/Option' +import Icon from '../Icon/Icon' +import { FormPropsType, FormItemPropType, PropsType } from '../DyForm/DyForm.d' +import isNil from 'lodash/isNil' +import { useVModel } from '@vueuse/core' + +const formItemElementMap: Record<string, any> = { + input: ElInput, + select: Select, +} + +const Type: Record<string, string> = { + select: 'select', +} + +interface FieldMapType { + [key: string]: Array<{ + label: string + value: string | number | boolean + }> +} +export default defineComponent({ + name: '琛ㄦ牸绛涢��', + props: { + title: { + type: String, + default: '', + }, + columns: { + type: Array, + default: () => [], + }, + tableRef: { + type: Object, + default: null, + }, + modelValue: { + type: Object, + default: null, + }, + text: { + type: String, + default: '', + }, + fieldMap: { + type: Object, + default: () => ({}), + }, + options: { + type: Array, + default: () => [], + }, + defaultOptions: { + type: Array, + default: () => [], + }, + }, + emits: ['update:modelValue', 'data', 'change'], + setup(props, { attrs, slots, emit }) { + const visible = ref(false) + const columnsFilter = ref<FormItemPropType>([]) + const isDisabled = ref<boolean>(false) + const defaultData = ref({}) + const data = ref({}) + const optionMap = ref<Record<string, any>>({}) + const form = computed({ + get() { + return props.modelValue + }, + set(v) { + emit('update:modelValue', v) + }, + }) + + const formData = props.modelValue ? form : data + let flag = false + /** + * 娣诲姞绛涢�夋潯浠� + */ + const onAddFilter = (evt?: Event) => { + if (!flag) { + evt?.stopPropagation() + + let length = columnsFilter.value.length + for (let index = 0; index < props.columns.length; index++) { + const element: any = props.columns[index] + if (element.el && !columnsFilter.value.includes(element)) { + columnsFilter.value.push(element) + break + } + } + if (length === columnsFilter.value.length - 1) { + isDisabled.value = true + } + } + } + + /** + * 閲嶇疆鎵�鏈夌姸鎬侊紝鍒濆鍖栬〃鏍� + * */ + const onReset = () => { + // columnsFilter.value = [] + // Object.entries(defaultData.value).forEach(([key, value]) => { + // formData.value[key] = value + // }) + // onAddFilter() + // onSearchTable() + } + /** + * 鎵撳紑寮圭獥 + */ + const onListener = () => { + visible.value = false + } + /** + * 瀛愮粍浠剁偣鍑� + * @param evt + */ + const onChildClick = async (evt: Event) => { + evt.stopPropagation() + await getOptions() + visible.value = true + } + /** + * 琛ㄦ牸鎼滅储 + */ + const onSearchTable = () => { + const tableRef = + props.tableRef && props.tableRef.value + ? props.tableRef.value + : props.tableRef + const data: Record<string, any> = { ...formData.value } + + Object.entries(data).forEach(([key, value]) => { + if (value === null || value === undefined) { + data[key] = '' + } + }) + if (tableRef) { + tableRef?.getList?.(data) + } else { + emit('data', data) + emit('change', data) + } + } + const hasFormData = computed(() => { + if (Object.keys(formData.value).length === 0) return false + return Object.entries(formData.value).every(([key, value]) => { + return value !== null && value !== undefined && value !== '' + }) + }) + /** + * 鍒濆鍖杁isabled鐘舵�� + */ + const initDisabled = () => { + const els = props.columns.filter((column: any) => column.el) + if (els.length <= 1) { + isDisabled.value = true + } else { + isDisabled.value = false + } + } + + watch( + () => props.columns, + (val) => { + if (props.columns.length) { + onAddFilter() + initDisabled() + flag = true + } + }, + { + immediate: true, + deep: true, + } + ) + + onMounted(() => { + if (props.columns.length) { + onAddFilter() + initDisabled() + flag = true + } + defaultData.value = { ...props.modelValue } + document.addEventListener('click', onListener) + }) + + onUnmounted(() => { + document.removeEventListener('click', onListener) + }) + + // const getOptions = async ( + // item: Record<string, any>, + // $props: Record<string, any> + // ) => { + // if (typeof item.options === 'function') { + // return await item.options() + // } else { + // let options: any = + // item.options || props.fieldMap?.[$props.item.prop] || [] + // if (props.defaultOptions.length) { + // options = options.concat(props.defaultOptions) + // } + // return options + // } + // } + const getOptions = async () => { + const columns = props.columns + for (let index = 0; index < columns.length; index++) { + const column: any = columns[index] + if (column.prop) { + let options: any[] = [] + if (typeof column.options === 'function') { + options = await column.options() + } else { + options = column.options || props.fieldMap?.[column.prop] || [] + } + if (props.defaultOptions.length) { + options = props.defaultOptions.concat(options) + } + optionMap.value[column.prop] = options + } + } + } + + const onClickBtn = (evt: Event) => { + evt.stopPropagation() + flag = false + onAddFilter() + } + + /** + * option + * @param $props + * @returns + */ + const Column = ($props: any) => { + const column: Record<string, any> = $props.item + const options = optionMap.value[column.prop] || [] + if (Type[column.el]) { + return options.map( + (el: { + label: string + description: string + value: string | number + }) => ( + <Option + label={el.description || el.label} + value={el.value} + ></Option> + ) + ) + } + return null + } + + return () => { + const icon = isDisabled.value ? 'icon_add2' : 'add-p' + return ( + <div onClick={onChildClick}> + <el-popover + visible={visible.value} + placement="bottom-start" + width={212} + show-arrow={false} + popper-class={styles.popover} + persistent={false} + popper-style={{ + marginTop: '-7px', + padding: '8px', + }} + trigger="click" + vSlots={{ + reference: () => ( + <span class={{ [styles.textColor]: hasFormData.value }}> + {slots.default?.()} + </span> + ), + }} + onShow={onReset} + > + <div + class={styles.box} + onClick={(evt: Event) => { + evt.stopPropagation() + }} + > + {columnsFilter.value.map((column: any) => { + const Widget = formItemElementMap[column.el] || null + return Widget ? ( + <div class={styles.filter}> + <span>{column.title}: </span> + <Widget + v-model={formData.value[column.prop]} + style="width: 119px" + size="small" + placeholder={`${column.placeholder}`} + onChange={onSearchTable} + clearable + teleported={false} + > + <Column item={column} /> + </Widget> + </div> + ) : null + })} + </div> + <IconButton + onClick={onClickBtn} + icon={icon} + disabled={isDisabled.value} + > + {props.text} + </IconButton> + </el-popover> + </div> + ) + } + }, +}) -- Gitblit v1.9.3