popupMixin.js 10 KB

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