data-form.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <script setup lang="ts">
  2. import WdButton from 'wot-design-uni/components/wd-button/wd-button.vue'
  3. import WdInput from 'wot-design-uni/components/wd-input/wd-input.vue'
  4. import WdPicker from 'wot-design-uni/components/wd-picker/wd-picker.vue'
  5. import { ConfigProviderThemeVars } from 'wot-design-uni'
  6. import { DataFormProps, DataFormSchema } from './data-form'
  7. import { addUnit } from 'wot-design-uni/components/common/util'
  8. import { omit } from 'radash'
  9. import WdForm from 'wot-design-uni/components/wd-form/wd-form.vue'
  10. const types = {
  11. TextField: WdInput,
  12. Submit: WdButton,
  13. Select: WdPicker,
  14. // Radio: WdRadioGroup,
  15. }
  16. const defaultProps = {
  17. TextField: {
  18. noBorder: true,
  19. style: {},
  20. customClass: 'rounded border border-[#e1e1e1] border-solid p-1',
  21. placeholder: ' ',
  22. },
  23. Submit: {
  24. customClass: 'w-full! rounded-lg! my-4!',
  25. block: true,
  26. },
  27. }
  28. const verticalDefaultProps = {
  29. TextField: {
  30. noBorder: true,
  31. style: {},
  32. // customClass: 'rounded border border-[#e1e1e1] border-solid p-1',
  33. customClass: 'text-red! bg-[#f5f7f9]! py-2 px-4 rounded-lg',
  34. placeholder: ' ',
  35. },
  36. Textarea: {
  37. customClass: 'bg-[#f5f7f9]! rounded-lg',
  38. },
  39. Submit: {
  40. customClass: 'w-full! rounded-lg! my-4!',
  41. block: true,
  42. },
  43. }
  44. const horizontalDefaultProps = {
  45. TextField: {
  46. customClass: 'text-red! bg-[#f5f7f9]! py-2 px-4 rounded-lg',
  47. placeholderClass: 'text-black/30',
  48. noBorder: true,
  49. },
  50. Select: {
  51. customClass: 'text-black/30! bg-[#f5f7f9]! py-.75 px-4 rounded-lg!',
  52. noBorder: true,
  53. cell: false,
  54. },
  55. Radio: {
  56. customClass: 'my--4!',
  57. },
  58. Checkbox: {
  59. customClass: 'my--4!',
  60. },
  61. TimePick: {
  62. customClass: 'm-0! bg-[#f5f7f9]! py-.75 px-4 rounded-lg!',
  63. },
  64. Textarea: {
  65. customClass: 'bg-[#f5f7f9]! rounded-lg',
  66. },
  67. }
  68. const themeVars: ConfigProviderThemeVars = {
  69. cellPadding: '0',
  70. cellWrapperPadding: '10rpx',
  71. radioButtonRadius: '8rpx',
  72. radioButtonBg: 'transparent',
  73. checkboxButtonRadius: '8rpx',
  74. checkboxButtonBg: 'transparent',
  75. textareaBg: 'transparent',
  76. }
  77. const modelValue = defineModel({
  78. type: Object,
  79. default: () => ({}),
  80. })
  81. const props = withDefaults(
  82. defineProps<{
  83. schema: DataFormSchema
  84. rules?: { [key: string]: { required: boolean; message: string }[] }
  85. direction?: 'horizontal' | 'vertical'
  86. }>(),
  87. { direction: 'vertical', labelShow: true },
  88. )
  89. const emits = defineEmits(['submit'])
  90. const form = ref<InstanceType<typeof WdForm>>()
  91. const action = ref(`${import.meta.env.VITE_SERVER_BASEURL}/app-api/infra/file/upload`)
  92. const submitDisabled = computed(() => {
  93. // console.log(Object.values(modelValue.value).some((it) => !it))
  94. return Object.values(
  95. omit(
  96. modelValue.value,
  97. Object.entries(props.schema)
  98. .filter(([_key, { required }]) => !required)
  99. .map(([key]) => key),
  100. ),
  101. ).some((it) => !it)
  102. })
  103. const submit = () => {
  104. emits('submit', modelValue)
  105. }
  106. const validate = async (): Promise<{ valid: boolean; errors: any[] }> => {
  107. return await form.value!.validate()
  108. }
  109. defineExpose({
  110. validate,
  111. submitDisabled,
  112. })
  113. </script>
  114. <template>
  115. <wd-config-provider :theme-vars="themeVars">
  116. <wd-form ref="form" error-type="toast" :rules="rules" :model="modelValue">
  117. <!-- <wd-cell-group border> -->
  118. <template
  119. v-for="(
  120. [prop, { type, label, labelWidth, hiddenLabel, existing, required, props }], index
  121. ) in Object.entries(schema)"
  122. :key="index"
  123. >
  124. <div
  125. v-if="existing ?? true"
  126. class="grid mb-4"
  127. :class="[direction === 'horizontal' ? 'items-start' : '']"
  128. :style="
  129. direction === 'horizontal'
  130. ? { 'grid-template-columns': `${addUnit(labelWidth || 100)} auto` }
  131. : {}
  132. "
  133. >
  134. <label
  135. v-if="type !== 'Submit' && !hiddenLabel"
  136. class="text-sm font-normal leading-relaxed"
  137. :class="[
  138. direction === 'horizontal'
  139. ? 'text-black/60 h-10 flex items-center'
  140. : 'mb-1 text-black/40',
  141. ]"
  142. :for="prop"
  143. >
  144. <span
  145. class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal"
  146. :class="required ? 'visible' : 'invisible'"
  147. >
  148. *
  149. </span>
  150. {{ label || prop }}
  151. </label>
  152. <wd-input
  153. v-if="type === 'TextField'"
  154. v-bind="{
  155. ...(direction === 'vertical'
  156. ? verticalDefaultProps[type]
  157. : horizontalDefaultProps[type]),
  158. ...omit(props, []),
  159. }"
  160. v-model="modelValue[prop]"
  161. ></wd-input>
  162. <wd-datetime-picker
  163. v-model="modelValue[prop]"
  164. v-if="type === 'TimePick'"
  165. v-bind="{
  166. ...(direction === 'vertical'
  167. ? verticalDefaultProps[type]
  168. : horizontalDefaultProps[type]),
  169. cell: false,
  170. ...props,
  171. }"
  172. />
  173. <wd-textarea
  174. v-if="type === 'Textarea'"
  175. v-model="modelValue[prop]"
  176. v-bind="{
  177. ...(direction === 'vertical'
  178. ? verticalDefaultProps[type]
  179. : horizontalDefaultProps[type]),
  180. cell: false,
  181. ...props,
  182. }"
  183. />
  184. <wd-picker
  185. v-if="type === 'Select'"
  186. v-bind="{
  187. ...(direction === 'vertical'
  188. ? verticalDefaultProps[type]
  189. : horizontalDefaultProps[type]),
  190. cell: false,
  191. ...props,
  192. }"
  193. v-model="modelValue[prop]"
  194. ></wd-picker>
  195. <wd-radio-group
  196. v-if="type === 'Radio'"
  197. v-bind="{
  198. ...(direction === 'vertical'
  199. ? verticalDefaultProps[type]
  200. : horizontalDefaultProps[type]),
  201. ...props,
  202. cell: true,
  203. shape: 'button',
  204. }"
  205. v-model="modelValue[prop]"
  206. >
  207. <template v-for="{ label, value } of props.columns" :key="value">
  208. <wd-radio :value="value">{{ label }}</wd-radio>
  209. </template>
  210. </wd-radio-group>
  211. <wd-checkbox-group
  212. v-if="type === 'Checkbox'"
  213. v-bind="{
  214. ...(direction === 'vertical'
  215. ? verticalDefaultProps[type]
  216. : horizontalDefaultProps[type]),
  217. ...props,
  218. cell: true,
  219. shape: 'button',
  220. }"
  221. v-model="modelValue[prop]"
  222. >
  223. <template v-for="{ label, value } of props.columns" :key="value">
  224. <wd-checkbox custom-class="mr-4!" :modelValue="value">{{ label }}</wd-checkbox>
  225. </template>
  226. </wd-checkbox-group>
  227. <wd-upload
  228. v-if="type === 'ImageUploader'"
  229. v-bind="{
  230. ...(direction === 'vertical'
  231. ? verticalDefaultProps[type]
  232. : horizontalDefaultProps[type]),
  233. ...props,
  234. action,
  235. fileList:
  236. (modelValue[prop] ?? '') === ''
  237. ? []
  238. : modelValue[prop]?.split(',').map((it) => ({ url: it })),
  239. }"
  240. @change="
  241. ({ fileList }) =>
  242. (modelValue[prop] = fileList
  243. .map(({ response }) => JSON.parse(response).data)
  244. .join(','))
  245. "
  246. ></wd-upload>
  247. <wd-button
  248. v-if="type === 'Submit'"
  249. v-bind="{
  250. ...(direction === 'vertical' ? verticalDefaultProps[type] : {}),
  251. ...omit(props, []),
  252. formType: 'submit',
  253. }"
  254. @click="submit"
  255. >
  256. <span v-if="type === 'Submit'">提交</span>
  257. </wd-button>
  258. </div>
  259. </template>
  260. <!-- </wd-cell-group> -->
  261. </wd-form>
  262. </wd-config-provider>
  263. </template>
  264. <style lang="less" scoped></style>