123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- <script setup lang="ts">
- import WdButton from 'wot-design-uni/components/wd-button/wd-button.vue'
- import WdInput from 'wot-design-uni/components/wd-input/wd-input.vue'
- import WdPicker from 'wot-design-uni/components/wd-picker/wd-picker.vue'
- 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 types = {
- TextField: WdInput,
- Submit: WdButton,
- Select: WdPicker,
- // Radio: WdRadioGroup,
- }
- const defaultProps = {
- TextField: {
- noBorder: true,
- style: {},
- customClass: 'rounded border border-[#e1e1e1] border-solid p-1',
- placeholder: ' ',
- },
- Submit: {
- customClass: 'w-full! rounded-lg! my-4!',
- block: true,
- },
- }
- const verticalDefaultProps = {
- TextField: {
- noBorder: true,
- style: {},
- // customClass: 'rounded border border-[#e1e1e1] border-solid p-1',
- customClass: 'text-red! bg-[#f5f7f9]! py-2 px-4 rounded-lg',
- placeholder: ' ',
- },
- Textarea: {
- customClass: 'bg-[#f5f7f9]! rounded-lg',
- },
- Submit: {
- customClass: 'w-full! rounded-lg! my-4!',
- block: true,
- },
- }
- const horizontalDefaultProps = {
- TextField: {
- customClass: 'text-red! bg-[#f5f7f9]! py-2 px-4 rounded-lg',
- placeholderClass: 'text-black/30',
- noBorder: true,
- },
- Select: {
- customClass: 'text-black/30! bg-[#f5f7f9]! py-.75 px-4 rounded-lg!',
- noBorder: true,
- cell: false,
- },
- Radio: {
- customClass: 'my--4!',
- },
- Checkbox: {
- customClass: 'my--4!',
- },
- TimePick: {
- customClass: 'm-0! bg-[#f5f7f9]! py-.75 px-4 rounded-lg!',
- },
- Textarea: {
- customClass: 'bg-[#f5f7f9]! rounded-lg',
- },
- }
- const themeVars: ConfigProviderThemeVars = {
- cellPadding: '0',
- cellWrapperPadding: '10rpx',
- radioButtonRadius: '8rpx',
- radioButtonBg: 'transparent',
- checkboxButtonRadius: '8rpx',
- checkboxButtonBg: 'transparent',
- textareaBg: 'transparent',
- }
- const modelValue = defineModel({
- type: Object,
- default: () => ({}),
- })
- const props = 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<InstanceType<typeof WdForm>>()
- const action = ref(`${import.meta.env.VITE_SERVER_BASEURL}/app-api/infra/file/upload`)
- const submitDisabled = computed(() => {
- // console.log(Object.values(modelValue.value).some((it) => !it))
- return Object.values(
- omit(
- modelValue.value,
- Object.entries(props.schema)
- .filter(([_key, { required }]) => !required)
- .map(([key]) => key),
- ),
- ).some((it) => !it)
- })
- const submit = () => {
- emits('submit', modelValue)
- }
- const validate = async (): Promise<{ valid: boolean; errors: any[] }> => {
- return await form.value!.validate()
- }
- onShow(() => {
- console.log('App Show', modelValue)
- })
- defineExpose({
- validate,
- submitDisabled,
- })
- </script>
- <template>
- <wd-config-provider :theme-vars="themeVars">
- <wd-form ref="form" error-type="toast" :rules="rules" :model="modelValue">
- <!-- <wd-cell-group border> -->
- <template
- v-for="(
- [prop, { type, label, labelWidth, hiddenLabel, existing, required, props, maxlength }],
- index
- ) in Object.entries(schema)"
- :key="index"
- >
- <div
- v-if="existing ?? true"
- class="grid mb-4"
- :class="[direction === 'horizontal' ? 'items-start' : '']"
- :style="
- direction === 'horizontal'
- ? { 'grid-template-columns': `${addUnit(labelWidth || 100)} auto` }
- : {}
- "
- >
- <label
- v-if="type !== 'Submit' && !hiddenLabel"
- class="text-sm font-normal leading-relaxed"
- :class="[
- direction === 'horizontal'
- ? 'text-black/60 h-10 flex items-center'
- : 'mb-1 text-black/40',
- ]"
- :for="prop"
- >
- <span
- class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal"
- :class="required ? 'visible' : 'invisible'"
- >
- *
- </span>
- {{ label || prop }}
- </label>
- <wd-input
- v-if="type === 'TextField'"
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- ...omit(props, []),
- }"
- v-model="modelValue[prop]"
- ></wd-input>
- <wd-datetime-picker
- v-model="modelValue[prop]"
- v-if="type === 'TimePick'"
- :minDate="315514870000"
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- cell: false,
- ...props,
- }"
- />
- <wd-textarea
- v-if="type === 'Textarea'"
- v-model="modelValue[prop]"
- class="h-[20px]"
- :maxlength="maxlength"
- show-word-limit
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- cell: false,
- ...props,
- }"
- />
- <wd-picker
- v-if="type === 'Select'"
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- cell: false,
- ...props,
- }"
- v-model="modelValue[prop]"
- ></wd-picker>
- <wd-radio-group
- v-if="type === 'Radio'"
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- ...props,
- cell: true,
- shape: 'button',
- }"
- v-model="modelValue[prop]"
- >
- <template v-for="{ label, value } of props.columns" :key="value">
- <wd-radio :value="value">{{ label }}</wd-radio>
- </template>
- </wd-radio-group>
- <wd-checkbox-group
- v-if="type === 'Checkbox'"
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- ...props,
- cell: true,
- shape: 'button',
- }"
- v-model="modelValue[prop]"
- >
- <template v-for="{ label, value } of props.columns" :key="value">
- <wd-checkbox custom-class="mr-4!" :modelValue="value">{{ label }}</wd-checkbox>
- </template>
- </wd-checkbox-group>
- <wd-upload
- v-if="type === 'ImageUploader'"
- v-bind="{
- ...(direction === 'vertical'
- ? verticalDefaultProps[type]
- : horizontalDefaultProps[type]),
- ...props,
- action,
- fileList:
- (modelValue[prop] ?? '') === ''
- ? []
- : modelValue[prop]?.split(',').map((it) => ({ url: it })),
- }"
- @change="
- ({ fileList }) => {
- const newUrls = fileList
- .map(({ response }) => {
- try {
- return JSON.parse(response).data
- } catch (e) {
- return null
- }
- })
- .filter(Boolean) // 过滤掉无效的 null 值
- const existingUrls = modelValue[prop]?.split(',') ?? []
- modelValue[prop] = [...existingUrls, ...newUrls].join(',')
- }
- "
- :before-remove="
- ({ file, fileList, resolve }) => {
- resolve(true)
- modelValue[prop] = fileList.map(({ url }) => url).join(',')
- }
- "
- ></wd-upload>
- <wd-button
- v-if="type === 'Submit'"
- v-bind="{
- ...(direction === 'vertical' ? verticalDefaultProps[type] : {}),
- ...omit(props, []),
- formType: 'submit',
- }"
- @click="submit"
- >
- <span v-if="type === 'Submit'">提交</span>
- </wd-button>
- </div>
- </template>
- <!-- </wd-cell-group> -->
- </wd-form>
- </wd-config-provider>
- </template>
- <style lang="less" scoped></style>
|