useForm.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import { ComputedRef, Slots, computed, Ref, unref, watch, nextTick } from 'vue'
  2. import LeSelect from '@/components/Select'
  3. // import LeSelect from 'lance-element-vue/packages/Select'
  4. import InputNumber from '@/components/InputNumber'
  5. // import InputNumber from 'lance-element-vue/packages/InputNumber'
  6. import InputNumberRange from '@/components/InputNumberRange'
  7. // import InputNumberRange from 'lance-element-vue/packages/InputNumberRange'
  8. import CustomRender from '@/components/CustomRender'
  9. // import CustomRender from 'lance-element-vue/packages/CustomRender'
  10. import { LeFormItem, ObjectOpts, SlotOption } from '@/components/FormConfig/formConfig.types.ts'
  11. import { get_formSlots, getOptions, renderOption } from '@/components/FormConfig/utils.ts'
  12. // import { LeTableOptions, LeTableProps } from '@/components/Table'
  13. // import { SearchFormProps } from '@/components/SearchForm'
  14. // import { FormConfigProps } from '../index-old.vue'
  15. import { useI18n } from 'vue-i18n'
  16. import { OptionItemProps } from '@/components/Select/select.types.ts'
  17. export type FormType = 'FormConfig' | 'SearchForm'
  18. export type SearchFormItem = LeFormItem
  19. export type useFormItemsOpts = {
  20. // props: SearchFormProps | FormConfigProps
  21. // props: { forms: LeFormItem[] }
  22. props: any
  23. params: ObjectOpts
  24. slots: Slots
  25. // tableRef: Ref
  26. }
  27. export type RenderFormItem = LeFormItem & { le_slots: ObjectOpts }
  28. export type RenderFormItemOpts = {
  29. form: LeFormItem & { le_slots: ObjectOpts }
  30. params: ObjectOpts
  31. isEdit?: boolean
  32. size?: 'large' | 'default' | 'small'
  33. t?: any
  34. itemStyle?: string
  35. }
  36. export const renderFormItem = (opts: RenderFormItemOpts) => {
  37. const { form, params, itemStyle, isEdit = true, size = 'default', t } = opts
  38. const {
  39. prop,
  40. itemType,
  41. itemWidth,
  42. options,
  43. change,
  44. // 申明: onChange 会导致(类input) change后触发两次(组件定义一次,原生change一次) 对组件定义进行过滤,仅留原生触发,组件触发onChange 用change 替代
  45. onChange,
  46. itemStyle: form_itemStyle = '',
  47. size: _size,
  48. placeholder,
  49. t_placeholder,
  50. le_slots,
  51. ...formOthers
  52. } = form
  53. const _options = options || []
  54. const _itemStyle = unref(itemStyle) + form_itemStyle + (itemWidth ? `;width: ${itemWidth}` : '')
  55. const _placeholder = t_placeholder ? t(t_placeholder) : placeholder
  56. let disabled = form.disabled
  57. if (disabled === undefined) {
  58. disabled = isEdit === false
  59. }
  60. // 优化后的 change事件
  61. const formatterChange = async () => {
  62. // console.log(params[prop], `value, params.${prop}, options`, _options)
  63. if (change) {
  64. return change(params[prop], _options, params)
  65. }
  66. }
  67. switch (itemType) {
  68. /* 自定义 le 自定义Select */
  69. case 'leSelect':
  70. // leSelect: 基于 element-plus el-select-v2扩展
  71. const slots_leSelect = {
  72. default: le_slots.option as SlotOption<OptionItemProps>
  73. }
  74. const leStyle = _itemStyle + (/width\:/g.test(_itemStyle) ? '' : ';width: 200px')
  75. return (
  76. <LeSelect
  77. {...formOthers}
  78. options={_options}
  79. v-model={params[prop]}
  80. isPopover={formOthers.isPopover ?? true}
  81. // 通过teleport插入到body (popper-append-to-body popperAppendToBody已弃用)
  82. teleported={formOthers.teleported ?? true}
  83. onChange={formatterChange}
  84. size={_size ?? size}
  85. placeholder={_placeholder}
  86. style={leStyle}
  87. v-slots={slots_leSelect}
  88. />
  89. )
  90. /* 自定义 render */
  91. case 'render':
  92. return <CustomRender form={form} params={params} />
  93. /* 下拉框 */
  94. case 'select':
  95. return (
  96. <el-select
  97. {...formOthers}
  98. v-model={params[prop]}
  99. onChange={formatterChange}
  100. style={_itemStyle}
  101. disabled={disabled}
  102. size={_size ?? size}
  103. placeholder={_placeholder}
  104. >
  105. {getOptions(_options, form).map((option, i) => {
  106. const { label, value, disabled } = option
  107. return (
  108. <el-option key={`${value}_${i}`} value={value} label={label} disabled={disabled} title={label}>
  109. {/*renderOption(ctx.slots, form.slots?.option, option)*/}
  110. {renderOption(le_slots.option, option)}
  111. </el-option>
  112. )
  113. })}
  114. </el-select>
  115. )
  116. /* 单选框 */
  117. case 'radio':
  118. return (
  119. <el-radio-group {...formOthers} v-model={params[prop]} disabled={disabled} size={_size ?? size} onChange={formatterChange} style={_itemStyle}>
  120. {getOptions(_options, form).map((option, i) => {
  121. const { label, value, disabled } = option
  122. return (
  123. <el-radio key={`${value}_${i}`} label={value} disabled={disabled} title={label}>
  124. {renderOption(le_slots.option, option)}
  125. </el-radio>
  126. )
  127. })}
  128. </el-radio-group>
  129. )
  130. /* 级联 */
  131. case 'cascader':
  132. const slots_cascader = {
  133. default: le_slots.option as SlotOption<{ data: any; node: any }>
  134. }
  135. return (
  136. <el-cascader
  137. {...formOthers}
  138. v-model={params[prop]}
  139. onChange={formatterChange}
  140. style={_itemStyle}
  141. disabled={disabled}
  142. size={_size ?? size}
  143. clearable={form.clearable ?? true}
  144. filterable={form.filterable ?? true}
  145. options={_options}
  146. placeholder={_placeholder}
  147. v-slots={slots_cascader}
  148. />
  149. )
  150. /* 数字 */
  151. case 'inputNumber':
  152. return (
  153. <InputNumber
  154. class="rate100"
  155. {...formOthers}
  156. slots={le_slots}
  157. v-model={params[prop]}
  158. onChange={formatterChange}
  159. style={_itemStyle}
  160. disabled={disabled}
  161. placeholder={_placeholder}
  162. size={_size ?? size}
  163. precision={form.precision || 0}
  164. v-slots={le_slots}
  165. />
  166. )
  167. /* 数字区间 */
  168. case 'inputNumberRange':
  169. const numberChange = (value: number, propKey: string) => {
  170. change && change(params[prop], _options, params, propKey)
  171. }
  172. return (
  173. <InputNumberRange
  174. prop={prop}
  175. {...formOthers}
  176. v-model={params[prop]}
  177. isValueArray
  178. onChange={numberChange}
  179. style={_itemStyle}
  180. disabled={disabled}
  181. placeholder={_placeholder}
  182. size={_size ?? size}
  183. precision={form.precision || 0}
  184. v-slots={formOthers?.slots}
  185. />
  186. )
  187. /* 日期选择(单日期 || 日期区间) */
  188. case 'datePicker':
  189. let dateOpts: any = {}
  190. dateOpts.valueFormat = form.valueFormat || 'MM/DD/YYYY'
  191. dateOpts.format = form.format || dateOpts.valueFormat
  192. // 区间类型
  193. if (/range$/.test(form.type || '')) {
  194. dateOpts = Object.assign(dateOpts, {
  195. startPlaceholder: t(form.startPlaceholder || 'le.filter.startDate'),
  196. endPlaceholder: t(form.endPlaceholder || 'le.filter.endDate'),
  197. unlinkPanels: form.unlinkPanels ?? true // 解除联动
  198. })
  199. } else {
  200. dateOpts.placeholder = _placeholder || t('le.filter.selectDate')
  201. }
  202. return (
  203. <el-date-picker
  204. {...formOthers}
  205. {...dateOpts}
  206. v-model={params[prop]}
  207. onChange={formatterChange}
  208. style={_itemStyle}
  209. disabled={disabled}
  210. size={_size ?? size}
  211. />
  212. )
  213. /* switch */
  214. case 'switch':
  215. return (
  216. <el-switch {...formOthers} v-model={params[prop]} size={_size ?? size} onChange={formatterChange} style={_itemStyle} disabled={disabled} />
  217. )
  218. case 'input':
  219. default:
  220. return (
  221. <el-input
  222. {...formOthers}
  223. v-model={params[prop]}
  224. size={_size ?? size}
  225. onChange={formatterChange}
  226. disabled={disabled}
  227. placeholder={_placeholder}
  228. style={_itemStyle}
  229. />
  230. )
  231. }
  232. }
  233. export function useFormItems(type: FormType, opts: useFormItemsOpts) {
  234. const { t } = useI18n()
  235. // const testType = type === 'SearchForm' ? SearchFormItem : LeFormItem
  236. const { /*props, computedOptions, */ params, props, slots /*, tableRef*/ } = opts
  237. const realForms = computed(() => {
  238. const forms = (props.forms || []) as LeFormItem[]
  239. // if (type === 'SearchForm') {
  240. // forms = (forms || []) as SearchFormItem[]
  241. // }
  242. return forms.map(form => {
  243. return {
  244. ...form,
  245. le_slots: get_formSlots(slots, form.slots)
  246. }
  247. })
  248. })
  249. const renderForms = (opts: { forms: RenderFormItem[]; gutter?: number; span?: any } = { forms: [] }) => {
  250. const { forms, gutter = 0, span } = opts
  251. return (
  252. <el-row class="form_wrap" gutter={gutter}>
  253. {(forms as RenderFormItem[]).map((form, idx) => {
  254. const { span: _span, t_label, label, ...others } = form
  255. const _label = t_label ? t(t_label) : label
  256. const formItemSlots = {
  257. label: form.le_slots.label // get_formSlotLabel(ctx.slots, form.slots?.label)
  258. }
  259. return (
  260. <el-col key={idx} span={_span ?? span}>
  261. <el-form-item {...others} label={_label} v-slots={formItemSlots}>
  262. {renderFormItem({
  263. form,
  264. params,
  265. isEdit: props.isEdit,
  266. size: props.size,
  267. itemStyle: props.itemStyle,
  268. t
  269. })}
  270. </el-form-item>
  271. </el-col>
  272. )
  273. })}
  274. </el-row>
  275. )
  276. }
  277. return {
  278. realForms,
  279. renderForms
  280. }
  281. }