add-address.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. <template>
  2. <view class="">
  3. <view style="padding:30rpx">
  4. <w-picker ref="Selector" mode="region" :defaultVal="pickerDefaultVal" themeColor="#F76454"
  5. @confirm="onConfirm" />
  6. <view class="nav">
  7. <textarea v-model="message" type="text" :placeholder="placeholder" class="discern"
  8. placeholder-class="discern-placeholder" />
  9. <view class="flex">
  10. <view class="nav-clearbutton flexC" @click="message = ''">清空</view>
  11. <view class="nav-button flexC" @click="text">识别</view>
  12. </view>
  13. </view>
  14. <view class="box">
  15. <view class="box-top">
  16. <view class="title">
  17. <text>收货人</text>
  18. <text class="star">*</text>
  19. </view>
  20. <input v-model="address.name" type="text" class="box-input" placeholder-class="input_placeholder"
  21. placeholder="请填写真实姓名" />
  22. </view>
  23. <view class="box-top">
  24. <navigator url="../phone-area/phone-area" class="phone-area flexB">
  25. <view class="flexS">
  26. <view class="title">
  27. <text class="text">国家/地区</text>
  28. <text class="star">*</text>
  29. </view>
  30. <text class="area">{{areaName}}({{address.areaCode}})</text>
  31. </view>
  32. <text class="icon cuIcon-right"></text>
  33. </navigator>
  34. </view>
  35. <view class="box-top">
  36. <view class="title">
  37. <text>手机号</text>
  38. <text class="star">*</text>
  39. </view>
  40. <input v-model="address.phone" type="number" class="box-input" placeholder-class="input_placeholder"
  41. placeholder="请填写手机号码" maxlength="11" />
  42. </view>
  43. <view class="box-top">
  44. <view class="title">
  45. <text>省市区</text>
  46. <text class="star">*</text>
  47. </view>
  48. <view class="picker flexB" @tap="choosePicker">
  49. <view :style="{ color: address.local ? '#333' : '#cbcbcb' }">
  50. {{ address.local ? address.local : '请选择所在地区' }}
  51. </view>
  52. <text class="iconfont iconzhcc_xiangxiajiantou"></text>
  53. </view>
  54. </view>
  55. <view class="box-bottom">
  56. <view class="title">
  57. <text>详细地址</text>
  58. <text class="star">*</text>
  59. </view>
  60. <view class="textbox">
  61. <textarea v-model="address.detail" type="text" placeholder="请填写详细地址" class="box-input"
  62. placeholder-class="input_placeholder" />
  63. </view>
  64. </view>
  65. <view class="box-changyong">
  66. <view class="title">设置为常用地址</view>
  67. <view style="display: flex;">
  68. <view class="set-control" :class="!address.type ? 'active' : ''" @click="address.type = 0">否
  69. </view>
  70. <view class="set-control" :class="address.type ? 'active' : ''" @click="address.type = 1">是
  71. </view>
  72. </view>
  73. </view>
  74. </view>
  75. <view class="remind">若自动识别的信息不准确,请手动修改</view>
  76. </view>
  77. <view class="bottom">
  78. <view class="bottom-btn" @tap="save">保存地址</view>
  79. </view>
  80. </view>
  81. </template>
  82. <script>
  83. import WPicker from '@/components/w-picker/w-picker.vue';
  84. import cityData from '@/components/citypicker/city-data/city.js';
  85. import areaData from '@/components/citypicker/city-data/area.js';
  86. import cityPicker from '@/components/citypicker/city-picker.vue';
  87. import provinceData from '@/components/citypicker/city-data/province.js';
  88. import areaList from '@/common/util/phone-area.js'
  89. import {
  90. _API_AddressAdd,
  91. _API_AddressUpdata,
  92. discernAdress
  93. } from '@/apis/address.js';
  94. import AddressParse from 'address-parse';
  95. export default {
  96. components: {
  97. WPicker,
  98. cityPicker
  99. },
  100. data() {
  101. return {
  102. placeholder: '请粘贴或输入整段地址,点击“识别”自动拆分姓名、电话和地址。例如:张三,15100001111,河南省郑州市金水区某路和某路交叉口某小区某号楼',
  103. title: '新增地址',
  104. id: '',
  105. message: '',
  106. result: '',
  107. address: {
  108. name: '',
  109. phone: '',
  110. local: '',
  111. province: '',
  112. city: '',
  113. area: '',
  114. detail: '',
  115. type: '',
  116. areaCode: '+86', // 手机号地区代码
  117. },
  118. requesting: false,
  119. pickerDefaultVal: ['河南省', '郑州市', '金水区'],
  120. areaName: '中国大陆', // 手机号地区名称
  121. isMatch: /^[1][3-9]\d{9}$/, //手机号验证
  122. codeList: areaList, //手机号区域选择
  123. };
  124. },
  125. created() {
  126. uni.$on('CHOOSEPHONECODE', (name, code, reg, phoneReg) => {
  127. // 监听 chooseArea 事件更新
  128. this.areaName = name; // 修改选择的手机号地区名称
  129. this.address.areaCode = code // 修改选择的手机号地区代码
  130. this.isMatch = phoneReg
  131. });
  132. },
  133. computed: {
  134. list() {
  135. return this.$store.state.address.list;
  136. }
  137. },
  138. methods: {
  139. checkCode(type, arr) {
  140. let t = '886'
  141. let a = '853'
  142. let x = '852'
  143. let msg = this.message.replace(/\s*/g, "")
  144. let index = ''
  145. let mobile = ''
  146. let code = ''
  147. if (msg.indexOf(t) != -1) {
  148. index = msg.indexOf(t)
  149. mobile = msg.substring(index + 3, index + 13)
  150. code = `+${t}`
  151. this.areaName = '中国台湾'
  152. this.isMatch = /^[0]{1}[9]{1}\d{8}$/
  153. } else if (msg.indexOf(a) != -1) {
  154. index = msg.indexOf(a)
  155. mobile = msg.substring(index + 3, index + 11)
  156. code = `+${a}`
  157. this.areaName = '中国澳门'
  158. this.isMatch = /^[6]\d{7}$/
  159. } else if (msg.indexOf(x) != -1) {
  160. index = msg.indexOf(x)
  161. mobile = msg.substring(index + 3, index + 11)
  162. code = `+${x}`
  163. this.areaName = '中国香港'
  164. this.isMatch = /^([6|9|5])\d{7}$/
  165. } else {
  166. code = '+86'
  167. this.areaName = '中国大陆'
  168. mobile = arr.mobile
  169. this.isMatch = /^[1][3-9]\d{9}$/
  170. }
  171. this.address = {
  172. name: arr.name.replace(/[0-9]/g, ''),
  173. phone: mobile,
  174. local: type === 0 ? `${arr.province}-${arr.city}-${arr.area}` : `${arr.province}-${arr.city}`,
  175. province: arr.province,
  176. city: arr.city,
  177. area: arr.area,
  178. detail: arr.details,
  179. type: '',
  180. areaCode: code,
  181. };
  182. },
  183. text() {
  184. if (this.message.length < 1) {
  185. uni.toast('请输入需要识别的内容');
  186. return;
  187. }
  188. this.result = AddressParse.parse(this.message);
  189. let arr = AddressParse.parse(this.message)[0];
  190. if (arr.mobile === '') {
  191. this.checkCode(0, arr)
  192. } else if (arr.area === '') {
  193. const params = {
  194. city: arr.city,
  195. details: arr.details
  196. };
  197. this.checkCode(1, arr)
  198. discernAdress(params).then(res => {
  199. if (res.code === 200) {
  200. if (!res.data.area) {
  201. uni.toast('有部分信息未能识别或错误,请核实或手动填写');
  202. return;
  203. }
  204. // this.address.name = arr.name;
  205. // this.address.phone = arr.mobile;
  206. // this.address.detail = arr.details;
  207. // this.address.province = arr.province;
  208. // this.address.city = arr.city;
  209. this.address.area = res.data.area;
  210. this.address.local = `${arr.province}-${arr.city}-${res.data.area}`;
  211. this.pickerDefaultVal = [this.address.province, this.address.city, this.address.area];
  212. } else {
  213. uni.toast('识别失败,请手动填写');
  214. }
  215. });
  216. } else {
  217. this.address.name = arr.name;
  218. this.address.phone = arr.mobile;
  219. this.address.detail = arr.details;
  220. this.address.province = arr.province;
  221. this.address.city = arr.city;
  222. this.address.area = arr.area;
  223. this.address.local = `${arr.province}-${arr.city}-${arr.area}`;
  224. this.pickerDefaultVal = [this.address.province, this.address.city, this.address.area];
  225. this.address.areaCode = '+86'
  226. }
  227. },
  228. save() {
  229. if (this.address.name) {
  230. this.address.name = this.address.name.replace(/\s*/g, '');
  231. }
  232. // 保存/修改地址
  233. if (!this.address.name.match(/^[\u4E00-\u9FA5\uf900-\ufa2d·s]{2,20}$/)) {
  234. // 校验姓名
  235. uni.toast('真实姓名不符合要求');
  236. return;
  237. }
  238. // if (!this.address.phone.match(/^1\d{10}$/)) {
  239. // // 校验手机号
  240. // uni.toast('手机号码不符合要求');
  241. // return;
  242. // }
  243. if (!this.address.phone) {
  244. uni.showToast({
  245. title: '手机号码不能为空',
  246. icon: 'none',
  247. mask: true
  248. });
  249. return
  250. }
  251. if (this.isMatch && !this.address.phone.match(this.isMatch)) {
  252. uni.showToast({
  253. title: '手机号所属与区号不一致',
  254. icon: 'none',
  255. mask: true
  256. });
  257. return
  258. }
  259. if (!this.address.area) {
  260. uni.toast('区域不能为空');
  261. return;
  262. }
  263. if (!this.address.local.trim().length) {
  264. // 校验是否选择地区
  265. uni.toast('请选择所在地区');
  266. return;
  267. }
  268. if (!this.address.detail.trim().length) {
  269. // 校验是否填写详细地址
  270. uni.toast('详细地址不能为空');
  271. return;
  272. }
  273. const address = {
  274. con_name: this.address.name,
  275. con_mobile: this.address.phone,
  276. province: this.address.province,
  277. city: this.address.city,
  278. area: this.address.area,
  279. address: this.address.detail,
  280. type: this.address.type,
  281. areaCode: this.address.areaCode
  282. };
  283. if (!this.requesting) {
  284. this.requesting = true;
  285. uni.showLoading({
  286. mask: true
  287. });
  288. if (this.title === '新增地址') {
  289. _API_AddressAdd(address).then(res => {
  290. this.requesting = false;
  291. if (res.code === 200) {
  292. uni.navigateBack();
  293. uni.$emit('ADDRESS', '新增地址成功');
  294. } else {
  295. uni.showModal({
  296. content: res.message || '新增地址失败',
  297. showCancel: false
  298. });
  299. }
  300. });
  301. } else if (this.title === '编辑地址') {
  302. address.id = this.id;
  303. _API_AddressUpdata(address).then(res => {
  304. this.requesting = false;
  305. if (res.code === 200) {
  306. uni.navigateBack();
  307. uni.$emit('ADDRESS', '修改地址成功');
  308. } else {
  309. uni.showModal({
  310. content: res.message || '修改地址失败',
  311. showCancel: false
  312. });
  313. }
  314. });
  315. }
  316. }
  317. },
  318. choosePicker() {
  319. // 显示地址选择器
  320. this.$refs.Selector.show();
  321. },
  322. onConfirm(e) {
  323. // 选择地址选择器
  324. this.address.local = `${e.checkArr[0]}-${e.checkArr[1]}-${e.checkArr[2]}`;
  325. this.address.province = e.checkArr[0];
  326. this.address.city = e.checkArr[1];
  327. this.address.area = e.checkArr[2];
  328. }
  329. },
  330. onLoad(opt) {
  331. if (opt.index) {
  332. // 如果传入 index 表示用户编辑地址
  333. this.title = '编辑地址'; // 修改页面标题
  334. const address = this.list[+opt.index];
  335. if (!address) {
  336. return false;
  337. }
  338. this.id = address.id; // 获取地址 id
  339. this.address.name = address.con_name;
  340. this.address.phone = address.con_mobile;
  341. this.address.province = address.province;
  342. this.address.city = address.city;
  343. this.address.area = address.area;
  344. this.address.type = address.type ? 1 : 0;
  345. this.address.local = `${address.province}-${address.city}-${address.area}`;
  346. this.address.detail = address.address;
  347. this.pickerDefaultVal = [address.province, address.city, address.area];
  348. this.address.areaCode = address.area_code ? address.area_code : '+86'
  349. if (this.address.areaCode) {
  350. let index = this.address.areaCode.indexOf('+')
  351. let code = this.address.areaCode.substring(index + 1)
  352. this.codeList[0].list.map(i => {
  353. if (i.code === Number(code)) {
  354. this.areaName = i.name
  355. this.isMatch = i.phoneReg
  356. }
  357. })
  358. }
  359. }
  360. }
  361. };
  362. </script>
  363. <style lang="scss" scoped>
  364. .nav {
  365. padding: 26rpx 20rpx 30rpx 30rpx;
  366. background-color: #fff;
  367. border-radius: 24rpx;
  368. display: flex;
  369. flex-direction: column;
  370. align-items: flex-end;
  371. .discern {
  372. width: 100%;
  373. height: 180rpx;
  374. font-size: 28rpx;
  375. &-placeholder {
  376. color: #c8c8c8;
  377. font-size: 28rpx;
  378. line-height: 44rpx;
  379. }
  380. }
  381. .flex {
  382. display: flex;
  383. view {
  384. width: 128rpx;
  385. height: 64rpx;
  386. font-size: 28rpx;
  387. border-radius: 32rpx;
  388. }
  389. }
  390. &-button {
  391. color: #ffffff;
  392. background: linear-gradient(90deg, #ff232c 0%, #ff571b 100%);
  393. }
  394. &-clearbutton {
  395. background: #fff4f3;
  396. border: 2rpx solid $base-color;
  397. color: $base-color;
  398. margin-right: 15rpx;
  399. }
  400. }
  401. .box {
  402. margin: 30rpx 0;
  403. background-color: #fff;
  404. border-radius: 24rpx;
  405. .phone-area {
  406. width: 100%;
  407. .area {
  408. color: #42b983;
  409. margin-left: 16rpx;
  410. }
  411. .icon {
  412. font-size: 40rpx;
  413. color: #999;
  414. }
  415. }
  416. &-input {
  417. font-size: 30rpx;
  418. min-height: 126rpx;
  419. width: 75%;
  420. margin-left: 20rpx;
  421. }
  422. &-top {
  423. min-height: 128rpx;
  424. padding: 0 30rpx;
  425. box-sizing: border-box;
  426. border-bottom: 2rpx solid #eeeeee;
  427. display: flex;
  428. justify-content: flex-start;
  429. align-items: center;
  430. .title {
  431. font-size: 34rpx;
  432. font-weight: bold;
  433. .star {
  434. color: $base-color;
  435. margin-left: 10rpx;
  436. font-size: 40rpx;
  437. }
  438. }
  439. .picker {
  440. width: 75%;
  441. min-height: 128rpx;
  442. margin-left: 20rpx;
  443. font-size: 30rpx;
  444. .iconfont {
  445. color: #cbcbcb;
  446. }
  447. }
  448. .input_placeholder {
  449. font-size: 30rpx;
  450. color: #cbcbcb;
  451. }
  452. }
  453. &-bottom {
  454. height: 166rpx;
  455. padding: 20rpx 30rpx;
  456. box-sizing: border-box;
  457. border-bottom: 2rpx solid #eeeeee;
  458. display: flex;
  459. justify-content: flex-start;
  460. align-items: flex-start;
  461. .title {
  462. font-size: 34rpx;
  463. font-weight: bold;
  464. .star {
  465. color: $base-color;
  466. margin-left: 10rpx;
  467. font-size: 40rpx;
  468. }
  469. }
  470. .input_placeholder {
  471. font-size: 30rpx;
  472. color: #cbcbcb;
  473. }
  474. .textbox {
  475. height: 166rpx;
  476. .box-input {
  477. width: 440rpx;
  478. height: 120rpx;
  479. font-size: 30rpx;
  480. }
  481. }
  482. }
  483. &-changyong {
  484. height: 110rpx;
  485. padding: 26rpx 20rpx 30rpx 30rpx;
  486. display: flex;
  487. justify-content: space-between;
  488. align-items: center;
  489. .title {
  490. // width: 146rpx;
  491. font-size: 28rpx;
  492. font-weight: 400;
  493. line-height: 29rpx;
  494. color: #585858;
  495. }
  496. .input_placeholder {
  497. font-size: 28rpx;
  498. font-weight: 400;
  499. line-height: 29rpx;
  500. }
  501. .set-used {
  502. margin-right: auto;
  503. color: #ea4a41;
  504. font-size: 30rpx;
  505. }
  506. .set-control {
  507. width: 110rpx;
  508. height: 55rpx;
  509. line-height: 55rpx;
  510. border-radius: 50rpx;
  511. text-align: center;
  512. background: #f8f8f8;
  513. color: #333333;
  514. font-size: 30rpx;
  515. &:nth-last-of-type(1) {
  516. margin-left: 20rpx;
  517. }
  518. &.active {
  519. background: linear-gradient(to right, #f97c55, #f44545) !important;
  520. color: #ffffff !important;
  521. }
  522. }
  523. }
  524. }
  525. .remind {
  526. text-align: center;
  527. color: #fb231f;
  528. font-size: 28rpx;
  529. }
  530. .bottom {
  531. position: fixed;
  532. bottom: 0;
  533. width: 100%;
  534. height: 100rpx;
  535. padding: 6rpx 30rpx;
  536. background-color: #fff;
  537. border-top: 1px solid #eeeeee;
  538. &-btn {
  539. color: #fff;
  540. text-align: center;
  541. font-size: 32rpx;
  542. font-weight: bold;
  543. line-height: 88rpx;
  544. height: 88rpx;
  545. background: linear-gradient(93deg, #ff232c 0%, #ff571b 100%);
  546. opacity: 1;
  547. border-radius: 44px;
  548. }
  549. }
  550. </style>