address_edit.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <template>
  2. <view class="bg-[var(--page-bg-color)] min-h-[100vh] overflow-hidden address-edit" :style="themeColor()">
  3. <u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
  4. <view class="sidebar-margin card-template mt-[var(--top-m)] py-[20rpx]">
  5. <view>
  6. <u-form-item label="收货人" prop="consignee" labelWidth="200rpx">
  7. <u-input fontSize="28rpx" v-model.trim="formData.consignee" border="none" clearable
  8. maxlength="25" placeholderStyle="color: #888" placeholder="请输入收货人姓名" />
  9. </u-form-item>
  10. </view>
  11. <view class="mt-[16rpx]">
  12. <u-form-item label="手机号码" prop="phone" labelWidth="200rpx">
  13. <u-input fontSize="28rpx" v-model.trim="formData.phone" maxlength="11" border="none" clearable
  14. placeholder="请输入手机号码" placeholderStyle="color: #888" />
  15. </u-form-item>
  16. </view>
  17. <view class="mt-[16rpx]">
  18. <u-form-item label="选择地区" prop="provincialCityCountry" labelWidth="200rpx">
  19. <view class="flex w-full items-center h-[52rpx]" @click="selectArea">
  20. <view v-if="!formData.provincialCityCountry" class="text-[#888] text-[28rpx] flex-1">请选择地区
  21. </view>
  22. <view v-else class="text-[28rpx] flex-1 leading-[1.4]">{{ formData.provincialCityCountry }}
  23. </view>
  24. </view>
  25. </u-form-item>
  26. </view>
  27. <view class="mt-[16rpx]">
  28. <u-form-item label="详细地址" prop="address" labelWidth="200rpx">
  29. <u-input fontSize="28rpx" v-model="formData.address" border="none" clearable maxlength="120"
  30. placeholder="请填写详细地址" placeholderStyle="color: #888" />
  31. </u-form-item>
  32. </view>
  33. </view>
  34. <view class="sidebar-margin card-template mt-[var(--top-m)] py-[10rpx]">
  35. <u-form-item label="设为默认地址" prop="name" :border-bottom="false" labelWidth="200rpx">
  36. <u-switch v-model="formData.defaultAddress" size="20" :activeValue="0" :inactiveValue="1"
  37. activeColor="var(--primary-color)" inactiveColor="var(--temp-bg)" />
  38. </u-form-item>
  39. </view>
  40. </u-form>
  41. <view class="w-full footer">
  42. <view
  43. class="py-[var(--top-m)] px-[var(--sidebar-m)] footer w-full fixed bottom-30 left-0 right-0 box-border">
  44. <button hover-class="none"
  45. class="primary-btn-bg !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500"
  46. @click="save" :disabled="btnDisabled" :loading="operateLoading"
  47. :class="{ 'opacity-50': btnDisabled }">保存
  48. </button>
  49. </view>
  50. </view>
  51. <cityPicker :column="column" :default-value="defaultValue" :mask-close-able="maskCloseAble" @confirm="confirm"
  52. @cancel="cancel" :visible="visible" />
  53. </view>
  54. </template>
  55. <script setup lang="ts">
  56. import { ref, computed, nextTick } from 'vue'
  57. import { onLoad } from '@dcloudio/uni-app'
  58. import { redirect } from '@/utils/common'
  59. import { addAddress, editAddress, getAddressInfo } from '@/app/api/member'
  60. import cityPicker from '@/uni_modules/piaoyi-cityPicker/components/piaoyi-cityPicker/piaoyi-cityPicker'
  61. const column = ref(3)
  62. const defaultValue = ref<any>(null)
  63. const maskCloseAble = ref<any>(true)
  64. const visible = ref<any>(false)
  65. const formData: any = ref({
  66. consignee: '', phone: '', provincialNo: '', cityNo: '', countryNo: '', provincialCityCountry: '', address: '', defaultAddress: 0
  67. })
  68. const formRef: any = ref(null)
  69. const source = ref('')
  70. const btnDisabled = ref(false)
  71. onLoad((data: any) => {
  72. if (data.id) {
  73. getAddressInfo(data.id).then((res: any) => {
  74. res.data.defaultAddress = Number(res.data.defaultAddress)
  75. res.data && Object.assign(formData.value, res.data)
  76. })
  77. } else {
  78. formData.value = {
  79. consignee: '', phone: '', provincialNo: '', cityNo: '', countryNo: '', provincialCityCountry: '', address: '', defaultAddress: 0
  80. }
  81. }
  82. })
  83. const rules = computed(() => {
  84. return {
  85. 'consignee': {
  86. type: 'string',
  87. required: true,
  88. message: '请输入收货人姓名',
  89. trigger: ['blur', 'change'],
  90. },
  91. 'phone': [
  92. {
  93. type: 'string',
  94. required: true,
  95. message: '请输入手机号码',
  96. trigger: ['blur', 'change'],
  97. },
  98. {
  99. validator(rule: any, value: any, callback: any) {
  100. let mobile = /^1[3-9]\d{9}$/;
  101. if (!mobile.test(value)) {
  102. callback(new Error('请输入正确的手机号'))
  103. } else {
  104. callback()
  105. }
  106. }
  107. }
  108. ],
  109. 'provincialCityCountry': {
  110. validator() {
  111. let bool = true;
  112. if (uni.$u.test.isEmpty(formData.value.provincialCityCountry)) {
  113. bool = false;
  114. }
  115. return bool
  116. },
  117. message: '请选择地区'
  118. },
  119. 'address': {
  120. type: 'string',
  121. required: true,
  122. message: '请填写详细地址',
  123. trigger: ['blur', 'change']
  124. }
  125. }
  126. })
  127. const cancel = () => {
  128. visible.value = false
  129. }
  130. const confirm = (res: any) => {
  131. formData.value.provincialCityCountry = res.provinceName + '/' + res.cityName + '/' + res.areaName
  132. formData.value.provincialNo = parseAreaCode(res.code).province;
  133. formData.value.cityNo = parseAreaCode(res.code).city;
  134. formData.value.countryNo = res.code;
  135. visible.value = false
  136. }
  137. const parseAreaCode = (code: any) => {
  138. const provinceCode = code.substring(0, 2) + '0000';
  139. const cityCode = code.substring(0, 4) + '00';
  140. const districtCode = code;
  141. return {
  142. province: provinceCode,
  143. city: cityCode,
  144. district: districtCode
  145. };
  146. }
  147. const selectArea = () => {
  148. visible.value = true
  149. }
  150. const operateLoading = ref(false)
  151. const save = () => {
  152. const save = formData.value.id ? editAddress : addAddress
  153. formRef.value.validate().then(() => {
  154. if (operateLoading.value) return
  155. operateLoading.value = true
  156. btnDisabled.value = true
  157. save(formData.value).then((res: any) => {
  158. operateLoading.value = false
  159. setTimeout(() => {
  160. btnDisabled.value = false
  161. if (source.value == 'shop_order_payment') {
  162. const selectAddress = uni.getStorageSync('selectAddressCallback')
  163. if (selectAddress) {
  164. selectAddress.address_id = res.data.id || formData.value.id
  165. uni.setStorage({
  166. key: 'selectAddressCallback',
  167. data: selectAddress,
  168. success() {
  169. redirect({ url: selectAddress.back, mode: 'redirectTo' })
  170. }
  171. })
  172. }
  173. } else {
  174. redirect({
  175. url: '/app/pages/member/address',
  176. mode: 'redirectTo',
  177. param: { source: source.value }
  178. })
  179. }
  180. }, 1000)
  181. }).catch(() => {
  182. operateLoading.value = false
  183. btnDisabled.value = false
  184. })
  185. })
  186. }
  187. </script>
  188. <style lang="scss" scoped>
  189. .address-edit :deep(.u-form-item__body__left__content__label) {
  190. font-size: 28rpx !important;
  191. }
  192. .address-edit :deep(.u-form-item__body__right) {
  193. display: flex;
  194. }
  195. .footer {
  196. height: calc(100rpx + var(--top-m) + var(--top-m) + constant(safe-area-inset-bottom)) !important;
  197. height: calc(100rpx + var(--top-m) + var(--top-m) + env(safe-area-inset-bottom)) !important;
  198. }
  199. .borders {
  200. border: 1px solid var(--primary-color);
  201. }
  202. </style>