address-edit.uvue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <cl-page>
  3. <view class="p-3">
  4. <view class="p-4 bg-white rounded-2xl dark:!bg-surface-800 mb-3">
  5. <cl-form ref="formRef" v-model="formData" :rules="rules" :disabled="saving">
  6. <cl-form-item :label="t('收货人')" prop="contact" required>
  7. <cl-input
  8. v-model="formData.contact"
  9. :placeholder="t('请输入收货人姓名')"
  10. ></cl-input>
  11. </cl-form-item>
  12. <cl-form-item :label="t('手机号')" prop="phone" required>
  13. <cl-input
  14. v-model="formData.phone"
  15. :placeholder="t('请输入手机号')"
  16. :maxlength="11"
  17. type="number"
  18. ></cl-input>
  19. </cl-form-item>
  20. <cl-form-item :label="t('地区')" prop="province" required>
  21. <cl-cascader
  22. v-model="regions"
  23. :placeholder="t('选择省市区')"
  24. :options="pcaOptions"
  25. @change="onRegionsChange"
  26. ></cl-cascader>
  27. </cl-form-item>
  28. <cl-form-item :label="t('详细地址')" prop="address" required>
  29. <cl-input
  30. v-model="formData.address"
  31. :placeholder="t('小区楼栋、门牌号、村等')"
  32. ></cl-input>
  33. </cl-form-item>
  34. </cl-form>
  35. </view>
  36. <cl-list>
  37. <cl-list-item :label="t('默认地址')">
  38. <cl-switch v-model="formData.isDefault"></cl-switch>
  39. </cl-list-item>
  40. </cl-list>
  41. </view>
  42. <cl-footer>
  43. <cl-button @tap="save()">{{ t("保存") }}</cl-button>
  44. </cl-footer>
  45. </cl-page>
  46. </template>
  47. <script lang="ts" setup>
  48. import { router, isEmpty, type Response, request, parse } from "@/cool";
  49. import { t } from "@/locale";
  50. import { useCascader, useForm, useUi, type ClFormRule } from "@/uni_modules/cool-ui";
  51. import { type Ref, ref } from "vue";
  52. import pca from "@/data/pca.json";
  53. import type { UserAddress } from "../types";
  54. const props = defineProps({
  55. id: {
  56. type: String,
  57. default: ""
  58. }
  59. });
  60. const ui = useUi();
  61. const { formRef, validate } = useForm();
  62. // 省市区级联选项数据
  63. const pcaOptions = useCascader(pca);
  64. // 地区选择的值,格式为 [省, 市, 区]
  65. const regions = ref<string[]>([]);
  66. // 表单数据,包含收货人、手机号、地区、详细地址、是否默认等字段
  67. const formData = ref<UserAddress>({
  68. contact: "",
  69. phone: "",
  70. province: "",
  71. city: "",
  72. district: "",
  73. address: "",
  74. isDefault: false
  75. }) as Ref<UserAddress>;
  76. // 表单验证规则,校验收货人、手机号、详细地址、地区等必填项
  77. const rules = new Map<string, ClFormRule[]>([
  78. ["contact", [{ required: true, message: t("收货人不能为空") }]],
  79. [
  80. "phone",
  81. [
  82. { required: true, message: t("手机号不能为空") },
  83. { pattern: /^1[3-9]\d{9}$/, message: t("手机号格式不正确") }
  84. ]
  85. ],
  86. ["address", [{ required: true, message: t("详细地址不能为空") }]],
  87. ["province", [{ required: true, message: t("所在地区不能为空") }]]
  88. ]);
  89. // 保存按钮loading状态
  90. const saving = ref(false);
  91. /**
  92. * 保存地址信息
  93. * 1. 校验表单
  94. * 2. 组装数据
  95. * 3. 请求后端接口,新增或更新地址
  96. */
  97. function save() {
  98. validate((valid, errors) => {
  99. if (valid) {
  100. ui.showLoading(t("保存中"));
  101. // 解构地区信息
  102. const [province, city, district] = regions.value;
  103. saving.value = true;
  104. // 合并表单数据和地区信息
  105. const data = {
  106. ...formData.value,
  107. province,
  108. city,
  109. district
  110. };
  111. // 根据是否有id判断是新增还是编辑
  112. request({
  113. url: `/app/user/address/${props.id != "" ? "update" : "add"}`,
  114. method: "POST",
  115. data
  116. })
  117. .then(() => {
  118. // 保存成功返回上一页
  119. router.back();
  120. })
  121. .catch((err) => {
  122. // 保存失败提示错误信息
  123. ui.showToast({ message: (err as Response).message! });
  124. })
  125. .finally(() => {
  126. ui.hideLoading();
  127. saving.value = false;
  128. });
  129. } else {
  130. // 校验失败提示第一个错误
  131. ui.showToast({ message: errors[0].message });
  132. }
  133. });
  134. }
  135. /**
  136. * 获取地址详情(编辑时调用)
  137. * 1. 请求后端接口获取地址详情
  138. * 2. 回填表单和地区选择
  139. */
  140. function getInfo() {
  141. request({
  142. url: "/app/user/address/info",
  143. data: { id: props.id }
  144. })
  145. .then((res) => {
  146. if (res != null) {
  147. // 解析并赋值表单数据
  148. formData.value = parse<UserAddress>(res)!;
  149. // 回填地区选择
  150. regions.value = [
  151. formData.value.province,
  152. formData.value.city,
  153. formData.value.district
  154. ];
  155. }
  156. })
  157. .catch((err) => {
  158. ui.showToast({ message: (err as Response).message! });
  159. });
  160. }
  161. /**
  162. * 地区选择变化时触发
  163. * @param value 选中的地区数组 [省, 市, 区]
  164. */
  165. function onRegionsChange(value: string[]) {
  166. const [province, city, district] = isEmpty(value) ? ["", "", ""] : value;
  167. formData.value.province = province;
  168. formData.value.city = city;
  169. formData.value.district = district;
  170. }
  171. onReady(() => {
  172. if (props.id != "") {
  173. getInfo();
  174. }
  175. });
  176. </script>