add-address.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
  2. // import parseAddress from "../../../../utils/address-parse";
  3. import AddressParse from "address-parse";
  4. import tickleBehavior, {
  5. getTickleContext,
  6. } from "../../../../core/behavior/tickle.behavior";
  7. import { getAddressDetailByPatientIdMethod } from "../../request";
  8. import { areaList } from "@vant/area-data";
  9. import {
  10. addAddressMethod,
  11. deleteAddressMethod,
  12. updateAddressMethod,
  13. } from "../../request";
  14. // module/diet/pages/delivery-address/delivery-address.ts
  15. // import parseAddress from '../../utils/smart-parse-address';
  16. Page({
  17. behaviors: [PageContainerBehavior, tickleBehavior],
  18. properties: {},
  19. data: {
  20. type: "",
  21. areaList,
  22. id: "",
  23. formData: {
  24. id: "",
  25. provinceCode: "",
  26. provinceName: "",
  27. cityCode: "",
  28. cityName: "",
  29. areaCode: "",
  30. areaName: "",
  31. liaison: "",
  32. phone: "",
  33. isDefault: "N",
  34. detailAddress: "",
  35. tagList: [],
  36. },
  37. region: "", // 展示在输入框的省市区字符串
  38. regionValue: [], // picker 选中的值
  39. regionPickerVisible: false,
  40. tagList: ["家", "公司", "学校"],
  41. selectedTag: "",
  42. focusName: false,
  43. focusPhone: false,
  44. focusDetail: false,
  45. smartPasteValue: "",
  46. smartPasteFocus: false,
  47. smartPastePlaceholder: "点击此处粘贴地址信息,可快速识别姓名、电话、地址",
  48. saving: false,
  49. },
  50. onClearDetail() {
  51. this.setData({ detail: "" });
  52. },
  53. onRegionConfirm(e: any) {
  54. this.setData({
  55. region:
  56. e.detail.values[0].name +
  57. ", " +
  58. e.detail.values[1].name +
  59. "," +
  60. e.detail.values[2].name,
  61. regionValue: e.detail.values,
  62. regionPickerVisible: false,
  63. "formData.provinceCode": e.detail.values[0].code,
  64. "formData.provinceName": e.detail.values[0].name,
  65. "formData.cityCode": e.detail.values[1].code,
  66. "formData.cityName": e.detail.values[1].name,
  67. "formData.areaCode": e.detail.values[2].code,
  68. "formData.areaName": e.detail.values[2].name,
  69. });
  70. },
  71. onRegionCancel(e: any) {
  72. this.setData({
  73. regionPickerVisible: false,
  74. });
  75. },
  76. // 输入框双向绑定
  77. onInput(e) {
  78. const { field } = e.currentTarget.dataset;
  79. // 这里 field 必须是 'name'、'phone'、'detail' 之一
  80. this.setData({ [field]: e.detail.value });
  81. },
  82. // 收货人
  83. onFocusInput() {
  84. this.setData({ focusName: true });
  85. },
  86. onBlurInput() {
  87. this.setData({ focusName: false });
  88. },
  89. // 手机号
  90. onFocusPhone() {
  91. this.setData({ focusPhone: true });
  92. },
  93. onBlurPhone() {
  94. this.setData({ focusPhone: false });
  95. },
  96. // 详细地址
  97. onFocusDetail() {
  98. this.setData({ focusDetail: true });
  99. },
  100. onBlurDetail() {
  101. this.setData({ focusDetail: false });
  102. },
  103. // 地区选择
  104. onShowRegionPicker() {
  105. this.setData({ regionPickerVisible: true });
  106. },
  107. // 标签选择
  108. onTagSelect(e) {
  109. const tag = e.currentTarget.dataset.tag;
  110. this.setData({ selectedTag: tag });
  111. },
  112. // 默认地址开关
  113. onDefaultChange(e) {
  114. if (e.detail.value) {
  115. this.setData({ "formData.isDefault": "Y" });
  116. } else {
  117. this.setData({ "formData.isDefault": "N" });
  118. }
  119. },
  120. // 添加地址
  121. async onAddress(params: any) {
  122. const res = await addAddressMethod(params);
  123. wx.showToast({ title: "添加成功", icon: "success" });
  124. },
  125. // 更新地址
  126. async onUpdate(params: any) {
  127. const res = await updateAddressMethod(params);
  128. wx.showToast({ title: "更新成功", icon: "success" });
  129. },
  130. // 保存
  131. async onSave() {
  132. if (this.data.saving) return; // 防重复
  133. try {
  134. const { liaison, phone, detailAddress } = this.data.formData;
  135. if (!liaison) {
  136. wx.showToast({ title: "收货人为空", icon: "none" });
  137. return;
  138. }
  139. if (!phone) {
  140. wx.showToast({ title: "手机号为空", icon: "none" });
  141. return;
  142. }
  143. // 手机号校验
  144. if (!/^1\d{10}$/.test(phone)) {
  145. wx.showToast({ title: "您填写的手机号有误", icon: "none" });
  146. return;
  147. }
  148. if (!this.data.region) {
  149. wx.showToast({ title: "所在地区为空", icon: "none" });
  150. return;
  151. }
  152. if (!detailAddress) {
  153. wx.showToast({ title: "详细地址为空", icon: "none" });
  154. return;
  155. }
  156. this.setData({
  157. "formData.tagList": [this.data.selectedTag],
  158. });
  159. this.setData({ saving: true });
  160. if (this.data.id) {
  161. this.data.formData.id = this.data.id;
  162. await this.onUpdate(this.data.formData);
  163. } else {
  164. await this.onAddress(this.data.formData);
  165. }
  166. this.setData({ saving: false });
  167. // if (this.data.type === "orderDetail") {
  168. // // 订单详情页
  169. // wx.redirectTo({
  170. // url: "/module/article/pages/order-detail/order-detail",
  171. // });
  172. // } else if (this.data.type === "orderList") {
  173. // // 订单列表页
  174. // wx.redirectTo({
  175. // url: "/module/article/pages/order-list/order-list",
  176. // });
  177. // }
  178. // 返回上一页
  179. const pages = getCurrentPages();
  180. if (pages.length > 1) {
  181. const prevPage = pages[pages.length - 2];
  182. if (
  183. prevPage.route ===
  184. "module/article/pages/manage-address/manage-address"
  185. ) {
  186. wx.redirectTo({
  187. url: "/module/article/pages/manage-address/manage-address",
  188. });
  189. }
  190. }
  191. // 保存成功后可自动跳转或提示
  192. } catch (error: any) {
  193. wx.showToast({ title: error.errMsg, icon: "none" });
  194. this.setData({ saving: false }); // 无论成功失败都重置
  195. }
  196. },
  197. // 输入框聚焦
  198. onSmartPasteFocus() {
  199. this.setData({
  200. smartPasteFocus: true,
  201. smartPastePlaceholder:
  202. "粘贴或输入地址信息,如:张三,1801889955,上海市浦东新区浦东大道100号,可自动识别并填充文本",
  203. });
  204. },
  205. // 输入框失焦
  206. onSmartPasteBlur() {
  207. if (this.data.smartPasteValue) {
  208. this.setData({
  209. smartPasteFocus: true,
  210. });
  211. } else {
  212. this.setData({
  213. smartPasteFocus: false,
  214. smartPastePlaceholder:
  215. "点击此处粘贴地址信息,可快速识别姓名、电话、地址",
  216. });
  217. }
  218. },
  219. // 输入内容
  220. onSmartPasteInput(e: any) {
  221. this.setData({ smartPasteValue: e.detail.value });
  222. },
  223. // 清空
  224. onClearSmartPaste() {
  225. this.setData({ smartPasteValue: "" });
  226. },
  227. // 获取省市区编码
  228. getCodeByName(list: any, name: any) {
  229. for (const code in list) {
  230. if (list[code] === name) {
  231. return code;
  232. }
  233. }
  234. return "";
  235. },
  236. // 删除地址
  237. async onDelete() {
  238. const that = this;
  239. wx.showModal({
  240. title: "确定删除地址?",
  241. content: "删除后地址不可恢复",
  242. confirmColor: "#1D6FF6",
  243. cancelColor: "#999",
  244. cancelText: "取消",
  245. confirmText: "删除",
  246. success: async (res) => {
  247. if (res.confirm) {
  248. const res = await deleteAddressMethod(that.data.id);
  249. wx.showToast({ title: "删除成功", icon: "success" });
  250. wx.navigateBack();
  251. }
  252. },
  253. });
  254. },
  255. // 识别
  256. async onRecognizeSmartPaste() {
  257. let value = this.data.smartPasteValue.trim();
  258. if (!value) return;
  259. // 1. 统一分隔符,兼容各种输入
  260. value = value.replace(/[\n,、;;::]/g, ",").replace(/\s+/g, ",");
  261. // 2. 提取手机号
  262. const phoneMatch = value.match(/1\\d{10}/);
  263. let phone = phoneMatch ? phoneMatch[0] : "";
  264. if (phone) value = value.replace(phone, ",");
  265. // 3. 提取姓名(2-4个汉字,通常在最前面)
  266. let name = "";
  267. let nameMatch = value.match(/^[\\u4e00-\\u9fa5]{2,4}/);
  268. if (nameMatch) {
  269. name = nameMatch[0];
  270. value = value.replace(name, ",");
  271. }
  272. // 5. 用智能库进一步解析省市区
  273. let [result] = AddressParse.parse(this.data.smartPasteValue);
  274. result = {
  275. ...result,
  276. name: result.name,
  277. phone: result.phone || result.mobile,
  278. detailAddress: result.details,
  279. county: result.area,
  280. provinceCode: result.provinceCode,
  281. cityCode: result.cityCode,
  282. areaCode: result.areaCode,
  283. };
  284. // 6. 校验
  285. if (!result.name || !result.phone) {
  286. wx.showToast({ title: "请按照提示输入正确的地址信息", icon: "none" });
  287. return;
  288. }
  289. if (!/^1\\d{10}$/.test(result.phone)) {
  290. wx.showToast({ title: "请按照提示输入正确的地址信息", icon: "none" });
  291. }
  292. // 7. 省市区识别
  293. const { province, city, county } = result;
  294. const { province_list, city_list, county_list } = this.data.areaList;
  295. if (province && city) {
  296. const provinceCode = this.getCodeByName(province_list, province);
  297. const cityCode = this.getCodeByName(city_list, city);
  298. const areaCode = this.getCodeByName(county_list, county);
  299. this.setData({
  300. "formData.provinceName": province,
  301. "formData.cityName": city,
  302. "formData.areaName": county || "",
  303. "formData.provinceCode": provinceCode,
  304. "formData.cityCode": cityCode,
  305. "formData.areaCode": areaCode,
  306. "formData.detailAddress": result.details,
  307. region: `${province},${city}${county ? "," + county : ""}`,
  308. });
  309. } else {
  310. wx.showToast({ title: "请按照提示输入正确的地址信息", icon: "none" });
  311. }
  312. // 其他字段(如姓名、手机号、详细地址)可以单独 setData,不受省市影响
  313. this.setData({
  314. "formData.liaison": result.name,
  315. "formData.phone": result.phone || result.mobile,
  316. });
  317. wx.showToast({ title: "已识别", icon: "success" });
  318. },
  319. // 获取地址详情
  320. async getAddressDetail(id: string) {
  321. try {
  322. const res = await getAddressDetailByPatientIdMethod(id);
  323. if (res && res.data) {
  324. this.setData({
  325. "formData.isDefault": res.data.isDefault,
  326. "formData.liaison": res.data.liaison,
  327. "formData.phone": res.data.phone,
  328. "formData.detailAddress": res.data.detailAddress,
  329. "formData.provinceName": res.data.provinceName,
  330. "formData.cityName": res.data.cityName,
  331. "formData.areaName": res.data.areaName,
  332. "formData.provinceCode": res.data.provinceCode,
  333. "formData.cityCode": res.data.cityCode,
  334. "formData.areaCode": res.data.areaCode,
  335. region: `${res.data.provinceName} ${res.data.cityName} ${res.data.areaName}`,
  336. });
  337. // 标签
  338. this.setData({
  339. "formData.tagList": res.data.tagList,
  340. });
  341. this.setData({
  342. selectedTag: res.data.tagList[0] || "",
  343. });
  344. }
  345. } catch (error: any) {
  346. getTickleContext.call(this).showWarnMessage(error.errMsg);
  347. }
  348. },
  349. onLoad(options) {
  350. if (options.id) {
  351. this.setData({ id: options.id });
  352. this.getAddressDetail(options.id);
  353. }
  354. if (options.type) {
  355. this.setData({ type: options.type });
  356. }
  357. if (options.imported) {
  358. const imported = JSON.parse(decodeURIComponent(options.imported));
  359. const { provinceName, cityName, areaName } = imported;
  360. const { province_list, city_list, county_list } = this.data.areaList;
  361. if (provinceName && cityName) {
  362. const provinceCode = this.getCodeByName(province_list, provinceName);
  363. const cityCode = this.getCodeByName(city_list, cityName);
  364. const areaCode = this.getCodeByName(county_list, areaName);
  365. this.setData({
  366. "formData.isDefault": imported.isDefault || "N",
  367. "formData.liaison": imported.liaison,
  368. "formData.phone": imported.phone,
  369. "formData.detailAddress": imported.detailAddress,
  370. "formData.provinceName": imported.provinceName,
  371. "formData.cityName": imported.cityName,
  372. "formData.areaName": imported.areaName,
  373. "formData.provinceCode": provinceCode,
  374. "formData.cityCode": cityCode,
  375. "formData.areaCode": areaCode,
  376. region: imported.region,
  377. });
  378. this.setData({
  379. selectedTag: "",
  380. type: "imported",
  381. });
  382. }
  383. }
  384. },
  385. });