tm-quickCity.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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. },
  133. color:{
  134. type:String,
  135. default:"primary"
  136. },
  137. height:{
  138. type:String|Number,
  139. default:900
  140. },
  141. value:{
  142. type:Boolean,
  143. default:false
  144. },
  145. black:{
  146. type:Boolean,
  147. default:null
  148. },
  149. // 跟随主题色的改变而改变。
  150. fllowTheme:{
  151. type:Boolean|String,
  152. default:true
  153. }
  154. },
  155. data() {
  156. return {
  157. list:[],
  158. active:0,
  159. active_2:0,
  160. active_3:0,
  161. childrenIndx:[null,null,null],
  162. indexobj:{
  163. pr:null,
  164. ar:null,
  165. co:null
  166. },
  167. pageIndex:0,
  168. sysHeight:0,
  169. showbgOpen:false,
  170. content_height:0,
  171. content_jian_height:0,
  172. hotCity_chuli:[]
  173. };
  174. },
  175. watch:{
  176. value:function(val){
  177. this.show = val;
  178. }
  179. },
  180. destroyed() {
  181. this.list=[];
  182. this.childrenIndx=[];
  183. },
  184. computed:{
  185. black_tmeme: function() {
  186. if (this.black !== null) return this.black;
  187. return this.$tm.vx.state().tmVuetify.black;
  188. },
  189. color_tmeme:function(){
  190. if(this.$tm.vx.state().tmVuetify.color!==null&&this.$tm.vx.state().tmVuetify.color && this.fllowTheme){
  191. return this.$tm.vx.state().tmVuetify.color;
  192. }
  193. return this.color;
  194. },
  195. show:{
  196. get:function(){
  197. return this.showbgOpen;
  198. },
  199. set:function(val){
  200. this.showbgOpen = val;
  201. this.$emit('input',val);
  202. this.$emit('update:value',val);
  203. }
  204. }
  205. },
  206. async mounted() {
  207. this.sysHeight = uni.getSystemInfoSync().windowHeight;
  208. this.show = this.value;
  209. this.content_height = this.height;
  210. this.hotCity_chuli_fun();
  211. await this.chiliFormatCity_area()
  212. await this.setDefaultValue();
  213. await this.content_heightToh();
  214. },
  215. methods: {
  216. hotCity_chuli_fun(){
  217. let hot = uni.$tm.deepClone(this.hotCity);
  218. let hostar = [];
  219. hot.forEach(item=>{
  220. if(item.length==1) hostar.push([item[0],null,null])
  221. if(item.length==2) hostar.push([item[0],item[1],null])
  222. if(item.length==3) hostar.push([item[0],item[1],item[2]])
  223. })
  224. this.hotCity_chuli = [...hostar];
  225. },
  226. async qiehuan(){
  227. this.show = !this.showbgOpen
  228. await this.content_heightToh();
  229. },
  230. async content_heightToh(){
  231. // if(!this.showbgOpen) return;
  232. if(this.hotCity_chuli.length==0){
  233. return this.height;
  234. }
  235. this.$nextTick(async function(){
  236. let h = await this.$Querey(".tm-quickCity-jg",this).catch(e=>{});
  237. if(!h[0]) return;
  238. h = h[0].height;
  239. let blv = uni.upx2px(4) / 4;
  240. this.content_jian_height = (h+h*blv)
  241. this.content_height = parseInt(this.height - this.content_jian_height);
  242. })
  243. },
  244. async selectedDefaultHot(index){
  245. this.$emit('update:defaultValue',this.hotCity[index]);
  246. let chl = this.childrenIndx[0];
  247. if(chl){
  248. this.$set(this.list[chl.index].children[chl.childrenIndex],'checked',false)
  249. }
  250. this.childrenIndx=[null,null,null];
  251. this.$nextTick(async function(){
  252. await this.setDefaultValue()
  253. this.cityClick_pr(this.list[this.childrenIndx[0]],this.childrenIndx[0])
  254. })
  255. },
  256. async setDefaultValue(){
  257. let t = this;
  258. function chuili(ix){
  259. let pitem = null;
  260. let chilIndex=-1;
  261. let pdindex = -1;
  262. if(ix==0&&t.defaultValue[ix]){
  263. pdindex = t.list.findIndex((item)=>{
  264. let citem = item.children
  265. let index = citem.findIndex(ditem=>{
  266. if(ditem.title == t.defaultValue[ix]){
  267. pitem = ditem;
  268. }
  269. return ditem.title == t.defaultValue[ix]
  270. })
  271. if(index>-1){
  272. chilIndex = index;
  273. }
  274. return index>-1;
  275. })
  276. }else if(t.defaultValue[ix]){
  277. let dsst = t.childrenIndx[ix-1].children
  278. pdindex = dsst.findIndex((item)=>{
  279. let citem = item.children
  280. let index = citem.findIndex(ditem=>{
  281. if(ditem.title == t.defaultValue[ix]){
  282. pitem = ditem;
  283. }
  284. return ditem.title == t.defaultValue[ix]
  285. })
  286. if(index>-1){
  287. chilIndex = index;
  288. }
  289. return index>-1;
  290. })
  291. }
  292. return {prevent:pdindex,chileIndex:chilIndex,item:pitem};
  293. }
  294. let d = chuili(0)
  295. if(d.prevent>-1){
  296. let chl = this.childrenIndx[0];
  297. if(chl){
  298. this.$set(this.list[chl.index].children[chl.childrenIndex],'checked',false)
  299. }
  300. t.childrenIndx.splice(0,1,{
  301. index:d.prevent,
  302. childrenIndex:d.chileIndex,
  303. title:d.item.title,
  304. children:t.chuliKey(d.item.children)
  305. })
  306. t.active = d.prevent;
  307. this.$set(this.list[d.prevent].children[d.chileIndex],'checked',true)
  308. }
  309. if(t.defaultValue[1]){
  310. let d = chuili(1)
  311. let chl = this.childrenIndx[1];
  312. if(chl){
  313. this.$set(this.childrenIndx[0].children[chl.index].children[chl.childrenIndex],'checked',false)
  314. }
  315. t.childrenIndx.splice(1,1,{
  316. index:d.prevent,
  317. childrenIndex:d.chileIndex,
  318. title:d.item.title,
  319. children:t.chuliKey(d.item.children)
  320. })
  321. this.$set(this.childrenIndx[0].children[d.prevent].children[d.chileIndex],'checked',true)
  322. }else{
  323. }
  324. await uni.$tm.sleep(50)
  325. if(t.defaultValue[2]){
  326. let d = chuili(2)
  327. let chl = this.childrenIndx[2];
  328. if(chl){
  329. this.$set(this.childrenIndx[1].children[chl.index].children[chl.childrenIndex],'checked',false)
  330. }
  331. t.childrenIndx.splice(2,1,{
  332. index:d.prevent,
  333. childrenIndex:d.chileIndex,
  334. title:d.item.title,
  335. children:[]
  336. })
  337. this.$set(this.childrenIndx[1].children[d.prevent].children[d.chileIndex],'checked',true)
  338. }
  339. },
  340. indexChange(index){
  341. let t= this;
  342. this.pageIndex = index;
  343. this.childrenIndx.forEach((item,idx)=>{
  344. if(idx===index && item!==null){
  345. if(t.defaultValue[idx]==item.title){
  346. if(idx==0){
  347. t.active = item.index;
  348. }else if(idx==1){
  349. t.active_2 = item.index;
  350. }else if(idx==2){
  351. t.active_3 = item.index;
  352. }
  353. }
  354. }
  355. })
  356. },
  357. cityClick_pr(data_item,tindex){
  358. if(tindex===0){
  359. let chl = this.childrenIndx[0];
  360. if(chl){
  361. this.$set(this.list[chl.index].children[chl.childrenIndex],'checked',false)
  362. }
  363. this.childrenIndx=[{
  364. index:data_item.prevent,
  365. childrenIndex:data_item.children,
  366. title:data_item.title,
  367. children:this.chuliKey(data_item.item.children)
  368. },null,null]
  369. this.$set(this.indexobj,'pr',data_item.prevent)
  370. this.$set(this.indexobj,'ar',null)
  371. this.$set(this.indexobj,'co',null)
  372. this.pageIndex=1;
  373. this.$set(this.list[data_item.prevent].children[data_item.children],'checked',true)
  374. }else if(tindex===1){
  375. let chl = this.childrenIndx[1]
  376. if(chl){
  377. this.$set(this.childrenIndx[0].children[chl.index].children[chl.childrenIndex],'checked',false)
  378. }
  379. this.childrenIndx.splice(1,1,{
  380. index:data_item.prevent,
  381. childrenIndex:data_item.children,
  382. title:data_item.title,
  383. children:this.chuliKey(data_item.item.children)
  384. })
  385. this.childrenIndx.splice(2,1,null)
  386. this.$set(this.indexobj,'pr',data_item.prevent)
  387. this.$set(this.indexobj,'ar',data_item.prevent)
  388. this.$set(this.indexobj,'co',null)
  389. this.pageIndex=2;
  390. this.$set(this.childrenIndx[0].children[data_item.prevent].children[data_item.children],'checked',true)
  391. }else if(tindex===2){
  392. let chl = this.childrenIndx[2]
  393. if(chl){
  394. this.$set(this.childrenIndx[1].children[chl.index].children[chl.childrenIndex],'checked',false)
  395. }
  396. this.childrenIndx.splice(2,1,{
  397. index:data_item.prevent,
  398. childrenIndex:data_item.children,
  399. title:data_item.title,
  400. children:[]
  401. })
  402. this.$set(this.indexobj,'pr',data_item.prevent)
  403. this.$set(this.indexobj,'ar',data_item.prevent)
  404. this.$set(this.indexobj,'co',data_item.prevent)
  405. this.$set(this.childrenIndx[1].children[data_item.prevent].children[data_item.children],'checked',true)
  406. }
  407. let cdite = this.defaultValue;
  408. this.childrenIndx.forEach((item,index)=>{
  409. if(item){
  410. cdite.splice(index,1,item.title)
  411. }else{
  412. cdite.splice(index,1,null)
  413. }
  414. })
  415. this.$emit("update:defaultValue",cdite)
  416. this.$emit("change",cdite)
  417. },
  418. //处理地区数据以便符合规范。
  419. async chiliFormatCity_area() {
  420. let list = [];
  421. let list_index_Str = []
  422. provinceData.forEach((item,index)=>{
  423. let quick = queryCnChart(item.label[0])["0"];
  424. if(item.label=='重庆市'){
  425. quick = 'C'
  426. }
  427. list.push({
  428. id:item.value,
  429. text:item.label,
  430. quick:quick,
  431. children:[]
  432. })
  433. })
  434. cityData.forEach((item,index)=>{
  435. item.forEach((citem,cindex)=>{
  436. list[index].children.push({
  437. id:citem.value,
  438. text:citem.label,
  439. quick:queryCnChart(citem.label[0])["0"],
  440. children:[]
  441. })
  442. })
  443. })
  444. list.forEach((item,index)=>{
  445. item.children.forEach((citem,cindex)=>{
  446. areaData[index][cindex].forEach(jitem=>{
  447. list[index].children[cindex].children.push({
  448. id:jitem.value,
  449. quick:queryCnChart(jitem.label[0])["0"],
  450. text:jitem.label
  451. })
  452. })
  453. })
  454. })
  455. function sortFun(a,b){
  456. var nameA = a.quick.toUpperCase(); // ignore upper and lowercase
  457. var nameB = b.quick.toUpperCase(); // ignore upper and lowercase
  458. if (nameA < nameB) {
  459. return -1;
  460. }
  461. if (nameA > nameB) {
  462. return 1;
  463. }
  464. // names must be equal
  465. return 0;
  466. }
  467. function sort(itemArray){
  468. if(typeof itemArray === 'object' && Array.isArray(itemArray)){
  469. itemArray.sort(sortFun)
  470. }else{
  471. return itemArray;
  472. }
  473. for(let i=0;i<itemArray.length;i++){
  474. let cl = itemArray[i]['children'];
  475. if(typeof cl === 'object' && Array.isArray(cl)){
  476. itemArray[i]['children'] = sort(cl);
  477. }
  478. }
  479. return itemArray;
  480. }
  481. let plst = sort(list);
  482. function fenzu(ar){
  483. let jg = {};
  484. for(let i=0;i<ar.length;i++){
  485. let cl = ar[i]['children'];
  486. if(typeof cl === 'object' && Array.isArray(cl)){
  487. if(typeof jg[ar[i].quick] !=='undefined'){
  488. jg[ar[i].quick].push({
  489. title:ar[i].text,
  490. index:ar[i].quick,
  491. children:fenzu(cl)
  492. })
  493. }else{
  494. jg[ar[i].quick] = [];
  495. jg[ar[i].quick].push({
  496. title:ar[i].text,
  497. index:ar[i].quick,
  498. children:fenzu(cl)
  499. })
  500. }
  501. }else{
  502. if(typeof jg[ar[i].quick] !=='undefined'){
  503. jg[ar[i].quick].push({
  504. title:ar[i].text,
  505. index:ar[i].quick,
  506. children:ar[i]
  507. })
  508. }else{
  509. jg[ar[i].quick] = [];
  510. jg[ar[i].quick].push({
  511. title:ar[i].text,
  512. index:ar[i].quick,
  513. children:ar[i]
  514. })
  515. }
  516. }
  517. }
  518. return jg;
  519. }
  520. let ruslt = fenzu(plst);
  521. let tddd = this.chuliKey(ruslt);
  522. this.list = tddd;
  523. return tddd;
  524. },
  525. chuliKey(arritem){
  526. let ruslt = arritem;
  527. let zuihojg = [];
  528. let keyarray = Object.keys(arritem);
  529. for(let i=0;i<keyarray.length;i++){
  530. zuihojg.push({
  531. title:keyarray[i],
  532. index:keyarray[i],
  533. children:ruslt[keyarray[i]]
  534. })
  535. }
  536. return zuihojg;
  537. }
  538. },
  539. }
  540. </script>
  541. <style lang="scss" scoped>
  542. .tm-quickCity{
  543. .tm-quickCity-fullbg{
  544. background-color: rgba(0,0,0,0.3);
  545. z-index: 501;
  546. .tm-quickCity-wk{
  547. bottom: 0;
  548. left: 0;
  549. .tm-quickCity-title{
  550. z-index: 10;
  551. .tm-calendar-close {
  552. position: absolute;
  553. top: 0rpx;
  554. right: 0rpx;
  555. height: 50rpx;
  556. line-height: 50rpx;
  557. width: 50rpx;
  558. }
  559. }
  560. }
  561. }
  562. }
  563. </style>