selectGridPicker.vue 15 KB

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