address.vue 14 KB

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