selectMorePicker.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <template name="aui-picker">
  2. <view class="aui-picker" v-if="SHOW" :class="{
  3. 'aui-picker-in': FADE==1,
  4. 'aui-picker-out': FADE==0}"
  5. >
  6. <view class="aui-picker-main">
  7. <view class="aui-mask" @click.stop="close"></view>
  8. <view class="aui-picker-header">
  9. <view @click.stop="close" class="aui-picker-close" >取消</view>
  10. <view class="aui-picker-title" v-if="title">{{title}}</view>
  11. <!-- <view class="aui-picker-close iconfont iconclose" ></view> -->
  12. <!-- <view class="aui-picker-sure" @click.stop="getSure" v-if="titflag">确定</view> -->
  13. <view class="aui-picker-sure" @click.stop="getSure" >确定</view>
  14. </view>
  15. <view class="aui-picker-nav">
  16. <view class="aui-picker-navitem"
  17. v-if="nav.length>0"
  18. v-for="(item, index) in nav"
  19. :key="index"
  20. :data-index="index"
  21. :class="[index==navCurrentIndex ? 'active' : '', 'aui-picker-navitem-'+index]"
  22. @click.stop="_changeNav($event)"
  23. >{{item.name}}</view>
  24. <!-- :style="{margin: nav.length>2 ? '0 10px 0 0' : '0 30px 0 0'}" -->
  25. <view class="aui-picker-navitem"
  26. :key="nav.length"
  27. :data-index="nav.length"
  28. :class="[nav.length==navCurrentIndex ? 'active' : '', 'aui-picker-navitem-'+nav.length]"
  29. @click.stop="_changeNav($event)"
  30. >请选择</view>
  31. <!-- :style="{margin: nav.length>2 ? '0 10px 0 0' : '0 30px 0 0'}" -->
  32. <view class="aui-picker-navborder" :style="{left: navBorderLeft+'px'}"></view>
  33. </view>
  34. <view class="aui-picker-content">
  35. <view class="aui-picker-lists">
  36. <view class="aui-picker-list"
  37. v-for="(list, index) in queryItems.length + 1"
  38. :key="index"
  39. :data-index="index"
  40. :class="[index==navCurrentIndex ? 'active' : '']"
  41. >
  42. <view class="aui-picker-list-warp" v-if="index == 0">
  43. <view class="aui-picker-item"
  44. v-for="(item, key) in items"
  45. v-if="item.pid=='0'"
  46. :key="key"
  47. :data-pindex="index"
  48. :data-index="key"
  49. :data-areaid="item.areaId"
  50. :data-pid="item.pid"
  51. :data-name="item.name"
  52. :class="{'active': result.length>index && result[index].areaid==item.areaId}"
  53. :style="{'background': touchConfig.index==key && touchConfig.pindex==index ? touchConfig.style.background : ''}"
  54. @click.stop="_chooseItem($event)"
  55. @touchstart="_btnTouchStart($event)"
  56. @touchmove="_btnTouchEnd($event)"
  57. @touchend="_btnTouchEnd($event)"
  58. >{{item.name}}</view>
  59. </view>
  60. <view class="aui-picker-list-warp" v-else>
  61. <view class="aui-picker-item"
  62. v-for="(item, key) in queryItems[index-1]"
  63. :key="key"
  64. :data-pindex="index"
  65. :data-index="key"
  66. :data-areaid="item.areaId"
  67. :data-pid="item.pid"
  68. :data-name="item.name"
  69. :class="{'active': result.length>index && result[index].areaid==item.areaId}"
  70. :style="{'background': touchConfig.index==key && touchConfig.pindex==index ? touchConfig.style.background : ''}"
  71. @click.stop="_chooseItem($event)"
  72. @touchstart="_btnTouchStart($event)"
  73. @touchmove="_btnTouchEnd($event)"
  74. @touchend="_btnTouchEnd($event)"
  75. >{{item.name}}</view>
  76. </view>
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </template>
  83. <script>
  84. export default {
  85. name: 'aui-picker',
  86. props: {
  87. title: { //标题
  88. type: String,
  89. default: ''
  90. },
  91. titflag:{
  92. type: Boolean,
  93. default: false,
  94. },
  95. layer: { //控制几级联动,默认无限级(跟随数据有无下级)
  96. type: Number,
  97. default: null
  98. },
  99. data: { //数据 如:[{id: '', name: '', children: [{id: '', name: ''}]}]
  100. type: Array,
  101. default (){
  102. return [
  103. // [{id: '', name: '', children: [{id: '', name: ''}]}]
  104. ]
  105. }
  106. }
  107. },
  108. data(){
  109. return {
  110. SHOW: false,
  111. FADE: -1,
  112. nav: [],
  113. items: [],
  114. queryItems: [],
  115. navCurrentIndex: 0,
  116. navBorderLeft: 25,
  117. result: [],
  118. touchConfig: {
  119. index: -1,
  120. pindex: -1,
  121. style: {
  122. color: '#197DE0',
  123. background: '#EFEFEF'
  124. }
  125. },
  126. loadflag:false
  127. }
  128. },
  129. created(){
  130. const _this = this;
  131. },
  132. watch:{
  133. data(){
  134. const _this = this;
  135. const data = _this.data;
  136. _this.items = _this._flatten(data, '0')
  137.     }  
  138.   },
  139. mounted(){
  140. },
  141. methods:{
  142. // 打开
  143. open(e){
  144. const _this = this;
  145. if(e==1){
  146. _this.reset(); //打开时重置picker
  147. }
  148. return new Promise(function(resolve, reject){
  149. _this.SHOW = true;
  150. _this.FADE = 1;
  151. resolve();
  152. });
  153. },
  154. // 关闭
  155. close(){
  156. const _this = this;
  157. return new Promise(function(resolve, reject){
  158. _this.FADE = 0;
  159. const _hidetimer = setTimeout(()=>{
  160. _this.SHOW = false;
  161. _this.FADE = -1;
  162. clearTimeout(_hidetimer);
  163. resolve();
  164. },100)
  165. });
  166. },
  167. //重置
  168. reset(){
  169. const _this = this;
  170. _this.queryItems = [];
  171. _this.nav = [];
  172. _this.navBorderLeft = 25;
  173. _this.navCurrentIndex = 0;
  174. _this.result = [];
  175. },
  176. //导航栏切换
  177. _changeNav(e){
  178. const _this = this;
  179. const index = Number(e.currentTarget.dataset.index);
  180. _this.navCurrentIndex = index;
  181. const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-"+index);
  182. _el.boundingClientRect(data => {
  183. _this.navBorderLeft = data.left + 15;
  184. }).exec();
  185. },
  186. //数据选择
  187. _chooseItem(e){
  188. // 加载的时候禁止点击
  189. if(this.loadflag){
  190. return
  191. }
  192. this.loadflag=true;
  193. const _this = this;
  194. const areaid = e.currentTarget.dataset.areaid;
  195. const name = e.currentTarget.dataset.name;
  196. const pid = e.currentTarget.dataset.pid;
  197. const _arr = [];
  198. // 获取新数据
  199. _this.result[_this.navCurrentIndex] = {areaid: areaid, name: name, pid: pid};
  200. _this.$http.post('boman-system/chinaArea/treeSelect',{pid: areaid}).then(res=>{
  201. this.loadflag=false;
  202. if(res.data.length>0){
  203. if(_this.navCurrentIndex == _this.queryItems.length)
  204. { //选择数据
  205. _this.queryItems.push(res.data);
  206. _this.nav.push({name: name});
  207. }
  208. else
  209. { //重新选择数据
  210. _this.queryItems.splice(_this.navCurrentIndex+1);
  211. _this.nav.splice(_this.navCurrentIndex+1);
  212. _this.queryItems.splice(_this.navCurrentIndex, 1, res.data);
  213. _this.nav.splice(_this.navCurrentIndex, 1, {name: name});
  214. //清空后面的选择
  215. _this.result.splice(Number(_this.navCurrentIndex+1))
  216. }
  217. _this.navCurrentIndex = _this.navCurrentIndex + 1;
  218. const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-"+_this.navCurrentIndex);
  219. setTimeout(()=>{
  220. _el.boundingClientRect(data => {
  221. _this.navBorderLeft = data.left + 15;
  222. }).exec();
  223. },100)
  224. }else{
  225. //无下级数据
  226. _this.close().then(()=>{
  227. _this.$emit("callback", {status: 0, data: _this.result});
  228. });
  229. }
  230. })
  231. // _this.result[_this.navCurrentIndex] = {areaId: areaId, name: name, pid: pid};
  232. // if(
  233. // (!_this._isDefine(_this.layer) && _this._isDefine(_this._deepQuery(_this.data, areaId).children))
  234. // ||
  235. // (_this.navCurrentIndex < (Number(_this.layer) - 1) && _this._isDefine(_this._deepQuery(_this.data, areaId).children))
  236. // )
  237. // { //有下级数据
  238. // _this._deepQuery(_this.data, areaId).children.forEach(function(item, index){
  239. // _arr.push({areaId: item.areaId, name: item.name, pid: id});
  240. // });
  241. // if(_this.navCurrentIndex == _this.queryItems.length)
  242. // { //选择数据
  243. // _this.queryItems.push(_arr);
  244. // _this.nav.push({name: name});
  245. // }
  246. // else
  247. // { //重新选择数据
  248. // _this.queryItems.splice(_this.navCurrentIndex+1, 1);
  249. // _this.nav.splice(_this.navCurrentIndex+1, 1);
  250. // _this.queryItems.splice(_this.navCurrentIndex, 1, _arr);
  251. // _this.nav.splice(_this.navCurrentIndex, 1, {name: name});
  252. // }
  253. // _this.navCurrentIndex = _this.navCurrentIndex + 1;
  254. // const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-"+_this.navCurrentIndex);
  255. // setTimeout(()=>{
  256. // _el.boundingClientRect(data => {
  257. // _this.navBorderLeft = data.left + 20;
  258. // }).exec();
  259. // },100)
  260. // }
  261. // else
  262. // { //无下级数据
  263. // _this.close().then(()=>{
  264. // _this.$emit("callback", {status: 0, data: _this.result});
  265. // });
  266. // }
  267. },
  268. getSure(){
  269. this.close().then(()=>{
  270. this.$emit("callback", {status: 0, data: this.result});
  271. });
  272. },
  273. //递归遍历——将树形结构数据转化为数组格式
  274. _flatten(tree, pid) {
  275. return tree.reduce((arr, {areaId, name, children = []}) =>
  276. arr.concat([{areaId, name, pid}], this._flatten(children, areaId)), [])
  277. },
  278. //根据id查询对应的数据(如查询id=10100对应的对象)
  279. _deepQuery(tree, areaId) {
  280. let isGet = false;
  281. let retNode = null;
  282. function deepSearch(tree, areaId){
  283. for(let i = 0; i < tree.length; i++) {
  284. if(tree[i].children && tree[i].children.length > 0) {
  285. deepSearch(tree[i].children, areaId);
  286. }
  287. if(areaId === tree[i].areaId || isGet) {
  288. isGet||(retNode = tree[i]);
  289. isGet = true;
  290. break;
  291. }
  292. }
  293. }
  294. deepSearch(tree, areaId);
  295. return retNode;
  296. },
  297. /***判断字符串是否为空
  298. @param {string} str 变量
  299. @example: aui.isDefine("变量");
  300. */
  301. _isDefine(str){
  302. if (str==null || str=="" || str=="undefined" || str==undefined || str=="null" || str=="(null)" || str=='NULL' || typeof (str)=='undefined'){
  303. return false;
  304. }else{
  305. str = str + "";
  306. str = str.replace(/\s/g, "");
  307. if (str == ""){return false;}
  308. return true;
  309. }
  310. },
  311. _btnTouchStart(e){
  312. const _this = this,
  313. index = Number(e.currentTarget.dataset.index),
  314. pindex = Number(e.currentTarget.dataset.pindex);
  315. _this.touchConfig.index = index;
  316. _this.touchConfig.pindex = pindex;
  317. },
  318. _btnTouchEnd(e){
  319. const _this = this,
  320. index = Number(e.currentTarget.dataset.index),
  321. pindex = Number(e.currentTarget.dataset.pindex);
  322. _this.touchConfig.index = -1;
  323. _this.touchConfig.pindex = -1;
  324. },
  325. }
  326. }
  327. </script>
  328. <style scoped>
  329. /* ====================
  330. 多级联动弹窗
  331. =====================*/
  332. .aui-picker{
  333. width: 100vw;
  334. height: 100vh;
  335. /* opacity: 0; */
  336. position: fixed;
  337. top: 0;
  338. left: 0;
  339. z-index: 1200;
  340. background: rgba(0,0,0,0.5);
  341. /* display: none; */
  342. }
  343. .aui-picker.aui-picker-in{
  344. -moz-animation: aui-fade-in .1s ease-out forwards;
  345. -ms-animation: aui-fade-in .1s ease-out forwards;
  346. -webkit-animation: aui-fade-in .1s ease-out forwards;
  347. animation: aui-fade-in .1s ease-out forwards;
  348. }
  349. .aui-picker.aui-picker-out{
  350. -moz-animation: aui-fade-out .1s ease-out forwards;
  351. -ms-animation: aui-fade-out .1s ease-out forwards;
  352. -webkit-animation: aui-fade-out .1s ease-out forwards;
  353. animation: aui-fade-out .1s ease-out forwards;
  354. }
  355. .aui-picker-main{
  356. width: 100vw;
  357. height: 50vh;
  358. background: #FFF;
  359. border-radius: 15px 15px 0 0;
  360. position: absolute;
  361. left: 0px;
  362. /* bottom: -50vh; */
  363. bottom: 0vh;
  364. z-index: 999;
  365. }
  366. .aui-picker.aui-picker-in .aui-picker-main{
  367. -moz-animation: aui-slide-up-screen .2s ease-out forwards;
  368. -ms-animation: aui-slide-up-screen .2s ease-out forwards;
  369. -webkit-animation: aui-slide-up-screen .2s ease-out forwards;
  370. animation: aui-slide-up-screen .2s ease-out forwards;
  371. }
  372. .aui-picker.aui-picker-out .aui-picker-main{
  373. -moz-animation: aui-slide-down-screen .2s ease-out forwards;
  374. -ms-animation: aui-slide-down-screen .2s ease-out forwards;
  375. -webkit-animation: aui-slide-down-screen .2s ease-out forwards;
  376. animation: aui-slide-down-screen .2s ease-out forwards;
  377. }
  378. .aui-picker-header{
  379. width: 100%;
  380. height: 50px;
  381. position: relative;
  382. z-index: 999;
  383. background: #F2F2F2;
  384. border-radius: 15px 15px 0 0;
  385. display: flex;
  386. align-items: center;
  387. }
  388. .aui-picker-header::after{
  389. content: '';
  390. width: 100%;
  391. height: 1px;
  392. background: rgba(100,100,100,.3);
  393. -moz-transform: scaleY(.3);
  394. -ms-transform: scaleY(.3);
  395. -webkit-transform: scaleY(.3);
  396. transform: scaleY(.3);
  397. position: absolute;
  398. left: 0;
  399. bottom: 0;
  400. z-index: 999;
  401. }
  402. .aui-picker-title{
  403. line-height: 20px;
  404. text-align: center;
  405. font-size: 17px;
  406. color: #333;
  407. padding: 15px;
  408. box-sizing: border-box;
  409. flex: 1;
  410. text-align: center;
  411. /* position: absolute;
  412. left: 50px;
  413. right: 50px;
  414. top: 0; */
  415. }
  416. .aui-picker-close{
  417. font-size: 15px;
  418. color: #333333;
  419. flex: 0 0 auto;
  420. height: 50px;
  421. width: 60px;
  422. text-align: center;
  423. line-height: 50px;
  424. }
  425. .aui-picker-sure{
  426. flex: 0 0 auto;
  427. font-size: 15px;
  428. color: #197DE0;
  429. height: 50px;
  430. width: 60px;
  431. text-align: center;
  432. line-height: 50px;
  433. }
  434. /* .aui-picker-close.iconfont{
  435. width: 50px;
  436. height: 50px;
  437. line-height: 50px;
  438. text-align: center;
  439. font-size: 20px;
  440. color: #aaa;
  441. border-radius: 0 10px 0 0;
  442. position: absolute;
  443. right: 0;
  444. top: 0;
  445. } */
  446. .aui-picker-content{
  447. width: 100%;
  448. height: -webkit-calc(100% - 100px);
  449. height: calc(100% - 100px);
  450. }
  451. .aui-picker-nav{
  452. width: 100%;
  453. height: 50px;
  454. text-align: left;
  455. padding: 0 20rpx;
  456. margin: 0 0 1px 0;
  457. justify-content: flex-start;
  458. white-space: nowrap;
  459. box-sizing: border-box;
  460. position: relative;
  461. display: flex;
  462. align-items: center;
  463. }
  464. .aui-picker-nav::after{
  465. content: '';
  466. width: 100%;
  467. height: 1px;
  468. background: rgba(100,100,100,.3);
  469. -moz-transform: scaleY(.3);
  470. -ms-transform: scaleY(.3);
  471. -webkit-transform: scaleY(.3);
  472. transform: scaleY(.3);
  473. position: absolute;
  474. left: 0;
  475. bottom: 0;
  476. z-index: 999;
  477. }
  478. .aui-picker-navitem{
  479. flex: 1;
  480. max-width:70px ;
  481. /* width: 80px; */
  482. line-height: 50px;
  483. font-size: 16px;
  484. text-align: center;
  485. display: inline-block;
  486. overflow: hidden;
  487. white-space: nowrap;
  488. text-overflow: ellipsis;
  489. }
  490. .aui-picker-navitem.active{
  491. color: #197DE0;
  492. }
  493. .aui-picker-navborder{
  494. width: 40px;
  495. height: 3px;
  496. background: #197DE0;
  497. border-radius: 5px;
  498. transition: left .15s;
  499. position: absolute;
  500. left: 25px;
  501. bottom: 0;
  502. }
  503. .aui-picker-lists{
  504. width: 100%;
  505. height: 100%;
  506. justify-content: space-around;
  507. white-space: nowrap;
  508. }
  509. .aui-picker-list{
  510. width: 100%;
  511. height: 100%;
  512. overflow: hidden;
  513. overflow-y: scroll;
  514. display: none;
  515. vertical-align: top;
  516. }
  517. .aui-picker-list.active{
  518. display: inline-block;
  519. }
  520. .aui-picker-list-warp{
  521. width: 100%;
  522. height: auto;
  523. box-sizing: border-box;
  524. padding: 15px 0;
  525. display: inline-block;
  526. }
  527. .aui-picker-item{
  528. width: 100%;
  529. height: 50px;
  530. line-height: 50px;
  531. padding: 0 15px;
  532. box-sizing: border-box;
  533. font-size: 15px;
  534. color: #333;
  535. position: relative;
  536. }
  537. .aui-picker-item.active{
  538. color: #197DE0;
  539. }
  540. .aui-picker-item.active::after{
  541. content: '✔';
  542. font-size: 15px;
  543. color: #197DE0;
  544. position: absolute;
  545. top: 0px;
  546. right: 10px;
  547. }
  548. </style>