完善下单逻辑及其ui展示,修复支付倒计时显示错误bug
This commit is contained in:
636
pages/address/address-edit.uvue
Normal file
636
pages/address/address-edit.uvue
Normal file
@@ -0,0 +1,636 @@
|
||||
<template>
|
||||
<view class="address-page">
|
||||
<scroll-view class="address-scroll" scroll-y="true">
|
||||
<view class="address-content">
|
||||
<view class="current-location-card">
|
||||
<text class="section-title">当前选择的位置</text>
|
||||
<text v-if="hasLocation()" class="current-location-name">{{ displayLocationTitle }}</text>
|
||||
<text v-if="hasLocation()" class="current-location-detail">{{ displayLocationDetail }}</text>
|
||||
<text v-else class="current-location-placeholder">请选择小区、医院、养老院或街道位置</text>
|
||||
</view>
|
||||
|
||||
<view class="form-card">
|
||||
<view class="form-item">
|
||||
<text class="form-label">联系人</text>
|
||||
<input v-model="form.contactName" class="form-input" placeholder="请输入联系人姓名" />
|
||||
</view>
|
||||
<view class="form-item form-item-border">
|
||||
<text class="form-label">手机号</text>
|
||||
<input v-model="form.contactPhone" class="form-input" type="number" maxlength="11" placeholder="请输入联系电话" />
|
||||
</view>
|
||||
<view class="form-item form-item-border form-item-tappable" @click="chooseServiceLocation">
|
||||
<view class="form-item-main">
|
||||
<text class="form-label">所在位置</text>
|
||||
<view v-if="hasLocation()" class="location-block">
|
||||
<text class="location-title">{{ displayLocationTitle }}</text>
|
||||
<text class="location-detail">{{ displayLocationDetail }}</text>
|
||||
</view>
|
||||
<text v-else class="location-placeholder">请选择小区/医院/养老院/街道</text>
|
||||
</view>
|
||||
<text class="form-action">重新选择</text>
|
||||
</view>
|
||||
<view class="map-entry-row" @click="goToMapSelect">
|
||||
<text class="map-entry-text">自定义地图选点</text>
|
||||
<text class="map-entry-arrow">></text>
|
||||
</view>
|
||||
<view class="form-item form-item-border">
|
||||
<text class="form-label">详细门牌号</text>
|
||||
<input v-model="form.houseNumber" class="form-input" placeholder="例如 3栋2单元1201 / 住院部3楼" />
|
||||
</view>
|
||||
<view class="form-item form-item-vertical">
|
||||
<text class="form-label">服务备注</text>
|
||||
<textarea v-model="form.remark" class="form-textarea" placeholder="例如 老人行动不便,请提前电话联系" maxlength="120"></textarea>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="bottom-bar">
|
||||
<button class="save-btn" @click="saveAddress">保存服务地址</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { computed, reactive } from 'vue'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { HomeServiceSelectedAddressType } from '@/types/home-service.uts'
|
||||
|
||||
const SELECTED_KEY = 'hss_selected_service_address'
|
||||
const LIST_KEY = 'hss_service_address_list'
|
||||
const MAP_DRAFT_KEY = 'hss_service_address_map_draft'
|
||||
|
||||
type ServiceAddressFormType = {
|
||||
addressId: string
|
||||
userId: string
|
||||
isDefault: boolean
|
||||
contactName: string
|
||||
contactPhone: string
|
||||
phone: string
|
||||
addressName: string
|
||||
locationName: string
|
||||
addressDetail: string
|
||||
locationAddress: string
|
||||
houseNumber: string
|
||||
doorNo: string
|
||||
fullAddress: string
|
||||
latitude: number
|
||||
longitude: number
|
||||
remark: string
|
||||
coordinateType: string
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
function createDefaultForm(): ServiceAddressFormType {
|
||||
const now = Date.now()
|
||||
const storedUserId = uni.getStorageSync('user_id') as string | null
|
||||
return {
|
||||
addressId: 'local-address-' + now,
|
||||
userId: storedUserId != null ? storedUserId : '',
|
||||
isDefault: true,
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
phone: '',
|
||||
addressName: '',
|
||||
locationName: '',
|
||||
addressDetail: '',
|
||||
locationAddress: '',
|
||||
houseNumber: '',
|
||||
doorNo: '',
|
||||
fullAddress: '',
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
remark: '',
|
||||
coordinateType: 'gcj02',
|
||||
createdAt: now,
|
||||
updatedAt: now
|
||||
}
|
||||
}
|
||||
|
||||
const form = reactive(createDefaultForm())
|
||||
let editingAddressId = ''
|
||||
|
||||
const displayLocationTitle = computed((): string => {
|
||||
if (form.locationName != '') {
|
||||
return form.locationName
|
||||
}
|
||||
if (form.addressName != '') {
|
||||
return form.addressName
|
||||
}
|
||||
if (form.locationAddress != '') {
|
||||
return form.locationAddress
|
||||
}
|
||||
return '未选择位置'
|
||||
})
|
||||
|
||||
const displayLocationDetail = computed((): string => {
|
||||
if (form.locationAddress != '') {
|
||||
return form.locationAddress
|
||||
}
|
||||
if (form.addressDetail != '') {
|
||||
return form.addressDetail
|
||||
}
|
||||
return '请先通过地图选点获取位置'
|
||||
})
|
||||
|
||||
function hasLocation(): boolean {
|
||||
return form.locationName != '' || form.addressName != '' || form.locationAddress != '' || form.addressDetail != ''
|
||||
}
|
||||
|
||||
function normalizeAddress(raw: HomeServiceSelectedAddressType): HomeServiceSelectedAddressType {
|
||||
const phoneText = raw.phone != null && raw.phone != '' ? raw.phone : (raw.contactPhone != null ? raw.contactPhone : '')
|
||||
const locationName = raw.locationName != null && raw.locationName != '' ? raw.locationName : (raw.addressName != null ? raw.addressName : '')
|
||||
const locationAddress = raw.locationAddress != null && raw.locationAddress != '' ? raw.locationAddress : (raw.addressDetail != null ? raw.addressDetail : '')
|
||||
const doorNo = raw.doorNo != null && raw.doorNo != '' ? raw.doorNo : (raw.houseNumber != null ? raw.houseNumber : '')
|
||||
const fullAddressText = raw.fullAddress != null && raw.fullAddress != '' ? raw.fullAddress : locationAddress + ' ' + doorNo
|
||||
return {
|
||||
...raw,
|
||||
addressId: raw.addressId != null && raw.addressId != '' ? raw.addressId : 'local-address-' + Date.now(),
|
||||
userId: raw.userId != null ? raw.userId : '',
|
||||
isDefault: raw.isDefault === true,
|
||||
contactName: raw.contactName != null ? raw.contactName : '',
|
||||
phone: phoneText,
|
||||
contactPhone: phoneText,
|
||||
locationName: locationName,
|
||||
addressName: locationName,
|
||||
locationAddress: locationAddress,
|
||||
addressDetail: locationAddress,
|
||||
doorNo: doorNo,
|
||||
houseNumber: doorNo,
|
||||
remark: raw.remark != null ? raw.remark : '',
|
||||
coordinateType: raw.coordinateType != null && raw.coordinateType != '' ? raw.coordinateType : 'gcj02',
|
||||
latitude: raw.latitude != null ? raw.latitude : 0,
|
||||
longitude: raw.longitude != null ? raw.longitude : 0,
|
||||
createdAt: raw.createdAt != null ? raw.createdAt : Date.now(),
|
||||
updatedAt: raw.updatedAt != null ? raw.updatedAt : Date.now(),
|
||||
fullAddress: fullAddressText.trim()
|
||||
}
|
||||
}
|
||||
|
||||
function applyAddress(address: HomeServiceSelectedAddressType): void {
|
||||
const normalized = normalizeAddress(address)
|
||||
form.addressId = normalized.addressId
|
||||
form.userId = normalized.userId
|
||||
form.isDefault = normalized.isDefault
|
||||
form.contactName = normalized.contactName
|
||||
form.contactPhone = normalized.contactPhone
|
||||
form.phone = normalized.phone != null ? normalized.phone : normalized.contactPhone
|
||||
form.addressName = normalized.addressName
|
||||
form.locationName = normalized.locationName != null ? normalized.locationName : normalized.addressName
|
||||
form.addressDetail = normalized.addressDetail
|
||||
form.locationAddress = normalized.locationAddress != null ? normalized.locationAddress : normalized.addressDetail
|
||||
form.houseNumber = normalized.houseNumber
|
||||
form.doorNo = normalized.doorNo != null ? normalized.doorNo : normalized.houseNumber
|
||||
form.fullAddress = normalized.fullAddress
|
||||
form.latitude = normalized.latitude
|
||||
form.longitude = normalized.longitude
|
||||
form.remark = normalized.remark
|
||||
form.coordinateType = normalized.coordinateType
|
||||
form.createdAt = normalized.createdAt
|
||||
form.updatedAt = normalized.updatedAt
|
||||
editingAddressId = normalized.addressId
|
||||
}
|
||||
|
||||
function readAddressList(): Array<HomeServiceSelectedAddressType> {
|
||||
const stored = uni.getStorageSync(LIST_KEY)
|
||||
if (stored == null) {
|
||||
return []
|
||||
}
|
||||
try {
|
||||
if (typeof stored === 'string') {
|
||||
const storedText = (stored as string).trim()
|
||||
if (storedText == '') {
|
||||
return []
|
||||
}
|
||||
const parsed = JSON.parse(storedText) as Array<HomeServiceSelectedAddressType> | null
|
||||
return parsed != null ? parsed : []
|
||||
}
|
||||
return stored as Array<HomeServiceSelectedAddressType>
|
||||
} catch (error) {
|
||||
console.error('解析服务地址列表失败', error)
|
||||
uni.removeStorageSync(LIST_KEY)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function writeAddressList(addresses: Array<HomeServiceSelectedAddressType>): void {
|
||||
uni.setStorageSync(LIST_KEY, JSON.stringify(addresses))
|
||||
}
|
||||
|
||||
function parseStoredAddress(rawValue: unknown): HomeServiceSelectedAddressType | null {
|
||||
if (rawValue == null) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
if (typeof rawValue === 'string') {
|
||||
const rawText = (rawValue as string).trim()
|
||||
if (rawText == '') {
|
||||
return null
|
||||
}
|
||||
const parsed = JSON.parse(rawText) as HomeServiceSelectedAddressType | null
|
||||
return parsed
|
||||
}
|
||||
return rawValue as HomeServiceSelectedAddressType
|
||||
} catch (error) {
|
||||
console.error('解析服务地址对象失败', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function ensureAddressListSeeded(): void {
|
||||
const addresses = readAddressList()
|
||||
if (addresses.length > 0) {
|
||||
return
|
||||
}
|
||||
const selected = uni.getStorageSync(SELECTED_KEY) as HomeServiceSelectedAddressType | null
|
||||
const normalizedSelected = parseStoredAddress(selected)
|
||||
if (normalizedSelected != null) {
|
||||
const seeded: Array<HomeServiceSelectedAddressType> = []
|
||||
seeded.push(normalizeAddress(normalizedSelected))
|
||||
writeAddressList(seeded)
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromAddressId(addressId: string): void {
|
||||
const addresses = readAddressList()
|
||||
for (let i = 0; i < addresses.length; i++) {
|
||||
if (addresses[i].addressId == addressId) {
|
||||
applyAddress(addresses[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
const selected = parseStoredAddress(uni.getStorageSync(SELECTED_KEY))
|
||||
if (selected != null && selected.addressId == addressId) {
|
||||
applyAddress(selected)
|
||||
}
|
||||
}
|
||||
|
||||
function loadCachedAddress(): void {
|
||||
if (editingAddressId != '') {
|
||||
loadFromAddressId(editingAddressId)
|
||||
return
|
||||
}
|
||||
const cachedAddress = parseStoredAddress(uni.getStorageSync(SELECTED_KEY))
|
||||
if (cachedAddress != null) {
|
||||
applyAddress(cachedAddress)
|
||||
}
|
||||
}
|
||||
|
||||
function applyMapDraft(): void {
|
||||
const draft = parseStoredAddress(uni.getStorageSync(MAP_DRAFT_KEY))
|
||||
if (draft == null) {
|
||||
return
|
||||
}
|
||||
const normalized = normalizeAddress(draft)
|
||||
form.addressName = normalized.addressName
|
||||
form.locationName = normalized.locationName != null ? normalized.locationName : normalized.addressName
|
||||
form.addressDetail = normalized.addressDetail
|
||||
form.locationAddress = normalized.locationAddress != null ? normalized.locationAddress : normalized.addressDetail
|
||||
form.latitude = normalized.latitude
|
||||
form.longitude = normalized.longitude
|
||||
form.coordinateType = normalized.coordinateType
|
||||
uni.removeStorageSync(MAP_DRAFT_KEY)
|
||||
if (normalized.locationName != '' || normalized.locationAddress != '') {
|
||||
uni.showToast({
|
||||
title: '已回填地图位置',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function mapChooseLocationResult(name: string, address: string, latitude: number, longitude: number): void {
|
||||
const title = name != '' ? name : address
|
||||
const detail = address != '' ? address : title
|
||||
form.addressName = title
|
||||
form.locationName = title
|
||||
form.addressDetail = detail
|
||||
form.locationAddress = detail
|
||||
form.latitude = latitude
|
||||
form.longitude = longitude
|
||||
form.coordinateType = 'gcj02'
|
||||
}
|
||||
|
||||
function resolveLocationFailToast(error: unknown): string {
|
||||
const errorText = String(error)
|
||||
if (errorText.indexOf('cancel') >= 0) {
|
||||
return ''
|
||||
}
|
||||
if (errorText.indexOf('auth deny') >= 0 || errorText.indexOf('authorize') >= 0 || errorText.indexOf('permission') >= 0 || errorText.indexOf('auth denied') >= 0) {
|
||||
return '需要开启定位权限才能选择服务地址'
|
||||
}
|
||||
if (errorText.indexOf('location') >= 0 || errorText.indexOf('getLocation') >= 0) {
|
||||
return '定位失败,请手动搜索或重新选择'
|
||||
}
|
||||
if (errorText.indexOf('map') >= 0 || errorText.indexOf('service') >= 0) {
|
||||
return '地图服务暂不可用,请稍后重试'
|
||||
}
|
||||
return '位置选择失败,请重试'
|
||||
}
|
||||
|
||||
function chooseServiceLocation(): void {
|
||||
uni.chooseLocation({
|
||||
success: (res) => {
|
||||
const locationName = res.name != null ? res.name : ''
|
||||
const locationAddress = res.address != null ? res.address : ''
|
||||
mapChooseLocationResult(locationName, locationAddress, res.latitude, res.longitude)
|
||||
},
|
||||
fail: (error) => {
|
||||
const toastText = resolveLocationFailToast(error)
|
||||
if (toastText == '') {
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: toastText,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function goToMapSelect(): void {
|
||||
uni.navigateTo({ url: '/pages/address/address-map-select' })
|
||||
}
|
||||
|
||||
function isPhoneValid(phone: string): boolean {
|
||||
return /^\d{11}$/.test(phone)
|
||||
}
|
||||
|
||||
function buildAddressPayload(): HomeServiceSelectedAddressType {
|
||||
const now = Date.now()
|
||||
const storedUserId = uni.getStorageSync('user_id') as string | null
|
||||
const phoneText = form.contactPhone != '' ? form.contactPhone : form.phone
|
||||
const locationName = form.locationName != '' ? form.locationName : form.addressName
|
||||
const locationAddress = form.locationAddress != '' ? form.locationAddress : form.addressDetail
|
||||
const doorNo = form.doorNo != '' ? form.doorNo : form.houseNumber
|
||||
return {
|
||||
addressId: form.addressId != '' ? form.addressId : 'local-address-' + now,
|
||||
userId: form.userId != '' ? form.userId : (storedUserId != null ? storedUserId : ''),
|
||||
isDefault: true,
|
||||
contactName: form.contactName,
|
||||
contactPhone: phoneText,
|
||||
phone: phoneText,
|
||||
addressName: locationName,
|
||||
locationName: locationName,
|
||||
addressDetail: locationAddress,
|
||||
locationAddress: locationAddress,
|
||||
houseNumber: doorNo,
|
||||
doorNo: doorNo,
|
||||
fullAddress: (locationAddress + ' ' + doorNo).trim(),
|
||||
latitude: form.latitude,
|
||||
longitude: form.longitude,
|
||||
remark: form.remark,
|
||||
coordinateType: 'gcj02',
|
||||
createdAt: form.createdAt > 0 ? form.createdAt : now,
|
||||
updatedAt: now
|
||||
}
|
||||
}
|
||||
|
||||
function saveAddress(): void {
|
||||
if (form.contactName == '') {
|
||||
uni.showToast({ title: '请输入联系人姓名', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (form.contactPhone == '') {
|
||||
uni.showToast({ title: '请输入联系电话', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!isPhoneValid(form.contactPhone)) {
|
||||
uni.showToast({ title: '请输入11位手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!hasLocation()) {
|
||||
uni.showToast({ title: '请选择所在位置', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (form.houseNumber == '') {
|
||||
uni.showToast({ title: '请输入详细门牌号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const savedAddress = buildAddressPayload()
|
||||
const addresses = readAddressList()
|
||||
let updated = false
|
||||
for (let i = 0; i < addresses.length; i++) {
|
||||
if (addresses[i].addressId == savedAddress.addressId) {
|
||||
addresses[i] = savedAddress
|
||||
updated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!updated) {
|
||||
addresses.unshift(savedAddress)
|
||||
}
|
||||
|
||||
writeAddressList(addresses)
|
||||
uni.setStorageSync(SELECTED_KEY, savedAddress)
|
||||
uni.showToast({
|
||||
title: '地址已保存',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
ensureAddressListSeeded()
|
||||
if (options == null) {
|
||||
loadCachedAddress()
|
||||
return
|
||||
}
|
||||
const addressId = options['id']
|
||||
if (addressId != null && String(addressId) != '') {
|
||||
editingAddressId = String(addressId)
|
||||
loadFromAddressId(editingAddressId)
|
||||
return
|
||||
}
|
||||
loadCachedAddress()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
applyMapDraft()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.address-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #f4f6f8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.address-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.address-content {
|
||||
padding: 24rpx 24rpx 180rpx;
|
||||
gap: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.current-location-card,
|
||||
.form-card {
|
||||
background: #ffffff;
|
||||
border-radius: 28rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 12rpx 28rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.section-title,
|
||||
.form-label,
|
||||
.location-title,
|
||||
.current-location-name,
|
||||
.map-entry-text {
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.current-location-name,
|
||||
.location-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.current-location-detail,
|
||||
.current-location-placeholder,
|
||||
.location-detail,
|
||||
.location-placeholder,
|
||||
.form-action,
|
||||
.form-input,
|
||||
.form-textarea,
|
||||
.map-entry-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #4b5563;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.current-location-placeholder,
|
||||
.location-placeholder {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.current-location-detail,
|
||||
.location-detail {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 18rpx 0;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.form-item-border {
|
||||
border-top: 1rpx solid #eef2f7;
|
||||
}
|
||||
|
||||
.form-item-vertical {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
border-top: 1rpx solid #eef2f7;
|
||||
}
|
||||
|
||||
.form-item-tappable {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.form-item-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
width: 132rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
min-height: 44rpx;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
min-height: 150rpx;
|
||||
margin-top: 12rpx;
|
||||
background: #f8fafc;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.location-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.form-action {
|
||||
color: #d97706;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.map-entry-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 18rpx 0 0;
|
||||
border-top: 1rpx dashed #eef2f7;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.map-entry-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.map-entry-arrow {
|
||||
color: #f97316;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20rpx 24rpx 36rpx;
|
||||
background: rgba(244, 246, 248, 0.96);
|
||||
box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, #ff8a65 0%, #ff7043 100%);
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
390
pages/address/address-list.uvue
Normal file
390
pages/address/address-list.uvue
Normal file
@@ -0,0 +1,390 @@
|
||||
<template>
|
||||
<view class="address-list-page">
|
||||
<scroll-view class="address-scroll" scroll-y="true">
|
||||
<view class="address-content">
|
||||
<view v-if="addresses.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📍</text>
|
||||
<text class="empty-title">还没有服务地址</text>
|
||||
<text class="empty-text">新增一个常用服务地址,预约时会更快</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-for="item in addresses"
|
||||
:key="item.addressId"
|
||||
:class="['address-card', isSelected(item) ? 'address-card-selected' : '']"
|
||||
@click="selectAddress(item)"
|
||||
>
|
||||
<view class="address-main">
|
||||
<view class="address-top-row">
|
||||
<text class="address-name">{{ item.contactName }}</text>
|
||||
<text class="address-phone">{{ getPhone(item) }}</text>
|
||||
<text v-if="item.isDefault" class="default-tag">默认</text>
|
||||
<text v-if="isSelected(item)" class="selected-tag">当前选择</text>
|
||||
</view>
|
||||
<text class="address-location">{{ getLocationTitle(item) }}</text>
|
||||
<text class="address-full">{{ getFullAddress(item) }}</text>
|
||||
<text v-if="item.remark != ''" class="address-remark">备注:{{ item.remark }}</text>
|
||||
</view>
|
||||
<view class="address-actions">
|
||||
<view class="action-btn" @click.stop="editAddress(item.addressId)">
|
||||
<text class="action-text">编辑</text>
|
||||
</view>
|
||||
<view class="action-btn action-btn-delete" @click.stop="deleteAddress(item.addressId)">
|
||||
<text class="action-text action-text-delete">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="bottom-bar">
|
||||
<button class="add-btn" @click="addAddress">新建服务地址</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { HomeServiceSelectedAddressType } from '@/types/home-service.uts'
|
||||
|
||||
const SELECTED_KEY = 'hss_selected_service_address'
|
||||
const LIST_KEY = 'hss_service_address_list'
|
||||
|
||||
const addresses = ref<Array<HomeServiceSelectedAddressType>>([])
|
||||
const selectedAddressId = ref('')
|
||||
|
||||
function normalizeAddress(raw: HomeServiceSelectedAddressType): HomeServiceSelectedAddressType {
|
||||
const phoneText = raw.phone != null && raw.phone != '' ? raw.phone : (raw.contactPhone != null ? raw.contactPhone : '')
|
||||
const locationName = raw.locationName != null && raw.locationName != '' ? raw.locationName : (raw.addressName != null ? raw.addressName : '')
|
||||
const locationAddress = raw.locationAddress != null && raw.locationAddress != '' ? raw.locationAddress : (raw.addressDetail != null ? raw.addressDetail : '')
|
||||
const doorNo = raw.doorNo != null && raw.doorNo != '' ? raw.doorNo : (raw.houseNumber != null ? raw.houseNumber : '')
|
||||
const fullAddressText = raw.fullAddress != null && raw.fullAddress != '' ? raw.fullAddress : locationAddress + ' ' + doorNo
|
||||
return {
|
||||
...raw,
|
||||
addressId: raw.addressId != null && raw.addressId != '' ? raw.addressId : 'local-address-' + Date.now(),
|
||||
userId: raw.userId != null ? raw.userId : '',
|
||||
isDefault: raw.isDefault === true,
|
||||
contactName: raw.contactName != null ? raw.contactName : '',
|
||||
phone: phoneText,
|
||||
contactPhone: phoneText,
|
||||
locationName: locationName,
|
||||
addressName: locationName,
|
||||
locationAddress: locationAddress,
|
||||
addressDetail: locationAddress,
|
||||
doorNo: doorNo,
|
||||
houseNumber: doorNo,
|
||||
remark: raw.remark != null ? raw.remark : '',
|
||||
coordinateType: raw.coordinateType != null && raw.coordinateType != '' ? raw.coordinateType : 'gcj02',
|
||||
latitude: raw.latitude != null ? raw.latitude : 0,
|
||||
longitude: raw.longitude != null ? raw.longitude : 0,
|
||||
createdAt: raw.createdAt != null ? raw.createdAt : Date.now(),
|
||||
updatedAt: raw.updatedAt != null ? raw.updatedAt : Date.now(),
|
||||
fullAddress: fullAddressText.trim()
|
||||
}
|
||||
}
|
||||
|
||||
function readAddressList(): Array<HomeServiceSelectedAddressType> {
|
||||
const stored = uni.getStorageSync(LIST_KEY)
|
||||
if (stored == null) {
|
||||
return []
|
||||
}
|
||||
try {
|
||||
let parsed: Array<HomeServiceSelectedAddressType> = []
|
||||
if (typeof stored === 'string') {
|
||||
const storedText = (stored as string).trim()
|
||||
if (storedText == '') {
|
||||
return []
|
||||
}
|
||||
const parsedValue = JSON.parse(storedText) as Array<HomeServiceSelectedAddressType> | null
|
||||
if (parsedValue == null) {
|
||||
return []
|
||||
}
|
||||
parsed = parsedValue
|
||||
} else {
|
||||
parsed = stored as Array<HomeServiceSelectedAddressType>
|
||||
}
|
||||
if (parsed == null || parsed.length == 0) {
|
||||
return []
|
||||
}
|
||||
const normalized: Array<HomeServiceSelectedAddressType> = []
|
||||
for (let i = 0; i < parsed.length; i++) {
|
||||
if (parsed[i] != null) {
|
||||
normalized.push(normalizeAddress(parsed[i]))
|
||||
}
|
||||
}
|
||||
return normalized
|
||||
} catch (error) {
|
||||
console.error('解析服务地址列表失败', error)
|
||||
uni.removeStorageSync(LIST_KEY)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function writeAddressList(list: Array<HomeServiceSelectedAddressType>): void {
|
||||
uni.setStorageSync(LIST_KEY, JSON.stringify(list))
|
||||
}
|
||||
|
||||
function loadAddresses(): void {
|
||||
const selected = uni.getStorageSync(SELECTED_KEY) as HomeServiceSelectedAddressType | null
|
||||
if (selected != null) {
|
||||
const normalizedSelected = normalizeAddress(selected)
|
||||
selectedAddressId.value = normalizedSelected.addressId
|
||||
}
|
||||
const list = readAddressList()
|
||||
if (list.length == 0 && selected != null) {
|
||||
const seeded: Array<HomeServiceSelectedAddressType> = []
|
||||
seeded.push(normalizeAddress(selected))
|
||||
addresses.value = seeded
|
||||
writeAddressList(seeded)
|
||||
return
|
||||
}
|
||||
addresses.value = list
|
||||
}
|
||||
|
||||
function isSelected(item: HomeServiceSelectedAddressType): boolean {
|
||||
return selectedAddressId.value != '' && item.addressId == selectedAddressId.value
|
||||
}
|
||||
|
||||
function getPhone(item: HomeServiceSelectedAddressType): string {
|
||||
if (item.phone != null && item.phone != '') {
|
||||
return item.phone
|
||||
}
|
||||
return item.contactPhone
|
||||
}
|
||||
|
||||
function getLocationTitle(item: HomeServiceSelectedAddressType): string {
|
||||
if (item.locationName != null && item.locationName != '') {
|
||||
return item.locationName
|
||||
}
|
||||
return item.addressName
|
||||
}
|
||||
|
||||
function getFullAddress(item: HomeServiceSelectedAddressType): string {
|
||||
if (item.fullAddress != '') {
|
||||
return item.fullAddress
|
||||
}
|
||||
const locationAddress = item.locationAddress != null && item.locationAddress != '' ? item.locationAddress : item.addressDetail
|
||||
const doorNo = item.doorNo != null && item.doorNo != '' ? item.doorNo : item.houseNumber
|
||||
return (locationAddress + ' ' + doorNo).trim()
|
||||
}
|
||||
|
||||
function selectAddress(item: HomeServiceSelectedAddressType): void {
|
||||
const normalized = normalizeAddress(item)
|
||||
uni.setStorageSync(SELECTED_KEY, normalized)
|
||||
selectedAddressId.value = normalized.addressId
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
function addAddress(): void {
|
||||
uni.navigateTo({ url: '/pages/address/address-edit' })
|
||||
}
|
||||
|
||||
function editAddress(addressId: string): void {
|
||||
uni.navigateTo({ url: '/pages/address/address-edit?id=' + addressId })
|
||||
}
|
||||
|
||||
function deleteAddress(addressId: string): void {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该服务地址吗?',
|
||||
success: (res) => {
|
||||
if (!res.confirm) {
|
||||
return
|
||||
}
|
||||
const nextList: Array<HomeServiceSelectedAddressType> = []
|
||||
for (let i = 0; i < addresses.value.length; i++) {
|
||||
if (addresses.value[i].addressId != addressId) {
|
||||
nextList.push(addresses.value[i])
|
||||
}
|
||||
}
|
||||
addresses.value = nextList
|
||||
writeAddressList(nextList)
|
||||
if (selectedAddressId.value == addressId) {
|
||||
selectedAddressId.value = ''
|
||||
uni.removeStorageSync(SELECTED_KEY)
|
||||
if (nextList.length > 0) {
|
||||
const nextSelected = normalizeAddress(nextList[0])
|
||||
selectedAddressId.value = nextSelected.addressId
|
||||
uni.setStorageSync(SELECTED_KEY, nextSelected)
|
||||
}
|
||||
}
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadAddresses()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.address-list-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #f4f6f8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.address-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.address-content {
|
||||
padding: 24rpx 24rpx 180rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.address-card {
|
||||
background: #ffffff;
|
||||
border-radius: 28rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 12rpx 28rpx rgba(15, 23, 42, 0.05);
|
||||
border: 2rpx solid transparent;
|
||||
}
|
||||
|
||||
.address-card-selected {
|
||||
border-color: rgba(249, 115, 22, 0.38);
|
||||
box-shadow: 0 16rpx 30rpx rgba(249, 115, 22, 0.12);
|
||||
}
|
||||
|
||||
.address-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.address-top-row,
|
||||
.address-actions,
|
||||
.bottom-bar,
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.address-top-row {
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.address-name,
|
||||
.address-location,
|
||||
.empty-title {
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.address-name {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.address-phone,
|
||||
.address-full,
|
||||
.address-remark,
|
||||
.empty-text,
|
||||
.action-text {
|
||||
font-size: 24rpx;
|
||||
color: #4b5563;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.address-location {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.default-tag,
|
||||
.selected-tag {
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.default-tag {
|
||||
background: #fff1eb;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.selected-tag {
|
||||
background: #fff7ed;
|
||||
color: #ea580c;
|
||||
}
|
||||
|
||||
.address-actions {
|
||||
justify-content: flex-end;
|
||||
gap: 16rpx;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 12rpx 22rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.action-btn-delete {
|
||||
background: #fff5f5;
|
||||
}
|
||||
|
||||
.action-text-delete {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 40rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 28rpx;
|
||||
box-shadow: 0 12rpx 28rpx rgba(15, 23, 42, 0.05);
|
||||
text-align: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20rpx 24rpx 36rpx;
|
||||
background: rgba(244, 246, 248, 0.96);
|
||||
box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, #ff8a65 0%, #ff7043 100%);
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
289
pages/address/address-map-select.uvue
Normal file
289
pages/address/address-map-select.uvue
Normal file
@@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<view class="map-page">
|
||||
<view class="search-card">
|
||||
<input v-model="keyword" class="search-input" placeholder="搜索小区、医院、养老院、街道" @input="handleKeywordInput" />
|
||||
</view>
|
||||
|
||||
<map
|
||||
class="address-map"
|
||||
:latitude="latitude"
|
||||
:longitude="longitude"
|
||||
:markers="markers"
|
||||
:show-location="true"
|
||||
:scale="16"
|
||||
></map>
|
||||
|
||||
<scroll-view class="poi-scroll" scroll-y="true">
|
||||
<view class="poi-card">
|
||||
<text class="poi-title">附近地址</text>
|
||||
<view
|
||||
v-for="item in poiList"
|
||||
:key="item.id"
|
||||
:class="['poi-item', selectedPoiId == item.id ? 'poi-item-selected' : '']"
|
||||
@click="selectPoi(item.id)"
|
||||
>
|
||||
<text class="poi-name">{{ item.name }}</text>
|
||||
<text class="poi-address">{{ item.address }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="bottom-bar">
|
||||
<button class="confirm-btn" @click="confirmAddress">确认地址</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { HomeServiceSelectedAddressType } from '@/types/home-service.uts'
|
||||
|
||||
const SELECTED_KEY = 'hss_selected_service_address'
|
||||
const MAP_DRAFT_KEY = 'hss_service_address_map_draft'
|
||||
|
||||
type MapPoiItemType = {
|
||||
id: string
|
||||
name: string
|
||||
address: string
|
||||
latitude: number
|
||||
longitude: number
|
||||
}
|
||||
|
||||
const keyword = ref('')
|
||||
const latitude = ref(24.28859)
|
||||
const longitude = ref(116.12264)
|
||||
const selectedPoiId = ref('poi-current')
|
||||
const poiList = ref<Array<MapPoiItemType>>([])
|
||||
const markers = ref<Array<UTSJSONObject>>([])
|
||||
|
||||
function buildMarkers(): Array<UTSJSONObject> {
|
||||
const result: Array<UTSJSONObject> = []
|
||||
const selected = getSelectedPoi()
|
||||
if (selected == null) {
|
||||
return result
|
||||
}
|
||||
const marker = new UTSJSONObject()
|
||||
marker.set('id', 1)
|
||||
marker.set('latitude', selected.latitude)
|
||||
marker.set('longitude', selected.longitude)
|
||||
marker.set('title', selected.name)
|
||||
result.push(marker)
|
||||
return result
|
||||
}
|
||||
|
||||
function createDefaultPoiList(selected: HomeServiceSelectedAddressType | null): Array<MapPoiItemType> {
|
||||
const locationName = selected != null && selected.locationName != null && selected.locationName != '' ? selected.locationName : '当前定位'
|
||||
const locationAddress = selected != null && selected.locationAddress != null && selected.locationAddress != '' ? selected.locationAddress : '请通过系统地图继续完善地址搜索'
|
||||
const baseLatitude = selected != null && selected.latitude != 0 ? selected.latitude : latitude.value
|
||||
const baseLongitude = selected != null && selected.longitude != 0 ? selected.longitude : longitude.value
|
||||
const result: Array<MapPoiItemType> = []
|
||||
result.push({
|
||||
id: 'poi-current',
|
||||
name: locationName,
|
||||
address: locationAddress,
|
||||
latitude: baseLatitude,
|
||||
longitude: baseLongitude
|
||||
})
|
||||
result.push({
|
||||
id: 'poi-near-1',
|
||||
name: locationName + '附近入口',
|
||||
address: locationAddress,
|
||||
latitude: baseLatitude + 0.0006,
|
||||
longitude: baseLongitude + 0.0006
|
||||
})
|
||||
result.push({
|
||||
id: 'poi-near-2',
|
||||
name: locationName + '周边推荐',
|
||||
address: locationAddress,
|
||||
latitude: baseLatitude - 0.0006,
|
||||
longitude: baseLongitude - 0.0006
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
function getSelectedPoi(): MapPoiItemType | null {
|
||||
for (let i = 0; i < poiList.value.length; i++) {
|
||||
if (poiList.value[i].id == selectedPoiId.value) {
|
||||
return poiList.value[i]
|
||||
}
|
||||
}
|
||||
return poiList.value.length > 0 ? poiList.value[0] : null
|
||||
}
|
||||
|
||||
function selectPoi(poiId: string): void {
|
||||
selectedPoiId.value = poiId
|
||||
const selected = getSelectedPoi()
|
||||
if (selected == null) {
|
||||
return
|
||||
}
|
||||
latitude.value = selected.latitude
|
||||
longitude.value = selected.longitude
|
||||
markers.value = buildMarkers()
|
||||
}
|
||||
|
||||
function handleKeywordInput(): void {
|
||||
if (keyword.value.trim() == '') {
|
||||
return
|
||||
}
|
||||
const base = getSelectedPoi()
|
||||
if (base == null) {
|
||||
return
|
||||
}
|
||||
const dynamicList: Array<MapPoiItemType> = []
|
||||
dynamicList.push({
|
||||
id: 'poi-search-1',
|
||||
name: keyword.value.trim(),
|
||||
address: base.address,
|
||||
latitude: base.latitude,
|
||||
longitude: base.longitude
|
||||
})
|
||||
dynamicList.push(base)
|
||||
poiList.value = dynamicList
|
||||
selectedPoiId.value = 'poi-search-1'
|
||||
markers.value = buildMarkers()
|
||||
}
|
||||
|
||||
function confirmAddress(): void {
|
||||
const selected = getSelectedPoi()
|
||||
if (selected == null) {
|
||||
uni.showToast({ title: '请先选择地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const draft = {
|
||||
addressId: '',
|
||||
userId: '',
|
||||
isDefault: true,
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
phone: '',
|
||||
addressName: selected.name,
|
||||
locationName: selected.name,
|
||||
addressDetail: selected.address,
|
||||
locationAddress: selected.address,
|
||||
houseNumber: '',
|
||||
doorNo: '',
|
||||
fullAddress: selected.address,
|
||||
latitude: selected.latitude,
|
||||
longitude: selected.longitude,
|
||||
remark: '',
|
||||
coordinateType: 'gcj02',
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now()
|
||||
} as HomeServiceSelectedAddressType
|
||||
uni.setStorageSync(MAP_DRAFT_KEY, JSON.stringify(draft))
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
const selected = uni.getStorageSync(SELECTED_KEY) as HomeServiceSelectedAddressType | null
|
||||
if (selected != null && selected.latitude != 0) {
|
||||
latitude.value = selected.latitude
|
||||
longitude.value = selected.longitude
|
||||
}
|
||||
poiList.value = createDefaultPoiList(selected)
|
||||
markers.value = buildMarkers()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.map-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #f4f6f8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 24rpx 24rpx 180rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-card,
|
||||
.poi-card {
|
||||
background: #ffffff;
|
||||
border-radius: 28rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 12rpx 28rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background: #f8fafc;
|
||||
border-radius: 999rpx;
|
||||
padding: 18rpx 24rpx;
|
||||
font-size: 26rpx;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.address-map {
|
||||
width: 100%;
|
||||
height: 45vh;
|
||||
border-radius: 28rpx;
|
||||
overflow: hidden;
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.poi-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.poi-title,
|
||||
.poi-name {
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.poi-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.poi-item {
|
||||
padding: 18rpx 0;
|
||||
border-top: 1rpx solid #eef2f7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.poi-item-selected {
|
||||
border-left: 6rpx solid #f97316;
|
||||
padding-left: 18rpx;
|
||||
}
|
||||
|
||||
.poi-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.poi-address {
|
||||
font-size: 24rpx;
|
||||
color: #4b5563;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20rpx 24rpx 36rpx;
|
||||
background: rgba(244, 246, 248, 0.96);
|
||||
box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, #ff8a65 0%, #ff7043 100%);
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user