Browse Source

feat: 更新数据表单组件,添加必填字段和标签宽度支持,优化表单验证逻辑

EvilDragon 3 months ago
parent
commit
595596708c

+ 2 - 1
packages/app/src/components/data-form.ts

@@ -1,5 +1,4 @@
 export interface DataFormProps {
-  labelWidth?: number
   defaultValue?: Date
   placeholder?: string
   columns?: { label: string; value: string }[]
@@ -11,8 +10,10 @@ export interface DataFormSchema {
   [key: symbol | string]: {
     type: 'TextField' | 'Select' | 'Radio' | 'Submit' | 'TimePick' | 'Textarea' | 'Checkbox'
     label?: string
+    labelWidth?: number
     hiddenLabel?: boolean
     existing?: boolean
+    required?: boolean
     props?: DataFormProps
   }
 }

+ 21 - 9
packages/app/src/components/data-form.vue

@@ -6,6 +6,7 @@ import { ConfigProviderThemeVars } from 'wot-design-uni'
 import { DataFormProps, DataFormSchema } from './data-form'
 import { addUnit } from 'wot-design-uni/components/common/util'
 import { omit } from 'radash'
+import WdForm from 'wot-design-uni/components/wd-form/wd-form.vue'
 
 const modelValue = defineModel({
   type: Object,
@@ -14,12 +15,13 @@ const modelValue = defineModel({
 withDefaults(
   defineProps<{
     schema: DataFormSchema
+    rules?: { [key: string]: { required: boolean; message: string }[] }
     direction?: 'horizontal' | 'vertical'
   }>(),
   { direction: 'vertical', labelShow: true },
 )
 const emits = defineEmits(['submit'])
-const form = ref()
+const form = ref<InstanceType<typeof WdForm>>()
 const types = {
   TextField: WdInput,
   Submit: WdButton,
@@ -79,18 +81,21 @@ const themeVars: ConfigProviderThemeVars = {
 const submit = () => {
   emits('submit', modelValue)
 }
-const validate = (): Promise<{ valid: boolean; errors: any[] }> => form.value?.validate()
+const validate = async (): Promise<{ valid: boolean; errors: any[] }> => {
+  return await form.value.validate()
+}
 defineExpose({
   validate,
 })
 </script>
 <template>
   <wd-config-provider :theme-vars="themeVars">
-    <wd-form ref="form" :model="modelValue">
+    <wd-form ref="form" error-type="toast" :rules="rules" :model="modelValue">
+      <!-- <wd-cell-group border> -->
       <template
-        v-for="([prop, { type, label, hiddenLabel, existing, props }], index) in Object.entries(
-          schema,
-        )"
+        v-for="(
+          [prop, { type, label, labelWidth, hiddenLabel, existing, required, props }], index
+        ) in Object.entries(schema)"
         :key="index"
       >
         <div
@@ -99,7 +104,7 @@ defineExpose({
           :class="[direction === 'horizontal' ? 'items-center' : '']"
           :style="
             direction === 'horizontal'
-              ? { 'grid-template-columns': `${addUnit(props?.labelWidth)} auto` }
+              ? { 'grid-template-columns': `${addUnit(labelWidth || 100)} auto` }
               : {}
           "
         >
@@ -109,6 +114,12 @@ defineExpose({
             :class="[direction === 'horizontal' ? 'text-black/60' : 'mb-1 text-black/40']"
             :for="prop"
           >
+            <span
+              v-if="required"
+              class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal"
+            >
+              *
+            </span>
             {{ label || prop }}
           </label>
           <wd-input
@@ -117,7 +128,7 @@ defineExpose({
               ...(direction === 'vertical'
                 ? verticalDefaultProps[type]
                 : horizontalDefaultProps[type]),
-              ...omit(props, ['labelWidth']),
+              ...omit(props, []),
             }"
             v-model="modelValue[prop]"
           ></wd-input>
@@ -192,7 +203,7 @@ defineExpose({
             v-if="type === 'Submit'"
             v-bind="{
               ...(direction === 'vertical' ? verticalDefaultProps[type] : {}),
-              ...props,
+              ...omit(props, []),
               formType: 'submit',
             }"
             @click="submit"
@@ -201,6 +212,7 @@ defineExpose({
           </wd-button>
         </div>
       </template>
+      <!-- </wd-cell-group> -->
     </wd-form>
   </wd-config-provider>
 </template>

+ 1 - 1
packages/app/src/pages/home/components/home-banner.vue

@@ -38,7 +38,7 @@ defineExpose({
 </script>
 <template>
   <div class="w-full h-full relative">
-    <div class="w-full h-full box-border pb-4">
+    <div class="w-full h-full box-border pb-6">
       <video
         class="w-full h-full"
         :id="`video-${id}`"

+ 69 - 7
packages/app/src/pages/publish/moment/index.vue

@@ -19,6 +19,8 @@ import { useRouter } from '../../../core/utils/router'
 import BottomAppBar from '@/components/bottom-app-bar.vue'
 import { toast } from '../../../core/utils/common'
 import { messages } from '../../../core/libs/messages'
+import { DataFormSchema } from '../../../components/data-form'
+import { ComponentExposed } from 'vue-component-type-helpers'
 
 const router = useRouter()
 const userStore = useUserStore()
@@ -28,12 +30,55 @@ const circleType = ref<CircleType>()
 const content = ref('')
 const fileList = ref([])
 const action = ref(`${import.meta.env.VITE_SERVER_BASEURL}/app-api/infra/file/upload`)
-const schema = ref({
-  spaceType: { label: '空间类型', type: 'Select', props: { columns: [] } },
-  designStyle: { label: '设计风格', type: 'Select', props: { columns: [] } },
-  spaceAddr: { label: '空间位置', type: 'TextField' },
-  customerDemand: { label: '客户需求', type: 'TextField' },
+const dataFormRef = ref<ComponentExposed<typeof DataForm>>()
+const schema = ref<DataFormSchema>({
+  spaceType: {
+    label: '空间类型:',
+    type: 'Select',
+    required: true,
+    labelWidth: 80,
+    props: {
+      columns: [],
+    },
+  },
+  designStyle: {
+    label: '设计风格:',
+    type: 'Select',
+    required: true,
+    labelWidth: 80,
+    props: { columns: [] },
+  },
+  // spaceAddr: { label: '空间位置', type: 'TextField' },
+  spaceExtent: {
+    label: '空间面积:',
+    type: 'TextField',
+    required: true,
+    labelWidth: 80,
+    props: { placeholder: '请输入空间面积' },
+  },
+  caseName: {
+    label: '案例名称:',
+    type: 'TextField',
+    required: true,
+    labelWidth: 80,
+    props: { placeholder: '请输入案例名称' },
+  },
+  circleDesc: {
+    label: '案例描述:',
+    type: 'Textarea',
+    required: true,
+    labelWidth: 80,
+    props: { placeholder: '请输入案例描述' },
+  },
+  // customerDemand: { label: '客户需求', type: 'TextField' },
 })
+const rules = {
+  spaceType: [{ required: true, message: '请选择空间类型' }],
+  designStyle: [{ required: true, message: '请选择设计风格' }],
+  spaceExtent: [{ required: true, message: '请填写空间面积' }],
+  caseName: [{ required: true, message: '请填写案例名称' }],
+  circleDesc: [{ required: true, message: '请填写案例描述' }],
+}
 const formData = ref({})
 const formInited = ref(false)
 const tagName = ref('')
@@ -43,11 +88,16 @@ const handleChange = ({ fileList: files }) => {
   console.log(fileList.value)
 }
 const handleSubmit = async () => {
-  publishing.value = true
+  const { valid, errors } = await dataFormRef.value.validate()
+  console.log(valid, errors)
+  if (!valid) {
+    return false
+  }
   if (!fileList.value.length) {
     toast(messages.moment.imageNotExist)
     return false
   }
+  publishing.value = true
   const { code, msg } = await createCircle({
     stylistId: userInfo.value.userId,
     stylistName: userInfo.value.nickname,
@@ -99,7 +149,13 @@ onLoad(async (query: { circleType: '1' | '2' }) => {
 <template>
   <div class="flex-grow bg-white p-3.5 flex flex-col">
     <template v-if="circleType === CircleType.case && formInited">
-      <DataForm v-model="formData" :schema="schema" direction="horizontal"></DataForm>
+      <DataForm
+        ref="dataFormRef"
+        v-model="formData"
+        :schema="schema"
+        :rules="rules"
+        direction="horizontal"
+      ></DataForm>
     </template>
     <template v-if="circleType === CircleType.moment">
       <wd-textarea v-model="content" placeholder="分享你此刻的想法" />
@@ -114,6 +170,12 @@ onLoad(async (query: { circleType: '1' | '2' }) => {
         <div class="w-7 h-7 relative flex-col justify-start items-start flex"></div>
       </div>
     </div> -->
+    <div v-if="circleType === '2'" class="flex items-center">
+      <span class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal">
+        *
+      </span>
+      <SectionHeading title="实景图:" size="base" custom-class="my-5"></SectionHeading>
+    </div>
     <wd-upload
       :file-list="fileList"
       image-mode="aspectFill"