liujie
2026-09-11 4f6f15b818704079ff2e4e4719b798f48d68a335
mingdaojingjiang_guide_app/pages/workbench/index.vue
@@ -75,7 +75,12 @@
        </template>
        <template v-else>
          <view class="empty-order">暂无订单请耐心等待</view>
          <button class="cancel-button" @tap="cancelReceiving">取消接单</button>
          <button
            class="cancel-button"
            :loading="cancelingReceiving"
            :disabled="cancelingReceiving"
            @tap="cancelReceiving"
          >取消接单</button>
        </template>
      </template>
      <template v-else-if="auditStatus === 'approved'">
@@ -152,6 +157,7 @@
import { playNotificationAlert } from '../../services/message'
import { getWorkbenchData } from '../../services/workbench'
import { formatDuration, maskPhone } from '../../utils/format'
import { resolveApiAssetUrl } from '../../utils/request'
import { formatRatingStars } from '../../utils/rating'
import { routeTo } from '../../utils/router'
import { getStatusBarHeight, upxToPx } from '../../utils/system'
@@ -162,6 +168,11 @@
function isSameId(left, right) {
  return left !== undefined && left !== null && right !== undefined && right !== null && String(left) === String(right)
}
function getDialablePhone(phone) {
  const value = String(phone || '').replace(/\D/g, '')
  return /^1\d{10}$/.test(value) ? value : ''
}
function buildReceivingConfig(session, scenics = [], fallback = {}) {
@@ -182,6 +193,13 @@
    latitude: meetingPoint.latitude ?? fallback.latitude,
    longitude: meetingPoint.longitude ?? fallback.longitude
  }
}
function parseApiDateTime(value) {
  if (!value) {
    return NaN
  }
  return new Date(String(value).trim().replace(' ', 'T')).getTime()
}
export default {
@@ -208,6 +226,10 @@
      countdownTimer: null,
      refreshTimer: null,
      refreshing: false,
      refreshPromise: null,
      receivingActionVersion: 0,
      cancelingReceiving: false,
      forceOffline: false,
      startingLecture: false,
      dialog: {
        visible: false,
@@ -303,9 +325,11 @@
          ? order.customers
          : [{ phone: order.customerPhone, people: order.quantity }]
        customers.forEach((customer) => {
          const phone = customer.phone || ''
          rows.push({
            no: rows.length + 1,
            phone: maskPhone(customer.phone || ''),
            phone: maskPhone(phone),
            phoneNumber: phone,
            people: Number(customer.people || 0)
          })
        })
@@ -328,6 +352,7 @@
      routeTo('/pages/login/index', 'reLaunch')
      return
    }
    this.forceOffline = false
    this.loadData()
    this.startRefresh()
  },
@@ -342,19 +367,25 @@
  methods: {
    loadData({ silent = false } = {}) {
      if (this.refreshing) {
        return Promise.resolve()
        return this.refreshPromise || Promise.resolve()
      }
      this.refreshing = true
      const actionVersion = this.receivingActionVersion
      const previousOrderId = this.activeReceivingOrder.id
      const previousOnlineStatus = this.guide.onlineStatus
      return getWorkbenchData().then((workbench) => {
      const isCurrentLoad = () => actionVersion === this.receivingActionVersion
      const loadRequest = getWorkbenchData().then((workbench) => {
        if (!isCurrentLoad()) {
          return null
        }
        const remoteGuide = workbench.guide || {}
        const loginGuide = (getLoginState() || {}).guide || {}
        const baseGuide = {
          ...loginGuide,
          ...remoteGuide,
          name: resolveGuideName(remoteGuide, loginGuide),
          phone: remoteGuide.mobile || loginGuide.phone
          phone: remoteGuide.mobile || loginGuide.phone,
          avatar: resolveApiAssetUrl(remoteGuide.avatar || loginGuide.avatar || '')
        }
        this.accountState = workbench.accountState || 'pending'
        this.auditRemark = workbench.auditRemark || ''
@@ -380,15 +411,22 @@
          getCurrentGuideReceivingSession(),
          this.loadReceivingScenics()
        ]).then(([localGuide, orders, receivingSession, receivingScenics]) => {
          if (!isCurrentLoad()) {
            return null
          }
          const activeOrders = orders.filter((item) => ['pending', 'accepted', 'arrived', 'serving'].includes(item.status))
          const keepOffline = this.forceOffline && activeOrders.length === 0
          const nextGuide = {
            ...baseGuide,
            ...localGuide,
            name: resolveGuideName(localGuide, baseGuide),
            phone: localGuide.phone || baseGuide.phone,
            onlineStatus: receivingSession
            onlineStatus: keepOffline ? 'offline' : receivingSession
              ? (receivingSession.status === 'IN_SERVICE' ? 'serving' : 'online')
              : 'offline',
            receivingConfig: buildReceivingConfig(receivingSession, receivingScenics, baseGuide.receivingConfig || {})
            receivingConfig: keepOffline
              ? null
              : buildReceivingConfig(receivingSession, receivingScenics, baseGuide.receivingConfig || {})
          }
          this.updateGuide(nextGuide)
          this.updateOrders(orders)
@@ -405,7 +443,10 @@
        }
      }).finally(() => {
        this.refreshing = false
        this.refreshPromise = null
      })
      this.refreshPromise = loadRequest
      return loadRequest
    },
    loadReceivingScenics() {
      if (this.receivingScenicsLoaded) {
@@ -450,14 +491,27 @@
      if (!this.isReceiving || !this.hasReceivingOrders) {
        return
      }
      this.countdownSeconds = this.isServing ? (this.activeReceivingOrder.durationMinutes || 30) * 60 : 15 * 60
      this.updateCountdown()
      if (this.countdownSeconds <= 0) {
        return
      }
      this.countdownTimer = setInterval(() => {
        this.updateCountdown()
        if (this.countdownSeconds <= 0) {
          this.clearCountdown()
          return
        }
        this.countdownSeconds -= 1
      }, 1000)
    },
    updateCountdown() {
      const order = this.activeReceivingOrder
      const durationMinutes = this.isServing ? Number(order.durationMinutes || 30) : 15
      const startAt = parseApiDateTime(this.isServing ? order.lectureStartTime : order.receiveStartTime)
      if (!Number.isFinite(startAt)) {
        this.countdownSeconds = durationMinutes * 60
        return
      }
      const remainingMilliseconds = startAt + durationMinutes * 60 * 1000 - Date.now()
      this.countdownSeconds = Math.max(0, Math.ceil(remainingMilliseconds / 1000))
    },
    clearCountdown() {
      if (this.countdownTimer) {
@@ -569,7 +623,19 @@
    },
    confirmDialog() {
      if (this.dialog.action === 'callCustomer') {
        uni.showToast({ title: '电话能力待接真实号码', icon: 'none' })
        const phoneNumber = getDialablePhone(this.pendingCustomer && this.pendingCustomer.phoneNumber)
        if (!phoneNumber) {
          uni.showToast({ title: '客户号码无效,无法拨打', icon: 'none' })
          return
        }
        uni.makePhoneCall({
          phoneNumber,
          fail: (error) => {
            if (!(error && String(error.errMsg || '').includes('cancel'))) {
              uni.showToast({ title: '无法发起电话,请检查设备电话权限', icon: 'none' })
            }
          }
        })
        return
      }
      if (this.dialog.action === 'finishLecture') {
@@ -580,10 +646,29 @@
        return
      }
      if (this.dialog.action === 'cancelReceiving') {
        updateGuideOnlineStatus('offline').then(() => {
          this.loadData()
          uni.showToast({ title: '已取消接单', icon: 'none' })
        })
        if (this.cancelingReceiving) {
          return
        }
        this.cancelingReceiving = true
        this.receivingActionVersion += 1
        const pendingLoad = this.refreshPromise || Promise.resolve()
        updateGuideOnlineStatus('offline')
          .then(() => {
            this.forceOffline = true
            this.updateGuide({ ...this.guide, onlineStatus: 'offline', receivingConfig: null })
            this.clearCountdown()
            return pendingLoad
          })
          .then(() => this.loadData({ silent: true }))
          .then(() => {
            uni.showToast({ title: '已取消接单', icon: 'none' })
          })
          .catch((error) => {
            uni.showToast({ title: error.message || '取消接单失败', icon: 'none' })
          })
          .finally(() => {
            this.cancelingReceiving = false
          })
      }
    }
  }