|
@@ -13,80 +13,331 @@
|
|
|
// eslint-disable-next-line unused-imports/no-unused-imports
|
|
|
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
|
|
|
import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js'
|
|
|
+import { divisionsTreeList } from '@/api/common'
|
|
|
+import { addressAdd, addressDetail, addressUpdate } from '@/api/mine'
|
|
|
+import { getPageParams, goBack } from '@/utils/page'
|
|
|
+import { toast } from '@/utils/toast'
|
|
|
|
|
|
defineOptions({
|
|
|
name: 'AddressBookOperate', // 地址簿新增&编辑
|
|
|
})
|
|
|
+
|
|
|
+const userInfo = computed(() => {
|
|
|
+ return getUserInfoHook()
|
|
|
+})
|
|
|
// z-paging
|
|
|
const paging = ref(null)
|
|
|
// 类似mixins,如果是页面滚动务必要写这一行,并传入当前ref绑定的paging,注意此处是paging,而非paging.value
|
|
|
useZPaging(paging)
|
|
|
|
|
|
-const model = ref({})
|
|
|
+// 编辑模式相关
|
|
|
+const isEditMode = ref(false)
|
|
|
+const addressId = ref<any>(null)
|
|
|
+
|
|
|
+const model = ref<any>({
|
|
|
+ areaCodes: [],
|
|
|
+ realName: '',
|
|
|
+ phone: '',
|
|
|
+ detail: '',
|
|
|
+ postCode: '',
|
|
|
+ isDefault: 0,
|
|
|
+})
|
|
|
+const area = ref<any[]>([[]])
|
|
|
+const form = ref<any>(null)
|
|
|
+
|
|
|
+// 地址显示文本(用于编辑模式回显)
|
|
|
+const addressDisplayText = ref('')
|
|
|
+
|
|
|
+// 监听地址选择变化,清空预设显示文本
|
|
|
+watch(() => model.value.areaCodes, (newVal) => {
|
|
|
+ if (newVal && newVal.length > 0) {
|
|
|
+ addressDisplayText.value = ''
|
|
|
+ }
|
|
|
+}, { deep: true })
|
|
|
+
|
|
|
+// 城市选择
|
|
|
+async function columnChange({ selectedItem, resolve, finish }: any) {
|
|
|
+ // 模拟异步请求
|
|
|
+ console.log(selectedItem)
|
|
|
+ const res = await divisionsTreeList({ pid: selectedItem.pid, name: selectedItem.name })
|
|
|
+ if (res.data[0].child && res.data[0].child.length) {
|
|
|
+ resolve(res.data[0].child)
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ finish()
|
|
|
+ }
|
|
|
+}
|
|
|
+// 格式化方法 - 动态显示所有选中项
|
|
|
+function displayFormat(selectedItems: any[]) {
|
|
|
+ // 如果是编辑模式且有预设的显示文本,优先使用预设文本
|
|
|
+ if (isEditMode.value && addressDisplayText.value) {
|
|
|
+ return addressDisplayText.value
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!selectedItems || selectedItems.length === 0) {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果只有一个选项,直接返回
|
|
|
+ if (selectedItems.length === 1) {
|
|
|
+ return selectedItems[0].name || selectedItems[0]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 动态拼接所有选中项,用 / 分隔
|
|
|
+ return selectedItems
|
|
|
+ .filter(item => item && (item.name || item)) // 过滤空值
|
|
|
+ .map(item => item.name || item) // 提取名称
|
|
|
+ .join('/')
|
|
|
+}
|
|
|
+
|
|
|
+// 存储原始地址数据(用于编辑模式保存)
|
|
|
+const originalAddressData = ref<any>(null)
|
|
|
+
|
|
|
+// 获取地址详情
|
|
|
+async function getAddressDetail() {
|
|
|
+ try {
|
|
|
+ await uni.showLoading({
|
|
|
+ title: 'Loading...',
|
|
|
+ })
|
|
|
+
|
|
|
+ const res = await addressDetail({ id: addressId.value })
|
|
|
+
|
|
|
+ if (res.code === '200' && res.data) {
|
|
|
+ const data = res.data
|
|
|
+
|
|
|
+ // 保存原始地址数据
|
|
|
+ originalAddressData.value = {
|
|
|
+ province: data.province,
|
|
|
+ city: data.city,
|
|
|
+ district: data.district,
|
|
|
+ street: data.street,
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建地址显示文本
|
|
|
+ const addressParts = [data.province, data.city, data.district, data.street].filter(Boolean)
|
|
|
+ addressDisplayText.value = addressParts.join('/')
|
|
|
+
|
|
|
+ model.value = {
|
|
|
+ areaCodes: [], // 编辑模式下先清空,用户点击时重新选择
|
|
|
+ realName: data.realName || '',
|
|
|
+ phone: data.phone || '',
|
|
|
+ detail: data.detail || '',
|
|
|
+ postCode: data.postCode || '',
|
|
|
+ isDefault: data.isDefault || false,
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('Address detail loaded:', data)
|
|
|
+ console.log('Display text:', addressDisplayText.value)
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ toast.error(res.message || 'Failed to load address details')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (error: any) {
|
|
|
+ console.error('Get address detail error:', error)
|
|
|
+ toast.error(error.message || 'Failed to load address details')
|
|
|
+ }
|
|
|
+ finally {
|
|
|
+ uni.hideLoading()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 保存地址
|
|
|
+async function save() {
|
|
|
+ try {
|
|
|
+ // 自定义校验
|
|
|
+ if (!model.value.realName?.trim()) {
|
|
|
+ toast.info('Please enter full name')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!model.value.phone?.trim()) {
|
|
|
+ toast.info('Please enter phone number')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 在新增模式下必须选择地址,编辑模式下可以使用原有地址
|
|
|
+ if (!isEditMode.value && (!model.value.areaCodes || model.value.areaCodes.length === 0)) {
|
|
|
+ toast.info('Please select province/district')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!model.value.detail?.trim()) {
|
|
|
+ toast.info('Please enter detailed address')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!model.value.postCode?.trim()) {
|
|
|
+ toast.info('Please enter postcode')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示加载提示
|
|
|
+ await uni.showLoading({
|
|
|
+ title: 'Saving...',
|
|
|
+ })
|
|
|
+
|
|
|
+ // 处理地址数据
|
|
|
+ let addressData: any = {}
|
|
|
+
|
|
|
+ if (model.value.areaCodes && model.value.areaCodes.length > 0) {
|
|
|
+ // 用户重新选择了地址
|
|
|
+ addressData = {
|
|
|
+ province: model.value.areaCodes[0],
|
|
|
+ city: model.value.areaCodes[1],
|
|
|
+ district: model.value.areaCodes[2],
|
|
|
+ street: model.value.areaCodes[3],
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (isEditMode.value && originalAddressData.value) {
|
|
|
+ // 编辑模式下用户没有重新选择地址,使用原始数据
|
|
|
+ addressData = originalAddressData.value
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 新增模式下必须选择地址
|
|
|
+ toast.info('Please select province/district')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const params = {
|
|
|
+ ...model.value,
|
|
|
+ realName: model.value.realName.trim(),
|
|
|
+ phone: model.value.phone.trim(),
|
|
|
+ detail: model.value.detail.trim(),
|
|
|
+ postCode: model.value.postCode.trim(),
|
|
|
+ ...addressData,
|
|
|
+ uid: userInfo.value.userId,
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是编辑模式,添加id参数并调用更新接口
|
|
|
+ if (isEditMode.value && addressId.value) {
|
|
|
+ params.id = addressId.value
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = isEditMode.value
|
|
|
+ ? await addressUpdate(params)
|
|
|
+ : await addressAdd(params)
|
|
|
+
|
|
|
+ uni.hideLoading()
|
|
|
+
|
|
|
+ if (res.code === '200') {
|
|
|
+ toast.success(isEditMode.value ? 'Address updated successfully' : 'Address saved successfully')
|
|
|
+
|
|
|
+ // 延迟返回上一页
|
|
|
+ setTimeout(() => {
|
|
|
+ goBack()
|
|
|
+ }, 1500)
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ toast.error(res.message || 'Save failed, please try again')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (error: any) {
|
|
|
+ uni.hideLoading()
|
|
|
+ console.error('Save address error:', error)
|
|
|
+ toast.error(error.message || 'Save failed, please try again')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onLoad(async (options: any) => {
|
|
|
+ try {
|
|
|
+ // 获取地区数据
|
|
|
+ const res = await divisionsTreeList({})
|
|
|
+ console.log(res)
|
|
|
+ area.value = [res.data]
|
|
|
+
|
|
|
+ // 检查是否为编辑模式
|
|
|
+ const params = getPageParams(options)
|
|
|
+ if (params.id) {
|
|
|
+ isEditMode.value = true
|
|
|
+ addressId.value = params.id
|
|
|
+ // 设置页面标题
|
|
|
+ uni.setNavigationBarTitle({
|
|
|
+ title: 'Edit Address',
|
|
|
+ })
|
|
|
+ // 获取地址详情
|
|
|
+ await getAddressDetail()
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 新增模式
|
|
|
+ uni.setNavigationBarTitle({
|
|
|
+ title: 'Add Address',
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (error: any) {
|
|
|
+ console.error('Page load error:', error)
|
|
|
+ toast.error(error.message || 'Page load failed')
|
|
|
+ }
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
<z-paging ref="paging" use-page-scroll>
|
|
|
<view class="py-20rpx">
|
|
|
<wd-form ref="form" :model="model">
|
|
|
- <wd-cell-group border>
|
|
|
+ <wd-cell-group>
|
|
|
<wd-input
|
|
|
- v-model="model.value1"
|
|
|
+ v-model="model.realName"
|
|
|
label="Full Name"
|
|
|
label-width="240rpx"
|
|
|
custom-label-class="text-28rpx"
|
|
|
- prop="value1"
|
|
|
clearable
|
|
|
placeholder="Full Name"
|
|
|
- :rules="[{ required: true, message: 'Full Name is required' }]"
|
|
|
+ required
|
|
|
/>
|
|
|
<wd-input
|
|
|
- v-model="model.value1"
|
|
|
+ v-model="model.phone"
|
|
|
label="Phone Number"
|
|
|
label-width="240rpx"
|
|
|
custom-label-class="text-28rpx"
|
|
|
- prop="value1"
|
|
|
clearable
|
|
|
placeholder="+88"
|
|
|
- :rules="[{ required: true, message: 'Phone Number is required' }]"
|
|
|
- />
|
|
|
- <wd-input
|
|
|
- v-model="model.value1"
|
|
|
- label="Privince/District"
|
|
|
- label-width="240rpx"
|
|
|
- custom-label-class="text-28rpx"
|
|
|
- prop="value1"
|
|
|
- clearable
|
|
|
- placeholder="Please choose"
|
|
|
- :rules="[{ required: true, message: 'Privince/District is required' }]"
|
|
|
+ required
|
|
|
/>
|
|
|
+ <wd-cell title="Privince/District" required vertical>
|
|
|
+ <wd-col-picker
|
|
|
+ v-model="model.areaCodes"
|
|
|
+ :columns="area"
|
|
|
+ clearable
|
|
|
+ placeholder="Please choose"
|
|
|
+ value-key="name"
|
|
|
+ label-key="name"
|
|
|
+ :column-change="columnChange"
|
|
|
+ required
|
|
|
+ :root-portal="true"
|
|
|
+ :z-index="9999"
|
|
|
+ :display-format="displayFormat"
|
|
|
+ />
|
|
|
+ </wd-cell>
|
|
|
+
|
|
|
<wd-cell title="Floor/Unit No./Street" vertical required>
|
|
|
<wd-textarea
|
|
|
- v-model="model.value1"
|
|
|
-
|
|
|
- prop="value1"
|
|
|
- clearable auto-height
|
|
|
+ v-model="model.detail"
|
|
|
+ clearable
|
|
|
+ auto-height
|
|
|
placeholder="Detailed address"
|
|
|
- :rules="[{ required: true, message: 'Floor/Unit No./Street is required' }]"
|
|
|
/>
|
|
|
</wd-cell>
|
|
|
<wd-input
|
|
|
- v-model="model.value1"
|
|
|
+ v-model="model.postCode"
|
|
|
label="Postcode"
|
|
|
label-width="240rpx"
|
|
|
custom-label-class="text-28rpx"
|
|
|
- prop="value1"
|
|
|
clearable
|
|
|
placeholder="Your postcode"
|
|
|
- :rules="[{ required: true, message: 'Postcode is required' }]"
|
|
|
+ required
|
|
|
/>
|
|
|
+ <wd-cell title="Default">
|
|
|
+ <wd-switch v-model="model.isDefault" :active-value="1" :inactive-value="0" size="42rpx" />
|
|
|
+ </wd-cell>
|
|
|
</wd-cell-group>
|
|
|
</wd-form>
|
|
|
</view>
|
|
|
<template #bottom>
|
|
|
<view class="bg-white/60 px-28rpx py-30rpx backdrop-blur-20">
|
|
|
- <wd-button plain block>
|
|
|
- Save
|
|
|
+ <wd-button plain block @click="save">
|
|
|
+ {{ isEditMode ? 'Update' : 'Save' }}
|
|
|
</wd-button>
|
|
|
</view>
|
|
|
</template>
|