286 lines
7.5 KiB
Vue
286 lines
7.5 KiB
Vue
<template>
|
||
<view class="wrap">
|
||
<view class="form-item">
|
||
<text class="label">姓名</text>
|
||
<input class="input" v-model="name" placeholder="请输入姓名" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">手机号</text>
|
||
<input class="input" v-model="mobile" placeholder="请输入手机号" />
|
||
</view>
|
||
<view class="form-item region-picker" @click="openRegionPicker">
|
||
<text class="label">省市区</text>
|
||
<picker
|
||
mode="region"
|
||
:value="regionValue"
|
||
@change="onRegionChange"
|
||
@cancel="onRegionCancel"
|
||
>
|
||
<view class="picker-value" :class="{ placeholder: !hasRegion }">
|
||
{{ hasRegion ? `${province} ${city} ${district}` : '请选择省市区' }}
|
||
<text class="arrow-icon">›</text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">详细地址</text>
|
||
<input class="input" v-model="detail" placeholder="请输入详细地址" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">设为默认</text>
|
||
<switch :checked="isDefault" @change="e => isDefault = e.detail.value" />
|
||
</view>
|
||
<button class="submit" :disabled="loading" @click="onSubmit">保存</button>
|
||
<view v-if="error" class="error">{{ error }}</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import { addAddress, updateAddress, listAddresses, setDefaultAddress } from '../../api/appUser'
|
||
|
||
const id = ref('')
|
||
const name = ref('')
|
||
const mobile = ref('')
|
||
const province = ref('')
|
||
const city = ref('')
|
||
const district = ref('')
|
||
const detail = ref('')
|
||
let isDefault = false
|
||
const loading = ref(false)
|
||
const error = ref('')
|
||
|
||
// 省市区选择器
|
||
const regionValue = computed(() => [province.value, city.value, district.value])
|
||
const hasRegion = computed(() => province.value && city.value && district.value)
|
||
|
||
function onRegionChange(e) {
|
||
const values = e.detail.value
|
||
province.value = values[0] || ''
|
||
city.value = values[1] || ''
|
||
district.value = values[2] || ''
|
||
}
|
||
|
||
function onRegionCancel() {
|
||
// picker 取消时不做处理
|
||
}
|
||
|
||
function openRegionPicker() {
|
||
// 点击整行时触发 picker
|
||
}
|
||
|
||
function fill(data) {
|
||
name.value = data.name || data.realname || ''
|
||
mobile.value = data.mobile || data.phone || ''
|
||
province.value = data.province || ''
|
||
city.value = data.city || ''
|
||
district.value = data.district || ''
|
||
detail.value = data.address || data.detail || ''
|
||
isDefault = !!data.is_default
|
||
}
|
||
|
||
async function init(idParam) {
|
||
if (!idParam) {
|
||
const data = uni.getStorageSync('edit_address') || {}
|
||
if (data && data.id) fill(data)
|
||
return
|
||
}
|
||
const user_id = uni.getStorageSync('user_id')
|
||
try {
|
||
const list = await listAddresses(user_id)
|
||
const arr = Array.isArray(list) ? list : (list && (list.list || list.items)) || []
|
||
const found = arr.find(a => String(a.id) === String(idParam))
|
||
if (found) fill(found)
|
||
} catch (e) {}
|
||
}
|
||
|
||
async function onSubmit() {
|
||
const user_id = uni.getStorageSync('user_id')
|
||
if (!name.value || !mobile.value || !province.value || !city.value || !district.value || !detail.value) {
|
||
uni.showToast({ title: '请完善必填信息', icon: 'none' })
|
||
return
|
||
}
|
||
loading.value = true
|
||
error.value = ''
|
||
const payload = {
|
||
name: name.value,
|
||
mobile: mobile.value,
|
||
province: province.value,
|
||
city: city.value,
|
||
district: district.value,
|
||
address: detail.value,
|
||
is_default: isDefault
|
||
}
|
||
try {
|
||
let savedId = id.value
|
||
if (id.value) {
|
||
await updateAddress(user_id, id.value, payload)
|
||
savedId = id.value
|
||
} else {
|
||
try {
|
||
const res = await addAddress(user_id, payload)
|
||
savedId = (res && (res.id || res.address_id)) || ''
|
||
} catch (eAdd) {
|
||
const sc = eAdd && eAdd.statusCode
|
||
const bc = (eAdd && eAdd.data && (eAdd.data.code || eAdd.code)) || undefined
|
||
const msg = eAdd && (eAdd.message || eAdd.errMsg || '')
|
||
const isUniqueErr = sc === 400 && (bc === 10011 || (msg && msg.toLowerCase().includes('unique')))
|
||
if (isUniqueErr) {
|
||
try {
|
||
const list = await listAddresses(user_id)
|
||
const arr = Array.isArray(list) ? list : (list && (list.list || list.items)) || []
|
||
const found = arr.find(a => (a.mobile === mobile.value || a.phone === mobile.value) && (a.address === detail.value || a.detail === detail.value) && (a.city === city.value) && (a.district === district.value) && (a.province === province.value))
|
||
if (found) {
|
||
savedId = found.id
|
||
await updateAddress(user_id, savedId, payload)
|
||
}
|
||
} catch (_) {}
|
||
} else {
|
||
throw eAdd
|
||
}
|
||
}
|
||
}
|
||
if (isDefault) {
|
||
if (!savedId) {
|
||
try {
|
||
const list = await listAddresses(user_id)
|
||
const arr = Array.isArray(list) ? list : (list && (list.list || list.items)) || []
|
||
const found = arr.find(a => (a.mobile === mobile.value || a.phone === mobile.value) && (a.address === detail.value || a.detail === detail.value))
|
||
if (found) savedId = found.id
|
||
} catch (_) {}
|
||
}
|
||
if (savedId) {
|
||
await setDefaultAddress(user_id, savedId)
|
||
}
|
||
}
|
||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||
uni.navigateBack()
|
||
} catch (e) {
|
||
error.value = e && (e.message || e.errMsg) || '保存失败'
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
onLoad((opts) => {
|
||
id.value = (opts && opts.id) || ''
|
||
init(id.value)
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
/* ============================================
|
||
柯大鸭潮玩 - 地址编辑页面
|
||
采用暖橙色调的表单设计
|
||
============================================ */
|
||
|
||
.wrap {
|
||
padding: $spacing-lg;
|
||
min-height: 100vh;
|
||
background: $bg-page;
|
||
}
|
||
|
||
/* 表单项 */
|
||
.form-item {
|
||
display: flex;
|
||
align-items: center;
|
||
background: #FFFFFF;
|
||
border-radius: $radius-lg;
|
||
padding: $spacing-lg $spacing-xl;
|
||
margin-bottom: $spacing-md;
|
||
box-shadow: $shadow-sm;
|
||
transition: all 0.2s;
|
||
|
||
&:focus-within {
|
||
box-shadow: $shadow-md;
|
||
transform: translateY(-2rpx);
|
||
}
|
||
}
|
||
|
||
.label {
|
||
width: 160rpx;
|
||
font-size: $font-md;
|
||
font-weight: 600;
|
||
color: $text-main;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.input {
|
||
flex: 1;
|
||
font-size: $font-md;
|
||
color: $text-main;
|
||
background: transparent;
|
||
height: 48rpx;
|
||
}
|
||
|
||
/* 省市区选择器 */
|
||
.region-picker {
|
||
cursor: pointer;
|
||
|
||
picker {
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
.picker-value {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
font-size: $font-md;
|
||
color: $text-main;
|
||
height: 48rpx;
|
||
line-height: 48rpx;
|
||
|
||
&.placeholder {
|
||
color: $text-tertiary;
|
||
}
|
||
}
|
||
|
||
.arrow-icon {
|
||
font-size: 36rpx;
|
||
color: $text-tertiary;
|
||
margin-left: 12rpx;
|
||
transform: rotate(0deg);
|
||
transition: transform 0.2s;
|
||
}
|
||
|
||
/* 提交按钮 */
|
||
.submit {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
line-height: 96rpx;
|
||
margin-top: 60rpx;
|
||
background: $gradient-brand !important;
|
||
color: #FFFFFF !important;
|
||
border-radius: $radius-round;
|
||
font-size: $font-lg;
|
||
font-weight: 800;
|
||
border: none;
|
||
box-shadow: $shadow-warm;
|
||
transition: all 0.2s ease;
|
||
|
||
&:active {
|
||
transform: scale(0.96);
|
||
box-shadow: none;
|
||
}
|
||
|
||
&[disabled] {
|
||
opacity: 0.6;
|
||
box-shadow: none;
|
||
background: $text-disabled !important;
|
||
}
|
||
}
|
||
|
||
/* 错误提示 */
|
||
.error {
|
||
color: $color-error;
|
||
font-size: $font-sm;
|
||
margin-top: $spacing-lg;
|
||
padding: $spacing-md;
|
||
background: rgba($color-error, 0.1);
|
||
border-radius: $radius-md;
|
||
text-align: center;
|
||
font-weight: 500;
|
||
}
|
||
</style> |