| | |
| | | <view class="page-content"> |
| | | <view v-if="activeMode === 'local'" class="service-point-card cell-card" @click="openStorePopup"> |
| | | <view class="cell-left with-icon"> |
| | | <image class="cell-icon" src="/static/icon/ic_store@2x.png" mode="widthFix"></image> |
| | | <view class="store-cell-copy"> |
| | | <text class="cell-title">{{ selectedStore ? selectedStore.name : servicePointPlaceholder }}</text> |
| | | <view class="store-cell-copy-addr"> |
| | | <image src="/static/icon/home_ic_location3@2x.png" mode="widthFix"></image> |
| | | <text v-if="selectedStore" class="store-cell-subtitle">{{ selectedStore.address }}</text> |
| | | <image class="cell-icon" src="/static/icon/ic_store@2x.png" mode="widthFix"></image> |
| | | <view class="store-cell-copy"> |
| | | <text class="cell-title">{{ selectedStore ? selectedStore.name : servicePointPlaceholder }}</text> |
| | | <view class="store-cell-copy-addr" v-if="selectedStore"> |
| | | <image src="/static/icon/home_ic_location3@2x.png" mode="widthFix"></image> |
| | | <text class="store-cell-subtitle">{{ selectedStore.address }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="cell-right"> |
| | | <u-icon name="arrow-right" size="22" color="#222222"></u-icon> |
| | | </view> |
| | | <view class="cell-right"> |
| | | <u-icon name="arrow-right" size="18" color="#222222"></u-icon> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="address-card section-card"> |
| | |
| | | </view> |
| | | <view class="form-row"> |
| | | <text class="form-label">收件人</text> |
| | | <input v-model="form.receiver" class="form-input" :class="{ 'filled-input': form.receiver }" type="text" placeholder="请输入收件人姓名" placeholder-style="color: #B2B2B2;" placeholder-class="input-placeholder" /> |
| | | <input v-model="form.receiver" class="form-input" :class="{ 'filled-input': form.receiver }" type="text" placeholder="请输入收件人姓名" placeholder-style="color: #B2B2B2;" /> |
| | | </view> |
| | | <view class="form-row no-border"> |
| | | <text class="form-label">收件电话</text> |
| | | <input v-model="form.mobile" class="form-input" :class="{ 'filled-input': form.mobile }" type="number" placeholder="请输入收件人电话" placeholder-style="color: #B2B2B2;" placeholder-class="input-placeholder" /> |
| | | <input v-model="form.mobile" class="form-input" :class="{ 'filled-input': form.mobile }" type="number" placeholder="请输入收件人电话" placeholder-style="color: #B2B2B2;" /> |
| | | </view> |
| | | <view class="form-row dashed-row"> |
| | | <text class="form-label">预计到店时间</text> |
| | | <view class="row-picker"> |
| | | <view class="row-picker" @click="showArriveTimePicker = true"> |
| | | <text :class="form.arriveTime ? 'picker-value-text' : 'placeholder-text'">{{ form.arriveTime || '请选择' }}</text> |
| | | <u-icon name="arrow-right" size="18" color="#222222"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="form-row no-border"> |
| | | <text class="form-label">预计取件时间</text> |
| | | <view class="row-picker"> |
| | | <view class="row-picker" @click="showPickupTimePicker = true"> |
| | | <text :class="form.pickupTime ? 'picker-value-text' : 'placeholder-text'">{{ form.pickupTime || '请选择' }}</text> |
| | | <u-icon name="arrow-right" size="18" color="#222222"></u-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <u-datetime-picker |
| | | :show="showArriveTimePicker" |
| | | v-model="arriveTimeValue" |
| | | mode="datetime" |
| | | confirmText="确定" |
| | | cancelText="取消" |
| | | title="选择预计到店时间" |
| | | @confirm="confirmArriveTime" |
| | | @cancel="showArriveTimePicker = false" |
| | | @close="showArriveTimePicker = false" |
| | | ></u-datetime-picker> |
| | | <u-datetime-picker |
| | | :show="showPickupTimePicker" |
| | | v-model="pickupTimeValue" |
| | | mode="datetime" |
| | | confirmText="确定" |
| | | cancelText="取消" |
| | | title="选择预计取件时间" |
| | | @confirm="confirmPickupTime" |
| | | @cancel="showPickupTimePicker = false" |
| | | @close="showPickupTimePicker = false" |
| | | ></u-datetime-picker> |
| | | |
| | | <view class="section-card luggage-card"> |
| | | <view class="section-head between"> |
| | |
| | | </view> |
| | | </view> |
| | | <view |
| | | v-for="item in luggageTypes" |
| | | :key="item.id" |
| | | v-for="(item, index) in luggageTypes" |
| | | :key="index" |
| | | class="luggage-item" |
| | | :class="{ active: luggageCountMap[item.id] > 0 }" |
| | | @tap="selectLuggage(item.id)" |
| | | :class="{ active: item.count > 0 }" |
| | | > |
| | | <view class="luggage-info"> |
| | | <image class="luggage-image" src="" mode="aspectFit"></image> |
| | | <image class="luggage-image" :src="item.iconFull" mode="widthFix"></image> |
| | | <view class="luggage-copy"> |
| | | <text class="luggage-name">{{ item.name }}</text> |
| | | <text class="luggage-size">{{ item.size }}</text> |
| | | <text class="luggage-size">{{ item.otherField || '' }}</text> |
| | | </view> |
| | | </view> |
| | | <view v-if="luggageCountMap[item.id] > 0" class="luggage-stepper" @tap.stop> |
| | | <view class="step-btn" @tap="decreaseCount(item.id)"> |
| | | <u-icon name="minus" size="18" color="#B9C0C9"></u-icon> |
| | | </view> |
| | | <text class="step-count">{{ luggageCountMap[item.id] }}</text> |
| | | <view class="step-btn active" @tap="increaseCount(item.id)"> |
| | | <u-icon name="plus" size="18" color="#FFFFFF"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view v-else class="luggage-stepper add-only" @tap.stop> |
| | | <view class="step-btn active" @tap="increaseCount(item.id)"> |
| | | <u-icon name="plus" size="18" color="#FFFFFF"></u-icon> |
| | | </view> |
| | | <view class="luggage-stepper"> |
| | | <image class="step-btn" src="/static/icon/ic_jian@2x.png" mode="widthFix" @click="decreaseCount(index)"></image> |
| | | <text class="step-count">{{ item.count }}</text> |
| | | <image class="step-btn" src="/static/icon/ic_jia@2x.png" mode="widthFix" @click="increaseCount(index)"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="section-card goods-card" @tap="openGoodsPopup"> |
| | | <view class="section-head between arrow-head"> |
| | | <view class="section-card goods-card"> |
| | | <view class="section-head between arrow-head" @click="showGoodsPopup = true"> |
| | | <view> |
| | | <text class="section-title">物品信息</text> |
| | | </view> |
| | | <view class="required-wrap"> |
| | | <text class="section-required goods-required-text">{{ selectedGoodsText }}</text> |
| | | <u-icon name="arrow-right" size="18" color="#A8AFBA"></u-icon> |
| | | <text class="section-required goods-required-text" :style="{ color: form.goodTypeName ? '#111111' : '#B2B2B2' }">{{ form.goodTypeName || '必选,请选择' }}</text> |
| | | <u-icon name="arrow-right" size="12" color="#A8AFBA"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="goods-upload-row"> |
| | | <view class="upload-box"> |
| | | <view class="upload-box" @click="chooseAndUploadImage(9)"> |
| | | <image class="upload-icon" src="/static/image/btn_upload@2x.png" mode="widthFix"></image> |
| | | </view> |
| | | <view v-for="(item, index) in uploadedImages" :key="index" class="uploaded-box"> |
| | | <view v-for="(item, index) in uploadedImages" :key="index" class="uploaded-box" @click="deleteImage(index)"> |
| | | <image class="uploaded-image" :src="item.url" mode="aspectFill"></image> |
| | | <text class="uploaded-delete">删除</text> |
| | | <text class="uploaded-delete" @click="deleteImage(index)">删除</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | </view> |
| | | <view class="store-search-bar"> |
| | | <image class="store-search-icon" src="/static/icon/ic_search2@2x.png" mode="widthFix"></image> |
| | | <input v-model="storeKeyword" class="store-search-input" type="text" placeholder="搜索服务点名称" placeholder-class="store-search-placeholder" /> |
| | | <input v-model="storeForm.keyword" class="store-search-input" type="text" @confirm="searchStore" placeholder="搜索服务点名称" placeholder-class="store-search-placeholder" /> |
| | | </view> |
| | | <scroll-view scroll-y class="store-list-scroll"> |
| | | <view |
| | | v-for="item in filteredStores" |
| | | :key="item.id" |
| | | class="store-option" |
| | | :class="{ active: tempSelectedStoreId === item.id }" |
| | | @tap="tempSelectedStoreId = item.id" |
| | | > |
| | | <scroll-view scroll-y class="store-list-scroll" @scrolltolower="getNearbyShopList"> |
| | | <view |
| | | v-for="(item, index) in storeList" |
| | | :key="index" |
| | | class="store-option" |
| | | :class="{ active: item.active }" |
| | | @click="storeList.forEach((row,i) => row.active = index === i)" |
| | | > |
| | | <view class="store-option-main"> |
| | | <image class="store-thumb" src="" mode="aspectFill"></image> |
| | | <view class="store-option-copy"> |
| | |
| | | <text class="goods-popup-desc">为确保物品寄递安全,请检查是否不夹带易燃易爆物品</text> |
| | | <view class="goods-tag-grid"> |
| | | <view |
| | | v-for="item in goodsOptions" |
| | | :key="item.id" |
| | | v-for="(item, index) in goodsOptions" |
| | | :key="index" |
| | | class="goods-tag" |
| | | :class="{ active: tempSelectedGoodsIds.includes(item.id) }" |
| | | @tap="toggleGoods(item.id)" |
| | | :class="{ active: item.active }" |
| | | @click="goodsOptions.forEach((row,i) => row.active = i === index)" |
| | | > |
| | | <text>{{ item.name }}</text> |
| | | </view> |
| | |
| | | </view> |
| | | </view> |
| | | <view class="amount-popup-content"> |
| | | <view v-for="item in amountDetails" :key="item.label" class="amount-row"> |
| | | <view v-for="(item, index) in amountData.itemList" :key="index" class="amount-row"> |
| | | <view class="amount-row-left"> |
| | | <text class="amount-row-label">{{ item.label }}</text> |
| | | <text v-if="item.count" class="amount-row-count">x{{ item.count }}</text> |
| | | <text class="amount-row-label">{{ item.categoryName }}</text> |
| | | <text v-if="item.quantity" class="amount-row-count">x{{ item.quantity }}</text> |
| | | </view> |
| | | <text class="amount-row-value">{{ item.value }}</text> |
| | | <text class="amount-row-value">{{ '¥' + item.unitPrice }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="agreement-bar popup-agreement-bar"> |
| | |
| | | <view class="bottom-action-row popup-action-row"> |
| | | <view class="total-wrap"> |
| | | <text class="total-label">总费用</text> |
| | | <text class="total-price">¥150.00</text> |
| | | <text class="total-price">{{ amountData ? '¥' + amountData.totalPrice : '¥--' }}</text> |
| | | <text class="detail-text">明细</text> |
| | | <u-icon name="arrow-up" size="18" color="#7B7F86"></u-icon> |
| | | <u-icon name="arrow-up" size="13" color="#7B7F86"></u-icon> |
| | | </view> |
| | | <view class="submit-btn active-submit-btn">立即下单</view> |
| | | <view class="submit-btn active-submit-btn" @click="createOrder">立即下单</view> |
| | | </view> |
| | | </view> |
| | | </u-popup> |
| | |
| | | <view class="bottom-action-row"> |
| | | <view class="total-wrap"> |
| | | <text class="total-label">总费用</text> |
| | | <text class="total-price">{{ totalPriceText }}</text> |
| | | <text class="total-price">{{ amountData ? '¥' + amountData.totalPrice : '¥--' }}</text> |
| | | <text class="detail-text detail-click" @tap.stop="openAmountPopup">明细</text> |
| | | <u-icon name="arrow-down" size="18" color="#999999"></u-icon> |
| | | </view> |
| | | <view class="submit-btn active-submit-btn">立即下单</view> |
| | | <view class="submit-btn active-submit-btn" @click="createOrder">立即下单</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapState } from 'vuex' |
| | | export default { |
| | | data() { |
| | | return { |
| | | showStorePopup: false, |
| | | showGoodsPopup: false, |
| | | showAmountPopup: false, |
| | | showArriveTimePicker: false, |
| | | showPickupTimePicker: false, |
| | | arriveTimeValue: Number(new Date()), |
| | | pickupTimeValue: Number(new Date()), |
| | | activeMode: 'local', |
| | | modeTabs: [ |
| | | { label: '就地寄存', value: 'local' }, |
| | | { label: '同城寄送', value: 'city' } |
| | | ], |
| | | agreementChecked: true, |
| | | storeKeyword: '', |
| | | tempSelectedStoreId: 2, |
| | | selectedStoreId: 2, |
| | | tempSelectedGoodsIds: [1], |
| | | selectedGoodsIds: [1], |
| | | selectedLuggageId: 1, |
| | | selectedServiceTime: 1, |
| | | luggageCountMap: { |
| | | 1: 1, |
| | | 2: 1, |
| | | 3: 0 |
| | | }, |
| | | form: { |
| | | receiver: '蔡子瑄', |
| | | mobile: '18166525368', |
| | | arriveTime: '2026-04-15 16:30', |
| | | pickupTime: '2026-04-17 16:30', |
| | | insurance: '200', |
| | | remark: '' |
| | | receiver: '', |
| | | mobile: '', |
| | | arriveTime: '', |
| | | pickupTime: '', |
| | | goodType: '', |
| | | goodTypeName: '', |
| | | insurance: '', |
| | | remark: '', |
| | | goodsImages: [], |
| | | }, |
| | | luggageTypes: [ |
| | | { id: 1, name: '大件行李箱', size: '24-28寸' }, |
| | | { id: 2, name: '中件行李箱', size: '24-28寸' }, |
| | | { id: 3, name: '小件行李箱', size: '24-28寸' } |
| | | ], |
| | | amountData: null, |
| | | luggageTypes: [], |
| | | serviceTimes: [ |
| | | { id: 1, name: '标准达(预计6小时内送达)', price: '¥50' }, |
| | | { id: 2, name: '急速达(预计4小时内送达)', price: '¥70' } |
| | | ], |
| | | storeList: [ |
| | | { id: 1, name: '中铁快运南站旗舰店', address: '合肥南站负一层100号', time: '周一至周日 7:00~23:00', distance: '239m' }, |
| | | { id: 2, name: '中铁快运合肥火车站', address: '合肥火车站一层12号', time: '周一至周日 7:00~23:00', distance: '12.8km' }, |
| | | { id: 3, name: '合肥火车站北广场', address: '合肥火车站一层12号', time: '周一至周日 7:00~23:00', distance: '13.1km' }, |
| | | { id: 4, name: '小铁无忧存', address: '合肥火车站一层12号', time: '周一至周日 7:00~23:00', distance: '16.3km' }, |
| | | { id: 5, name: '中铁快运合肥火车站', address: '合肥火车站一层12号', time: '周一至周日 7:00~23:00', distance: '20.8km' } |
| | | ], |
| | | goodsOptions: [ |
| | | { id: 1, name: '文件' }, |
| | | { id: 2, name: '衣服' }, |
| | | { id: 3, name: '滑雪板' }, |
| | | { id: 4, name: '发票' }, |
| | | { id: 5, name: '手机' }, |
| | | { id: 6, name: '电脑' }, |
| | | { id: 7, name: '鞋子' }, |
| | | { id: 8, name: '护肤品' }, |
| | | { id: 9, name: '白酒' }, |
| | | { id: 10, name: '鞋子' }, |
| | | { id: 11, name: '护肤品' }, |
| | | { id: 12, name: '白酒' }, |
| | | { id: 13, name: '鞋子' }, |
| | | { id: 14, name: '护肤品' }, |
| | | { id: 15, name: '白酒' } |
| | | ], |
| | | amountDetails: [ |
| | | { label: '大件行李箱', count: 1, value: '¥35' }, |
| | | { label: '中件行李箱', count: 1, value: '¥35' }, |
| | | { label: '寄存天数', count: '', value: '2天' }, |
| | | { label: '行李保价', count: '', value: '¥10' } |
| | | ], |
| | | uploadedImages: [ |
| | | { url: '/static/icon/nav_home_sel@2x.png' }, |
| | | { url: '/static/icon/nav_xingcheng_sel@2x.png' } |
| | | ] |
| | | |
| | | storeList: [], |
| | | selectedStore: null, |
| | | storeForm: { |
| | | keyword: '', |
| | | page: 1, |
| | | isSearch: true |
| | | }, |
| | | |
| | | goodsOptions: [], |
| | | uploadedImages: [] |
| | | } |
| | | }, |
| | | watch: { |
| | | 'form.insurance': { |
| | | handler() { |
| | | this.calculateLocalPrice() |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | selectedStore() { |
| | | return this.storeList.find(item => item.id === this.selectedStoreId) || null |
| | | }, |
| | | ...mapState(['latitude', 'longitude', 'cityId']), |
| | | servicePointPlaceholder() { |
| | | return this.activeMode === 'city' ? '选择寄送服务点' : '选择寄存服务点' |
| | | }, |
| | | filteredStores() { |
| | | const keyword = (this.storeKeyword || '').trim() |
| | | if (!keyword) { |
| | | return this.storeList |
| | | } |
| | | return this.storeList.filter(item => item.name.includes(keyword) || item.address.includes(keyword)) |
| | | }, |
| | | selectedGoodsText() { |
| | | if (!this.selectedGoodsIds.length) { |
| | |
| | | return '¥150.00' |
| | | } |
| | | }, |
| | | onLoad() { |
| | | this.getNearbyShopList() |
| | | this.getCategoryList() |
| | | this.getCitySizeList() |
| | | }, |
| | | methods: { |
| | | async uploadFiles(filePaths, maxCount = 9) { |
| | | if (!filePaths || filePaths.length === 0) { |
| | | return [] |
| | | } |
| | | const limitedPaths = filePaths.slice(0, maxCount) |
| | | const uploadTasks = limitedPaths.map(filePath => { |
| | | return new Promise((resolve, reject) => { |
| | | uni.uploadFile({ |
| | | url: this.$baseUrl + '/web/public/upload', |
| | | filePath: filePath, |
| | | name: 'file', |
| | | formData: { |
| | | folder: 'orders' |
| | | }, |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | const data = JSON.parse(res.data) |
| | | if (data.code === 200) { |
| | | resolve(data.data) |
| | | } else { |
| | | reject(new Error(data.msg || '上传失败')) |
| | | } |
| | | } else { |
| | | reject(new Error('上传失败')) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | }) |
| | | try { |
| | | const results = await Promise.all(uploadTasks) |
| | | return results |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: '上传失败', |
| | | icon: 'none' |
| | | }) |
| | | throw error |
| | | } |
| | | }, |
| | | deleteImage(index) { |
| | | this.uploadedImages.splice(index, 1) |
| | | this.form.goodsImages.splice(index, 1) |
| | | }, |
| | | async chooseAndUploadImage(maxCount = 9) { |
| | | const currentCount = this.form.goodsImages.length |
| | | const remainingCount = maxCount - currentCount |
| | | if (remainingCount <= 0) { |
| | | uni.showToast({ |
| | | title: `最多上传${maxCount}张图片`, |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | uni.chooseImage({ |
| | | count: remainingCount, |
| | | sizeType: ['compressed'], |
| | | sourceType: ['album', 'camera'], |
| | | success: async (res) => { |
| | | const tempFilePaths = res.tempFilePaths |
| | | uni.showLoading({ |
| | | title: '上传中...', |
| | | mask: true |
| | | }) |
| | | try { |
| | | const uploadResults = await this.uploadFiles(tempFilePaths, maxCount) |
| | | const fullPaths = uploadResults.map(item => item.url || item.path || item) |
| | | this.uploadedImages = [...this.uploadedImages, ...fullPaths.map(url => ({ url }))] |
| | | this.form.goodsImages = [...this.form.goodsImages, ...fullPaths] |
| | | uni.hideLoading() |
| | | uni.showToast({ |
| | | title: '上传成功', |
| | | icon: 'success' |
| | | }) |
| | | } catch (error) { |
| | | uni.hideLoading() |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | searchStore() { |
| | | this.storeList = [] |
| | | this.storeForm.page = 1 |
| | | this.storeForm.isSearch = true |
| | | this.getNearbyShopList() |
| | | }, |
| | | switchMode(mode) { |
| | | this.activeMode = mode |
| | | }, |
| | |
| | | this.showAmountPopup = true |
| | | }, |
| | | openStorePopup() { |
| | | this.tempSelectedStoreId = this.selectedStoreId |
| | | this.tempSelectedStoreId = null |
| | | this.showStorePopup = true |
| | | }, |
| | | confirmStore() { |
| | | this.selectedStoreId = this.tempSelectedStoreId |
| | | this.selectedStore = this.storeList.find(item => item.active) |
| | | this.showStorePopup = false |
| | | }, |
| | | openGoodsPopup() { |
| | | this.tempSelectedGoodsIds = [...this.selectedGoodsIds] |
| | | this.showGoodsPopup = true |
| | | }, |
| | | toggleGoods(id) { |
| | | if (this.tempSelectedGoodsIds.includes(id)) { |
| | | this.tempSelectedGoodsIds = this.tempSelectedGoodsIds.filter(item => item !== id) |
| | | return |
| | | } |
| | | this.tempSelectedGoodsIds = [...this.tempSelectedGoodsIds, id] |
| | | this.calculateLocalPrice() |
| | | }, |
| | | confirmGoods() { |
| | | this.selectedGoodsIds = [...this.tempSelectedGoodsIds] |
| | | this.form.goodTypeName = this.goodsOptions.find(item => item.active)?.name || '' |
| | | this.form.goodType = this.goodsOptions.find(item => item.active)?.id || '' |
| | | this.showGoodsPopup = false |
| | | }, |
| | | selectLuggage(id) { |
| | | this.selectedLuggageId = id |
| | | confirmArriveTime(e) { |
| | | const date = new Date(e.value) |
| | | const year = date.getFullYear() |
| | | const month = String(date.getMonth() + 1).padStart(2, '0') |
| | | const day = String(date.getDate()).padStart(2, '0') |
| | | const hour = String(date.getHours()).padStart(2, '0') |
| | | const minute = String(date.getMinutes()).padStart(2, '0') |
| | | this.form.arriveTime = `${year}-${month}-${day} ${hour}:${minute}` |
| | | this.showArriveTimePicker = false |
| | | this.calculateLocalPrice() |
| | | }, |
| | | increaseCount(id) { |
| | | const current = this.luggageCountMap[id] || 0 |
| | | this.$set(this.luggageCountMap, id, current + 1) |
| | | confirmPickupTime(e) { |
| | | const date = new Date(e.value) |
| | | const year = date.getFullYear() |
| | | const month = String(date.getMonth() + 1).padStart(2, '0') |
| | | const day = String(date.getDate()).padStart(2, '0') |
| | | const hour = String(date.getHours()).padStart(2, '0') |
| | | const minute = String(date.getMinutes()).padStart(2, '0') |
| | | this.form.pickupTime = `${year}-${month}-${day} ${hour}:${minute}` |
| | | this.showPickupTimePicker = false |
| | | this.calculateLocalPrice() |
| | | }, |
| | | decreaseCount(id) { |
| | | const current = this.luggageCountMap[id] || 0 |
| | | if (current <= 0) { |
| | | // 物品分类 |
| | | async getCategoryList() { |
| | | const res = await this.$u.api.getCategoryList({ type: 2 }) |
| | | if (res.code === 200) { |
| | | res.data.forEach(item => { |
| | | item.active = false |
| | | }) |
| | | this.goodsOptions = res.data || [] |
| | | } |
| | | }, |
| | | async getCitySizeList() { |
| | | const res = await this.$u.api.getCitySizeList({ cityId: this.cityId }) |
| | | if (res.code === 200) { |
| | | res.data.forEach(item => { |
| | | item.count = 0 |
| | | }) |
| | | this.luggageTypes = res.data || [] |
| | | } |
| | | }, |
| | | async getNearbyShopList() { |
| | | if (!this.storeForm.isSearch) return; |
| | | const res = await this.$u.api.getNearbyShopList({ |
| | | capacity: 10, |
| | | page: this.storeForm.page, |
| | | model: { |
| | | latitude: this.latitude, |
| | | longitude: this.longitude, |
| | | cityId: this.cityId, |
| | | sortType: 1 |
| | | } |
| | | }) |
| | | if (res.code === 200) { |
| | | res.data.records.forEach(item => { |
| | | item.active = false |
| | | }) |
| | | this.storeList = [...this.storeList, ...res.data.records || []] |
| | | this.storeForm.page++ |
| | | this.storeForm.isSearch = this.storeList.length <= res.data.total |
| | | } |
| | | }, |
| | | increaseCount(index) { |
| | | this.luggageTypes[index].count++ |
| | | this.calculateLocalPrice() |
| | | }, |
| | | decreaseCount(index) { |
| | | if (this.luggageTypes[index].count > 0) { |
| | | this.luggageTypes[index].count-- |
| | | } |
| | | this.calculateLocalPrice() |
| | | }, |
| | | async calculateLocalPrice() { |
| | | if (!this.selectedStore || !this.form.arriveTime || !this.form.pickupTime) { |
| | | return |
| | | } |
| | | this.$set(this.luggageCountMap, id, current - 1) |
| | | const luggageList = this.luggageTypes |
| | | .filter(item => item.count > 0) |
| | | .map(item => ({ |
| | | categoryId: item.id, |
| | | quantity: item.count |
| | | })) |
| | | if (luggageList.length === 0) { |
| | | return |
| | | } |
| | | const res = await this.$u.api.calculateLocalPrice({ |
| | | cityId: this.cityId, |
| | | shopId: this.selectedStore.id, |
| | | depositStartTime: this.form.arriveTime + ':00', |
| | | depositEndTime: this.form.pickupTime + ':00', |
| | | items: luggageList, |
| | | declaredAmount: this.form.insurance || 0 |
| | | }) |
| | | if (res.code === 200) { |
| | | res.data.itemList.forEach(item => { |
| | | item.unitPrice = item.unitPrice / 100 |
| | | }) |
| | | res.data.totalPrice = res.data.totalPrice / 100 |
| | | this.amountData = res.data |
| | | } |
| | | }, |
| | | async createOrder() { |
| | | if (!this.selectedStore) { |
| | | uni.showToast({ title: '请选择门店', icon: 'none' }) |
| | | return |
| | | } |
| | | if (!this.form.arriveTime) { |
| | | uni.showToast({ title: '请选择预计到店时间', icon: 'none' }) |
| | | return |
| | | } |
| | | if (!this.form.pickupTime) { |
| | | uni.showToast({ title: '请选择预计取件时间', icon: 'none' }) |
| | | return |
| | | } |
| | | const luggageList = this.luggageTypes |
| | | .filter(item => item.count > 0) |
| | | .map(item => ({ |
| | | categoryId: item.id, |
| | | quantity: item.count |
| | | })) |
| | | if (luggageList.length === 0) { |
| | | uni.showToast({ title: '请选择行李类型', icon: 'none' }) |
| | | return |
| | | } |
| | | const items = luggageList.map(item => ({ |
| | | categoryId: item.categoryId, |
| | | quantity: item.quantity |
| | | })) |
| | | const res = await this.$u.api.createOrder({ |
| | | cityId: this.cityId, |
| | | declaredAmount: this.form.insurance || 0, |
| | | depositShopId: this.selectedStore.id, |
| | | expectedDepositTime: this.form.arriveTime + ':00', |
| | | expectedTakeTime: this.form.pickupTime + ':00', |
| | | goodType: this.form.goodType, |
| | | goodsImages: this.form.goodsImages, |
| | | items: items, |
| | | remark: this.form.remark, |
| | | takePhone: this.form.mobile, |
| | | takeUser: this.form.receiver, |
| | | type: this.activeMode === 'local' ? 0 : 1 |
| | | }) |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: '下单成功', icon: 'success' }) |
| | | if (res.data) { |
| | | this.processPayment(res.data.response) |
| | | } |
| | | } |
| | | }, |
| | | processPayment(paymentData) { |
| | | uni.requestPayment({ |
| | | provider: 'wxpay', |
| | | timeStamp: paymentData.timeStamp || '', |
| | | nonceStr: paymentData.nonceStr || '', |
| | | package: paymentData.packageValue || '', |
| | | signType: paymentData.signType || 'MD5', |
| | | paySign: paymentData.paySign || '', |
| | | success: (res) => { |
| | | uni.showToast({ title: '支付成功', icon: 'success' }) |
| | | }, |
| | | fail: (err) => { |
| | | if (err.errMsg.includes('cancel')) { |
| | | uni.showToast({ title: '已取消支付', icon: 'none' }) |
| | | } else { |
| | | uni.showToast({ title: '支付失败', icon: 'none' }) |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | .store-list-scroll { |
| | | max-height: 70vh; |
| | | min-height: 50vh; |
| | | margin-top: 30rpx; |
| | | } |
| | | |
| | |
| | | font-size: 30rpx; |
| | | color: #222222; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | .cell-title.placeholder { |
| | | color: #9097a3; |
| | | font-weight: 400; |
| | | } |
| | | |
| | | .store-cell-subtitle { |
| | |
| | | } |
| | | |
| | | .step-btn { |
| | | width: 36rpx; |
| | | height: 36rpx; |
| | | border-radius: 18rpx; |
| | | background: #eef1f5; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .step-btn.active { |
| | | background: #27aef8; |
| | | width: 44rpx; |
| | | height: 44rpx; |
| | | } |
| | | |
| | | .step-count { |
| | |
| | | .goods-upload-row { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 12rpx; |
| | | margin-top: 8rpx; |
| | | } |
| | |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | height: 28rpx; |
| | | line-height: 28rpx; |
| | | height: 40rpx; |
| | | line-height: 40rpx; |
| | | text-align: center; |
| | | font-size: 18rpx; |
| | | color: #ffffff; |
| | | background: rgba(0, 0, 0, 0.48); |
| | | font-weight: 400; |
| | | font-size: 24rpx; |
| | | color: #FFFFFF; |
| | | background: rgba(0,0,0,0.5); |
| | | border-radius: 0rpx 0rpx 8rpx 8rpx; |
| | | } |
| | | |
| | | .service-time-card { |