sen
2026-03-31 cc41746818af3b619167947ecb4401ccdfbec2d8
ui/car_wx_app/pages/beReferred/index.vue
@@ -1,261 +1,648 @@
<template>
    <view class="container">
        <!-- 标题栏 -->
          <u-navbar :placeholder="true" bgColor="#409effc1"  :titleStyle="{ color: '#fff'}" title="被指派行程">
            <view slot="left" class="u-nav-slot" @click="leftClick">
              <u-icon color="#fff" name="list" size="28"></u-icon>
  <view class="container">
    <!-- 自定义导航栏 -->
    <u-navbar
      :placeholder="true"
      bg-color="#409eff"
      title="被指派行程"
      :title-style="{ color: '#fff', fontSize: '16px', fontWeight: 500 }"
    >
      <view slot="left" class="nav-left-slot" @click="toggleSidebar">
        <u-icon name="list" color="#fff" size="22" />
      </view>
    </u-navbar>
    <!-- 主要内容区 -->
    <view class="main-content">
      <!-- 下拉刷新容器 -->
      <scroll-view
        class="task-scroll"
        scroll-y
        :refresher-enabled="true"
        :refresher-triggered="isRefreshing"
        @refresherrefresh="onRefresh"
        @scrolltolower="onLoadMore"
        :lower-threshold="100"
      >
        <view class="scroll-content">
          <!-- 加载骨架屏 -->
          <view v-if="loading && !taskList.length">
            <view v-for="i in 3" :key="i" class="skeleton-item">
              <u-skeleton :rows="4" :title="false" avatar avatar-size="0" />
            </view>
          </u-navbar>
        <!-- 行程列表-可下拉滚动 -->
        <scroll-view class="task-scroll" scroll-y="true" style="height: calc(100vh - 120rpx);">
            <view class="task-item" v-for="(item, index) in taskList" :key="index" @click="historyClick(item)">
                <view class="task-field">
                    <view class="field-label">调度单号</view>
                    <view class="field-value">{{ item.dispatchNo }}</view>
                </view>
                <view class="task-field">
                    <view class="field-label">运输工具号码</view>
                    <view class="field-value">{{ item.licensePlate }}</view>
                </view>
                <view class="task-field">
                    <view class="field-label">路线</view>
                    <view class="field-value">{{ item.transportLine }}</view>
                </view>
                <view class="task-field">
                    <view class="field-label">客户</view>
                    <view class="field-value">{{ item.customerName }}</view>
                </view>
                <view class="task-field">
                    <view class="field-label">当前状态</view>
                    <view class="field-value status">{{ item.statusStr }}</view>
                </view>
                <view class="task-field">
                    <view class="field-label">要求最晚发车时间</view>
                    <view class="field-value">{{ item.latestDeparture }}</view>
                </view>
            </view>
        </scroll-view>
        <!-- 底部操作栏 -->
        <view class="bottom-bar">
            <u-button class="bottom-btn" @click="goToURL" text="历史调度单"></u-button>
            <!-- <view class="bottom-btn">垫付列表</view> -->
            <u-button class="bottom-btn" @click="goToURLDF" text="垫付列表"></u-button>
        </view>
      <u-popup :zIndex="1000" :overlayZIndex="999" :show="show" mode="left" @close="close">
        <view style="width: 250px">
          <view class="logo-info">
            <image src="/static/oss.png" style="width: 120px;height: 120px"></image>
            <h3 class="company">智慧物流调度平台</h3>
          </view>
          <text class="lout" @click="goToUrl('/pages/login/index')">退出登录</text>
          <!-- 空状态 -->
          <view v-else-if="!taskList.length" class="empty-state">
            <u-empty mode="list" text="暂无被指派行程" />
          </view>
          <!-- 行程列表 -->
          <view v-else>
            <view
              v-for="item in taskList"
              :key="item.dispatchId"
              class="task-card"
              @click="handleTaskClick(item)"
            >
              <!-- 卡片头部:单号和状态 -->
              <view class="card-header">
                <view class="dispatch-no">
                  <text class="label">调度单号</text>
                  <text class="value">{{ item.dispatchNo }}</text>
                </view>
                <view class="status-tag" :class="item.statusClass">
                  {{ item.statusStr }}
                </view>
              </view>
              <!-- 卡片主体 -->
              <view class="card-body">
                <view class="info-row">
                  <u-icon name="car" size="16" color="#909399" />
                  <text class="info-text">{{ item.licensePlate || '未分配车辆' }}</text>
                </view>
                <view class="route-section">
                  <view class="route-line">
                    <view class="route-dot start" />
                    <view class="route-path" />
                    <view class="route-dot end" />
                  </view>
                  <view class="route-info">
                    <text class="route-text">{{ item.transportLine || '暂无路线信息' }}</text>
                  </view>
                </view>
                <view class="info-row">
                  <u-icon name="account" size="16" color="#909399" />
                  <text class="info-text">{{ item.customerName || '未知客户' }}</text>
                </view>
                <view class="info-row time-row">
                  <u-icon name="clock" size="16" color="#f0ae2d" />
                  <text class="time-text">最晚发车:{{ item.latestDeparture }}</text>
                </view>
              </view>
            </view>
            <!-- 加载更多 -->
            <view class="load-more">
              <u-loading-icon v-if="loadingMore" size="16" text="加载中..." />
              <text v-else-if="!hasMore" class="no-more">没有更多了</text>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
    <!-- 底部操作栏 -->
    <view class="bottom-bar">
      <view class="bottom-btn" @click="goToHistory">
        <u-icon name="order" size="18" color="#409eff" />
        <text>历史调度单</text>
      </view>
      <view class="divider" />
      <view class="bottom-btn" @click="goToPaymentList">
        <u-icon name="red-packet" size="18" color="#ff6b6b" />
        <text>垫付列表</text>
      </view>
    </view>
    <!-- 侧边栏弹窗 -->
    <u-popup
      :show="showSidebar"
      mode="left"
      :overlay-opacity="0.3"
      @close="closeSidebar"
    >
      <view class="sidebar-content">
        <view class="sidebar-header">
          <image src="/static/oss.png" class="logo" mode="aspectFit" />
          <text class="company-name">智慧物流调度平台</text>
        </view>
      </u-popup>
    </view>
        <view class="sidebar-menu">
          <view class="menu-item" @click="handleLogout">
            <text class="menu-text">退出登录</text>
          </view>
        </view>
      </view>
    </u-popup>
  </view>
</template>
<script>
import { getSsignedItineraryList } from '@/common/beReferred'
import { navigateTo, PAGES } from '@/common/router'
import { getSsignedItineraryList } from "@/common/beReferred";
// 状态映射配置
const STATUS_MAP = {
  '待甩挂': { class: 'status-pending', color: '#ff6b6b' },
  '待接挂': { class: 'status-pending', color: '#ff6b6b' },
  '运输中': { class: 'status-transporting', color: '#409eff' },
  '已完成': { class: 'status-completed', color: '#67c23a' },
  '已取消': { class: 'status-cancelled', color: '#909399' }
}
export default {
    data() {
        return {
            taskList: [
            ],
          show:false,
        };
    },
    created() {
        this.getList();
    },
    methods: {
        getList() {
            getSsignedItineraryList().then((res1) => {
                this.taskList = res1;
                // uni.$u.toast(res.message);
            })
        },
        goToURL() {
            setTimeout(function () {
                uni.$u.route('/pages/history/index');
            }, 1000)
        },
        goToURLDF() {
            setTimeout(function () {
                uni.$u.route('/pages/paymentList/index?name='+'垫付列表'+'&router='+'pages/beReferred/index');
            }, 1000)
        },
        historyClick(item) {
               // wx.navigateTo({
               //      url: '/pages/examine/index?id=' + item.dispatchId+'&name='+'上传行程'+'&router='+'pages/beReferred/index'
               //  });
            if( ['待甩挂', '待接挂'].includes(item.statusStr) ){
                uni.$u.route('/pages/transportation/index?id=' + item.dispatchId+'&name='+'上传行程'+'&router='+'pages/beReferred/index'+'&statusStr='+item.statusStr);
            }else {
                wx.navigateTo({
                    url: '/pages/examine/index?id=' + item.dispatchId+'&name='+'上传行程'+'&router='+'pages/beReferred/index'
                });
            }
        },
      leftClick(){
          this.show = true;
      },
        goToUrl(){
          this.show = false;
          this.$u.vuex('sso_user_token',undefined);
            uni.reLaunch({
              url: '/pages/login/index'
            })
        },
      close(){
          this.show = false;
      }
  data() {
    return {
      taskList: [],
      loading: false,
      loadingMore: false,
      isRefreshing: false,
      showSidebar: false,
      pageNum: 1,
      pageSize: 10,
      hasMore: true,
      _isFirstLoad: true
    }
};
  },
  onLoad() {
    this._isFirstLoad = true
    this.fetchTaskList()
  },
  onShow() {
    // 首次加载时跳过(onLoad 已经调用过了)
    if (this._isFirstLoad) {
      this._isFirstLoad = false
      return
    }
    // 从其他页面返回时刷新数据
    this.pageNum = 1
    this.fetchTaskList(true)
  },
  onPullDownRefresh() {
    this.onRefresh()
  },
  onReachBottom() {
    this.onLoadMore()
  },
  methods: {
    // 格式化时间
    formatTime(timeStr) {
      if (!timeStr) return '暂无'
      const date = new Date(timeStr.replace(/-/g, '/'))
      if (isNaN(date.getTime())) return timeStr
      const month = (date.getMonth() + 1).toString().padStart(2, '0')
      const day = date.getDate().toString().padStart(2, '0')
      const hours = date.getHours().toString().padStart(2, '0')
      const minutes = date.getMinutes().toString().padStart(2, '0')
      return `${month}-${day} ${hours}:${minutes}`
    },
    // 加载数据
    async fetchTaskList(isRefresh = false) {
      if (this.loading && !isRefresh) return
      this.loading = true
      try {
        const res = await getSsignedItineraryList({
          pageNum: this.pageNum,
          pageSize: this.pageSize
        })
        const list = (res || []).map(item => ({
          ...item,
          statusClass: STATUS_MAP[item.statusStr]?.class || 'status-default'
        }))
        if (isRefresh) {
          this.taskList = list
          uni.stopPullDownRefresh()
          this.isRefreshing = false
        } else {
          this.taskList = [...this.taskList, ...list]
        }
        this.hasMore = list.length === this.pageSize
      } catch (error) {
        console.error('获取行程列表失败:', error)
        uni.$u.toast('获取数据失败,请重试')
      } finally {
        this.loading = false
        this.loadingMore = false
      }
    },
    // 下拉刷新
    onRefresh() {
      this.isRefreshing = true
      this.pageNum = 1
      this.fetchTaskList(true)
    },
    // 加载更多
    onLoadMore() {
      if (!this.hasMore || this.loadingMore) return
      this.loadingMore = true
      this.pageNum++
      this.fetchTaskList()
    },
    // 跳转到历史记录
    goToHistory() {
      navigateTo(PAGES.HISTORY)
    },
    // 跳转到垫付列表
    goToPaymentList() {
      navigateTo(PAGES.PAYMENT_LIST)
    },
    // 任务点击
    handleTaskClick(item) {
      // 根据状态决定跳转页面
      if (['待甩挂', '待接挂'].includes(item.statusStr)) {
        navigateTo(PAGES.TRANSPORTATION, {
          id: item.dispatchId,
          statusStr: item.statusStr,
          name: '上传行程'
        })
      } else {
        // 直接跳转到操作页面,避免路由分发导致的闪烁
        uni.navigateTo({
          url: `/pages/examine/operate?id=${item.dispatchId}&name=上传行程`
        })
      }
    },
    // 切换侧边栏
    toggleSidebar() {
      this.showSidebar = !this.showSidebar
    },
    // 关闭侧边栏
    closeSidebar() {
      this.showSidebar = false
    },
    // 退出登录
    handleLogout() {
      uni.showModal({
        title: '提示',
        content: '确定要退出登录吗?',
        success: (res) => {
          if (res.confirm) {
            // 显示加载提示
            uni.showLoading({
              title: '退出中...',
              mask: true
            })
            // 清除登录状态
            uni.$u.vuex('sso_user_token', undefined)
            uni.$u.vuex('user_info', undefined)
            // 延迟跳转,避免白屏
            setTimeout(() => {
              uni.hideLoading()
              uni.reLaunch({
                url: '/pages/login/index'
              })
            }, 300)
          }
          this.closeSidebar()
        }
      })
    }
  }
}
</script>
<style lang="scss" scoped>
// 变量定义
$primary-color: #409eff;
$success-color: #67c23a;
$warning-color: #e6a23c;
$danger-color: #f56c6c;
$info-color: #909399;
$bg-color: #f5f7fa;
$card-bg: #ffffff;
$text-primary: #303133;
$text-regular: #606266;
$text-secondary: #909399;
<style scoped>
// 布局
.container {
    display: flex;
    flex-direction: column;
    height: 100vh;
    background-color: #f7f7f7;
    /* 给底部固定栏预留空间,避免内容被遮挡 */
    padding-bottom: 100rpx;
    box-sizing: border-box;
  min-height: 100vh;
  background-color: $bg-color;
  display: flex;
  flex-direction: column;
}
/* 标题栏 */
.title-bar {
    height: 80rpx;
    line-height: 80rpx;
    text-align: center;
    font-size: 32rpx;
    font-weight: 500;
    background-color: #fff;
    border-bottom: 1rpx solid #eee;
    z-index: 10;
    /* 确保标题栏在滚动区上层 */
.main-content {
  flex: 1;
  overflow: hidden;
  padding-bottom: 100rpx;
}
/* 行程滚动区域 */
// 导航栏
.nav-left-slot {
  padding: 0 20rpx;
  display: flex;
  align-items: center;
}
// 滚动区域
.task-scroll {
    flex: 1;
    padding: 20rpx;
    box-sizing: border-box;
    z-index: 1;
    /* 滚动区层级低于标题栏和底部栏 */
  height: calc(100vh - 44px - 100rpx); // 减去导航栏高度(44px)和底部栏高度(100rpx)
  box-sizing: border-box;
}
/* 行程项、字段样式保持不变 */
.task-item {
    background-color: #fff;
    border-radius: 12rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.scroll-content {
  padding: 20rpx;
  min-height: 100%;
}
.task-field {
    display: flex;
    margin-bottom: 20rpx;
// 骨架屏
.skeleton-item {
  background: $card-bg;
  border-radius: 16rpx;
  padding: 30rpx;
  margin-bottom: 20rpx;
}
.field-label {
    width: 180rpx;
    color: #666;
    font-size: 28rpx;
// 空状态
.empty-state {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 60vh;
}
.field-value {
    flex: 1;
    color: #333;
    font-size: 28rpx;
// 任务卡片
.task-card {
  background: $card-bg;
  border-radius: 16rpx;
  padding: 30rpx;
  margin-bottom: 20rpx;
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
  transition: transform 0.2s, box-shadow 0.2s;
  &:active {
    transform: scale(0.98);
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  }
}
.status {
    color: #ff6b6b;
    font-weight: 500;
// 卡片头部
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 20rpx;
  border-bottom: 1rpx solid #ebeef5;
  margin-bottom: 20rpx;
}
/* 底部操作栏-关键修改:固定定位 */
.bottom-bar {
    /* 固定在屏幕底部,不随滚动移动 */
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    height: 100rpx;
    border-top: 1rpx solid #eee;
    background-color: #fff;
    /* 顶部阴影,区分内容区和底部栏 */
    box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
    z-index: 999;
    /* 确保在所有内容上层 */
.dispatch-no {
  display: flex;
  flex-direction: column;
  .label {
    font-size: 22rpx;
    color: $text-secondary;
    margin-bottom: 4rpx;
  }
  .value {
    font-size: 30rpx;
    font-weight: 600;
    color: $text-primary;
    font-family: 'Courier New', monospace;
  }
}
.bottom-btn {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 28rpx;
    color: #333;
    border-right: 1rpx solid #eee;
}
.bottom-btn:last-child {
    border-right: none;
}
.logo-info {
  text-align: center;
  position: absolute;
  left: 50%;
  top: 15%;
  width: 100%;
  transform: translateX(-50%);
}
.company {
  font-size: 18px;
  color: #5d6680;
// 状态标签
.status-tag {
  padding: 8rpx 20rpx;
  border-radius: 8rpx;
  font-size: 24rpx;
  font-weight: 500;
}
.user {
  line-height: 40px;
  color: #a2a2a2;
  font-size: 14px;
.status-pending {
  background-color: rgba($danger-color, 0.1);
  color: $danger-color;
}
.status-transporting {
  background-color: rgba($primary-color, 0.1);
  color: $primary-color;
}
.lout {
.status-completed {
  background-color: rgba($success-color, 0.1);
  color: $success-color;
}
.status-cancelled {
  background-color: rgba($info-color, 0.1);
  color: $info-color;
}
.status-default {
  background-color: rgba($info-color, 0.1);
  color: $info-color;
}
// 卡片主体
.card-body {
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}
.info-row {
  display: flex;
  align-items: center;
  gap: 12rpx;
}
.info-text {
  font-size: 28rpx;
  color: $text-regular;
}
.time-row {
  margin-top: 8rpx;
  padding-top: 16rpx;
  border-top: 1rpx dashed #ebeef5;
}
.time-text {
  font-size: 26rpx;
  color: $warning-color;
  font-weight: 500;
}
// 路线展示
.route-section {
  display: flex;
  align-items: center;
  gap: 20rpx;
  padding: 16rpx 0;
}
.route-line {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 24rpx;
}
.route-dot {
  width: 16rpx;
  height: 16rpx;
  border-radius: 50%;
  &.start {
    background-color: $success-color;
    box-shadow: 0 0 8rpx rgba($success-color, 0.4);
  }
  &.end {
    background-color: $danger-color;
    box-shadow: 0 0 8rpx rgba($danger-color, 0.4);
  }
}
.route-path {
  width: 2rpx;
  height: 40rpx;
  background: linear-gradient(to bottom, $success-color, $danger-color);
  margin: 8rpx 0;
}
.route-info {
  flex: 1;
}
.route-text {
  font-size: 28rpx;
  color: $text-primary;
  font-weight: 500;
  line-height: 1.5;
}
// 加载更多
.load-more {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 30rpx 0;
  .no-more {
    font-size: 24rpx;
    color: $text-secondary;
  }
}
// 底部栏
.bottom-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100rpx;
  background: $card-bg;
  display: flex;
  align-items: center;
  box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
  z-index: 100;
}
.bottom-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 8rpx;
  height: 100%;
  transition: background-color 0.2s;
  &:active {
    background-color: rgba($primary-color, 0.05);
  }
  text {
    font-size: 24rpx;
    color: $text-regular;
  }
}
.divider {
  width: 1rpx;
  height: 50rpx;
  background-color: #ebeef5;
}
// 侧边栏
.sidebar-content {
  width: 500rpx;
  height: 100vh;
  background: $card-bg;
  display: flex;
  flex-direction: column;
}
.sidebar-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 240rpx 40rpx 60rpx;
  .logo {
    width: 160rpx;
    height: 160rpx;
    border-radius: 20rpx;
    margin-bottom: 24rpx;
    box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
  }
  .company-name {
    font-size: 32rpx;
    color: $primary-color;
    font-weight: 600;
    text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
  }
}
.sidebar-menu {
  flex: 1;
  padding: 40rpx 0;
  position: absolute;
  bottom: 65px;
  left: 50%;
  transform: translateX(-50%);
  color: #f0ae2d;
}
.menu-item {
  display: flex;
  align-items: center;
  gap: 20rpx;
  padding: 30rpx 40rpx;
  transition: background-color 0.2s;
</style>
  &:active {
    background-color: rgba($warning-color, 0.1);
  }
  .menu-text {
    font-size: 30rpx;
    color: $warning-color;
  }
}
</style>