index.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { ElFormItem, ElTooltip } from 'element-plus'
  2. import { h, toRef, inject, computed, ref, unref, onErrorCaptured } from 'vue'
  3. import { getInputComponent, getViewComponent, getH5InputComponent } from '../cip-form-input/util'
  4. // import { handleFormConfig } from '../helper/d-render'
  5. import { isEmpty, debounce, isEmptyObject } from '@cip/utils/util'
  6. import { useWatchFieldDepend, useFieldValue, useRules } from './form-item-hooks'
  7. import { isHideLabel, getLabelWidth } from './util'
  8. import './index.less'
  9. import { useFormInject } from '../hooks/use-form'
  10. export default {
  11. name: 'CipFormItem',
  12. props: {
  13. config: Object, // 字段配置信息
  14. fieldKey: String, // 字段名
  15. model: { // 字段所属model
  16. type: Object,
  17. default: () => ({})
  18. },
  19. readonly: Boolean, // 是否只读-即查看模式
  20. customSlots: Function,
  21. showTemplate: { // 是否展示模版值[注:表单设计中使用]
  22. type: Boolean,
  23. default: false
  24. },
  25. tableDependOnValues: Object,
  26. inTable: {
  27. type: Boolean,
  28. default: false
  29. },
  30. componentKey: String,
  31. grid: [Number, Boolean],
  32. tableData: Array,
  33. onSearch: Function // 供CipSearchForm使用
  34. },
  35. emits: ['update:model'],
  36. setup (props, context) {
  37. onErrorCaptured((e) => {
  38. // el-form-item 在onBeforeUnmount会访问el.value.firstElementChild 由于切换较快的原因会抱错
  39. process.env.NODE_ENV === 'development' && console.log(e)
  40. return false
  41. })
  42. // elForm组件实例
  43. const elForm = inject('elForm', {})
  44. const { cipForm } = useFormInject()
  45. const equipment = computed(() => {
  46. return unref(cipForm.equipment) || 'pc'
  47. })
  48. const NoFormItem = (props, { slots }) => {
  49. return <div class="el-form-item">
  50. {slots.default?.()}
  51. </div>
  52. }
  53. const FormItemComponent = computed(() => {
  54. return isEmptyObject(elForm) ? NoFormItem : ElFormItem
  55. })
  56. const updateModel = debounce((value) => {
  57. context.emit('update:model', value)
  58. }, 50, false)
  59. // FormItem及Input组件渲染模式 ['hidden','read','read-write']
  60. const status = computed(() => {
  61. // 为设置则默认开始可读可写模式
  62. const config = formItemConfig.value
  63. if (props.readonly) {
  64. if (config.readable === false) return 'hidden'
  65. return 'read'
  66. }
  67. if (isEmpty(config.readable) && isEmpty(config.writable)) {
  68. return 'read-write'
  69. }
  70. if (config.writable) {
  71. return 'read-write'
  72. } else if (config.readable) {
  73. return 'read'
  74. } else {
  75. return 'hidden'
  76. }
  77. })
  78. // Input组件实际使用的配置
  79. const formItemConfig = computed(() => {
  80. return runningConfig.value || props.config // handleFormConfig()
  81. })
  82. const model = toRef(props, 'model')
  83. const fieldKey = toRef(props, 'fieldKey')
  84. // 仅正对modelValue生效
  85. const changeEffect = computed(() => {
  86. return formItemConfig.value.changeEffect
  87. })
  88. // modelValue
  89. const [modelValue, updateModelValue] = useFieldValue(fieldKey, model, updateModel, changeEffect)
  90. const otherKey = computed(() => formItemConfig.value?.otherKey)
  91. // otherValue
  92. const [otherValue, updateOtherValue] = useFieldValue(otherKey, model, updateModel)
  93. // 监听依赖 触发响应事件
  94. const { dependOnValues, outDependOnValues, runningConfig } = useWatchFieldDepend(props, context, { updateModelValue, updateOtherValue })
  95. const readonly = toRef(props, 'readonly')
  96. // rules
  97. const { usingRules, rules } = useRules(formItemConfig, readonly, status, otherValue, dependOnValues)
  98. // Input组件是否展示标记(需要控制form-item内置的margin-bottom)
  99. const childStatus = ref(true)
  100. // FormItem label
  101. const formItemLabel = () => {
  102. // 隐藏label或者label为空时直接返回空字符串
  103. if (formItemConfig.value.hideLabel === true || isEmpty(formItemConfig.value.label)) return ''
  104. // 表单为只读模式下展示样式控制
  105. const labelId = formItemConfig.value.directory ? props.fieldKey : undefined
  106. const result = [h('span', { class: { 'is-readonly': props.readonly }, id: labelId }, [formItemConfig.value.label])]
  107. // 存在说明
  108. if (formItemConfig.value.description) {
  109. const descriptionComp = (
  110. <ElTooltip effect={formItemConfig.value.descriptionEffect || 'light'} placement={'top'}>
  111. {{
  112. content: () => formItemConfig.value.description,
  113. default: () => <i class={'el-icon-question'} style={'margin-left:2px'}/>
  114. }}
  115. </ElTooltip>
  116. )
  117. result.push(descriptionComp)
  118. }
  119. // 仅在正在使用rules的input中且required为true时展示必填标记
  120. if (usingRules.value && formItemConfig.value.required) {
  121. const requiredAsterisk = (<span class={'cip-danger-color'}>*</span>)
  122. result.unshift(requiredAsterisk)
  123. }
  124. // 有标签后缀的增加标签后缀
  125. if (elForm.labelSuffix) {
  126. result.push(elForm.labelSuffix)
  127. }
  128. return result
  129. }
  130. const inlineErrorMessage = computed(() => {
  131. return props.inTable || cipForm.equipment === 'mobile'
  132. })
  133. // FormItem ErrorMessage
  134. const errorMessageNode = ({ error }) => {
  135. if (!inlineErrorMessage.value) return null
  136. return (
  137. <ElTooltip content={error}>
  138. <i class={'el-icon-warning cip-danger-color '}/>
  139. </ElTooltip>
  140. )
  141. }
  142. const labelPosition = computed(() => {
  143. // 依赖elForm实例数据总是在变换、故修改为有父组件cip-form下发数据
  144. if (!isEmpty(formItemConfig.value.labelPosition)) {
  145. return formItemConfig.value.labelPosition === 'top'
  146. }
  147. return unref(cipForm.labelPosition) === 'top'
  148. })
  149. // FormItem
  150. const formItem = () => {
  151. return h(FormItemComponent.value, {
  152. class: {
  153. 'pos-top': labelPosition.value,
  154. // 'pos-top--padding': labelPositionTopPadding, 暂时关闭position === top时内容的缩进
  155. 'hide-label': isHideLabel(formItemConfig.value),
  156. 'content--end': formItemConfig.value.contentEnd
  157. },
  158. prop: formItemConfig.value.ruleKey || props.fieldKey, // 子表单内的输入框会生成一个ruleKey
  159. rules: rules.value,
  160. labelWidth: getLabelWidth(formItemConfig.value),
  161. inlineMessage: inlineErrorMessage.value
  162. }, {
  163. label: formItemLabel,
  164. error: errorMessageNode,
  165. default: () => {
  166. if (props.customSlots) {
  167. return props.customSlots({
  168. fieldKey: props.fieldKey,
  169. updateModel: updateModelValue
  170. })
  171. }
  172. const type = formItemConfig.value.type || 'default'
  173. const componentProps = {
  174. key: props.componentKey,
  175. id: props.fieldKey,
  176. fieldKey: props.fieldKey,
  177. modelValue: modelValue.value,
  178. otherValue: otherValue.value,
  179. model: model.value, // 即将废弃
  180. config: formItemConfig.value,
  181. usingRules: usingRules.value,
  182. rules: rules.value,
  183. dependOnValues: dependOnValues.value,
  184. outDependOnValues: outDependOnValues.value,
  185. disabled: formItemConfig.value.importantDisabled !== undefined
  186. ? formItemConfig.value.importantDisabled
  187. : formItemConfig.value.disabled,
  188. showTemplate: props.showTemplate,
  189. tableData: props.tableData,
  190. onStatusChange: (status) => { // 子组件触发事件控制父组件是否显示
  191. childStatus.value = status
  192. },
  193. onSearch: props.onSearch
  194. }
  195. if (status.value === 'read-write') {
  196. const inputComponentProps = {
  197. ...componentProps,
  198. 'onUpdate:modelValue': updateModelValue,
  199. 'onUpdate:otherValue': updateOtherValue
  200. }
  201. if (unref(cipForm.equipment) === 'mobile') {
  202. return h(getH5InputComponent(type), inputComponentProps)
  203. } else {
  204. return h(getInputComponent(type), inputComponentProps)
  205. }
  206. } else {
  207. return h(getViewComponent(type), componentProps)
  208. }
  209. }
  210. })
  211. }
  212. const cipFormStyle = computed(() => {
  213. if (props.grid) {
  214. return { gridColumn: `span ${formItemConfig.value.span || 1}` }
  215. } else {
  216. return (elForm.inline && !props.inTable) ? formItemConfig.value.style : ''
  217. }
  218. })
  219. return () => {
  220. if (status.value === 'hidden') return null
  221. return (
  222. <div
  223. style={cipFormStyle.value}
  224. class={[
  225. 'cip-form-item',
  226. 'el-form-item__wrapper',
  227. `cip-form-item--${equipment.value}`,
  228. {
  229. 'cip-form-item--label-position-top': labelPosition.value,
  230. 'cip-form-item--hidden': !childStatus.value || formItemConfig.value.hideItem,
  231. 'cip-form-item--in-table': props.inTable
  232. }
  233. ]}>
  234. {formItem()}
  235. </div>
  236. )
  237. }
  238. }
  239. }