address.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <template>
  2. <div class="nut-address">
  3. <nut-popup
  4. v-model="showPopup"
  5. round
  6. position="bottom"
  7. class="choose-address"
  8. @close="close"
  9. @click-overlay="clickOverlay"
  10. @open="closeWay = 'self'"
  11. >
  12. <div class="title">
  13. <span class="arrow" @click="switchModule" v-if="showModule == 'custom' && type == 'exist' && backBtnIcon">
  14. <nut-icon type="self" :url="backBtnIcon"></nut-icon>
  15. </span>
  16. <span class="arrow" v-else></span>
  17. <span v-if="type == 'custom'">{{ customAddressTitle }}</span>
  18. <span v-if="type == 'exist'">{{ existAddressTitle }}</span>
  19. <span @click="handClose('hand')"><nut-icon v-if="closeBtnIcon" size="18px" type="self" :url="closeBtnIcon"></nut-icon></span>
  20. </div>
  21. <!-- 请选择 -->
  22. <div class="custom-address" v-if="showModule == 'custom'">
  23. <div class="region-tab">
  24. <div
  25. class="tab-item"
  26. :class="[index == tabIndex ? 'active' : '']"
  27. v-for="(item, key, index) in selectedRegion"
  28. :key="index"
  29. :ref="'tab-item-' + key"
  30. @click="changeRegionTab(item, key, index)"
  31. ><span>{{ getTabName(item, index) }}</span></div
  32. >
  33. <span class="region-tab-line" ref="regionLine"></span>
  34. </div>
  35. <div class="region-con">
  36. <ul class="region-group">
  37. <li
  38. v-for="(item, index) in regionList[tabName[tabIndex]]"
  39. :key="index"
  40. class="region-item"
  41. :class="[selectedRegion[tabName[tabIndex]].id == item.id ? 'active' : '']"
  42. @click="nextAreaList(item)"
  43. >
  44. <nut-icon type="self" :url="require('../../assets/svg/hook-red.svg')" v-if="selectedRegion[tabName[tabIndex]].id == item.id"></nut-icon
  45. >{{ item.name }}</li
  46. >
  47. </ul>
  48. </div>
  49. </div>
  50. <!-- 配送至 -->
  51. <div class="exist-address" v-if="showModule == 'exist'">
  52. <div class="exist-address-group">
  53. <ul class="exist-ul">
  54. <li
  55. class="exist-item"
  56. :class="[item.selectedAddress ? 'active' : '']"
  57. v-for="(item, index) in existAddress"
  58. :key="index"
  59. @click="selectedExist(item)"
  60. >
  61. <nut-icon type="self" :url="item.selectedAddress ? selectedIcon : defaultIcon"></nut-icon>
  62. <span>{{ item.provinceName + item.cityName + item.countyName + item.townName + item.addressDetail }}</span>
  63. </li>
  64. </ul>
  65. </div>
  66. <div class="choose-other" @click="switchModule" v-if="isShowCustomAddress && showModule == 'exist'">
  67. <div class="btn">{{ customAndExistTitle }}</div>
  68. </div>
  69. </div>
  70. </nut-popup>
  71. </div>
  72. </template>
  73. <script>
  74. import Popup from './../popup/popup.vue';
  75. import Icon from './../icon/icon.vue';
  76. import { TweenMax } from 'gsap';
  77. export default {
  78. name: 'nut-address',
  79. props: {
  80. value: {
  81. type: Boolean,
  82. default: false
  83. },
  84. type: {
  85. type: String,
  86. default: 'custom'
  87. },
  88. customAddressTitle: {
  89. type: String,
  90. default: '请选择所在地区'
  91. },
  92. province: {
  93. type: Array,
  94. default: () => []
  95. }, // 省
  96. city: {
  97. type: Array,
  98. default: () => []
  99. }, // 市
  100. country: {
  101. type: Array,
  102. default: () => []
  103. }, // 县
  104. town: {
  105. type: Array,
  106. default: () => []
  107. }, // 镇
  108. isShowCustomAddress: {
  109. type: Boolean,
  110. default: true
  111. }, // 是否显示‘选择其他地区’按钮 type=‘exist’ 生效
  112. existAddress: {
  113. type: Array,
  114. default: () => []
  115. }, // 现存地址列表
  116. existAddressTitle: {
  117. type: String,
  118. default: '配送至'
  119. },
  120. customAndExistTitle: {
  121. type: String,
  122. default: '选择其他地址'
  123. },
  124. defaultIcon: {
  125. // 地址选择列表前 - 默认的图标
  126. type: String,
  127. default: require('../../assets/svg/address-location.svg')
  128. },
  129. selectedIcon: {
  130. // 地址选择列表前 - 选中的图标
  131. type: String,
  132. default: require('../../assets/svg/tick-red.svg')
  133. },
  134. closeBtnIcon:{
  135. // 关闭弹框按钮 icon
  136. type: String,
  137. default: require('../../assets/svg/circle-cross.svg')
  138. },
  139. backBtnIcon:{
  140. // 选择其他地址左上角返回 icon
  141. type: String,
  142. default: require('../../assets/svg/arrows-back.svg')
  143. }
  144. },
  145. data() {
  146. return {
  147. showPopup: false,
  148. showModule: 'exist', //展示 exist 还是 custom 主要用于‘选择其他地址’
  149. tabIndex: 0,
  150. tabName: ['province', 'city', 'country', 'town'],
  151. regionList: {
  152. province: this.province,
  153. city: this.city,
  154. country: this.country,
  155. town: this.town
  156. }, //省、市、县、镇列表,地址id字符串,地址字符串
  157. selectedRegion: {
  158. province: {},
  159. city: {},
  160. country: {},
  161. town: {}
  162. }, //已选择的 省、市、县、镇
  163. selectedExistAddress: {} // 当前选择的地址
  164. };
  165. },
  166. components: {
  167. 'nut-popup': Popup,
  168. 'nut-icon': Icon
  169. },
  170. watch: {
  171. value(newVal, oldVal) {
  172. this.showPopup = newVal;
  173. },
  174. showPopup(newVal, oldVal) {
  175. if (newVal == false) this.$emit('input', false);
  176. if (newVal == true) {
  177. this.showModule = this.type;
  178. }
  179. },
  180. province(newVal, oldVal) {
  181. this.regionList.province = newVal;
  182. },
  183. city(newVal, oldVal) {
  184. this.regionList.city = newVal;
  185. },
  186. country(newVal, oldVal) {
  187. this.regionList.country = newVal;
  188. },
  189. town(newVal, oldVal) {
  190. this.regionList.town = newVal;
  191. },
  192. existAddress(newVal, oldVal) {
  193. this.existAddress = newVal;
  194. newVal.forEach((item, index) => {
  195. if (item.selectedAddress) {
  196. this.selectedExistAddress = item;
  197. }
  198. });
  199. }
  200. },
  201. mounted() {},
  202. methods: {
  203. //获取已选地区列表名称
  204. getTabName(item, index) {
  205. if (item.name) return item.name;
  206. if (this.tabIndex < index) {
  207. return item.name;
  208. } else {
  209. return '请选择';
  210. }
  211. },
  212. // 切换下一级列表
  213. nextAreaList(item) {
  214. // onchange 接收的参数
  215. const calBack = {
  216. custom: this.tabName[this.tabIndex]
  217. };
  218. this.selectedRegion[this.tabName[this.tabIndex]] = item;
  219. for (let i = this.tabIndex; i < this.tabName.length - 1; i++) {
  220. this.selectedRegion[this.tabName[i + 1]] = {};
  221. }
  222. if (this.tabIndex < 3) {
  223. this.tabIndex = this.tabIndex + 1;
  224. this.lineAnimation();
  225. // 切换下一个
  226. calBack.next = this.tabName[this.tabIndex];
  227. calBack.value = item;
  228. this.$emit('onChange', calBack);
  229. } else {
  230. this.handClose();
  231. }
  232. },
  233. //切换地区Tab
  234. changeRegionTab(item, key, index) {
  235. this.tabIndex = index;
  236. this.lineAnimation();
  237. },
  238. // 移动下面的红线
  239. lineAnimation() {
  240. const name = 'tab-item-' + this.tabName[this.tabIndex];
  241. this.$nextTick(() => {
  242. if (this.$refs[name] && this.$refs[name][0]) {
  243. const distance = this.$refs[name][0].offsetLeft;
  244. TweenMax.to(this.$refs.regionLine, 0.5, { left: distance });
  245. }
  246. });
  247. },
  248. // 选择现有地址
  249. selectedExist(item) {
  250. let copyExistAdd = this.existAddress;
  251. let prevExistAdd = {};
  252. copyExistAdd.forEach((list, index) => {
  253. if (list.selectedAddress) {
  254. prevExistAdd = list;
  255. }
  256. list.selectedAddress = false;
  257. });
  258. item.selectedAddress = true;
  259. this.selectedExistAddress = item;
  260. this.$emit('selected', prevExistAdd, item, copyExistAdd);
  261. this.handClose();
  262. },
  263. // 关闭
  264. close() {
  265. const that = this;
  266. const resCopy = Object.assign({}, this.selectedRegion);
  267. const res = {
  268. type: this.showModule,
  269. data: {}
  270. };
  271. if (this.showModule == 'custom') {
  272. const { province, city, country, town } = resCopy;
  273. resCopy.addressIdStr = [province.id || 0, city.id || 0, country.id || 0, town.id || 0].join('_');
  274. resCopy.addressStr = [province.name, city.name, country.name, town.name].join('');
  275. res.data = resCopy;
  276. } else {
  277. res.data = this.selectedExistAddress;
  278. }
  279. this.initAddress();
  280. if (this.closeWay == 'self') {
  281. this.$emit('close', res);
  282. } else {
  283. // this.$emit('close',{type:'hand'})
  284. }
  285. setTimeout(() => {
  286. that.showModule = 'type';
  287. }, 500);
  288. },
  289. // 手动关闭 点击叉号,或者蒙层
  290. handClose(type = 'self') {
  291. if(!this.closeBtnIcon) return
  292. if (type == 'hand') {
  293. this.closeWay = 'hand';
  294. } else {
  295. this.closeWay = 'self';
  296. }
  297. this.showPopup = false;
  298. },
  299. // 点击遮罩层关闭
  300. clickOverlay() {
  301. this.closeWay = 'hand';
  302. },
  303. // 初始化
  304. initAddress() {
  305. for (let i = 0; i < this.tabName.length; i++) {
  306. this.selectedRegion[this.tabName[i]] = {};
  307. }
  308. this.tabIndex = 0;
  309. this.lineAnimation();
  310. },
  311. // 选择其他地址
  312. switchModule() {
  313. if(this.showModule == 'exist'){
  314. this.showModule = 'custom'
  315. }else{
  316. this.showModule = 'exist'
  317. }
  318. this.initAddress();
  319. this.$emit('switchModule', { type: this.showModule });
  320. }
  321. }
  322. };
  323. </script>