|
|
@@ -35,10 +35,11 @@
|
|
|
<text v-else class="placeholder">搜索手机号/姓名</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <view class="field-row">
|
|
|
+ <view class="field-row" @click="openPetPicker">
|
|
|
<text class="field-label">选择宠物</text>
|
|
|
<text :class="['field-value', !formData.petName ? 'placeholder' : '']">{{ formData.petName ||
|
|
|
'点击选择宠物档案' }}</text>
|
|
|
+ <uni-icons type="right" size="14" color="#ccc"></uni-icons>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
@@ -59,35 +60,82 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 接宠路线 -->
|
|
|
+ <!-- 接宠路线 (起点=用户家, 终点=门店) @Author: Antigravity -->
|
|
|
<view class="route-box" v-if="formData.transportMode !== 'return_home'">
|
|
|
<view class="route-icon pick"><text>接</text></view>
|
|
|
<view class="route-fields">
|
|
|
- <input class="route-input" v-model="formData.pickArea" placeholder="省/市/区" />
|
|
|
- <input class="route-input" v-model="formData.pickAddress" placeholder="详细地址" />
|
|
|
+ <text class="addr-label">起点</text>
|
|
|
+ <uni-data-picker :localdata="regionTree" v-model="formData.pickArea"
|
|
|
+ :map="{ text: 'name', value: 'code' }" @change="onRegionChange('pick', $event)">
|
|
|
+ <view class="premium-cascader-display">
|
|
|
+ <text :class="['display-text', !formData.pickArea ? 'placeholder' : '']">
|
|
|
+ {{ pickAreaLabel || '选择省/市/区' }}
|
|
|
+ </text>
|
|
|
+ <uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </uni-data-picker>
|
|
|
+ <input class="route-input" v-model="formData.pickAddress" placeholder="详细地址 (街道/门牌号)" />
|
|
|
+ <text class="addr-label">终点</text>
|
|
|
+ <uni-data-picker :localdata="regionTree" v-model="formData.pickEndArea"
|
|
|
+ :map="{ text: 'name', value: 'code' }" @change="onRegionChange('pickEnd', $event)">
|
|
|
+ <view class="premium-cascader-display">
|
|
|
+ <text :class="['display-text', !formData.pickEndArea ? 'placeholder' : '']">
|
|
|
+ {{ pickEndAreaLabel || '选择省/市/区' }}
|
|
|
+ </text>
|
|
|
+ <uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </uni-data-picker>
|
|
|
+ <input class="route-input" v-model="formData.pickEndAddress" placeholder="详细地址 (街道/门牌号)" />
|
|
|
<view class="contact-row">
|
|
|
<input class="route-input half" v-model="formData.pickContact" placeholder="联系人" />
|
|
|
<input class="route-input half" v-model="formData.pickPhone" placeholder="电话"
|
|
|
type="tel" />
|
|
|
</view>
|
|
|
- <input class="route-input" v-model="formData.pickTime" placeholder="选择接宠时间" />
|
|
|
+ <view class="time-picker-row">
|
|
|
+ <uni-datetime-picker type="datetime" v-model="formData.pickTime" placeholder="选择接宠时间"
|
|
|
+ :border="false" :hide-second="true">
|
|
|
+ </uni-datetime-picker>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <view class="route-divider"><text class="divider-text">服务门店</text></view>
|
|
|
-
|
|
|
- <!-- 送宠路线 -->
|
|
|
+ <!-- 送宠路线 (起点=门店, 终点=用户家) @Author: Antigravity -->
|
|
|
<view class="route-box" v-if="formData.transportMode !== 'pick_up'">
|
|
|
<view class="route-icon send"><text>送</text></view>
|
|
|
<view class="route-fields">
|
|
|
- <input class="route-input" v-model="formData.sendArea" placeholder="省/市/区" />
|
|
|
- <input class="route-input" v-model="formData.sendAddress" placeholder="详细地址" />
|
|
|
+ <text class="addr-label">起点</text>
|
|
|
+ <uni-data-picker :localdata="regionTree" v-model="formData.sendStartArea"
|
|
|
+ :map="{ text: 'name', value: 'code' }" @change="onRegionChange('sendStart', $event)">
|
|
|
+ <view class="premium-cascader-display">
|
|
|
+ <text :class="['display-text', !formData.sendStartArea ? 'placeholder' : '']">
|
|
|
+ {{ sendStartAreaLabel || '选择省/市/区' }}
|
|
|
+ </text>
|
|
|
+ <uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </uni-data-picker>
|
|
|
+ <input class="route-input" v-model="formData.sendStartAddress"
|
|
|
+ placeholder="详细地址 (街道/门牌号)" />
|
|
|
+ <text class="addr-label">终点</text>
|
|
|
+ <uni-data-picker :localdata="regionTree" v-model="formData.sendArea"
|
|
|
+ :map="{ text: 'name', value: 'code' }" @change="onRegionChange('send', $event)">
|
|
|
+ <view class="premium-cascader-display">
|
|
|
+ <text :class="['display-text', !formData.sendArea ? 'placeholder' : '']">
|
|
|
+ {{ sendAreaLabel || '选择省/市/区' }}
|
|
|
+ </text>
|
|
|
+ <uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </uni-data-picker>
|
|
|
+ <input class="route-input" v-model="formData.sendAddress" placeholder="详细地址 (街道/门牌号)" />
|
|
|
<view class="contact-row">
|
|
|
<input class="route-input half" v-model="formData.sendContact" placeholder="联系人" />
|
|
|
<input class="route-input half" v-model="formData.sendPhone" placeholder="电话"
|
|
|
type="tel" />
|
|
|
</view>
|
|
|
- <input class="route-input" v-model="formData.sendTime" placeholder="预计送回时间(可选)" />
|
|
|
+ <view class="time-picker-row">
|
|
|
+ <uni-datetime-picker type="datetime" v-model="formData.sendTime"
|
|
|
+ placeholder="预计送还时间(可选)" :border="false" :hide-second="true">
|
|
|
+ </uni-datetime-picker>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -102,8 +150,16 @@
|
|
|
<input class="field-input" v-model="formData.packageName" placeholder="请输入套餐名称(选填)" />
|
|
|
</view>
|
|
|
<text class="address-title">上门服务地址</text>
|
|
|
- <input class="full-input" v-model="formData.serviceArea" placeholder="省/市/区" />
|
|
|
- <input class="full-input" v-model="formData.serviceAddress" placeholder="详细地址 (街道/门牌号)" />
|
|
|
+ <uni-data-picker :localdata="regionTree" v-model="formData.serviceArea"
|
|
|
+ :map="{ text: 'name', value: 'code' }" @change="onRegionChange('service', $event)">
|
|
|
+ <view class="premium-full-picker">
|
|
|
+ <text :class="['display-text', !formData.serviceArea ? 'placeholder' : '']">
|
|
|
+ {{ serviceAreaLabel || '请选择省/市/区' }}
|
|
|
+ </text>
|
|
|
+ <uni-icons type="right" size="14" color="#ccc"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </uni-data-picker>
|
|
|
+ <input class="full-input" v-model="formData.serviceAddress" placeholder="详细地址 (街道/路名/门牌号)" />
|
|
|
|
|
|
<view class="booking-section">
|
|
|
<view class="booking-header">
|
|
|
@@ -112,9 +168,15 @@
|
|
|
</view>
|
|
|
<view class="time-item-row" v-for="(time, index) in formData.feedTimes" :key="index">
|
|
|
<text class="index">{{ index + 1 }}.</text>
|
|
|
- <input class="time-input" v-model="time.start" placeholder="开始时间" />
|
|
|
+ <view class="flex-picker-box">
|
|
|
+ <uni-datetime-picker type="datetime" v-model="time.start" placeholder="开始"
|
|
|
+ :border="false" class="inline-picker" :hide-second="true"></uni-datetime-picker>
|
|
|
+ </view>
|
|
|
<text class="to-line">~</text>
|
|
|
- <input class="time-input" v-model="time.end" placeholder="结束时间(可选)" />
|
|
|
+ <view class="flex-picker-box">
|
|
|
+ <uni-datetime-picker type="datetime" v-model="time.end" placeholder="结束" :border="false"
|
|
|
+ class="inline-picker" :hide-second="true"></uni-datetime-picker>
|
|
|
+ </view>
|
|
|
<view class="action-buttons">
|
|
|
<view class="circle-btn add" v-if="index === formData.feedTimes.length - 1"
|
|
|
@click="addFeedTime">
|
|
|
@@ -159,9 +221,11 @@
|
|
|
<view class="popup-mask" v-if="showShopPicker" @click="showShopPicker = false">
|
|
|
<view class="popup-content" @click.stop>
|
|
|
<text class="popup-title">选择服务门店</text>
|
|
|
- <view class="popup-item" v-for="shop in shopList" :key="shop" @click="onShopSelect(shop)">
|
|
|
- <text>{{ shop }}</text>
|
|
|
- </view>
|
|
|
+ <scroll-view scroll-y="true" class="popup-scroll">
|
|
|
+ <view class="popup-item" v-for="shop in shopList" :key="shop.id" @click="onShopSelect(shop)">
|
|
|
+ <text>{{ shop.name }}</text>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
@@ -169,26 +233,70 @@
|
|
|
<view class="popup-mask" v-if="showUserPopup" @click="showUserPopup = false">
|
|
|
<view class="popup-content user-popup" @click.stop>
|
|
|
<text class="popup-title">选择宠主</text>
|
|
|
- <view class="popup-item" v-for="user in userList" :key="user.id" @click="onUserSelect(user)">
|
|
|
- <text class="user-item-name">{{ user.name }}</text>
|
|
|
- <text class="user-item-phone">{{ user.phone }}</text>
|
|
|
+ <view class="search-bar">
|
|
|
+ <input class="search-input" v-model="userSearchKey" placeholder="输入姓名或手机号搜索"
|
|
|
+ @confirm="fetchUsers" />
|
|
|
+ <uni-icons type="search" size="18" color="#999" @click="fetchUsers"></uni-icons>
|
|
|
</view>
|
|
|
+ <scroll-view scroll-y="true" class="popup-scroll">
|
|
|
+ <view class="popup-item" v-for="user in userList" :key="user.id" @click="onUserSelect(user)">
|
|
|
+ <text class="user-item-name">{{ user.name }}</text>
|
|
|
+ <text class="user-item-phone">{{ user.phone || user.phoneNumber }}</text>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 宠物选择弹窗 -->
|
|
|
+ <view class="popup-mask" v-if="showPetPopup" @click="showPetPopup = false">
|
|
|
+ <view class="popup-content" @click.stop>
|
|
|
+ <text class="popup-title">选择宠物</text>
|
|
|
+ <scroll-view scroll-y="true" class="popup-scroll">
|
|
|
+ <view class="popup-item" v-for="pet in petList" :key="pet.id" @click="onPetSelect(pet)">
|
|
|
+ <view class="pet-item-cell">
|
|
|
+ <image :src="pet.avatar" class="pet-avatar-mini" mode="aspectFill"></image>
|
|
|
+ <text>{{ pet.name }} ({{ pet.breed }})</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-if="petList.length === 0" class="empty-tips">该用户下暂无宠物档案</view>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, computed } from 'vue'
|
|
|
+import { ref, reactive, computed, watch } from 'vue'
|
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
|
import navBar from '@/components/nav-bar/index.vue'
|
|
|
+import { listStoreOnOrder } from '@/api/system/store'
|
|
|
+import { listCustomerOnOrder } from '@/api/archieves/customer'
|
|
|
+import { listPetByUser } from '@/api/archieves/pet'
|
|
|
+import { createOrder } from '@/api/order/order'
|
|
|
+import { listRegionTree } from '@/api/system/region'
|
|
|
|
|
|
+const showPetPopup = ref(false)
|
|
|
const activeService = ref('transport')
|
|
|
const showShopPicker = ref(false)
|
|
|
const showUserPopup = ref(false)
|
|
|
const selectedUser = ref(null)
|
|
|
+const selectedShop = ref(null)
|
|
|
+const selectedPet = ref(null)
|
|
|
+const shopList = ref([])
|
|
|
+const userList = ref([])
|
|
|
+const petList = ref([])
|
|
|
+const userSearchKey = ref('')
|
|
|
+const serviceInfo = ref(null)
|
|
|
+const regionTree = ref([])
|
|
|
+const pickAreaLabel = ref('')
|
|
|
+const pickEndAreaLabel = ref('')
|
|
|
+const sendStartAreaLabel = ref('')
|
|
|
+const sendAreaLabel = ref('')
|
|
|
+const serviceAreaLabel = ref('')
|
|
|
|
|
|
const currentServiceName = computed(() => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ if (serviceInfo.value) return serviceInfo.value.name
|
|
|
const map = { transport: '宠物接送', feed: '上门喂遛', wash: '上门洗护' }
|
|
|
return map[activeService.value]
|
|
|
})
|
|
|
@@ -197,25 +305,184 @@ const serviceIcon = computed(() => {
|
|
|
return map[activeService.value]
|
|
|
})
|
|
|
const serviceDesc = computed(() => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ if (serviceInfo.value) return serviceInfo.value.remark
|
|
|
const map = { transport: '专车接送 · 全程监护', feed: '喂食添水 · 陪玩遛狗', wash: '专业设备 · 深度清洁' }
|
|
|
return map[activeService.value]
|
|
|
})
|
|
|
|
|
|
onLoad((options) => {
|
|
|
if (options.service) activeService.value = options.service
|
|
|
+ // @Author: Antigravity
|
|
|
+ const stored = uni.getStorageSync('currentService')
|
|
|
+ if (stored) {
|
|
|
+ serviceInfo.value = stored
|
|
|
+ }
|
|
|
+ // 初始化获取数据
|
|
|
+ fetchShops()
|
|
|
+ fetchUsers()
|
|
|
+ fetchRegionTree()
|
|
|
})
|
|
|
|
|
|
+const fetchRegionTree = () => {
|
|
|
+ listRegionTree().then(res => {
|
|
|
+ console.log('移动端获取到的地区树数据:', res)
|
|
|
+ regionTree.value = res || []
|
|
|
+ }).catch(err => {
|
|
|
+ console.error('获取地区树异常:', err)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const onRegionChange = (type, e) => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ const text = e.detail.value.map(v => v.text).join(' / ')
|
|
|
+ if (type === 'pick') pickAreaLabel.value = text
|
|
|
+ else if (type === 'pickEnd') pickEndAreaLabel.value = text
|
|
|
+ else if (type === 'sendStart') sendStartAreaLabel.value = text
|
|
|
+ else if (type === 'send') sendAreaLabel.value = text
|
|
|
+ else if (type === 'service') serviceAreaLabel.value = text
|
|
|
+}
|
|
|
+
|
|
|
+// 根据 code 递归查找地区名称全路径 @Author: Antigravity
|
|
|
+const findRegionLabel = (code, list) => {
|
|
|
+ if (!code || !list || list.length === 0) return ''
|
|
|
+ // 如果是路径格式,取最后一位
|
|
|
+ const targetCode = code.includes('/') ? code.split('/').pop() : code
|
|
|
+
|
|
|
+ const find = (nodes, target) => {
|
|
|
+ for (let item of nodes) {
|
|
|
+ if (item.code === target) return item.name
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const childMatch = find(item.children, target)
|
|
|
+ if (childMatch) return item.name + ' / ' + childMatch
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ return find(list, targetCode) || ''
|
|
|
+}
|
|
|
+
|
|
|
const formData = reactive({
|
|
|
- shopName: '', petName: '', packageName: '',
|
|
|
+ merchantId: '', shopName: '',
|
|
|
+ customerId: '', customerName: '',
|
|
|
+ petId: '', petName: '',
|
|
|
+ packageName: '',
|
|
|
transportMode: 'round_trip',
|
|
|
- pickArea: '', pickAddress: '', pickContact: '', pickPhone: '', pickTime: '',
|
|
|
- sendArea: '', sendAddress: '', sendContact: '', sendPhone: '', sendTime: '',
|
|
|
+ pickArea: '', pickAddress: '', pickEndArea: '', pickEndAddress: '', pickContact: '', pickPhone: '', pickTime: '',
|
|
|
+ sendStartArea: '', sendStartAddress: '', sendArea: '', sendAddress: '', sendContact: '', sendPhone: '', sendTime: '',
|
|
|
serviceArea: '', serviceAddress: '',
|
|
|
feedTimes: [{ start: '', end: '' }],
|
|
|
otherNote: '',
|
|
|
quoteAmount: ''
|
|
|
})
|
|
|
|
|
|
+const fetchShops = () => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ const query = { pageNum: 1, pageSize: 50 }
|
|
|
+ if (serviceInfo.value && serviceInfo.value.id) {
|
|
|
+ query.serviceId = serviceInfo.value.id
|
|
|
+ }
|
|
|
+ listStoreOnOrder(query).then(res => {
|
|
|
+ shopList.value = res.rows || []
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const fetchUsers = () => {
|
|
|
+ listCustomerOnOrder({ pageNum: 1, pageSize: 20, content: userSearchKey.value }).then(res => {
|
|
|
+ userList.value = res.rows || []
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const fetchPets = (userId) => {
|
|
|
+ listPetByUser(userId).then(res => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ // 移动端 request.js 自动解构 data,res 即为宠物列表数组
|
|
|
+ petList.value = Array.isArray(res) ? res : (res.rows || [])
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const onShopSelect = (shop) => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ selectedShop.value = shop
|
|
|
+ formData.merchantId = shop.id
|
|
|
+ formData.shopName = shop.name
|
|
|
+ showShopPicker.value = false
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+const onUserSelect = (user) => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ selectedUser.value = user
|
|
|
+ formData.customerId = user.id
|
|
|
+ formData.customerName = user.name
|
|
|
+
|
|
|
+ // 重置宠物
|
|
|
+ formData.petId = ''
|
|
|
+ formData.petName = ''
|
|
|
+ selectedPet.value = null
|
|
|
+ fetchPets(user.id)
|
|
|
+ showUserPopup.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 核心回填逻辑:watch 响应门店/用户/地区树任一变化,对齐 Web 端 watch([store, user]) @Author: Antigravity
|
|
|
+watch(
|
|
|
+ [selectedShop, selectedUser, regionTree],
|
|
|
+ ([shop, user, tree]) => {
|
|
|
+ // 门店区域码与地址 (areaCode 为逗号分隔) @Author: Antigravity
|
|
|
+ const storeArea = shop?.areaCode || ''
|
|
|
+ const storeAddr = shop?.address || ''
|
|
|
+ const storeLeaf = storeArea.includes(',') ? storeArea.split(',').pop() : storeArea
|
|
|
+
|
|
|
+ // 用户区域码与地址 (regionCode 为斜杠分隔) @Author: Antigravity
|
|
|
+ const userArea = user?.regionCode || ''
|
|
|
+ const userAddr = user?.address || ''
|
|
|
+ const userPhone = user?.phoneNumber || user?.phone || ''
|
|
|
+ const userLeaf = userArea.includes('/') ? userArea.split('/').pop() : userArea
|
|
|
+
|
|
|
+ // 回填接宠路线:起点=用户家,终点=门店 @Author: Antigravity
|
|
|
+ formData.pickArea = userLeaf
|
|
|
+ formData.pickAddress = userAddr
|
|
|
+ formData.pickEndArea = storeLeaf
|
|
|
+ formData.pickEndAddress = storeAddr
|
|
|
+ formData.pickContact = user?.name || ''
|
|
|
+ formData.pickPhone = userPhone
|
|
|
+ pickAreaLabel.value = findRegionLabel(userArea, tree)
|
|
|
+ pickEndAreaLabel.value = findRegionLabel(storeArea.replace(/,/g, '/'), tree)
|
|
|
+
|
|
|
+ // 回填送宠路线:起点=门店,终点=用户家 @Author: Antigravity
|
|
|
+ formData.sendStartArea = storeLeaf
|
|
|
+ formData.sendStartAddress = storeAddr
|
|
|
+ formData.sendArea = userLeaf
|
|
|
+ formData.sendAddress = userAddr
|
|
|
+ formData.sendContact = user?.name || ''
|
|
|
+ formData.sendPhone = userPhone
|
|
|
+ sendStartAreaLabel.value = findRegionLabel(storeArea.replace(/,/g, '/'), tree)
|
|
|
+ sendAreaLabel.value = findRegionLabel(userArea, tree)
|
|
|
+
|
|
|
+ // 回填上门服务地址 @Author: Antigravity
|
|
|
+ formData.serviceArea = userLeaf
|
|
|
+ formData.serviceAddress = userAddr
|
|
|
+ serviceAreaLabel.value = findRegionLabel(userArea, tree)
|
|
|
+ },
|
|
|
+ { deep: true }
|
|
|
+)
|
|
|
+
|
|
|
+const openPetPicker = () => {
|
|
|
+ if (!formData.customerId) {
|
|
|
+ uni.showToast({ title: '请先选择宠主', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ showPetPopup.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const onPetSelect = (pet) => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ selectedPet.value = pet
|
|
|
+ formData.petId = pet.id
|
|
|
+ formData.petName = pet.name
|
|
|
+ showPetPopup.value = false
|
|
|
+}
|
|
|
+
|
|
|
const totalFulfillmentCommission = computed(() => {
|
|
|
if (formData.quoteAmount && !isNaN(parseFloat(formData.quoteAmount))) return parseFloat(formData.quoteAmount).toFixed(2)
|
|
|
return '0.00'
|
|
|
@@ -227,30 +494,94 @@ const transportModes = [
|
|
|
{ label: '单程送', value: 'return_home' }
|
|
|
]
|
|
|
|
|
|
-const shopList = ['爱宠生活馆 (三里屯店)', '爱宠生活馆 (国贸店)', '萌宠乐园 (朝阳大悦城店)']
|
|
|
-const userList = [
|
|
|
- { id: 1, name: '张先生', phone: '13800138000' },
|
|
|
- { id: 2, name: '李小姐', phone: '13900139000' },
|
|
|
- { id: 3, name: '王先生', phone: '13612345678' }
|
|
|
-]
|
|
|
-
|
|
|
-const onShopSelect = (shop) => { formData.shopName = shop; showShopPicker.value = false }
|
|
|
-const onUserSelect = (user) => { selectedUser.value = user; showUserPopup.value = false }
|
|
|
-
|
|
|
const addFeedTime = () => { formData.feedTimes.push({ start: '', end: '' }) }
|
|
|
const removeFeedTime = (index) => { formData.feedTimes.splice(index, 1) }
|
|
|
|
|
|
-const onSubmit = () => {
|
|
|
- if (!selectedUser.value) { uni.showToast({ title: '请先选择宠主用户', icon: 'none' }); return }
|
|
|
+const onSubmit = async () => {
|
|
|
+ // @Author: Antigravity
|
|
|
+ if (!formData.merchantId) { uni.showToast({ title: '请选择门店', icon: 'none' }); return }
|
|
|
+ if (!formData.customerId) { uni.showToast({ title: '请选择宠主', icon: 'none' }); return }
|
|
|
+ if (!formData.petId) { uni.showToast({ title: '请选择宠物', icon: 'none' }); return }
|
|
|
if (!formData.quoteAmount) { uni.showToast({ title: '请输入报价金额', icon: 'none' }); return }
|
|
|
- uni.showLoading({ title: '正在提交订单...' })
|
|
|
- setTimeout(() => {
|
|
|
+
|
|
|
+ uni.showLoading({ title: '提交中...', mask: true })
|
|
|
+
|
|
|
+ try {
|
|
|
+ const subOrders = []
|
|
|
+ const baseMode = serviceInfo.value?.mode || 0
|
|
|
+ const defaultContact = selectedUser.value?.name || ''
|
|
|
+ const defaultPhone = selectedUser.value?.phone || selectedUser.value?.phoneNumber || ''
|
|
|
+
|
|
|
+ if (activeService.value === 'transport') {
|
|
|
+ // 接送逻辑:使用表单中回填/编辑后的起终点地址 @Author: Antigravity
|
|
|
+ if (formData.transportMode === 'round_trip' || formData.transportMode === 'pick_up') {
|
|
|
+ subOrders.push({
|
|
|
+ mode: baseMode,
|
|
|
+ type: formData.transportMode === 'round_trip' ? 0 : 2,
|
|
|
+ contact: formData.pickContact || defaultContact,
|
|
|
+ contactPhoneNumber: formData.pickPhone || defaultPhone,
|
|
|
+ serviceTime: formData.pickTime || '',
|
|
|
+ endServiceTime: formData.pickTime || '',
|
|
|
+ fromCode: formData.pickArea || '',
|
|
|
+ fromAddress: formData.pickAddress || '',
|
|
|
+ toCode: formData.pickEndArea || '',
|
|
|
+ toAddress: formData.pickEndAddress || ''
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (formData.transportMode === 'round_trip' || formData.transportMode === 'return_home') {
|
|
|
+ subOrders.push({
|
|
|
+ mode: baseMode,
|
|
|
+ type: formData.transportMode === 'round_trip' ? 1 : 3,
|
|
|
+ contact: formData.sendContact || defaultContact,
|
|
|
+ contactPhoneNumber: formData.sendPhone || defaultPhone,
|
|
|
+ serviceTime: formData.sendTime || '',
|
|
|
+ endServiceTime: formData.sendTime || '',
|
|
|
+ fromCode: formData.sendStartArea || '',
|
|
|
+ fromAddress: formData.sendStartAddress || '',
|
|
|
+ toCode: formData.sendArea || '',
|
|
|
+ toAddress: formData.sendAddress || ''
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 上门喂遛或洗护逻辑,补全 fromCode/toCode/toAddress @Author: Antigravity
|
|
|
+ formData.feedTimes.forEach(time => {
|
|
|
+ subOrders.push({
|
|
|
+ mode: baseMode,
|
|
|
+ contact: defaultContact,
|
|
|
+ contactPhoneNumber: defaultPhone,
|
|
|
+ serviceTime: time.start,
|
|
|
+ endServiceTime: time.end || time.start,
|
|
|
+ fromCode: formData.serviceArea || '',
|
|
|
+ fromAddress: formData.serviceAddress,
|
|
|
+ toCode: formData.serviceArea || '',
|
|
|
+ toAddress: formData.serviceAddress
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const payload = {
|
|
|
+ store: formData.merchantId,
|
|
|
+ storeSite: selectedShop.value?.site,
|
|
|
+ customer: formData.customerId,
|
|
|
+ pet: formData.petId,
|
|
|
+ groupPurchasePackageName: formData.packageName || '',
|
|
|
+ service: serviceInfo.value?.id,
|
|
|
+ orderCommission: Math.round(Number(formData.quoteAmount) * 100),
|
|
|
+ remark: formData.otherNote,
|
|
|
+ tenantId: selectedShop.value?.tenantId,
|
|
|
+ subOrders: subOrders
|
|
|
+ }
|
|
|
+
|
|
|
+ await createOrder(payload)
|
|
|
uni.hideLoading()
|
|
|
uni.showToast({ title: '下单成功', icon: 'success' })
|
|
|
setTimeout(() => {
|
|
|
uni.reLaunch({ url: '/pages/order/list/index' })
|
|
|
}, 1500)
|
|
|
- }, 1500)
|
|
|
+ } catch (error) {
|
|
|
+ uni.hideLoading()
|
|
|
+ console.error('下单失败:', error)
|
|
|
+ }
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
@@ -483,25 +814,13 @@ const onSubmit = () => {
|
|
|
flex: 1;
|
|
|
}
|
|
|
|
|
|
-.route-divider {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- padding: 16rpx 0;
|
|
|
-}
|
|
|
-
|
|
|
-.route-divider::before,
|
|
|
-.route-divider::after {
|
|
|
- content: '';
|
|
|
- flex: 1;
|
|
|
- height: 1rpx;
|
|
|
- background: #eee;
|
|
|
-}
|
|
|
-
|
|
|
-.divider-text {
|
|
|
- padding: 0 24rpx;
|
|
|
- font-size: 22rpx;
|
|
|
- color: #999;
|
|
|
+/* 起点/终点标签 @Author: Antigravity */
|
|
|
+.addr-label {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-top: 16rpx;
|
|
|
+ margin-bottom: 4rpx;
|
|
|
}
|
|
|
|
|
|
.address-title {
|
|
|
@@ -522,10 +841,68 @@ const onSubmit = () => {
|
|
|
margin-bottom: 8rpx;
|
|
|
}
|
|
|
|
|
|
+.premium-cascader-display {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 16rpx 8rpx;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+
|
|
|
+ .display-text {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ &.placeholder {
|
|
|
+ color: #ccc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.premium-full-picker {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 24rpx 12rpx;
|
|
|
+ background: #f9f9f9;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+
|
|
|
+ .display-text {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ &.placeholder {
|
|
|
+ color: #ccc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.booking-section {
|
|
|
margin-top: 24rpx;
|
|
|
}
|
|
|
|
|
|
+.time-picker-row {
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+ padding: 4rpx 0;
|
|
|
+}
|
|
|
+
|
|
|
+.flex-picker-box {
|
|
|
+ flex: 1;
|
|
|
+ background: #fcfcfc;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ border: 1rpx solid #f0f0f0;
|
|
|
+ height: 64rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .inline-picker {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.booking-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
@@ -631,10 +1008,11 @@ const onSubmit = () => {
|
|
|
right: 0;
|
|
|
background: #fff;
|
|
|
padding: 20rpx 32rpx;
|
|
|
+ padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
|
- z-index: 100;
|
|
|
+ z-index: 10;
|
|
|
}
|
|
|
|
|
|
.quotation-fulfillmentCommission-box {
|
|
|
@@ -721,4 +1099,45 @@ const onSubmit = () => {
|
|
|
.user-item-phone {
|
|
|
color: #999;
|
|
|
}
|
|
|
+
|
|
|
+.popup-scroll {
|
|
|
+ max-height: 600rpx;
|
|
|
+ margin-top: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.search-bar {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 40rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ height: 72rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.search-input {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.pet-item-cell {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.pet-avatar-mini {
|
|
|
+ width: 60rpx;
|
|
|
+ height: 60rpx;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ background: #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-tips {
|
|
|
+ text-align: center;
|
|
|
+ color: #999;
|
|
|
+ font-size: 24rpx;
|
|
|
+ padding: 40rpx 0;
|
|
|
+}
|
|
|
</style>
|