tm-quickCity.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. <template>
  2. <view class="tm-quickCity fulled">
  3. <view @click="qiehuan"><slot ></slot></view>
  4. <view @click.stop="qiehuan" v-if="showbgOpen" class="tm-quickCity-fullbg fulled fixed b-0 l-0" :style="{height:sysHeight+'px'}">
  5. <view :style="{height:(height+250+content_jian_height)+'rpx'}" @click.stop="" :class="[black_tmeme?'grey-darken-5 bk':'white']" class=" tm-quickCity-wk absolute fulled round-t-10" >
  6. <view class="tm-quickCity-title px-32 pt-32 shadow-4 relative">
  7. <view class="text-size-g text-weight-b mb-32 relative">
  8. <view>
  9. 请选择城市位置
  10. </view>
  11. <view @click.stop="qiehuan" class="tm-calendar-close rounded flex-center" :class="black_tmeme?'grey-darken-3':'grey-lighten-3'">
  12. <tm-icons dense name="icon-times" size="24" :color="black_tmeme?'white':'grey'"></tm-icons>
  13. </view>
  14. </view>
  15. <view class="flex-start pb-24">
  16. <view :class="[pageIndex==0?`outlined ${color_tmeme} text-weight-b`:'outlined grey',black_tmeme?'bk':'']" @click="indexChange(0)" class=" px-12 py-6 round-10 flex-center text-size-s mr-24 flex-shrink">
  17. <text>{{childrenIndx[0]!==null?childrenIndx[0].title:'请选择省'}}</text>
  18. </view>
  19. <view v-if="childrenIndx[0]!==null" class="flex-center pr-24">
  20. <tm-icons :color="black_tmeme?'white':'grey'" :size="24" dense name="icon-angle-right"></tm-icons>
  21. </view>
  22. <view style="max-width: 120rpx;" :class="[pageIndex==1?`outlined ${color_tmeme} text-weight-b`:'outlined grey',black_tmeme?'bk':'']" @click="indexChange(1)" v-if="childrenIndx[0]!==null" class="px-12 py-6 round-10 flex-center text-size-s mr-24 pb-10 ">
  23. <text class="text-overflow">{{childrenIndx[1]!==null?childrenIndx[1].title:'请选择市'}}</text>
  24. </view>
  25. <view v-if="childrenIndx[1]!==null" class="flex-center pr-24">
  26. <tm-icons :color="black_tmeme?'white':'grey'" :size="24" dense name="icon-angle-right"></tm-icons>
  27. </view>
  28. <view style="max-width: 220rpx;" :class="[pageIndex==2?`outlined ${color_tmeme} text-weight-b`:'outlined grey',black_tmeme?'bk':'']" @click="indexChange(2)" v-if="childrenIndx[1]!==null" class="px-12 py-6 round-10 flex-center text-size-s mr-24 pb-10 ">
  29. <text class="text-overflow">{{childrenIndx[2]!==null?childrenIndx[2].title:'请选择县/区'}}</text>
  30. </view>
  31. </view>
  32. </view>
  33. <view v-if="hotCity_chuli.length>0" class="tm-quickCity-jg py-24">
  34. <view :class="[black_tmeme?'grey-darken-5':'grey-lighten-4 text']" class="text-size-s text-weight-b py-12 px-32">热门城市</view>
  35. <view class="px-20 pt-12">
  36. <tm-tags model="text" rounded :black="black_tmeme" @click="selectedDefaultHot(index)" v-for="(item,index) in hotCity_chuli" :key="index" color="white" :fllowTheme="fllowTheme">
  37. {{item[1]?item[1]:item[0]}}
  38. </tm-tags>
  39. </view>
  40. </view>
  41. <view v-show="pageIndex==0">
  42. <tm-quickIndex :black="black_tmeme" :fllowTheme="false" :color="color_tmeme" key="ref_k_1" v-model="active" :height="content_height" :list="list">
  43. <template v-slot:cell="{data}">
  44. <view @click="cityClick_pr(data,0)" :class="[data.black?'bk':'',data.children!==data.total-1?'border-grey-lighten-4-b-1':'']" class="py-24 mx-32 flex-between">
  45. <view class="text-size-n flex-start" >
  46. <view v-if="data.item['checked']" class="mr-24">
  47. <tm-icons :color="data.color" dense name="icon-check"></tm-icons>
  48. </view>
  49. <text :class="[data.item['checked']?'text-'+data.color+' text-weight-b':'']" class="text-size-n">
  50. {{data.title}}
  51. </text>
  52. </view>
  53. </view>
  54. </template>
  55. </tm-quickIndex>
  56. </view>
  57. <view v-if="pageIndex==1">
  58. <tm-quickIndex :black="black_tmeme" :fllowTheme="false" :color="color_tmeme" key="ref_k_2" v-model="active_2" v-if="childrenIndx[0]" :height="content_height" :list="childrenIndx[0].children">
  59. <template #cell="{data}">
  60. <view @click="cityClick_pr(data,1)" :class="[data.black?'bk':'',data.children!==data.total-1?'border-grey-lighten-4-b-1':'']" class="py-24 mx-32 border-grey-lighten-4-b-1 flex-between">
  61. <view class="text-size-n flex-start" >
  62. <view v-if="data.item['checked']" class="mr-24">
  63. <tm-icons :color="data.color" dense name="icon-check"></tm-icons>
  64. </view>
  65. <text :class="[data.item['checked']?'text-'+data.color+' text-weight-b':'']" class="text-size-n">
  66. {{data.title}}
  67. </text>
  68. </view>
  69. </view>
  70. </template>
  71. </tm-quickIndex>
  72. </view>
  73. <view v-if="pageIndex==2">
  74. <tm-quickIndex :black="black_tmeme" :fllowTheme="false" :color="color_tmeme" key="ref_k_3" v-model="active_3" v-if="childrenIndx[1]" :height="content_height" :list="childrenIndx[1].children">
  75. <template #cell="{data}">
  76. <view @click="cityClick_pr(data,2)" :class="[data.black?'bk':'',data.children!==data.total-1?'border-grey-lighten-4-b-1':'']" class="py-24 mx-32 border-grey-lighten-4-b-1 flex-between">
  77. <view class="text-size-n flex-start" >
  78. <view v-if="data.item['checked']" class="mr-24">
  79. <tm-icons :color="data.color" dense name="icon-check"></tm-icons>
  80. </view>
  81. <text :class="[data.item['checked']?'text-'+data.color+' text-weight-b':'']" class="text-size-n">
  82. {{data.title}}
  83. </text>
  84. </view>
  85. </view>
  86. </template>
  87. </tm-quickIndex>
  88. </view>
  89. </view>
  90. </view>
  91. </view>
  92. </template>
  93. <script>
  94. /**
  95. * 城区索引选择
  96. * @property {Boolean} value = [true|false] 显示和隐藏组件,请使用v-model或者value.sync你也可以不提供些参数,使用默认插槽也可打开,请见示例。
  97. * @property {Array} default-value = [] 默认选择的地址:[],字符串数组,如["江西省","南昌市"]。使用.sync双向绑定。
  98. * @property {Array} hot-city = [] 默认选择的地址:[],格式同default-value
  99. * @property {String} color = [] 默认:primary,主题色
  100. * @property {Number} height = [] 默认:1000,组件高度,单位rpx
  101. * @property {Boolean} black = [true|false] 默认:false,是否深色模式
  102. * @property {Function} change 选中地址时触发,返回当前选中的地区数组。
  103. */
  104. import provinceData from '@/tm-vuetify/tool/util/province.js';
  105. import cityData from '@/tm-vuetify/tool/util/city.js';
  106. import areaData from '@/tm-vuetify/tool/util/area.js';
  107. import queryCnChart from "@/tm-vuetify/tool/function/findCnChart.js";
  108. import tmQuickIndex from "@/tm-vuetify/components/tm-quickIndex/tm-quickIndex.vue"
  109. import tmListitem from "@/tm-vuetify/components/tm-listitem/tm-listitem.vue"
  110. import tmGrouplist from "@/tm-vuetify/components/tm-grouplist/tm-grouplist.vue"
  111. import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
  112. import tmTags from "@/tm-vuetify/components/tm-tags/tm-tags.vue"
  113. export default {
  114. components:{tmQuickIndex,tmListitem,tmGrouplist,tmIcons,tmTags
  115. },
  116. name:"tm-quickCity",
  117. model:{
  118. event:'input',
  119. prop:'value'
  120. },
  121. props:{
  122. defaultValue:{
  123. type:Array,
  124. default:()=>{
  125. return [];
  126. }
  127. },
  128. hotCity:{
  129. type:Array,
  130. default:()=>[["江西省","南昌市"],["北京市"],["广东省","广州市"],["浙江省","杭州市"],["重庆市"],["湖南省","长沙市"],["湖北省","武汉市"],["四川省","成都市"]]
  131. },
  132. color:{
  133. type:String,
  134. default:"primary"
  135. },
  136. height:{
  137. type:String|Number,
  138. default:900
  139. },
  140. value:{
  141. type:Boolean,
  142. default:false
  143. },
  144. black:{
  145. type:Boolean,
  146. default:null
  147. },
  148. // 跟随主题色的改变而改变。
  149. fllowTheme:{
  150. type:Boolean|String,
  151. default:true
  152. }
  153. },
  154. data() {
  155. return {
  156. list:[],
  157. active:0,
  158. active_2:0,
  159. active_3:0,
  160. childrenIndx:[null,null,null],
  161. indexobj:{
  162. pr:null,
  163. ar:null,
  164. co:null
  165. },
  166. pageIndex:0,
  167. sysHeight:0,
  168. showbgOpen:false,
  169. content_height:0,
  170. content_jian_height:0,
  171. hotCity_chuli:[]
  172. };
  173. },
  174. watch:{
  175. value:function(val){
  176. this.show = val;
  177. }
  178. },
  179. destroyed() {
  180. this.list=[];
  181. this.childrenIndx=[];
  182. },
  183. computed:{
  184. black_tmeme: function() {
  185. if (this.black !== null) return this.black;
  186. return this.$tm.vx.state().tmVuetify.black;
  187. },
  188. color_tmeme:function(){
  189. if(this.$tm.vx.state().tmVuetify.color!==null&&this.$tm.vx.state().tmVuetify.color && this.fllowTheme){
  190. return this.$tm.vx.state().tmVuetify.color;
  191. }
  192. return this.color;
  193. },
  194. show:{
  195. get:function(){
  196. return this.showbgOpen;
  197. },
  198. set:function(val){
  199. this.showbgOpen = val;
  200. this.$emit('input',val);
  201. this.$emit('update:value',val);
  202. }
  203. }
  204. },
  205. async mounted() {
  206. this.sysHeight = uni.getSystemInfoSync().windowHeight;
  207. this.show = this.value;
  208. this.content_height = this.height;
  209. this.hotCity_chuli_fun();
  210. await this.chiliFormatCity_area()
  211. await this.setDefaultValue();
  212. await this.content_heightToh();
  213. },
  214. methods: {
  215. hotCity_chuli_fun(){
  216. let hot = uni.$tm.deepClone(this.hotCity);
  217. let hostar = [];
  218. hot.forEach(item=>{
  219. if(item.length==1) hostar.push([item[0],null,null])
  220. if(item.length==2) hostar.push([item[0],item[1],null])
  221. if(item.length==3) hostar.push([item[0],item[1],item[2]])
  222. })
  223. this.hotCity_chuli = [...hostar];
  224. },
  225. async qiehuan(){
  226. this.show = !this.showbgOpen
  227. await this.content_heightToh();
  228. },
  229. async content_heightToh(){
  230. // if(!this.showbgOpen) return;
  231. if(this.hotCity_chuli.length==0){
  232. return this.height;
  233. }
  234. this.$nextTick(async function(){
  235. let h = await this.$Querey(".tm-quickCity-jg",this).catch(e=>{});
  236. if(!h[0]) return;
  237. h = h[0].height;
  238. let blv = uni.upx2px(4) / 4;
  239. this.content_jian_height = (h+h*blv)
  240. this.content_height = parseInt(this.height - this.content_jian_height);
  241. })
  242. },
  243. async selectedDefaultHot(index){
  244. this.$emit('update:defaultValue',this.hotCity[index]);
  245. let chl = this.childrenIndx[0];
  246. if(chl){
  247. this.$set(this.list[chl.index].children[chl.childrenIndex],'checked',false)
  248. }
  249. this.childrenIndx=[null,null,null];
  250. this.$nextTick(async function(){
  251. await this.setDefaultValue()
  252. this.cityClick_pr(this.list[this.childrenIndx[0]],this.childrenIndx[0])
  253. })
  254. },
  255. async setDefaultValue(){
  256. let t = this;
  257. function chuili(ix){
  258. let pitem = null;
  259. let chilIndex=-1;
  260. let pdindex = -1;
  261. if(ix==0&&t.defaultValue[ix]){
  262. pdindex = t.list.findIndex((item)=>{
  263. let citem = item.children
  264. let index = citem.findIndex(ditem=>{
  265. if(ditem.title == t.defaultValue[ix]){
  266. pitem = ditem;
  267. }
  268. return ditem.title == t.defaultValue[ix]
  269. })
  270. if(index>-1){
  271. chilIndex = index;
  272. }
  273. return index>-1;
  274. })
  275. }else if(t.defaultValue[ix]){
  276. let dsst = t.childrenIndx[ix-1].children
  277. pdindex = dsst.findIndex((item)=>{
  278. let citem = item.children
  279. let index = citem.findIndex(ditem=>{
  280. if(ditem.title == t.defaultValue[ix]){
  281. pitem = ditem;
  282. }
  283. return ditem.title == t.defaultValue[ix]
  284. })
  285. if(index>-1){
  286. chilIndex = index;
  287. }
  288. return index>-1;
  289. })
  290. }
  291. return {prevent:pdindex,chileIndex:chilIndex,item:pitem};
  292. }
  293. let d = chuili(0)
  294. if(d.prevent>-1){
  295. let chl = this.childrenIndx[0];
  296. if(chl){
  297. this.$set(this.list[chl.index].children[chl.childrenIndex],'checked',false)
  298. }
  299. t.childrenIndx.splice(0,1,{
  300. index:d.prevent,
  301. childrenIndex:d.chileIndex,
  302. title:d.item.title,
  303. children:t.chuliKey(d.item.children)
  304. })
  305. t.active = d.prevent;
  306. this.$set(this.list[d.prevent].children[d.chileIndex],'checked',true)
  307. }
  308. if(t.defaultValue[1]){
  309. let d = chuili(1)
  310. let chl = this.childrenIndx[1];
  311. if(chl){
  312. this.$set(this.childrenIndx[0].children[chl.index].children[chl.childrenIndex],'checked',false)
  313. }
  314. t.childrenIndx.splice(1,1,{
  315. index:d.prevent,
  316. childrenIndex:d.chileIndex,
  317. title:d.item.title,
  318. children:t.chuliKey(d.item.children)
  319. })
  320. this.$set(this.childrenIndx[0].children[d.prevent].children[d.chileIndex],'checked',true)
  321. }else{
  322. }
  323. await uni.$tm.sleep(50)
  324. if(t.defaultValue[2]){
  325. let d = chuili(2)
  326. let chl = this.childrenIndx[2];
  327. if(chl){
  328. this.$set(this.childrenIndx[1].children[chl.index].children[chl.childrenIndex],'checked',false)
  329. }
  330. t.childrenIndx.splice(2,1,{
  331. index:d.prevent,
  332. childrenIndex:d.chileIndex,
  333. title:d.item.title,
  334. children:[]
  335. })
  336. this.$set(this.childrenIndx[1].children[d.prevent].children[d.chileIndex],'checked',true)
  337. }
  338. },
  339. indexChange(index){
  340. let t= this;
  341. this.pageIndex = index;
  342. this.childrenIndx.forEach((item,idx)=>{
  343. if(idx===index && item!==null){
  344. if(t.defaultValue[idx]==item.title){
  345. if(idx==0){
  346. t.active = item.index;
  347. }else if(idx==1){
  348. t.active_2 = item.index;
  349. }else if(idx==2){
  350. t.active_3 = item.index;
  351. }
  352. }
  353. }
  354. })
  355. },
  356. cityClick_pr(data_item,tindex){
  357. if(tindex===0){
  358. let chl = this.childrenIndx[0];
  359. if(chl){
  360. this.$set(this.list[chl.index].children[chl.childrenIndex],'checked',false)
  361. }
  362. this.childrenIndx=[{
  363. index:data_item.prevent,
  364. childrenIndex:data_item.children,
  365. title:data_item.title,
  366. children:this.chuliKey(data_item.item.children)
  367. },null,null]
  368. this.$set(this.indexobj,'pr',data_item.prevent)
  369. this.$set(this.indexobj,'ar',null)
  370. this.$set(this.indexobj,'co',null)
  371. this.pageIndex=1;
  372. this.$set(this.list[data_item.prevent].children[data_item.children],'checked',true)
  373. }else if(tindex===1){
  374. let chl = this.childrenIndx[1]
  375. if(chl){
  376. this.$set(this.childrenIndx[0].children[chl.index].children[chl.childrenIndex],'checked',false)
  377. }
  378. this.childrenIndx.splice(1,1,{
  379. index:data_item.prevent,
  380. childrenIndex:data_item.children,
  381. title:data_item.title,
  382. children:this.chuliKey(data_item.item.children)
  383. })
  384. this.childrenIndx.splice(2,1,null)
  385. this.$set(this.indexobj,'pr',data_item.prevent)
  386. this.$set(this.indexobj,'ar',data_item.prevent)
  387. this.$set(this.indexobj,'co',null)
  388. this.pageIndex=2;
  389. this.$set(this.childrenIndx[0].children[data_item.prevent].children[data_item.children],'checked',true)
  390. }else if(tindex===2){
  391. let chl = this.childrenIndx[2]
  392. if(chl){
  393. this.$set(this.childrenIndx[1].children[chl.index].children[chl.childrenIndex],'checked',false)
  394. }
  395. this.childrenIndx.splice(2,1,{
  396. index:data_item.prevent,
  397. childrenIndex:data_item.children,
  398. title:data_item.title,
  399. children:[]
  400. })
  401. this.$set(this.indexobj,'pr',data_item.prevent)
  402. this.$set(this.indexobj,'ar',data_item.prevent)
  403. this.$set(this.indexobj,'co',data_item.prevent)
  404. this.$set(this.childrenIndx[1].children[data_item.prevent].children[data_item.children],'checked',true)
  405. }
  406. let cdite = this.defaultValue;
  407. this.childrenIndx.forEach((item,index)=>{
  408. if(item){
  409. cdite.splice(index,1,item.title)
  410. }else{
  411. cdite.splice(index,1,null)
  412. }
  413. })
  414. this.$emit("update:defaultValue",cdite)
  415. this.$emit("change",cdite)
  416. },
  417. //处理地区数据以便符合规范。
  418. async chiliFormatCity_area() {
  419. let list = [];
  420. let list_index_Str = []
  421. provinceData.forEach((item,index)=>{
  422. let quick = queryCnChart(item.label[0])["0"];
  423. if(item.label=='重庆市'){
  424. quick = 'C'
  425. }
  426. list.push({
  427. id:item.value,
  428. text:item.label,
  429. quick:quick,
  430. children:[]
  431. })
  432. })
  433. cityData.forEach((item,index)=>{
  434. item.forEach((citem,cindex)=>{
  435. list[index].children.push({
  436. id:citem.value,
  437. text:citem.label,
  438. quick:queryCnChart(citem.label[0])["0"],
  439. children:[]
  440. })
  441. })
  442. })
  443. list.forEach((item,index)=>{
  444. item.children.forEach((citem,cindex)=>{
  445. areaData[index][cindex].forEach(jitem=>{
  446. list[index].children[cindex].children.push({
  447. id:jitem.value,
  448. quick:queryCnChart(jitem.label[0])["0"],
  449. text:jitem.label
  450. })
  451. })
  452. })
  453. })
  454. function sortFun(a,b){
  455. var nameA = a.quick.toUpperCase(); // ignore upper and lowercase
  456. var nameB = b.quick.toUpperCase(); // ignore upper and lowercase
  457. if (nameA < nameB) {
  458. return -1;
  459. }
  460. if (nameA > nameB) {
  461. return 1;
  462. }
  463. // names must be equal
  464. return 0;
  465. }
  466. function sort(itemArray){
  467. if(typeof itemArray === 'object' && Array.isArray(itemArray)){
  468. itemArray.sort(sortFun)
  469. }else{
  470. return itemArray;
  471. }
  472. for(let i=0;i<itemArray.length;i++){
  473. let cl = itemArray[i]['children'];
  474. if(typeof cl === 'object' && Array.isArray(cl)){
  475. itemArray[i]['children'] = sort(cl);
  476. }
  477. }
  478. return itemArray;
  479. }
  480. let plst = sort(list);
  481. function fenzu(ar){
  482. let jg = {};
  483. for(let i=0;i<ar.length;i++){
  484. let cl = ar[i]['children'];
  485. if(typeof cl === 'object' && Array.isArray(cl)){
  486. if(typeof jg[ar[i].quick] !=='undefined'){
  487. jg[ar[i].quick].push({
  488. title:ar[i].text,
  489. index:ar[i].quick,
  490. children:fenzu(cl)
  491. })
  492. }else{
  493. jg[ar[i].quick] = [];
  494. jg[ar[i].quick].push({
  495. title:ar[i].text,
  496. index:ar[i].quick,
  497. children:fenzu(cl)
  498. })
  499. }
  500. }else{
  501. if(typeof jg[ar[i].quick] !=='undefined'){
  502. jg[ar[i].quick].push({
  503. title:ar[i].text,
  504. index:ar[i].quick,
  505. children:ar[i]
  506. })
  507. }else{
  508. jg[ar[i].quick] = [];
  509. jg[ar[i].quick].push({
  510. title:ar[i].text,
  511. index:ar[i].quick,
  512. children:ar[i]
  513. })
  514. }
  515. }
  516. }
  517. return jg;
  518. }
  519. let ruslt = fenzu(plst);
  520. let tddd = this.chuliKey(ruslt);
  521. this.list = tddd;
  522. return tddd;
  523. },
  524. chuliKey(arritem){
  525. let ruslt = arritem;
  526. let zuihojg = [];
  527. let keyarray = Object.keys(arritem);
  528. for(let i=0;i<keyarray.length;i++){
  529. zuihojg.push({
  530. title:keyarray[i],
  531. index:keyarray[i],
  532. children:ruslt[keyarray[i]]
  533. })
  534. }
  535. return zuihojg;
  536. }
  537. },
  538. }
  539. </script>
  540. <style lang="scss" scoped>
  541. .tm-quickCity{
  542. .tm-quickCity-fullbg{
  543. background-color: rgba(0,0,0,0.3);
  544. z-index: 501;
  545. .tm-quickCity-wk{
  546. bottom: 0;
  547. left: 0;
  548. .tm-quickCity-title{
  549. z-index: 10;
  550. .tm-calendar-close {
  551. position: absolute;
  552. top: 0rpx;
  553. right: 0rpx;
  554. height: 50rpx;
  555. line-height: 50rpx;
  556. width: 50rpx;
  557. }
  558. }
  559. }
  560. }
  561. }
  562. </style>