<template> <div> <q-form ref="myForm" class="my-form row fit"> <div :class="['item', item.col || 'col-12']" v-for="(item, index) in state.config" :key="index" > <template v-if="item.solt"> <div class="form-item-solt fit"> <slot :name="item.solt"></slot> </div> </template> <template v-else-if="item.type === 'password'"> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <q-input :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" :type="item.isPwd ? 'password' : 'text'" v-model="formValue[item.fild]" v-bind="item.bind" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " > <template v-slot:append> <q-icon :name="item.isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer" @click="item.isPwd = !item.isPwd" /> </template> </q-input> </div> </template> <template v-else-if="item.type === 'date'"> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <date-time-pick :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " format24h v-model="formValue[item.fild]" :config="item.bind" :label="item.label" :label-required="item.required" /> </div> </template> <template v-else-if="item.type === 'time'"> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <time-pick :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" format24h v-model="formValue[item.fild]" :config="item.bind" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " /> </div> </template> <template v-else-if="item.type === 'dateMultiple'"> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <date-multiple :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" v-model="formValue[item.fild]" :config="item.bind" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " /> </div> </template> <template v-else-if="item.type === 'dateRange'"> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <date-range :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" v-model="formValue[item.fild]" :config="item.bind" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " /> </div> </template> <template v-else-if="item.type === 'color'"> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <color-pick :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" v-model="formValue[item.fild]" :config="item.bind" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " /> </div> </template> <template v-else> <div class="item-content"> <div class="label-title" :class="{ 'text-required': item.required }" > {{ item.label }} </div> <q-input :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" v-if="item.type !== 'select'" :type="item.type" v-model="formValue[item.fild]" v-bind="item.bind" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " /> <q-select class="my-select" :class="{ 'form-label-padding-bottom': isEmpty(item.bind?.rules), }" v-if="item.type === 'select'" v-bind="item.bind" v-model="formValue[item.fild]" :dense=" item.bind.dense === undefined ? props.dense : item.bind.dense " :disable=" item.bind.disable === undefined ? props.disable : item.bind.disable " :readonly=" item.bind.readonly === undefined ? props.readonly : item.bind.readonly " /> </div> </template> </div> </q-form> </div> </template> <script lang="ts" setup> import { onMounted, reactive, ref, watch } from 'vue'; import DateTimePick from './DateTimePick.vue'; import TimePick from './TimePick.vue'; import DateMultiple from './DateMultiple.vue'; import DateRange from './DateRange.vue'; import ColorPick from './ColorPick.vue'; import { cloneDeep, isObjEqual, isEmpty } from 'src/common/utils'; interface Props { config: any; modelValue: any; disable?: boolean; readonly?: boolean; dense?: boolean; } const props = withDefaults(defineProps<Props>(), { config: () => { return {}; }, modelValue: () => { return {}; }, disable: false, readonly: false, dense: false, }); defineExpose({ validate, reset, }); const emit = defineEmits<{ (e: 'update:modelValue', value: any): void; }>(); const myForm = ref<any>(null); const state = reactive({ config: cloneDeep(props.config), }); const formValue = ref<any>(cloneDeep(props.modelValue)); watch( () => props.config, (val) => { state.config = cloneDeep(val); }, { deep: true, } ); watch( () => props.modelValue, (val: any) => { if (!isObjEqual(val, formValue.value)) { formValue.value = val; } }, { deep: true, } ); watch( formValue.value, (val) => { emit('update:modelValue', val); }, { deep: true } ); onMounted(() => { // }); async function validate() { return await myForm.value.validate().then((success: any) => { if (success) { return true; } else { return false; } }); } function reset() { myForm.value.resetValidation(); } </script> <style lang="scss" scoped> .item { padding: $padding-sm; padding-bottom: 0; padding-top: 0; // border: 1px solid red; } .item-content { height: 100%; width: 100%; display: flex; flex-direction: column; flex-wrap: nowrap; } .label-title { height: 22px; font-size: 14px; line-height: 22px; color: $gray-text; margin-bottom: 2px; display: inline-block; } // :deep(.my-select // > :nth-child(1) // > :nth-child(1) // > :nth-child(1) // > :nth-child(1) // > span) { // white-space: nowrap; // overflow: hidden; // } </style>