| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088 |
- <template>
- <view class="order-container">
- <!-- 用户信息卡片 -->
- <view class="info-card">
- <view class="address-item">
- <view class="img-status-text">
- <image src="/static/img/icon-send.png" mode="" class="img"></image>
- </view>
- <view class="user-info">
- <AddressInfo v-if="addressSend.addressId" :address="addressSend" />
- <view v-else class="create-btn" @click="handleAddAddress('sender')">新建寄件人</view>
- </view>
- <view class="img-status-text right" @click="openAddressBook('sender')">
- <image src="/static/img/create-order-address.png" mode="" class="address-image"></image>
- <view class="status-text">地址薄</view>
- </view>
- </view>
- <view class="address-item">
- <view class="img-status-text">
- <image src="/static/img/create-order-change.png" mode="" class="img-change"></image>
- </view>
- <view class="user-info">
- <view class="line"></view>
- </view>
- </view>
- <view class="address-item">
- <view class="img-status-text">
- <image src="/static/img/icon-receive.png" mode="" class="img"></image>
- </view>
- <view class="user-info">
- <AddressInfo v-if="addressReceive.addressId" :address="addressReceive" />
- <view v-else class="create-btn" @click="handleAddAddress('receiver')">新建收件人</view>
- </view>
- <view class="img-status-text right" @click="openAddressBook('receiver')">
- <image src="/static/img/create-order-address.png" mode="" class="address-image"></image>
- <view class="status-text">地址薄</view>
- </view>
- </view>
- </view>
- <view class="pickup-title">上门取件</view>
- <!-- 物品信息 -->
- <view class="goods-card">
- <view class="goods-item">
- <view class="item-label required">期望上门时间</view>
- <view class="time-value" @click="handleTimeClick">
- <text class="value">{{ selectedTimeLabel }}</text>
- <u-icon class="arrow" name='arrow-right' size="18"></u-icon>
- </view>
- </view>
- <view class="goods-item">
- <view class="item-label required">快递类型</view>
- <view class="item-control">
- <picker :range="expressTypes" :range-key="'label'" @change="onExpressTypeChange"
- :value="expressTypeIndex" :disabled="!expressTypes.length">
- <view class="picker-text">
- {{ expressTypes[expressTypeIndex]?.label || (expressTypes.length ? '请选择快递类型' : '加载中...') }}
- </view>
- </picker>
- <u-icon class="arrow" name='arrow-right' size="18"></u-icon>
- </view>
- </view>
- <view class="goods-item">
- <view class="item-label required">物品信息</view>
- <view class="item-control">
- <input class="input-field" placeholder="请输入物品信息" placeholder-class="placeholder" v-model="goodsInfo"
- maxlength="50" @input="onGoodsInfoInput" />
- </view>
- </view>
- <!-- 总体重 -->
- <view class="goods-item">
- <view class="item-label">总体重(KG)</view>
- <view class="item-control btn">
- <view class="control-btn minus" @click="decreaseWeight">-</view>
- <view class="control-value">
- <input class="input-field" placeholder="" placeholder-class="placeholder" :value="weight"
- @input="handleWeightInput" type="digit" />
- </view>
- <view class="control-btn plus" @click="increaseWeight">+</view>
- </view>
- </view>
- <!-- 总体积 -->
- <view class="goods-item">
- <view class="item-label">总体积(m³)</view>
- <view class="item-control btn">
- <view class="control-btn minus" @click="decreaseVolume">-</view>
- <view class="control-value">
- <input class="input-field" placeholder="" placeholder-class="placeholder" :value="volume"
- @input="handleVolumeInput" type="digit" />
- </view>
- <view class="control-btn plus" @click="increaseVolume">+</view>
- </view>
- </view>
- <!-- 件数 -->
- <view class="goods-item">
- <view class="item-label">件数(件)</view>
- <view class="item-control btn">
- <view class="control-btn minus" @click="decreaseQuantity">-</view>
- <view class="control-value">
- <input class="input-field" placeholder="" placeholder-class="placeholder" :value="quantity"
- @input="handleQuantityInput" type="number" />
- </view>
- <view class="control-btn plus" @click="increaseQuantity">+</view>
- </view>
- </view>
- </view>
- <!-- 增值服务标题(带折叠箭头) -->
- <view class="pickup-title" style="display: flex; justify-content: space-between; align-items: center;">
- <text>增值服务</text>
- <u-icon :name="showValueAdded ? 'arrow-up' : 'arrow-down'" size="18" color="#999" @click="showValueAdded = !showValueAdded"></u-icon>
- </view>
- <!-- 增值服务卡片(折叠内容) -->
- <view class="goods-card">
- <block v-if="showValueAdded">
- <!-- 展开状态:显示所有增值服务项 -->
- <view class="goods-item" v-if="product === '1'">
- <view class="item-label">包装服务</view>
- <view class="item-control">
- <switch color="#007AFF" :checked="valueServices.isPack" @change="onPackagingChange" />
- </view>
- </view>
- <view class="goods-item">
- <view class="item-label">保价</view>
- <view class="item-control">
- <switch color="#007AFF" :checked="insuranceAmountChecked" @change="onInsuranceChange" />
- </view>
- </view>
- <view v-if="insuranceAmountChecked" class="goods-item">
- <view class="item-label">保价金额(元)</view>
- <view class="item-control">
- <input class="input-field" placeholder="请输入保价金额" placeholder-class="placeholder"
- v-model="insuranceAmount" type="digit" maxlength="10" @input="validateInsuranceAmount" />
- </view>
- </view>
- <!-- 超长超重(顺丰/京东) -->
- <!-- <view class="goods-item" v-if="product === '2'">
- <view class="item-label">超长超重</view>
- <view class="item-control">
- <switch color="#007AFF" :checked="valueServices.isOverLongWeight" @change="onOverweightChange" />
- </view>
- </view> -->
- <!-- 签单返还 -->
- <view class="goods-item" v-if="product === '1'">
- <view class="item-label">签单返还</view>
- <view class="item-control">
- <switch color="#007AFF" :checked="valueServices.isReceiptCollect" @change="onSignReturnChange" />
- </view>
- </view>
- <!-- 顺丰:签单返还类型选择 -->
- <view v-if="product === '2' && valueServices.isReceiptCollect" class="goods-item">
- <view class="item-label">返还类型</view>
- <view class="item-control">
- <picker :range="receiptReturnTypes" :range-key="'label'" @change="onReceiptReturnTypeChange"
- :value="receiptReturnTypeIndex">
- <view class="picker-text">{{ receiptReturnTypes[receiptReturnTypeIndex]?.label || '请选择返还类型' }}
- </view>
- </picker>
- <u-icon class="arrow" name='arrow-right' size="18"></u-icon>
- </view>
- </view>
- <!-- 打木架(仅顺丰) -->
- <!-- <view v-if="product === '2'" class="goods-item">
- <view class="item-label">打木架</view>
- <view class="item-control">
- <switch color="#007AFF" :checked="valueServices.isWoodenCrate" @change="onWoodenFrameChange" />
- </view>
- </view> -->
- </block>
- <block v-else>
- <!-- 折叠状态:显示一个可点击的展开提示行 -->
- <view class="goods-item" @click="showValueAdded = true">
- <view class="item-label">增值服务</view>
- <view class="item-control">
- <text class="picker-text">点击展开</text>
- <u-icon name="arrow-down" size="18" color="#999"></u-icon>
- </view>
- </view>
- </block>
- </view>
- <!-- 协议同意 -->
- <view class="agreement-card">
- <!-- <view class="agreement-item">
- <checkbox color="#1B64F0" :checked="agreed" @click="toggleAgreement" style="transform:scale(0.8)" />
- <text class="agreement-text">我已理解并同意遵守《快件服务协议》</text>
- </view> -->
- <view class="price-notice">
- 实际费用以快递员核实为准
- </view>
- </view>
- <view class="add-btn-container">
- <!-- 下单按钮 -->
- <view class="submit-btn" :class="{ disabled: !agreed }" :disabled="!agreed" @click="submitOrder">
- 提交订单
- </view>
- </view>
- <!-- 安全区域占位 -->
- <view class="safe-area"></view>
- <!-- 时间选择弹窗 -->
- <TimePopup :visible="showTimePicker" @close="showTimePicker = false" @confirm="handleTimeConfirm" />
- </view>
- </template>
- <script setup>
- import {
- ref,
- computed,
- onMounted,
- onUnmounted,
- watch,
- } from 'vue'
- import {
- onLoad
- } from '@dcloudio/uni-app'
- import TimePopup from './components/TimePopup.vue'
- import AddressInfo from '@/components/AddressInfo.vue'
- import {
- createOrder,
- dictList
- } from '../../api/order'
- import {
- getDefaultAddress
- } from '../../api/address'
- // ==================== 数据存储 ====================
- const getAddressFromStorage = (key) => {
- const address = uni.getStorageSync(key)
- return address || {
- id: '',
- name: '',
- phone: '',
- address: '',
- provinceName: '',
- cityName: '',
- countyName: '',
- isDefault: false
- }
- }
- const saveAddressToStorage = (key, address) => {
- uni.setStorageSync(key, address)
- }
- // ==================== 页面参数 ====================
- const product = ref('1') // 1-京东,2-顺丰
- // ==================== 地址信息 ====================
- const addressSend = ref(getAddressFromStorage('senderAddress'))
- const addressReceive = ref(getAddressFromStorage('receiverAddress'))
- // ==================== 物品信息 ====================
- const goodsInfo = ref('')
- const weight = ref(0.5)
- const volume = ref(0.001)
- const quantity = ref(1)
- // ==================== 时间选择 ====================
- const showTimePicker = ref(false)
- const selectedTimeData = ref({
- startTime:undefined,
- endTime:undefined
- })
- const selectedTimeLabel = computed(() => {
- if (!selectedTimeData.value.startTime) return '一小时内'
- const {
- dateLabel,
- timeLabel,
- isToday,
- isImmediate
- } = selectedTimeData.value
- if (isToday && isImmediate) return '一小时内'
- return `${dateLabel} ${timeLabel}`
- })
- // ==================== 快递类型字典(动态获取)====================
- const jdDictList = ref([]) // 京东快递类型字典
- const sfDictList = ref([]) // 顺丰快递类型字典
- const expressTypeIndex = ref(0)
- const expressTypes = ref([]) // 当前显示的快递类型列表(已转换为 {label, value} 格式)
- // 从后端获取字典数据
- const fetchDictData = async () => {
- try {
- uni.showLoading({
- title: '加载字典...'
- })
- // 并发请求两个字典
- const [jdRes, sfRes] = await Promise.all([
- dictList('jd_logistics_product_code'),
- dictList('sf_logistics_product_code')
- ])
- if (jdRes.code === 200) {
- jdDictList.value = jdRes.data || []
- }
- if (sfRes.code === 200) {
- sfDictList.value = sfRes.data || []
- }
- updateExpressTypes()
- } catch (error) {
- console.error('获取快递类型字典失败', error)
- uni.showToast({
- title: '字典加载失败',
- icon: 'none'
- })
- } finally {
- uni.hideLoading()
- }
- }
- // 根据 product 更新 expressTypes
- const updateExpressTypes = () => {
- let sourceList = []
- if (product.value === '1') {
- sourceList = jdDictList.value
- } else if (product.value === '2') {
- sourceList = sfDictList.value
- }
- // 转换为 picker 需要的格式:{ label: dictLabel, value: dictValue }
- expressTypes.value = sourceList.map(item => ({
- label: item.dictLabel,
- value: item.dictValue
- }))
- // 重置选中索引
- expressTypeIndex.value = 0
- }
- // ==================== 增值服务 ====================
- const valueServices = ref({
- isPack: false,
- isOverLongWeight: false,
- isReceiptCollect: false,
- isWoodenCrate: false
- })
- // 保价金额
- const insuranceAmountChecked = ref(false)
- const insuranceAmount = ref('')
- const insuranceAmountError = ref('')
- // 签单返还类型(顺丰)
- const receiptReturnTypeIndex = ref(0)
- const receiptReturnTypes = ref([{
- label: '电子凭证',
- value: 'electronic'
- },
- {
- label: '纸质凭证',
- value: 'paper'
- }
- ])
- // ==================== 折叠控制 ====================
- const showValueAdded = ref(false) // 默认折叠
- // ==================== 协议 ====================
- const agreed = ref(true)
- // ==================== 表单操作 ====================
- // 重量
- const increaseWeight = () => {
- weight.value = parseFloat((weight.value + 0.1).toFixed(3))
- if (weight.value > 100) weight.value = 100
- }
- const decreaseWeight = () => {
- weight.value = parseFloat((weight.value - 0.1).toFixed(3))
- if (weight.value < 0.1) weight.value = 0.1
- }
- const handleWeightInput = (e) => {
- let value = e.detail.value.replace(/[^\d.]/g, '')
- const parts = value.split('.')
- if (parts.length > 2) value = parts[0] + '.' + parts.slice(1).join('')
- if (value.includes('.')) {
- const decimal = value.split('.')[1]
- if (decimal && decimal.length > 3) value = parseFloat(value).toFixed(3)
- }
- const num = parseFloat(value) || 0.1
- weight.value = Math.max(0.1, Math.min(100, num))
- }
- // 体积
- const increaseVolume = () => {
- volume.value = parseFloat((volume.value + 0.001).toFixed(3))
- // if (volume.value > 10) volume.value = 10
- }
- const decreaseVolume = () => {
- volume.value = parseFloat((volume.value - 0.001).toFixed(3))
- if (volume.value < 0.001) volume.value = 0.001
- }
- const handleVolumeInput = (e) => {
- let value = e.detail.value.replace(/[^\d.]/g, '')
- const parts = value.split('.')
- if (parts.length > 2) value = parts[0] + '.' + parts.slice(1).join('')
- if (value.includes('.')) {
- const decimal = value.split('.')[1]
- if (decimal && decimal.length > 3) value = parseFloat(value).toFixed(3)
- }
- const num = parseFloat(value) || 0.001
- volume.value = Math.max(0.001, Math.min(10, num))
- }
- // 件数
- const increaseQuantity = () => {
- quantity.value += 1
- // if (quantity.value > 99) quantity.value = 99
- }
- const decreaseQuantity = () => {
- quantity.value -= 1
- if (quantity.value < 1) quantity.value = 1
- }
- const handleQuantityInput = (e) => {
- let value = e.detail.value.replace(/\D/g, '')
- const int = parseInt(value) || 1
- // quantity.value = Math.max(1, Math.min(99, int))
- }
- // 物品信息
- const onGoodsInfoInput = (e) => {
- goodsInfo.value = e.detail.value
- if (goodsInfo.value.length > 50) goodsInfo.value = goodsInfo.value.substring(0, 50)
- }
- // 快递类型
- const onExpressTypeChange = (e) => {
- expressTypeIndex.value = e.detail.value
- }
- // 增值服务开关
- const onPackagingChange = (e) => {
- valueServices.value.isPack = e.detail.value
- }
- const onOverweightChange = (e) => {
- valueServices.value.isOverLongWeight = e.detail.value
- }
- const onWoodenFrameChange = (e) => {
- valueServices.value.isWoodenCrate = e.detail.value
- }
- const onInsuranceChange = (e) => {
- insuranceAmountChecked.value = e.detail.value
- if (e.detail.value) {
- insuranceAmount.value = '0'
- } else {
- insuranceAmount.value = ''
- insuranceAmountError.value = ''
- }
- }
- const onSignReturnChange = (e) => {
- valueServices.value.isReceiptCollect = e.detail.value
- if (!e.detail.value) {
- receiptReturnTypeIndex.value = 0
- }
- }
- const onReceiptReturnTypeChange = (e) => {
- receiptReturnTypeIndex.value = e.detail.value
- }
- // 保价金额验证
- const validateInsuranceAmount = () => {
- if (!insuranceAmount.value) return true
- const amount = parseFloat(insuranceAmount.value)
- if (isNaN(amount) || amount < 0) {
- insuranceAmountError.value = '保价金额必须为数字且大于0'
- return false
- }
- // if (amount > 1000000) {
- // insuranceAmountError.value = '保价金额不能超过100万'
- // return false
- // }
- const decimal = insuranceAmount.value.toString().split('.')[1]
- if (decimal && decimal.length > 2) {
- insuranceAmountError.value = '最多两位小数'
- return false
- }
- insuranceAmountError.value = ''
- return true
- }
- // 时间选择
- const handleTimeClick = () => {
- showTimePicker.value = true
- }
- const handleTimeConfirm = (time) => {
- selectedTimeData.value = time
- showTimePicker.value = false
- }
- // 地址簿相关
- const openAddressBook = (type) => {
- uni.navigateTo({
- url: `/pages/address/address_list?addType=${type}&from=order`
- })
- }
- const handleAddAddress = (type) => {
- uni.navigateTo({
- url: `/pages/address/edit?addType=${type}&from=order`
- })
- }
- // 协议切换
- const toggleAgreement = () => {
- agreed.value = !agreed.value
- }
- // ==================== 表单验证(增强) ====================
- const validateForm = () => {
- // 地址
- if (!addressSend.value.addressId) {
- uni.showToast({
- title: '请选择寄件人地址',
- icon: 'none'
- });
- return false
- }
- if (!addressReceive.value.addressId) {
- uni.showToast({
- title: '请选择收件人地址',
- icon: 'none'
- });
- return false
- }
- // 时间
- if (!selectedTimeData.value.startTime) {
- // 这没有数据的时候 默认一个小时内 获取当前时间
- selectedTimeData.value.startTime = twentyMinutesLater()
- selectedTimeData.value.endTime = oneHourLater()
- }
- if (isBeforeNineAM(selectedTimeData.value.startTime) || isAfterEightPM(selectedTimeData.value.startTime)) {
- // 不在工作时间内
- uni.showToast({
- title: '请选择合适的寄件时间',
- icon: 'none'
- });
- selectedTimeData.value.startTime = undefined
- selectedTimeData.value.endTime = undefined
- return false
- }
- if (isAfterEightPM(selectedTimeData.value.endTime)) {
- uni.showToast({
- title: '请选择合适的寄件时间',
- icon: 'none'
- });
- selectedTimeData.value.startTime = undefined
- selectedTimeData.value.endTime = undefined
- return false
- }
- // 快递类型
- if (!expressTypes.value.length) {
- uni.showToast({
- title: '快递类型字典加载中,请稍后',
- icon: 'none'
- });
- return false
- }
- if (!expressTypes.value[expressTypeIndex.value]) {
- uni.showToast({
- title: '请选择快递类型',
- icon: 'none'
- });
- return false
- }
- // 物品信息
- if (!goodsInfo.value.trim()) {
- uni.showToast({
- title: '请输入物品信息',
- icon: 'none'
- });
- return false
- }
- if (goodsInfo.value.length > 50) {
- uni.showToast({
- title: '物品信息不能超过50字',
- icon: 'none'
- });
- return false
- }
- // 重量/体积/件数范围
- if (weight.value < 0) {
- uni.showToast({
- title: '请输入正确的重量',
- icon: 'none'
- });
- return false
- }
- if (volume.value < 0) {
- uni.showToast({
- title: '请输入正确的体积',
- icon: 'none'
- });
- return false
- }
- if (quantity.value < 1) {
- uni.showToast({
- title: '请输入正确的件数',
- icon: 'none'
- });
- return false
- }
- // 保价金额验证
- if (insuranceAmount.value !== '' && insuranceAmount.value !== null) {
- if (!validateInsuranceAmount()) {
- uni.showToast({
- title: insuranceAmountError.value || '保价金额无效',
- icon: 'none'
- })
- return false
- }
- }
- // 顺丰签单返还类型
- if (product.value === '2' && valueServices.value.isReceiptCollect && !receiptReturnTypes.value[
- receiptReturnTypeIndex.value]) {
- uni.showToast({
- title: '请选择签单返还类型',
- icon: 'none'
- });
- return false
- }
- // 协议
- if (!agreed.value) {
- uni.showToast({
- title: '请同意服务协议',
- icon: 'none'
- });
- return false
- }
- return true
- }
- const oneHourLater = () => {
- const now = new Date()
- return now.getHours() + now.getMinutes() / 60 + 1
- }
- const twentyMinutesLater = () => {
- const now = new Date()
- now.setMinutes(now.getMinutes() + 1)
- return now
- }
- function isBeforeNineAM(date) {
- return date.getHours() < 9;
- }
- function isAfterEightPM(date) {
- return date.getHours() > 20 || (date.getHours() === 20 && date.getMinutes() > 0);
- }
- const isSubmit = ref(false)
- // ==================== 提交订单 ====================
- const submitOrder = async () => {
- if (!validateForm()) return
- if (isSubmit.value) return
- isSubmit.value = true
- // 构建增值服务JSON
- const addedService = {
- isPack: valueServices.value.isPack || null,
- guaranteeMoney: insuranceAmount.value && insuranceAmount.value !== '' ? parseFloat(insuranceAmount
- .value) : null,
- isReceiptCollect: product.value === '2' ?
- (valueServices.value.isReceiptCollect ? receiptReturnTypes.value[receiptReturnTypeIndex.value]
- ?.value : null) : (valueServices.value.isReceiptCollect ? true : null),
- isOverLongWeight: valueServices.value.isOverLongWeight || null,
- isWoodenCrate: valueServices.value.isWoodenCrate || null
- }
- // 移除null字段
- Object.keys(addedService).forEach(key => {
- if (addedService[key] === null || addedService[key] === undefined) {
- delete addedService[key]
- }
- })
- const orderData = {
- orderType: product.value,
- senderName: addressSend.value.contactName,
- senderPhone: addressSend.value.contactPhone,
- senderProvince: addressSend.value.provinceName,
- senderCity: addressSend.value.cityName,
- senderCounty: addressSend.value.countyName,
- senderAddress: addressSend.value.detailedAddress,
- receiverName: addressReceive.value.contactName,
- receiverPhone: addressReceive.value.contactPhone,
- receiverProvince: addressReceive.value.provinceName,
- receiverCity: addressReceive.value.cityName,
- receiverCounty: addressReceive.value.countyName,
- receiverAddress: addressReceive.value.detailedAddress,
- goodsName: goodsInfo.value,
- goodsWeight: weight.value,
- goodsVolume: volume.value,
- goodsQty: quantity.value,
- sendStartTime: selectedTimeData.value.startTime,
- sendEndTime: selectedTimeData.value.endTime,
- productCode: expressTypes.value[expressTypeIndex.value]?.value, // 直接从动态字典取 dictValue
- addedService: JSON.stringify(addedService)
- }
- uni.showLoading({
- title: '提交中...',
- mask: true
- })
- try {
- const res = await createOrder(orderData)
- uni.hideLoading()
- if (res.code === 200) {
- uni.showToast({
- title: '下单成功',
- icon: 'success',
- mask: true
- })
- uni.switchTab({
- url: '/pages/order/index'
- })
- } else {
- isSubmit.value = false
- uni.showToast({
- title: res.msg || '下单失败',
- icon: 'none'
- })
- }
- } catch (error) {
- isSubmit.value = false
- uni.showToast({
- title: error,
- icon: 'none'
- })
- console.error('订单提交失败', error)
- } finally {
- // uni.hideLoading()
- }
- }
- // ==================== 获取默认寄件地址 ====================
- const fetchDefaultSenderAddress = async () => {
- try {
- const res = await getDefaultAddress()
- if (res.code === 200 && res.data) {
- addressSend.value = res.data
- addressSend.value.defaultFlag = res.data.defaultFlag === '1'
- saveAddressToStorage('senderAddress', res.data)
- }
- } catch (error) {
- console.error('获取默认地址失败', error)
- }
- }
- // ==================== 生命周期 ====================
- onLoad((option) => {
- if (option.product) {
- product.value = option.product
- }
- // 获取快递类型字典
- fetchDictData()
- })
- onMounted(() => {
- // 监听地址选择返回
- uni.$on('addressSelected', (data) => {
- if (data.type === 'sender') {
- addressSend.value = data.address
- saveAddressToStorage('senderAddress', data.address)
- } else if (data.type === 'receiver') {
- addressReceive.value = data.address
- saveAddressToStorage('receiverAddress', data.address)
- }
- })
- if (!addressSend.addressId) {
- fetchDefaultSenderAddress()
- }
- })
- // 组件卸载时取消监听
- onUnmounted(() => {
- uni.$off('addressSelected')
- })
- </script>
- <style lang="scss" scoped>
- /* 完全保持原有样式,未做任何修改 */
- .order-container {
- min-height: 100vh;
- background-color: #F5F7FA;
- padding-bottom: 180rpx;
- box-sizing: border-box;
- }
- .info-card {
- margin: 0rpx 20rpx;
- background: #fff;
- border-radius: 32rpx;
- padding: 20rpx;
- margin-bottom: 20rpx;
- .address-item {
- display: flex;
- align-items: center;
- &:first-child {
- padding-bottom: 20rpx;
- }
- &:last-child {
- padding-top: 20rpx;
- }
- }
- .user-info {
- flex: 1;
- margin-left: 26rpx;
- .line {
- width: 590rpx;
- height: 3rpx;
- background-color: #F1F3F8;
- }
- .create-btn {
- height: 88rpx;
- font-weight: 400;
- font-size: 40rpx;
- color: #999999;
- line-height: 88rpx;
- }
- }
- .right {
- margin-left: 20rpx;
- }
- .img-status-text {
- align-self: center;
- text-align: center;
- flex-shrink: 0;
- .img {
- width: 56rpx;
- height: 56rpx;
- }
- .img-change {
- width: 40rpx;
- height: 40rpx;
- }
- .address-image {
- width: 48rpx;
- height: 48rpx;
- }
- .status-text {
- font-size: 28rpx;
- color: #333;
- font-weight: 400;
- }
- }
- }
- .pickup-title {
- margin: 0rpx 20rpx;
- font-size: 32rpx;
- font-weight: 600;
- color: #333;
- margin-bottom: 20rpx;
- }
- .goods-card {
- margin: 0rpx 20rpx;
- background-color: #fff;
- border-radius: 32rpx;
- padding: 0rpx 20rpx;
- margin-bottom: 20rpx;
- }
- .goods-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- height: 100rpx;
- border-bottom: 1rpx solid #F1F3F8;
- &:last-child {
- border-bottom: none;
- margin-bottom: 0;
- }
- .item-label {
- font-size: 28rpx;
- color: #666;
- font-weight: 400;
- &.required::before {
- content: '*';
- color: #ff4444;
- margin-right: 8rpx;
- }
- }
- .item-control {
- display: flex;
- align-items: center;
- .picker-text {
- font-size: 28rpx;
- color: #333;
- text-align: right;
- margin-right: 8rpx;
- }
- &.btn {
- background: #fff;
- border: 1rpx solid #dcdfe6;
- border-radius: 5rpx;
- height: 59rpx;
- width: 266rpx;
- box-sizing: border-box;
- }
- .control-btn {
- width: 56rpx;
- height: 56rpx;
- background: #F5F7FA;
- border-radius: 4rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 36rpx;
- color: #666;
- &.minus {
- color: #999;
- border-right: 1rpx solid #dcdfe6;
- }
- &.plus {
- color: #999;
- border-left: 1rpx solid #dcdfe6;
- }
- }
- .control-value {
- width: 154rpx;
- text-align: center;
- font-size: 24rpx;
- color: #333;
- .input-field {
- width: 100%;
- height: 100rpx;
- line-height: 100rpx;
- padding: 0 24rpx;
- font-size: 28rpx;
- color: #333;
- text-align: center;
- &::placeholder {
- color: #999;
- }
- }
- }
- }
- .input-field {
- width: 100%;
- height: 100rpx;
- line-height: 100rpx;
- padding: 0 24rpx;
- font-size: 28rpx;
- color: #333;
- text-align: right;
- &::placeholder {
- color: #999;
- }
- }
- .time-value {
- display: flex;
- align-items: center;
- font-size: 28rpx;
- color: #333;
- line-height: 88rpx;
- height: 88rpx;
- .value {
- margin-right: 16rpx;
- }
- .arrow {
- margin-left: 32rpx;
- color: #999;
- font-size: 28rpx;
- font-weight: bold;
- }
- }
- .arrow {
- color: #999;
- }
- }
- .agreement-card {
- margin: 0rpx 20rpx;
- background-color: #fff;
- border-radius: 32rpx;
- padding: 30rpx 20rpx;
- margin-bottom: 20rpx;
- .agreement-item {
- display: flex;
- align-items: center;
- margin-bottom: 20rpx;
- .agreement-text {
- font-size: 28rpx;
- color: #333;
- margin-left: 10rpx;
- }
- }
- .price-notice {
- font-size: 24rpx;
- color: #ff7d00;
- text-align: center;
- }
- }
- .add-btn-container {
- width: 100%;
- position: fixed;
- bottom: 0rpx;
- padding: 32rpx;
- background-color: #fff;
- border-top: 1rpx solid #eee;
- box-sizing: border-box;
- /* 确定按钮 */
- .submit-btn {
- height: 88rpx;
- background: #1B64F0;
- border-radius: 44rpx;
- color: #fff;
- font-size: 32rpx;
- font-weight: 500;
- height: 88rpx;
- line-height: 88rpx;
- text-align: center
- }
- }
- .safe-area {
- height: 140rpx;
- }
- </style>
|