popupMixin.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import classNames from './classNames'
  2. import eventsMixin from './eventsMixin'
  3. const DEFAULT_TRIGGER = 'onClick'
  4. const CELL_NAME = '../cell/index'
  5. const FIELD_NAME = '../field/index'
  6. const defaultToolbar = {
  7. title: '请选择',
  8. cancelText: '取消',
  9. confirmText: '确定',
  10. }
  11. const defaultEvents = {
  12. onChange() {},
  13. onConfirm() {},
  14. onCancel() {},
  15. onVisibleChange() {},
  16. onValueChange() {},
  17. }
  18. const defaultPlatformProps = {
  19. labelPropName: 'label',
  20. format(values, props) {
  21. return Array.isArray(values.displayValue) ? values.displayValue.join(',') : values.displayValue
  22. },
  23. }
  24. const defaultFieldNames = {
  25. label: 'label',
  26. value: 'value',
  27. children: 'children',
  28. }
  29. export default function popupMixin(selector = '#wux-picker', platformProps = defaultPlatformProps) {
  30. return Behavior({
  31. behaviors: [eventsMixin({ defaultEvents })],
  32. properties: {
  33. toolbar: {
  34. type: Object,
  35. value: defaultToolbar,
  36. },
  37. trigger: {
  38. type: String,
  39. value: DEFAULT_TRIGGER,
  40. },
  41. defaultVisible: {
  42. type: Boolean,
  43. value: false,
  44. },
  45. visible: {
  46. type: Boolean,
  47. value: false,
  48. },
  49. controlled: {
  50. type: Boolean,
  51. value: false,
  52. },
  53. disabled: {
  54. type: Boolean,
  55. value: false,
  56. },
  57. },
  58. data: {
  59. mounted: false,
  60. popupVisible: false,
  61. inputValue: [],
  62. },
  63. methods: {
  64. /**
  65. * 设置组件显示或隐藏
  66. */
  67. setVisibleState(popupVisible, callback = () => {}) {
  68. if (this.data.popupVisible !== popupVisible) {
  69. const params = {
  70. mounted: true,
  71. inputValue: this.data.value, // forceUpdate
  72. popupVisible,
  73. }
  74. this.setData(popupVisible ? params : { popupVisible }, () => {
  75. // collect field component & forceUpdate
  76. if (popupVisible && this.hasFieldDecorator) {
  77. const field = this.getFieldElem()
  78. if (field) {
  79. field.changeValue(field.data.value)
  80. }
  81. }
  82. callback()
  83. })
  84. }
  85. },
  86. /**
  87. * 触发 visibleChange 事件
  88. */
  89. fireVisibleChange(popupVisible) {
  90. if (this.data.popupVisible !== popupVisible) {
  91. if (!this.data.controlled) {
  92. this.setVisibleState(popupVisible)
  93. }
  94. this.setScrollValue(undefined)
  95. this.triggerEvent('visibleChange', { visible: popupVisible })
  96. }
  97. },
  98. /**
  99. * 打开
  100. */
  101. open() {
  102. this.fireVisibleChange(true)
  103. },
  104. /**
  105. * 关闭
  106. */
  107. close(callback) {
  108. if (typeof callback === 'function') {
  109. const values = this.getPickerValue(this.scrollValue || this.data.inputValue)
  110. callback.call(this, this.formatPickerValue(values))
  111. }
  112. this.fireVisibleChange(false)
  113. },
  114. /**
  115. * 组件关闭时重置其内部数据
  116. */
  117. onClosed() {
  118. this.picker = null
  119. this.setData({ mounted: false, inputValue: null })
  120. },
  121. /**
  122. * 点击确定按钮时的回调函数
  123. */
  124. onConfirm() {
  125. this.close((values) => {
  126. this.triggerEvent('change', values) // collect field component
  127. this.triggerEvent('confirm', values)
  128. })
  129. },
  130. /**
  131. * 点击取消按钮时的回调函数
  132. */
  133. onCancel() {
  134. this.close((values) => this.triggerEvent('cancel', values))
  135. },
  136. /**
  137. * 每列数据选择变化后的回调函数
  138. */
  139. onValueChange(e) {
  140. if (!this.data.mounted) return
  141. const { value } = e.detail
  142. if (this.data.cascade) {
  143. this.setCasecadeScrollValue(value)
  144. } else {
  145. this.setScrollValue(value)
  146. }
  147. this.updated(value, true)
  148. this.triggerEvent('valueChange', this.formatPickerValue(e.detail))
  149. },
  150. /**
  151. * 获取当前 picker 的值
  152. */
  153. getPickerValue(value = this.data.inputValue) {
  154. this.picker = this.picker || this.selectComponent(selector)
  155. return this.picker && this.picker.getValue(value)
  156. },
  157. /**
  158. * 格式化 picker 返回值
  159. */
  160. formatPickerValue(values) {
  161. return {
  162. ...values,
  163. [platformProps.labelPropName]: platformProps.format(values, this.data),
  164. }
  165. },
  166. /**
  167. * 获取 field 父元素
  168. */
  169. getFieldElem() {
  170. return this.field = (this.field || this.getRelationNodes(FIELD_NAME)[0])
  171. },
  172. /**
  173. * 设置子元素 props
  174. */
  175. setChildProps() {
  176. if (this.data.disabled) return
  177. const elements = this.getRelationNodes(CELL_NAME)
  178. const { trigger = DEFAULT_TRIGGER } = this.data
  179. if (elements.length > 0) {
  180. elements.forEach((inputElem) => {
  181. const { inputEvents } = inputElem.data
  182. const oriInputEvents = inputElem.data.oriInputEvents || { ...inputEvents }
  183. inputEvents[trigger] = (...args) => {
  184. if (oriInputEvents && oriInputEvents[trigger]) {
  185. oriInputEvents[trigger](...args)
  186. }
  187. this.onTriggerClick()
  188. }
  189. inputElem.setData({ oriInputEvents, inputEvents })
  190. })
  191. }
  192. },
  193. /**
  194. * 触发事件
  195. */
  196. onTriggerClick() {
  197. this.fireVisibleChange(!this.data.popupVisible)
  198. },
  199. /**
  200. * 阻止移动触摸
  201. */
  202. noop() {},
  203. /**
  204. * 更新值
  205. */
  206. updated(inputValue, isForce) {
  207. if (!this.hasFieldDecorator || isForce) {
  208. if (this.data.inputValue !== inputValue) {
  209. this.setData({ inputValue })
  210. }
  211. }
  212. },
  213. /**
  214. * 记录每列数据的变化值
  215. */
  216. setScrollValue(value) {
  217. this.scrollValue = value
  218. },
  219. /**
  220. * 记录每列数据的变化值 - 针对于级联
  221. */
  222. setCasecadeScrollValue(value) {
  223. if (value && this.scrollValue) {
  224. const length = this.scrollValue.length
  225. if (length === value.length && this.scrollValue[length - 1] === value[length - 1]) {
  226. return
  227. }
  228. }
  229. this.setScrollValue(value)
  230. },
  231. },
  232. lifetimes: {
  233. ready() {
  234. const { defaultVisible, visible, controlled, value } = this.data
  235. const popupVisible = controlled ? visible : defaultVisible
  236. // 若 defaultFieldNames 存在,则缓存之,并传递给子组件,只生效一次
  237. if ('defaultFieldNames' in this.data) {
  238. this.setData({
  239. fieldNames: Object.assign({}, defaultFieldNames, this.data.defaultFieldNames),
  240. })
  241. }
  242. this.mounted = true
  243. this.scrollValue = undefined
  244. this.setVisibleState(popupVisible)
  245. this.setChildProps()
  246. },
  247. detached() {
  248. this.mounted = false
  249. },
  250. },
  251. definitionFilter(defFields) {
  252. // set default child
  253. Object.assign(defFields.relations = (defFields.relations || {}), {
  254. [CELL_NAME]: {
  255. type: 'child',
  256. observer() {
  257. this.setChildProps()
  258. },
  259. },
  260. [FIELD_NAME]: {
  261. type: 'ancestor',
  262. },
  263. })
  264. // set default classes
  265. Object.assign(defFields.computed = (defFields.computed || {}), {
  266. classes: ['prefixCls', function(prefixCls) {
  267. const wrap = classNames(prefixCls)
  268. const toolbar = `${prefixCls}__toolbar`
  269. const inner = `${prefixCls}__inner`
  270. const cancel = classNames(`${prefixCls}__button`, {
  271. [`${prefixCls}__button--cancel`]: true,
  272. })
  273. const confirm = classNames(`${prefixCls}__button`, {
  274. [`${prefixCls}__button--confirm`]: true,
  275. })
  276. const hover = `${prefixCls}__button--hover`
  277. const title = `${prefixCls}__title`
  278. return {
  279. wrap,
  280. toolbar,
  281. inner,
  282. cancel,
  283. confirm,
  284. hover,
  285. title,
  286. }
  287. }],
  288. })
  289. // set default observers
  290. Object.assign(defFields.observers = (defFields.observers || {}), {
  291. visible(popupVisible) {
  292. if (this.data.controlled) {
  293. this.setVisibleState(popupVisible)
  294. }
  295. },
  296. value(value) {
  297. this.updated(value)
  298. },
  299. })
  300. },
  301. })
  302. }