MrShi
5 天以前 f82526f5b34a023e11310adba11cae555adc40cb
bicycle/pages/order/order.vue
@@ -11,8 +11,10 @@
               class="search-box__input"
               type="text"
               v-model="keyword"
               placeholder="搜索车辆编号/手机号"
               placeholder="搜索用户手机号"
               placeholder-class="search-box__placeholder"
               confirm-type="search"
               @confirm="handleSearch"
            />
         </view>
      </view>
@@ -23,29 +25,34 @@
      </view>
      <view class="order-list">
         <view class="order-card" v-for="item in filteredOrders" :key="item.id">
         <view class="order-card" v-for="item in list" :key="item.id">
            <view class="order-card__head">
               <view class="order-card__code">车辆编号:{{ item.code }}</view>
               <view class="order-card__amount" v-if="activeTab === 'completed'">{{ item.amount }}</view>
               <view class="order-card__code">订单编号:{{ item.code || '-' }}</view>
            </view>
            <view class="order-card__row">车辆类型:{{ item.vehicleType }}</view>
            <view class="order-card__row">用户手机:{{ item.mobile }}</view>
            <view class="order-card__row">骑行时间:{{ item.rideTime }}</view>
            <view class="order-card__row">车辆编号:{{ item.bikeCode || '-' }}</view>
            <view class="order-card__row">订单类型:{{ item.bikeTypeText }}</view>
            <view class="order-card__row">结算车型:{{ item.paramName || '-' }}</view>
            <view class="order-card__row">用户手机:{{ item.phone || '-' }}</view>
            <view class="order-card__row">骑行开始时间:{{ item.rentDate || '-' }}</view>
            <view v-if="activeTab === 'completed'" class="order-card__row">结算时间:{{ item.closeDate || '-' }}</view>
            <view class="order-card__footer">
               <view class="order-card__track" @click="handleAction('查看轨迹')">查看轨迹</view>
               <view v-if="Number(item.bikeType) === 1" class="order-card__track" @click="handleAction('查看轨迹', item)">查看轨迹</view>
               <view class="order-card__actions" v-if="activeTab === 'renting'">
                  <view class="action-btn action-btn--line" @click="handleAction('开锁')">开锁</view>
                  <view class="action-btn action-btn--line" @click="handleAction('关锁')">关锁</view>
                  <view class="action-btn action-btn--primary" @click="handleAction('强制还车')">强制还车</view>
                  <view v-if="Number(item.bikeType) === 1" class="action-btn action-btn--line" @click="handleAction('开锁', item)">开锁</view>
                  <view v-if="Number(item.bikeType) === 1" class="action-btn action-btn--line" @click="handleAction('关锁', item)">关锁</view>
                  <view class="action-btn action-btn--primary" @click="handleAction('强制还车', item)">强制还车</view>
               </view>
               <view class="order-card__actions" v-else>
                  <view class="action-btn action-btn--warn" @click="handleAction('退款')">退款</view>
                  <view class="action-btn action-btn--warn" @click="handleAction('退款', item)">退款</view>
               </view>
            </view>
         </view>
         <view class="empty-state" v-if="!filteredOrders.length">暂无订单数据</view>
         <view class="empty-state" v-if="!list.length && !loading">暂无订单数据</view>
         <view class="load-more-tip" v-if="list.length">
            {{ loading ? '加载中...' : (finished ? '没有更多了' : '上拉加载更多') }}
         </view>
      </view>
      <u-popup :show="showVehicleTypePopup" :round="16" mode="bottom" @close="showVehicleTypePopup = false">
@@ -68,49 +75,49 @@
            <view class="huanche_tips">强制还车后,车辆行程结束并自动结算</view>
            <view class="huanche_footer">
               <view class="huanche_footer_btn line" @click="show = false">取消</view>
               <view class="huanche_footer_btn pr">确定还车</view>
               <view class="huanche_footer_btn pr" @click="confirmForceBack">确定还车</view>
            </view>
         </view>
      </u-popup>
      <!-- 订单退款 -->
      <u-popup :show="show1" :closeOnClickOverlay="true" mode="bottom" bgColor="#fff" :round="10" @close="show1 = false">
      <u-popup :show="show1" :closeOnClickOverlay="true" mode="bottom" bgColor="#fff" :round="10" @close="closeRefundPopup">
         <view class="huanche">
            <view class="huanche_title">订单退款</view>
            <view class="huanche_info">
               <view class="huanche_info_item">
                  <view class="label">充值金额:</view>
                  <view class="val">100元</view>
                  <view class="val">{{ formatMoneyText(refundInfo.goodsorderMoney) }}</view>
               </view>
               <view class="huanche_info_item">
                  <view class="label">结算金额:</view>
                  <view class="val">50元</view>
                  <view class="val">{{ formatMoneyText(refundInfo.closeMoney) }}</view>
               </view>
               <view class="huanche_info_item">
                  <view class="label">已退金额:</view>
                  <view class="val">-</view>
                  <view class="val">{{ formatMoneyText(refundInfo.hasRefundMoney) }}</view>
               </view>
               <view class="huanche_info_item">
                  <view class="label">可退金额:</view>
                  <view class="val">50元</view>
                  <view class="val">{{ formatMoneyText(refundInfo.canBanlanceMoney) }}</view>
               </view>
            </view>
            <view class="huanche_form">
               <view class="huanche_form_item">
                  <view class="lable"><text>*</text>退款金额(元)</view>
                  <view class="val">
                     <input type="text" placeholder="请输入退款金额" />
                     <input v-model="refundForm.money" type="digit" placeholder="请输入退款金额" />
                  </view>
               </view>
               <view class="huanche_form_item">
                  <view class="lable">退款说明</view>
                  <view class="val">
                     <input type="text" placeholder="请输入退款原因" />
                     <input v-model="refundForm.reason" type="text" placeholder="请输入退款原因" />
                  </view>
               </view>
            </view>
            <view class="huanche_footer">
               <view class="huanche_footer_btn line" @click="show1 = false">取消</view>
               <view class="huanche_footer_btn que">确定还车</view>
               <view class="huanche_footer_btn line" @click="closeRefundPopup">取消</view>
               <view class="huanche_footer_btn que" @click="confirmRefund">确认退款</view>
            </view>
         </view>
      </u-popup>
@@ -118,63 +125,6 @@
</template>
<script>
   const rentingOrders = [
      {
         id: 1,
         code: 'BH1018',
         vehicleType: '双排四座电动代步车',
         mobile: '180****9930',
         rideTime: '2025-05-01 11:30:52',
         type: 'electric'
      },
      {
         id: 2,
         code: 'BH1018',
         vehicleType: '双排四座电动代步车',
         mobile: '180****9930',
         rideTime: '2025-05-01 11:30:52',
         type: 'electric'
      },
      {
         id: 3,
         code: 'BH1018',
         vehicleType: '双排四座电动代步车',
         mobile: '180****9930',
         rideTime: '2025-05-01 11:30:52',
         type: 'electric'
      }
   ]
   const completedOrders = [
      {
         id: 11,
         code: 'BH1018',
         vehicleType: '双排四座电动代步车',
         mobile: '180****9930',
         rideTime: '05-01 11:30:52~05-01 12:21:11',
         amount: '¥100.00',
         type: 'electric'
      },
      {
         id: 12,
         code: 'BH1018',
         vehicleType: '双排四座电动代步车',
         mobile: '180****9930',
         rideTime: '05-01 11:30:52~05-01 12:21:11',
         amount: '¥100.00',
         type: 'electric'
      },
      {
         id: 13,
         code: 'BH1018',
         vehicleType: '双排四座电动代步车',
         mobile: '180****9930',
         rideTime: '05-01 11:30:52~05-01 12:21:11',
         amount: '¥100.00',
         type: 'electric'
      }
   ]
   export default {
      data() {
         return {
@@ -183,34 +133,71 @@
            activeTab: 'renting',
            keyword: '',
            selectedVehicleType: '',
            page: 1,
            list: [],
            loading: false,
            finished: false,
            updatingLock: false,
            forceBackLoading: false,
            refundLoading: false,
            currentOrderId: '',
            refundOrderId: '',
            refundInfo: {
               canBanlanceMoney: '',
               closeMoney: '',
               goodsorderMoney: '',
               hasRefundMoney: ''
            },
            refundForm: {
               money: '',
               reason: ''
            },
            searchTimer: null,
            showVehicleTypePopup: false,
            vehicleTypeOptions: [
               { label: '车辆类型', value: '' },
               { label: '双排四座电动代步车', value: 'electric' },
               { label: '普通自行车', value: 'bike' }
            ],
            rentingOrders,
            completedOrders
               { label: '订单类型', value: '' },
               { label: '自行车', value: '0' },
               { label: '电车', value: '1' }
            ]
         }
      },
      computed: {
         currentVehicleTypeLabel() {
            const current = this.vehicleTypeOptions.find(item => item.value === this.selectedVehicleType)
            return current ? current.label : '车辆类型'
            return current ? current.label : '订单类型'
         },
         filteredOrders() {
            const source = this.activeTab === 'renting' ? this.rentingOrders : this.completedOrders
            const keyword = this.keyword.trim().toLowerCase()
            return source.filter(item => {
               const matchType = !this.selectedVehicleType || item.type === this.selectedVehicleType
               const searchText = `${item.code}${item.mobile}`.toLowerCase()
               const matchKeyword = !keyword || searchText.includes(keyword)
               return matchType && matchKeyword
            })
         currentStatus() {
            return this.activeTab === 'renting' ? 1 : 4
         }
      },
      watch: {
         activeTab() {
            this.resetAndLoad()
         },
         selectedVehicleType() {
            this.resetAndLoad()
         },
         keyword() {
            clearTimeout(this.searchTimer)
            this.searchTimer = setTimeout(() => {
               this.resetAndLoad()
            }, 300)
         }
      },
      onLoad() {
         this.getList()
      },
      onReachBottom() {
         this.getList()
      },
      onUnload() {
         clearTimeout(this.searchTimer)
      },
      methods: {
         handleSearch() {
            clearTimeout(this.searchTimer)
            this.resetAndLoad()
         },
         toggleVehicleType() {
            this.showVehicleTypePopup = true
         },
@@ -218,11 +205,247 @@
            this.selectedVehicleType = value
            this.showVehicleTypePopup = false
         },
         handleAction(text) {
         resetAndLoad() {
            this.page = 1
            this.list = []
            this.finished = false
            this.getList()
         },
         formatMoneyText(value) {
            if (value === '' || value === null || value === undefined) {
               return '-'
            }
            return `${value}元`
         },
         resetRefundState() {
            this.refundOrderId = ''
            this.refundInfo = {
               canBanlanceMoney: '',
               closeMoney: '',
               goodsorderMoney: '',
               hasRefundMoney: ''
            }
            this.refundForm = {
               money: '',
               reason: ''
            }
         },
         closeRefundPopup() {
            this.show1 = false
            this.resetRefundState()
         },
         async updateOrderLock(item, lockStatus) {
            if (!item.bikeCode) {
               uni.showToast({
                  title: '车辆编号不能为空',
                  icon: 'none'
               })
               return
            }
            if (this.updatingLock) {
               return
            }
            this.updatingLock = true
            try {
               const res = await this.$u.api.updateLockStatus({
                  ids: item.bikeCode,
                  lockStatus
               })
               uni.showToast({
                  title: res.code === 200 ? (lockStatus === 1 ? '开锁成功' : '关锁成功') : (res.msg || (lockStatus === 1 ? '开锁失败' : '关锁失败')),
                  icon: 'none'
               })
            } catch (error) {
               uni.showToast({
                  title: error.message || (lockStatus === 1 ? '开锁失败' : '关锁失败'),
                  icon: 'none'
               })
            } finally {
               this.updatingLock = false
            }
         },
         async openRefundPopup(item) {
            if (!item.id) {
               uni.showToast({
                  title: '订单ID不能为空',
                  icon: 'none'
               })
               return
            }
            if (this.refundLoading) {
               return
            }
            this.refundLoading = true
            try {
               const res = await this.$u.api.getGoodsorderCanBanlanceDTO({
                  orderId: item.id
               })
               if (res.code === 200) {
                  this.refundOrderId = item.id
                  this.refundInfo = {
                     canBanlanceMoney: res.data ? res.data.canBanlanceMoney : '',
                     closeMoney: res.data ? res.data.closeMoney : '',
                     goodsorderMoney: res.data ? res.data.goodsorderMoney : '',
                     hasRefundMoney: res.data ? res.data.hasRefundMoney : ''
                  }
                  this.refundForm = {
                     money: '',
                     reason: ''
                  }
                  this.show1 = true
               }
            } finally {
               this.refundLoading = false
            }
         },
         async confirmRefund() {
            if (!this.refundOrderId) {
               uni.showToast({
                  title: '订单ID不能为空',
                  icon: 'none'
               })
               return
            }
            if (!this.refundForm.money) {
               uni.showToast({
                  title: '请输入退款金额',
                  icon: 'none'
               })
               return
            }
            if (this.refundLoading) {
               return
            }
            this.refundLoading = true
            try {
               const res = await this.$u.api.backGoodsorder({
                  orderId: this.refundOrderId,
                  reason: this.refundForm.reason.trim(),
                  money: this.refundForm.money
               })
               if (res.code === 200) {
                  this.closeRefundPopup()
                  this.resetAndLoad()
                  uni.showToast({
                     title: '退款成功',
                     icon: 'none'
                  })
               }
            } finally {
               this.refundLoading = false
            }
         },
         async confirmForceBack() {
            if (!this.currentOrderId) {
               uni.showToast({
                  title: '订单ID不能为空',
                  icon: 'none'
               })
               return
            }
            if (this.forceBackLoading) {
               return
            }
            this.forceBackLoading = true
            try {
               const res = await this.$u.api.forceBack({
                  id: this.currentOrderId
               })
               if (res.code === 200) {
                  this.show = false
                  this.currentOrderId = ''
                  this.resetAndLoad()
               }
            } finally {
               this.forceBackLoading = false
            }
         },
         formatBikeType(bikeType) {
            return Number(bikeType) === 1 ? '电车' : '自行车'
         },
         async getList() {
            if (this.loading || this.finished) {
               return
            }
            this.loading = true
            const model = {
               phone: this.keyword.trim(),
               status: this.currentStatus
            }
            if (this.selectedVehicleType !== '') {
               model.bikeType = Number(this.selectedVehicleType)
            }
            try {
               const res = await this.$u.api.operationOrderPage({
                  capacity: 10,
                  page: this.page,
                  model
               })
               if (res.code === 200) {
                  const records = (res.data && res.data.records) || []
                  const total = Number(res.data && res.data.total)
                  const list = records.map(item => ({
                     ...item,
                     bikeTypeText: this.formatBikeType(item.bikeType)
                  }))
                  this.list.push(...list)
                  this.page += 1
                  if (!records.length || (Number.isFinite(total) && total > 0 ? this.list.length >= total : records.length < 10)) {
                     this.finished = true
                  }
               }
            } finally {
               this.loading = false
            }
         },
         async handleAction(text, item = {}) {
            if (text === '开锁') {
               await this.updateOrderLock(item, 1)
               return
            }
            if (text === '关锁') {
               await this.updateOrderLock(item, 0)
               return
            }
            if (text === '强制还车') {
               this.currentOrderId = item.id || ''
               this.show = true
            } else if (text === '退款') {
               this.show1 = true
               await this.openRefundPopup(item)
            } else if (text === '查看轨迹') {
               uni.navigateTo({
                  url: `/pages/trajectory/trajectory?id=${item.id}`
               })
            }
            // uni.showToast({
            //    title: `${text}功能待接入`,
@@ -416,6 +639,7 @@
      margin-left: 16rpx;
      font-size: 26rpx;
      color: #333333;
      background: transparent;
   }
   .search-box__placeholder {
@@ -488,14 +712,6 @@
      line-height: 58rpx;
   }
   .order-card__amount {
      font-weight: 600;
      font-size: 28rpx;
      color: #FF5A31;
      line-height: 50rpx;
      flex-shrink: 0;
   }
   .order-card__row {
      font-weight: 400;
      font-size: 26rpx;
@@ -503,6 +719,14 @@
      line-height: 44rpx;
   }
   .load-more-tip {
      padding: 28rpx 0 10rpx;
      font-size: 24rpx;
      line-height: 36rpx;
      text-align: center;
      color: #999999;
   }
   .order-card__footer {
      margin-top: 22rpx;
      padding: 18rpx 0 20rpx;