DynamicFormControl.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <template>
  2. <text
  3. v-if="item.type === 'static-text' "
  4. class="form-static-text"
  5. :style="(params.style as any)"
  6. :class="(params.class as any)"
  7. >
  8. {{ params?.text ?? model ?? null }}
  9. </text>
  10. <Field
  11. ref="formItemRef"
  12. v-else-if="item.type && filedInternalTypes.includes(item.type)"
  13. :label="label"
  14. :multiline="item.type === 'textarea'"
  15. :tags="item.type.endsWith('-tag')"
  16. :name="item.name"
  17. :modelValue="model"
  18. @update:modelValue="onValueChanged"
  19. :maxlength="260"
  20. :showBottomBorder="!isLast"
  21. :required="Boolean(item.rules?.length)"
  22. :rules="item.rules"
  23. :disabled="disabled"
  24. :readonly="readonly"
  25. v-bind="{
  26. ...params,
  27. ...extraDefine?.itemProps || {},
  28. ...item.formProps,
  29. }"
  30. />
  31. <Field
  32. v-else
  33. ref="formItemRef"
  34. :label="label"
  35. :name="item.name"
  36. :required="Boolean(item.rules?.length)"
  37. :showRightArrow="extraDefine?.needArrow"
  38. :showBottomBorder="!isLast"
  39. :requireChildRef="() => itemRef"
  40. :rules="item.rules"
  41. :disabled="disabled"
  42. :readonly="readonly"
  43. v-bind="{
  44. ...extraDefine?.itemProps || {},
  45. ...item.formProps,
  46. }"
  47. >
  48. <!-- <text>fullName: {{item.name}}</text> -->
  49. <slot name="insertion">
  50. <template v-if="item.type === 'custom'">
  51. <slot name="formCeil" :data="data" />
  52. </template>
  53. <template v-else-if="item.type === 'number'">
  54. <Stepper
  55. ref="itemRef"
  56. :modelValue="model"
  57. :disabled="disabled || readonly"
  58. @update:modelValue="onValueChanged"
  59. v-bind="params"
  60. />
  61. </template>
  62. <template v-else-if="item.type === 'switch'">
  63. <Switch
  64. ref="itemRef"
  65. :modelValue="model"
  66. :disabled="disabled || readonly"
  67. @update:modelValue="onValueChanged"
  68. v-bind="params"
  69. />
  70. </template>
  71. <template v-else-if="item.type === 'radio-value'">
  72. <RadioValue
  73. ref="itemRef"
  74. :modelValue="model"
  75. :disabled="disabled || readonly"
  76. @update:modelValue="onValueChanged"
  77. v-bind="(params as any as RadioValueProps)"
  78. />
  79. </template>
  80. <template v-else-if="item.type === 'radio-id'">
  81. <RadioIdField
  82. ref="itemRef"
  83. :modelValue="model"
  84. :disabled="disabled || readonly"
  85. @update:modelValue="onValueChanged"
  86. v-bind="(params as any as RadioIdFieldProps)"
  87. />
  88. </template>
  89. <template v-else-if="item.type === 'select'">
  90. <view>
  91. <NaPickerField
  92. ref="itemRef"
  93. :modelValue="model"
  94. :disabled="disabled || readonly"
  95. @update:modelValue="onValueChanged"
  96. v-bind="(params as any as PickerFieldProps)"
  97. />
  98. </view>
  99. </template>
  100. <template v-else-if="item.type === 'rate'">
  101. <Rate
  102. ref="itemRef"
  103. :modelValue="model"
  104. :disabled="disabled || readonly"
  105. @update:modelValue="onValueChanged"
  106. v-bind="(params as any as RateProps)"
  107. />
  108. </template>
  109. <template v-else-if="item.type === 'uploader'">
  110. <UploaderField
  111. ref="itemRef"
  112. :modelValue="model"
  113. :disabled="disabled"
  114. :readonly="readonly"
  115. @update:modelValue="onValueChanged"
  116. v-bind="(params as any as UploaderFieldProps)"
  117. />
  118. </template>
  119. <template v-else-if="item.type === 'select-id'">
  120. <PickerIdField
  121. ref="itemRef"
  122. :modelValue="model"
  123. :disabled="disabled"
  124. :readonly="readonly"
  125. @update:modelValue="onValueChanged"
  126. v-bind="(params as any as PickerIdFieldProps)"
  127. />
  128. </template>
  129. <template v-else-if="item.type === 'select-city'">
  130. <PickerCityField
  131. ref="itemRef"
  132. :modelValue="model"
  133. :disabled="disabled"
  134. :readonly="readonly"
  135. @update:modelValue="onValueChanged"
  136. v-bind="(params as any)"
  137. />
  138. </template>
  139. <template v-else-if="item.type === 'select-address'">
  140. <PickerAddressField
  141. ref="itemRef"
  142. :modelValue="model"
  143. :disabled="disabled"
  144. :readonly="readonly"
  145. @update:modelValue="onValueChanged"
  146. v-bind="(params as any)"
  147. />
  148. </template>
  149. <template v-else-if="item.type === 'select-lonlat'">
  150. <PickerLonlat
  151. ref="itemRef"
  152. :modelValue="model"
  153. :disabled="disabled || readonly"
  154. @update:modelValue="(v:any) => onValueChanged(v)"
  155. v-bind="params"
  156. />
  157. </template>
  158. <template v-else-if="item.type === 'check-box'">
  159. <CheckBox
  160. ref="itemRef"
  161. :modelValue="model"
  162. :disabled="disabled || readonly"
  163. @update:modelValue="onValueChanged"
  164. v-bind="params"
  165. />
  166. </template>
  167. <template v-else-if="item.type === 'check-box-list'">
  168. <CheckBoxList
  169. ref="itemRef"
  170. :modelValue="model"
  171. :disabled="disabled || readonly"
  172. @update:modelValue="onValueChanged"
  173. v-bind="(params)"
  174. />
  175. </template>
  176. <template v-else-if="item.type === 'check-box-tree'">
  177. <CheckBoxTreeList
  178. ref="itemRef"
  179. :modelValue="model"
  180. :disabled="disabled || readonly"
  181. @update:modelValue="onValueChanged"
  182. v-bind="(params as any as CheckBoxTreeListProps)"
  183. />
  184. </template>
  185. <template v-else-if="item.type === 'check-box-int'">
  186. <CheckBoxToInt
  187. ref="itemRef"
  188. :modelValue="model"
  189. :disabled="disabled || readonly"
  190. @update:modelValue="onValueChanged"
  191. v-bind="params"
  192. />
  193. </template>
  194. <template v-else-if="item.type === 'datetime'">
  195. <view>
  196. <DateTimePickerField
  197. ref="itemRef"
  198. :modelValue="model"
  199. v-bind="params"
  200. @update:modelValue="(e: any) => onValueChanged(e)"
  201. />
  202. </view>
  203. </template>
  204. <template v-else-if="item.type === 'time'">
  205. <view>
  206. <TimePickerField
  207. ref="itemRef"
  208. :modelValue="model"
  209. v-bind="params"
  210. @update:modelValue="(e: any) => onValueChanged(e)"
  211. />
  212. </view>
  213. </template>
  214. <template v-else-if="item.type === 'date'">
  215. <view>
  216. <DatePickerField
  217. ref="itemRef"
  218. :modelValue="model"
  219. v-bind="params"
  220. @update:modelValue="(e: any) => onValueChanged(e)"
  221. />
  222. </view>
  223. </template>
  224. <template v-else-if="item.type === 'button'">
  225. <Button
  226. ref="itemRef"
  227. :disabled="disabled || readonly"
  228. v-bind="params"
  229. />
  230. </template>
  231. <template v-else-if="item.type === 'image'">
  232. <Image
  233. ref="itemRef"
  234. v-bind="params"
  235. />
  236. </template>
  237. <template v-else-if="item.type === 'alert'">
  238. <Alert
  239. ref="itemRef"
  240. v-bind="params"
  241. />
  242. </template>
  243. <ComponentRender v-else
  244. ref="itemRef"
  245. :modelValue="model"
  246. @update:modelValue="onValueChanged"
  247. :params="params"
  248. :item="item"
  249. :name="name"
  250. :isLast="isLast"
  251. :disabled="disabled"
  252. :readonly="readonly"
  253. />
  254. </slot>
  255. </Field>
  256. </template>
  257. <script setup lang="ts">
  258. import { computed, inject, onBeforeUnmount, onMounted, ref, type PropType, type Ref } from 'vue';
  259. import type { IDynamicFormItem, IDynamicFormItemCallback, IDynamicFormMessageCenter, IDynamicFormObject, IDynamicFormOptions, IDynamicFormRef } from '.';
  260. import Field from '../form/Field.vue';
  261. import Stepper from '../form/Stepper.vue';
  262. import NaPickerField, { type PickerFieldProps } from '../form/PickerField.vue';
  263. import CheckBox from '../form/CheckBox.vue';
  264. import Switch from '../form/Switch.vue';
  265. import RadioValue from './wrappers/RadioValue.vue';
  266. import PickerIdField from './wrappers/PickerIdField.vue';
  267. import CheckBoxList from './wrappers/CheckBoxList.vue';
  268. import CheckBoxToInt from './wrappers/CheckBoxToInt.vue';
  269. import type { RadioValueProps } from './wrappers/RadioValue';
  270. import type { PickerIdFieldProps } from './wrappers/PickerIdField';
  271. import PickerCityField from './wrappers/PickerCityField.vue';
  272. import PickerLonlat from './wrappers/PickerLonlat.vue';
  273. import DateTimePickerField from '../form/DateTimePickerField.vue';
  274. import TimePickerField from '../form/TimePickerField.vue';
  275. import DatePickerField from '../form/DatePickerField.vue';
  276. import UploaderField, { type UploaderFieldProps } from '../form/UploaderField.vue';
  277. import RadioIdField from './wrappers/RadioIdField.vue';
  278. import type { RadioIdFieldProps } from './wrappers/RadioIdField';
  279. import Rate, { type RateProps } from '../form/Rate.vue';
  280. import ComponentConfigs from '@/common/components/dynamicf/ComponentConfigs';
  281. import ComponentRender from '@/common/components/dynamicf/ComponentRender.vue';
  282. import type { Rules } from 'async-validator';
  283. import PickerAddressField from './wrappers/PickerAddressField.vue';
  284. import Button from '../basic/Button.vue';
  285. import Alert from '../feedback/Alert.vue';
  286. import Image from '../basic/Image.vue';
  287. import CheckBoxTreeList, { type CheckBoxTreeListProps } from './wrappers/CheckBoxTreeList.vue';
  288. import { useInjectFormContext, useInjectFormItemContext } from '../form/FormContext';
  289. export interface FormCeilProps {
  290. model: unknown,
  291. rawModel: unknown,
  292. parent?: IDynamicFormItem,
  293. parentModel: unknown,
  294. onModelUpdate: (v: unknown) => void,
  295. item: IDynamicFormItem,
  296. name: string,
  297. disabled: boolean,
  298. additionalProps: Record<string, unknown>,
  299. }
  300. const filedInternalTypes = [
  301. 'text',
  302. 'textarea',
  303. 'text-tag',
  304. ]
  305. const props = defineProps({
  306. item: {
  307. type: Object as PropType<IDynamicFormItem>,
  308. required: true,
  309. },
  310. name: {
  311. type: String,
  312. default: ''
  313. },
  314. parent: {
  315. type: Object as PropType<IDynamicFormItem>,
  316. default: null
  317. },
  318. disabled: {
  319. type: Boolean,
  320. default: false
  321. },
  322. model: {
  323. type: null
  324. },
  325. parentModel: {
  326. type: null
  327. },
  328. rawModel: {
  329. type: Object as PropType<Record<string, unknown>>,
  330. default: null
  331. },
  332. noLabel: {
  333. type: Boolean,
  334. default: false
  335. },
  336. formWrapperColDefault: {
  337. type: Object,
  338. default: null
  339. },
  340. formLabelColDefault: {
  341. type: Object,
  342. default: null
  343. },
  344. isFirst: {
  345. type: Boolean,
  346. default: false,
  347. },
  348. isLast: {
  349. type: Boolean,
  350. default: false,
  351. },
  352. });
  353. const emit = defineEmits([ 'update:model' ]);
  354. const formItemRef = ref();
  355. const finalOptions = inject<Ref<IDynamicFormOptions>>('finalOptions');
  356. const globalParams = inject<Ref<IDynamicFormObject>>('globalParams');
  357. const formRef = inject<IDynamicFormRef>('formRef');
  358. const formName = inject('formName', '');
  359. const context = useInjectFormItemContext();
  360. const formContext = useInjectFormContext();
  361. const disabled = computed(() => props.disabled || formContext?.disabled.value || context?.disabled.value);
  362. const readonly = computed(() => formContext?.readonly.value || context?.readonly.value);
  363. function evaluateCallback(val: unknown|IDynamicFormItemCallback<unknown>) {
  364. if (typeof val === 'object' && typeof (val as IDynamicFormItemCallback<unknown>).callback === 'function')
  365. return (val as IDynamicFormItemCallback<unknown>).callback(
  366. props.model,
  367. props.rawModel,
  368. props.parentModel,
  369. {
  370. item: props.item,
  371. parent: props.parent,
  372. form: formRef!,
  373. formGlobalParams: globalParams?.value || {},
  374. formRules: (finalOptions?.value.formRules ?? {}) as Record<string, Rules>,
  375. isFirst: props.isFirst,
  376. isLast: props.isLast,
  377. }
  378. );
  379. return val as unknown;
  380. }
  381. function evaluateCallbackObj(val: Record<string, unknown|IDynamicFormItemCallback<unknown>>) {
  382. const newObj = {} as Record<string, unknown>;
  383. for (const key in val) {
  384. if (Object.prototype.hasOwnProperty.call(val, key))
  385. newObj[key] = evaluateCallback(val[key]);
  386. }
  387. return newObj;
  388. }
  389. const extraDefine = computed(() => ComponentConfigs.find((item) => item.name === props.item.type))
  390. const params = computed(() => {
  391. return {
  392. ...extraDefine.value?.props || {},
  393. ...evaluateCallbackObj(props.item.additionalProps as any)
  394. } as Record<string, unknown>
  395. })
  396. const label = computed(() => evaluateCallback(props.item.label) as string)
  397. const data = computed<FormCeilProps>(() => {
  398. return {
  399. name: props.name,
  400. item: props.item,
  401. model: props.model,
  402. onModelUpdate: onValueChanged,
  403. rawModel: props.rawModel,
  404. parentModel: props.parentModel,
  405. parent: props.parent,
  406. rules: props.item.rules,
  407. disabled: props.disabled,
  408. additionalProps: props.item.additionalProps as Record<string, unknown>,
  409. }
  410. })
  411. const itemRef = ref();
  412. const messageCenter = inject<IDynamicFormMessageCenter>('messageCenter');
  413. function onValueChanged(v: any) {
  414. props.item.watch?.(props.model, v, props.rawModel, getComponentRef());
  415. emit('update:model', v);
  416. }
  417. function getComponentRef() {
  418. if (typeof itemRef.value.getItemRef === 'function')
  419. return itemRef.value.getItemRef();
  420. return itemRef.value;
  421. }
  422. onMounted(() => {
  423. props.item.mounted?.(props.model, props.rawModel, getComponentRef());
  424. messageCenter?.addWidgetRef(props.item.name, props.item.type ?? '', getComponentRef);
  425. })
  426. onBeforeUnmount(() => {
  427. props.item.beforeUnmount?.(props.model, props.rawModel, getComponentRef());
  428. messageCenter?.removeWidgetRef(props.item.name, props.item.type ?? '', getComponentRef);
  429. })
  430. defineOptions({
  431. options: {
  432. virtualHost: true,
  433. }
  434. })
  435. </script>