tui-picker.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. <template>
  2. <view class="tui-picker__box">
  3. <view class="tui-mask__screen" :class="[visible?'tui-picker__mask-show':'']" @tap="maskClick"></view>
  4. <view class="tui-picker__wrap" :style="{backgroundColor:backgroundColor}"
  5. :class="[visible?'tui-picker__show':'',radius?'tui-picker__radius':'']">
  6. <view class="tui-picker__header" :style="{backgroundColor:headerBgColor}">
  7. <view class="tui-picker__btn-cancle" hover-class="tui-picker__opcity" :hover-stay-time="150"
  8. @tap.stop="hidePicker"
  9. :style="{color:cancelColor,fontSize:btnSize+'rpx',fontWeight:bold?'bold':'normal'}">{{cancelText}}
  10. </view>
  11. <view class="tui-picker__title" :style="{fontSize:titleSize+'rpx',color:titleColor}">{{title}}</view>
  12. <view class="tui-picker__btn-sure" hover-class="tui-picker__opcity" :hover-stay-time="150"
  13. @tap.stop="picker"
  14. :style="{color:confirmColor,fontSize:btnSize+'rpx',fontWeight:bold?'bold':'normal'}">{{confirmText}}
  15. </view>
  16. </view>
  17. <picker-view :mask-style="maskStyle" :indicator-style="indicatorStyle" class="tui-picker__view"
  18. :value="vals" @change="columnPicker" @pickstart="pickstart" @pickend="pickend">
  19. <picker-view-column>
  20. <view :style="{color:color,fontSize:size+'px'}" v-for="(item,index) in layer1__data" :key="index"
  21. class="tui-picker__item">{{item}}</view>
  22. </picker-view-column>
  23. <picker-view-column v-if="layer==2 || layer==3">
  24. <view :style="{color:color,fontSize:size+'px'}" v-for="(item,index) in layer2__data" :key="index"
  25. class="tui-picker__item">{{item}}</view>
  26. </picker-view-column>
  27. <picker-view-column v-if="layer==3">
  28. <view :style="{color:color,fontSize:size+'px'}" v-for="(item,index) in layer3__data" :key="index"
  29. class="tui-picker__item">{{item}}</view>
  30. </picker-view-column>
  31. </picker-view>
  32. </view>
  33. </view>
  34. </template>
  35. <script>
  36. export default {
  37. name: "tui-picker",
  38. emits: ['pickstart', 'pickend', 'hide', 'change'],
  39. props: {
  40. //数据层级
  41. layer: {
  42. type: [Number, String],
  43. default: 1
  44. },
  45. //data数据
  46. pickerData: {
  47. type: Array,
  48. default () {
  49. return []
  50. }
  51. },
  52. //是否显示
  53. show: {
  54. type: Boolean,
  55. default: false
  56. },
  57. //默认值,text集合
  58. value: {
  59. type: Array,
  60. default () {
  61. return []
  62. }
  63. },
  64. //设置选择器中间选中框的样式
  65. indicatorStyle: {
  66. type: String,
  67. default: 'height: 48px;'
  68. },
  69. //设置蒙层的样式
  70. maskStyle: {
  71. type: String,
  72. default: ''
  73. },
  74. //是否显示圆角
  75. radius: {
  76. type: Boolean,
  77. default: false
  78. },
  79. //header背景色
  80. headerBgColor: {
  81. type: String,
  82. default: '#fff'
  83. },
  84. //显示标题
  85. title: {
  86. type: String,
  87. default: ''
  88. },
  89. //标题字体大小
  90. titleSize: {
  91. type: [Number, String],
  92. default: 34
  93. },
  94. //标题字体颜色
  95. titleColor: {
  96. type: String,
  97. default: '#333'
  98. },
  99. //确认按钮文本
  100. confirmText: {
  101. type: String,
  102. default: '确定'
  103. },
  104. //确认按钮文本颜色
  105. confirmColor: {
  106. type: String,
  107. default: '#5677fc'
  108. },
  109. //取消按钮文本
  110. cancelText: {
  111. type: String,
  112. default: '取消'
  113. },
  114. //取消按钮文本颜色
  115. cancelColor: {
  116. type: String,
  117. default: '#888'
  118. },
  119. //按钮字体大小
  120. btnSize: {
  121. type: [Number, String],
  122. default: 32
  123. },
  124. //按钮字体是否加粗
  125. bold: {
  126. type: Boolean,
  127. default: true
  128. },
  129. //内容背景色
  130. backgroundColor: {
  131. type: String,
  132. default: '#fff'
  133. },
  134. //内容字体颜色
  135. color: {
  136. type: String,
  137. default: '#333'
  138. },
  139. //picker内容字体大小 px
  140. size: {
  141. type: [Number, String],
  142. default: 16
  143. },
  144. //点击遮罩 是否可关闭
  145. maskClosable: {
  146. type: Boolean,
  147. default: true
  148. },
  149. //自定义参数
  150. params: {
  151. type: [Number, String],
  152. default: 0
  153. }
  154. },
  155. data() {
  156. return {
  157. visible: false,
  158. vals: [0],
  159. layer1__data: [],
  160. layer2__data: [],
  161. layer3__data: [],
  162. isEnd: true,
  163. timer: null
  164. };
  165. },
  166. created() {
  167. this.initData(-1, 0, 0);
  168. setTimeout(() => {
  169. this.setDefaultOptions()
  170. }, 50)
  171. this.visible = this.show;
  172. },
  173. watch: {
  174. show(val) {
  175. this.visible = val;
  176. },
  177. value(vals) {
  178. if (vals && vals.length > 0) {
  179. this.setDefaultOptions()
  180. }
  181. },
  182. pickerData(newVal) {
  183. this.initData(-1, 0, 0)
  184. setTimeout(() => {
  185. this.setDefaultOptions()
  186. }, 50)
  187. }
  188. },
  189. methods: {
  190. hidePicker() {
  191. this.visible = false;
  192. this.$emit('hide', {
  193. params: this.params
  194. })
  195. },
  196. maskClick() {
  197. if (!this.maskClosable) return;
  198. this.hidePicker()
  199. },
  200. getValue(key = 'text', layer = 1) {
  201. let vals = this.vals;
  202. let data = this.pickerData;
  203. let result = ''
  204. if (layer === 1) {
  205. result = data[vals[0]][key]
  206. } else if (layer == 2) {
  207. if (data[vals[0]].children) {
  208. result = data[vals[0]].children[vals[1]][key]
  209. }
  210. } else {
  211. if (data[vals[0]].children && data[vals[0]].children[vals[1]].children) {
  212. result = data[vals[0]].children[vals[1]].children[vals[2]][key]
  213. }
  214. }
  215. return result;
  216. },
  217. loop() {
  218. if (this.isEnd) {
  219. this.pickerChange()
  220. } else {
  221. setTimeout(() => {
  222. this.loop()
  223. }, 50)
  224. }
  225. },
  226. picker() {
  227. this.hidePicker()
  228. // #ifdef MP-WEIXIN
  229. this.loop()
  230. // #endif
  231. // #ifndef MP-WEIXIN
  232. this.pickerChange()
  233. // #endif
  234. },
  235. pickerChange() {
  236. if(!this.show) return;
  237. let text = [];
  238. let value = [];
  239. let result = '';
  240. if (this.pickerData.length > 0) {
  241. if (this.layer == 1) {
  242. text = this.getValue();
  243. value = this.getValue('value');
  244. result = text;
  245. } else if (this.layer == 2) {
  246. text = [this.getValue(), this.getValue('text', 2)];
  247. value = [this.getValue('value'), this.getValue('value', 2)];
  248. result = text.join('');
  249. } else {
  250. text = [this.getValue(), this.getValue('text', 2), this.getValue('text', 3)];
  251. value = [this.getValue('value'), this.getValue('value', 2), this.getValue('value', 3)];
  252. result = text.join('');
  253. }
  254. }
  255. this.$emit('change', {
  256. text: text,
  257. value: value,
  258. index: this.vals,
  259. result: result,
  260. params: this.params
  261. })
  262. },
  263. toArr(data) {
  264. let arr = [];
  265. if (data && data.length > 0) {
  266. for (let item of data) {
  267. arr.push(item.text);
  268. }
  269. }
  270. return arr;
  271. },
  272. checkChildrenData(data, layer, index, idx) {
  273. let arr = [];
  274. if (layer === 1) {
  275. if (data[index])
  276. arr = data[index].children || [];
  277. } else {
  278. if (data[index] && data[index].children && data[index].children[idx])
  279. arr = data[index].children[idx].children || [];
  280. }
  281. return arr;
  282. },
  283. setDefaultOptions() {
  284. let textArr = this.value;
  285. let vals = [];
  286. if (this.layer1__data.length > 0 && textArr.length > 0) {
  287. textArr.forEach((item, idx) => {
  288. let index = this[`layer${idx+1}__data`].indexOf(item);
  289. if (idx === 0) {
  290. this.layer2__data = this.toArr(this.checkChildrenData(this.pickerData, 1, index))
  291. } else if (idx === 1) {
  292. this.layer3__data = this.toArr(this.checkChildrenData(this.pickerData, 2, vals[0],
  293. index))
  294. }
  295. if (index === -1) {
  296. vals.push(0)
  297. } else {
  298. vals.push(index)
  299. }
  300. })
  301. } else {
  302. if (this.layer == 1) {
  303. vals = [0]
  304. } else if (this.layer == 2) {
  305. vals = [0, 0];
  306. this.layer2__data = this.toArr(this.checkChildrenData(this.pickerData, 1, 0))
  307. } else {
  308. vals = [0, 0, 0];
  309. this.layer2__data = this.toArr(this.checkChildrenData(this.pickerData, 1, 0))
  310. this.layer3__data = this.toArr(this.checkChildrenData(this.pickerData, 2, 0,
  311. 0))
  312. }
  313. }
  314. this.vals = vals;
  315. },
  316. initData(layer, index, idx) {
  317. let data = this.pickerData;
  318. if (!data || data.length === 0) return;
  319. if (this.layer == 1) {
  320. this.layer1__data = this.toArr(data)
  321. } else if (this.layer == 2) {
  322. if (layer === -1)
  323. this.layer1__data = this.toArr(data)
  324. this.layer2__data = this.toArr(this.checkChildrenData(data, 1, index))
  325. } else {
  326. if (layer === -1)
  327. this.layer1__data = this.toArr(data)
  328. if (layer === 0 || layer === -1)
  329. this.layer2__data = this.toArr(this.checkChildrenData(data, 1, index))
  330. this.layer3__data = this.toArr(this.checkChildrenData(data, 2, index, idx))
  331. }
  332. },
  333. columnPicker: function(e) {
  334. let value = e.detail.value;
  335. if (this.layer == 1) {
  336. this.layer__one(value)
  337. } else if (this.layer == 2) {
  338. this.layer__two(value)
  339. } else {
  340. this.layer__three(value)
  341. }
  342. },
  343. layer__one(value) {
  344. if (this.vals[0] !== value[0]) {
  345. this.vals = value;
  346. }
  347. },
  348. layer__two(value) {
  349. if (this.vals[0] !== value[0]) {
  350. this.initData(0, value[0])
  351. this.vals = [value[0], 0]
  352. } else {
  353. this.vals = value
  354. }
  355. },
  356. layer__three(value) {
  357. if (this.vals[0] !== value[0]) {
  358. this.initData(0, value[0], 0)
  359. this.vals = [value[0], 0, 0]
  360. } else if (this.vals[1] !== value[1]) {
  361. this.initData(0, value[0], value[1])
  362. this.vals = [value[0], value[1], 0]
  363. } else {
  364. this.vals = value
  365. }
  366. },
  367. pickstart(e) {
  368. // #ifdef MP-WEIXIN
  369. clearTimeout(this.timer)
  370. this.isEnd = false;
  371. // #endif
  372. //仅微信小程序支持
  373. this.$emit('pickstart', {
  374. params: this.params
  375. })
  376. },
  377. pickend(e) {
  378. //仅微信小程序支持
  379. // #ifdef MP-WEIXIN
  380. this.timer = setTimeout(() => {
  381. this.isEnd = true;
  382. }, 100)
  383. // #endif
  384. this.$emit('pickend', {
  385. params: this.params
  386. })
  387. }
  388. }
  389. }
  390. </script>
  391. <style scoped>
  392. .tui-mask__screen {
  393. position: fixed;
  394. top: 0;
  395. left: 0;
  396. right: 0;
  397. bottom: 0;
  398. background: rgba(0, 0, 0, 0.6);
  399. z-index: 1001;
  400. transition: all 0.3s ease-in-out;
  401. opacity: 0;
  402. visibility: hidden;
  403. }
  404. .tui-picker__mask-show {
  405. opacity: 1;
  406. visibility: visible;
  407. }
  408. .tui-picker__wrap {
  409. width: 100%;
  410. position: fixed;
  411. left: 0;
  412. right: 0;
  413. bottom: 0;
  414. z-index: 1002;
  415. visibility: hidden;
  416. transform: translate3d(0, 100%, 0);
  417. transform-origin: center;
  418. transition: all 0.3s ease-in-out;
  419. min-height: 20rpx;
  420. }
  421. .tui-picker__radius {
  422. border-top-left-radius: 24rpx;
  423. border-top-right-radius: 24rpx;
  424. overflow: hidden;
  425. }
  426. .tui-picker__show {
  427. transform: translate3d(0, 0, 0);
  428. visibility: visible;
  429. }
  430. .tui-picker__header {
  431. width: 100%;
  432. height: 92rpx;
  433. padding: 0 30rpx;
  434. display: flex;
  435. justify-content: space-between;
  436. align-items: center;
  437. box-sizing: border-box;
  438. position: relative;
  439. }
  440. .tui-picker__header::after {
  441. content: '';
  442. position: absolute;
  443. border-bottom: 1rpx solid rgba(0, 0, 0, .1);
  444. -webkit-transform: scaleY(0.5);
  445. transform: scaleY(0.5);
  446. bottom: 0;
  447. right: 0;
  448. left: 0;
  449. }
  450. .tui-picker__btn-cancle {
  451. padding: 20rpx;
  452. flex-shrink: 0;
  453. /* #ifdef H5 */
  454. cursor: pointer;
  455. /* #endif */
  456. }
  457. .tui-picker__btn-sure {
  458. padding: 20rpx;
  459. flex-shrink: 0;
  460. /* #ifdef H5 */
  461. cursor: pointer;
  462. /* #endif */
  463. }
  464. .tui-picker__title {
  465. white-space: nowrap;
  466. overflow: hidden;
  467. text-overflow: ellipsis;
  468. flex: 1;
  469. padding: 0 30rpx;
  470. box-sizing: border-box;
  471. text-align: center;
  472. }
  473. .tui-picker__view {
  474. width: 100%;
  475. height: 260px;
  476. }
  477. .tui-picker__item {
  478. line-height: 48px;
  479. text-align: center;
  480. }
  481. .tui-picker__opcity {
  482. opacity: 0.5;
  483. }
  484. </style>