lcw-select-address.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <template>
  2. <uni-popup type="bottom" ref="selectAddressRef">
  3. <view class="select-address-box">
  4. <view class="header-box">
  5. <view></view>
  6. <view class="title-box">选择所在地区</view>
  7. <view class="close-icon" @tap="close">
  8. <view class="iconfont icon-a-Shutdown-01">x</view>
  9. </view>
  10. </view>
  11. <view class="main-box">
  12. <view class="address-info">
  13. <view class="address-item" @tap="setSelectStatus(0)">
  14. <view class="strip-item">
  15. <view
  16. :class="{
  17. 'show-line': address.province,
  18. active: address.province
  19. }"
  20. ></view>
  21. </view>
  22. <view
  23. class="name-box"
  24. :class="{
  25. active: selectStatus == 0
  26. }"
  27. >
  28. <text v-if="address.province">
  29. {{ address.province }}
  30. </text>
  31. <text v-else>请选择所在省份</text>
  32. </view>
  33. <view class="icon-box">
  34. <view class="iconfont icon-xiangyou">
  35. {{ '>' }}
  36. </view>
  37. </view>
  38. </view>
  39. <view v-if="address.province" class="address-item" @tap="setSelectStatus(1)">
  40. <view class="strip-item">
  41. <view
  42. :class="{
  43. 'show-line': address.city,
  44. active: address.city
  45. }"
  46. ></view>
  47. </view>
  48. <view
  49. class="name-box"
  50. :class="{
  51. active: selectStatus == 1
  52. }"
  53. >
  54. <text v-if="address.city">{{ address.city }}</text>
  55. <text v-else>请选择所在城市</text>
  56. </view>
  57. <view class="icon-box">
  58. <view class="iconfont icon-xiangyou">
  59. {{ '>' }}
  60. </view>
  61. </view>
  62. </view>
  63. <view v-if="address.city" class="address-item" @tap="setSelectStatus(2)">
  64. <view class="strip-item last-strip-item">
  65. <view
  66. :class="{
  67. 'show-line': showStreet,
  68. active: address.area
  69. }"
  70. ></view>
  71. </view>
  72. <view
  73. class="name-box"
  74. :class="{
  75. active: selectStatus == 2
  76. }"
  77. >
  78. <text v-if="address.area">{{ address.area }}</text>
  79. <text v-else>请选择所在区/县</text>
  80. </view>
  81. <view class="icon-box">
  82. <view class="iconfont icon-xiangyou">
  83. {{ '>' }}
  84. </view>
  85. </view>
  86. </view>
  87. <view v-if="showStreet" class="address-item" @tap="setSelectStatus(3)">
  88. <view class="strip-item last-strip-item">
  89. <view
  90. :class="{
  91. active: address.street
  92. }"
  93. ></view>
  94. </view>
  95. <view
  96. class="name-box"
  97. :class="{
  98. active: selectStatus == 3
  99. }"
  100. >
  101. <text v-if="address.street">
  102. {{ address.street }}
  103. </text>
  104. <text v-else>请选择所在乡/镇</text>
  105. </view>
  106. <view class="icon-box">
  107. <view class="iconfont icon-xiangyou">
  108. {{ '>' }}
  109. </view>
  110. </view>
  111. </view>
  112. </view>
  113. <view class="address-select-box">
  114. <scroll-view
  115. class="content-box"
  116. :style="{ height: scrollHeight }"
  117. scroll-y="true"
  118. >
  119. <view v-if="selectStatus === 0">
  120. <view class="select-tip">请选择省份</view>
  121. <view
  122. class="select-item"
  123. :class="{
  124. active: activeProvinvial.name === i.name
  125. }"
  126. v-for="i in provinvial"
  127. :key="i.code"
  128. @tap="changeProvinvial(i)"
  129. >
  130. {{ i.name }}
  131. </view>
  132. </view>
  133. <view v-else-if="selectStatus === 1">
  134. <view class="select-tip">请选择城市</view>
  135. <view
  136. class="select-item"
  137. :class="{
  138. active: activeCity.name === i.name
  139. }"
  140. v-for="i in citys"
  141. :key="i.code"
  142. @tap="changeCity(i)"
  143. >
  144. {{ i.name }}
  145. </view>
  146. </view>
  147. <view v-else-if="selectStatus === 2">
  148. <view class="select-tip">请选择区/县</view>
  149. <view
  150. class="select-item"
  151. :class="{
  152. active: activeArea.name === i.name
  153. }"
  154. v-for="i in areas"
  155. :key="i.code"
  156. @tap="changeArea(i)"
  157. >
  158. {{ i.name }}
  159. </view>
  160. </view>
  161. <view v-else>
  162. <view class="select-tip">请选择乡/镇</view>
  163. <view
  164. class="select-item"
  165. :class="{
  166. active: activeStreet.name === i.name
  167. }"
  168. v-for="i in streets"
  169. :key="i.code"
  170. @tap="changeStreet(i)"
  171. >
  172. {{ i.name }}
  173. </view>
  174. </view>
  175. </scroll-view>
  176. </view>
  177. </view>
  178. </view>
  179. </uni-popup>
  180. </template>
  181. <script>
  182. import { post } from "@/request/api.js"
  183. export default {
  184. emits: ['change'],
  185. props: {
  186. address: {
  187. type: Object,
  188. require: true,
  189. default: () => ({
  190. province: '',
  191. city: '',
  192. area: '',
  193. street: ''
  194. })
  195. }
  196. },
  197. data() {
  198. return {
  199. //全部数据
  200. provinvial: [], // 省
  201. cityData: [],
  202. areaData: [],
  203. streetsData: [],
  204. // 筛选后的数据
  205. citys: [], // 城市
  206. areas: [], // 区/县
  207. streets: [], // 镇
  208. // 选择的省、市、区具体名称
  209. activeProvinvial: { name: '' }, // 选择中的省份
  210. activeCity: {
  211. name: ''
  212. }, // 选中的城市
  213. activeArea: { name: '' }, // 选中的区县
  214. activeStreet: { name: '' }, // 选中的乡镇
  215. // 0 1 2 3 当前正在选择 省 市 区 镇
  216. selectStatus: 0,
  217. regional_code: [],
  218. };
  219. },
  220. computed: {
  221. // 高度计算
  222. scrollHeight() {
  223. let height = 920;
  224. if (this.address.province) {
  225. height -= 80;
  226. }
  227. if (this.address.city) {
  228. height -= 80;
  229. }
  230. if (this.address.area && 'street' in this.address) {
  231. height -= 80;
  232. }
  233. return height + 'rpx';
  234. },
  235. // 是否展示4级
  236. showStreet() {
  237. return Boolean(
  238. this.selectStatus === 3 ||
  239. (this.activeArea.name && 'street' in this.address)
  240. );
  241. }
  242. },
  243. watch: {
  244. address: {
  245. handler(newHandler) {
  246. if (newHandler.province && !this.activeProvinvial.code) {
  247. this.setActiveProvinvial();
  248. }
  249. if (newHandler.city && !this.activeCity.code) {
  250. this.setActiveCity();
  251. }
  252. if (newHandler.area && !this.activeArea.code) {
  253. this.setActiveArea();
  254. }
  255. if (newHandler.street && !this.activeStreet.code) {
  256. this.setActiveStreet();
  257. }
  258. },
  259. deep: true
  260. }
  261. },
  262. mounted() {
  263. this.getProvinvial();
  264. },
  265. methods: {
  266. // 更新正在选择内容的标记
  267. setSelectStatus(newSelectStatus) {
  268. this.selectStatus = newSelectStatus;
  269. },
  270. // 关闭 popup
  271. close() {
  272. this.$refs.selectAddressRef.close();
  273. },
  274. // 打开 popup
  275. open() {
  276. this.$refs.selectAddressRef.open();
  277. },
  278. // 选择省
  279. changeProvinvial(i) {
  280. if (i.name !== this.address.province) {
  281. this.regional_code[0] = i.code
  282. this.activeProvinvial = i;
  283. this.activeCity = {};
  284. this.activeArea = {};
  285. this.activeStreet = {};
  286. const newAddress = {
  287. province: i.name,
  288. city: '',
  289. area: ''
  290. };
  291. if ('street' in this.address) {
  292. newAddress.street = '';
  293. }
  294. this.$emit('change', newAddress);
  295. }
  296. this.citys = this.cityData.filter(
  297. item => item.provinceCode === i.code
  298. );
  299. this.selectStatus = 1;
  300. },
  301. // 选择市
  302. changeCity(i) {
  303. if (i.name !== this.address.city) {
  304. this.regional_code[1] = i.code
  305. this.activeCity = i;
  306. this.activeArea = {};
  307. this.activeStreet = {};
  308. const newAddress = {
  309. province: this.address.province,
  310. city: i.name,
  311. area: ''
  312. };
  313. if ('street' in this.address) {
  314. newAddress.street = '';
  315. }
  316. this.$emit('change', newAddress);
  317. }
  318. this.areas = this.areaData.filter(item => item.cityCode === i.code);
  319. this.selectStatus = 2;
  320. },
  321. // 选择区县
  322. changeArea(i) {
  323. if (i.name !== this.address.area) {
  324. this.regional_code[2] = i.code
  325. this.activeArea = i;
  326. this.activeStreet = {};
  327. const newAddress = {
  328. province: this.address.province,
  329. city: this.address.city,
  330. area: i.name,
  331. regional_code: this.regional_code.join()
  332. };
  333. if ('street' in this.address) {
  334. newAddress.street = '';
  335. }
  336. this.$emit('change', newAddress);
  337. }
  338. if ('street' in this.address) {
  339. this.selectStatus = 3;
  340. this.streets = this.streetsData.filter(
  341. item => item.areaCode === i.code
  342. );
  343. } else {
  344. this.close();
  345. }
  346. },
  347. // 选择乡镇
  348. changeStreet(i) {
  349. if (i.name !== this.address.street) {
  350. this.regional_code[3] = i.code
  351. this.activeStreet = i;
  352. this.$emit('change', {
  353. ...this.address,
  354. street: i.name,
  355. regional_code: this.regional_code.join()
  356. });
  357. }
  358. this.close();
  359. },
  360. // 省份数据获取
  361. getProvinvial() {
  362. post("v1/user/areaConfig",{type:'provinces'}).then(res=>{
  363. if(res.code == 0){
  364. let da = res.data.data
  365. // let da = JSON.parse(res.data.data)
  366. this.provinvial = da;
  367. this.setActiveProvinvial();
  368. this.getCity();
  369. }
  370. })
  371. },
  372. setActiveProvinvial() {
  373. if (this.address.province && !this.activeProvinvial.code) {
  374. const provinceIndex = this.provinvial.findIndex(
  375. i => i.name == this.address.province
  376. );
  377. if (provinceIndex !== -1) {
  378. this.activeProvinvial = this.provinvial[provinceIndex];
  379. }
  380. }
  381. },
  382. // 城市数据获取
  383. getCity() {
  384. post("v1/user/areaConfig",{type:'cities'}).then(res=>{
  385. if(res.code == 0){
  386. let da = res.data.data
  387. // let da = JSON.parse(res.data.data)
  388. this.cityData = da;
  389. this.setActiveCity();
  390. this.getArea();
  391. }
  392. })
  393. },
  394. setActiveCity() {
  395. if (this.cityData && this.cityData.length === 0) {
  396. return;
  397. }
  398. this.citys = this.cityData.filter(
  399. item => item.provinceCode === this.activeProvinvial.code
  400. );
  401. if (this.address.city && !this.activeCity.code) {
  402. const cityIndex = this.cityData.findIndex(
  403. i =>
  404. i.name == this.address.city &&
  405. this.activeProvinvial.code == i.provinceCode
  406. );
  407. if (cityIndex !== -1) {
  408. this.activeCity = this.cityData[cityIndex];
  409. }
  410. }
  411. },
  412. // 区县数据
  413. getArea() {
  414. post("v1/user/areaConfig",{type:'areas'}).then(res=>{
  415. if(res.code == 0){
  416. let da = res.data.data
  417. // let da = JSON.parse(res.data.data)
  418. this.areaData = da;
  419. this.setActiveArea();
  420. if ('street' in this.address) {
  421. this.getStreets();
  422. }
  423. }
  424. })
  425. },
  426. setActiveArea() {
  427. if (this.areaData.length === 0) {
  428. return;
  429. }
  430. this.areas = this.areaData.filter(
  431. item => item.cityCode === this.activeCity.code
  432. );
  433. if (this.address.area && !this.activeArea.code) {
  434. const areaIndex = this.areaData.findIndex(
  435. i =>
  436. i.name == this.address.area &&
  437. i.cityCode == this.activeCity.code
  438. );
  439. if (areaIndex !== -1) {
  440. this.activeArea = this.areaData[areaIndex];
  441. }
  442. }
  443. },
  444. // 镇 社区
  445. getStreets() {
  446. post("v1/user/areaConfig",{type:'streets'}).then(res=>{
  447. if(res.code == 0){
  448. let da = res.data.data
  449. // let da = JSON.parse(res.data.data)
  450. this.streetsData = da;
  451. this.setActiveStreet();
  452. }
  453. })
  454. },
  455. setActiveStreet() {
  456. if (this.streetsData.length === 0) {
  457. return;
  458. }
  459. this.streets = this.streetsData.filter(
  460. item => item.areaCode === this.activeArea.code
  461. );
  462. if (this.address.street && !this.activeStreet.code) {
  463. const streetIndex = this.streetsData.findIndex(
  464. i => i.name == this.address.street
  465. );
  466. if (streetIndex !== -1) {
  467. this.activeStreet = this.streetsData[streetIndex];
  468. }
  469. }
  470. }
  471. }
  472. };
  473. </script>
  474. <style lang="scss">
  475. .select-address-box {
  476. height: 1100rpx;
  477. border-top-left-radius: 20rpx;
  478. border-top-right-radius: 20rpx;
  479. background-color: #fff;
  480. // padding: 30rpx 0;
  481. .header-box {
  482. display: flex;
  483. justify-content: space-between;
  484. font-size: 32rpx;
  485. font-weight: bold;
  486. color: #333;
  487. // margin-bottom: 40rpx;
  488. height: 80rpx;
  489. line-height: 80rpx;
  490. padding: 0 20rpx;
  491. > .title-box {
  492. font-size: 36rpx;
  493. }
  494. .iconfont {
  495. color: #72899b;
  496. padding: 0 20rpx;
  497. }
  498. }
  499. .main-box {
  500. height: calc(100% - 80rpx);
  501. .address-info {
  502. padding: 0 20rpx;
  503. > .address-item {
  504. display: flex;
  505. align-items: center;
  506. height: 80rpx;
  507. .strip-item {
  508. color: #333;
  509. margin-right: 48rpx;
  510. position: relative;
  511. > view {
  512. width: 12rpx;
  513. height: 12rpx;
  514. border-radius: 50%;
  515. border: 2rpx solid #086df7;
  516. transition: all 0.25s;
  517. box-sizing: border-box;
  518. }
  519. .show-line {
  520. &::after {
  521. content: '';
  522. position: absolute;
  523. top: 100%;
  524. left: 50%;
  525. width: 2rpx;
  526. height: 69rpx;
  527. background-color: #086df7;
  528. }
  529. /* #ifndef H5*/
  530. &::after {
  531. transform: translateX(-50%);
  532. }
  533. /* #endif */
  534. /* #ifdef VUE2 */
  535. &::after {
  536. transform: translateX(-50%);
  537. }
  538. /* #endif */
  539. }
  540. .active {
  541. background: #086df7;
  542. }
  543. }
  544. .name-box {
  545. font-size: 28rpx;
  546. flex: 1;
  547. }
  548. .active {
  549. color: #086df7;
  550. }
  551. .icon-box {
  552. .iconfont {
  553. font-size: 24rpx;
  554. color: #72899b;
  555. }
  556. }
  557. }
  558. }
  559. .address-select-box {
  560. padding: 20rpx 20rpx 0;
  561. border-top: 2rpx solid #d4d4d4;
  562. .content-box {
  563. height: 600rpx;
  564. .select-tip {
  565. font-size: 32rpx;
  566. color: #333;
  567. font-weight: bold;
  568. padding-bottom: 20rpx;
  569. }
  570. .select-item {
  571. padding: 20rpx 20rpx;
  572. font-size: 28rpx;
  573. color: #333;
  574. }
  575. .active {
  576. color: #086df7;
  577. }
  578. }
  579. }
  580. }
  581. }
  582. </style>