wlzboy
2025-09-25 4648a3bee638e9a99d2d80b66f8833b261a2db91
feat:设计 app ui
10个文件已修改
37个文件已添加
5291 ■■■■■ 已修改文件
app/.gitignore 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/README.md 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/wechat.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages.json 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/bind-vehicle.vue 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/index.vue 605 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/login.vue 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/login/wechat.vue 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/message/index.vue 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/mine/index.vue 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/mine/info/index.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/task/create.vue 856 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/task/detail.vue 600 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/task/index.vue 798 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/task/settlement.vue 747 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/plugins/modal.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/README.md 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/create-active.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/create-active.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/create.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/create.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/home-active.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/home-active.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/home.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/home.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/messages-active.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/messages-active.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/messages.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/messages.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/profile-active.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/profile-active.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/profile.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/profile.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/tasks-active.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/tasks-active.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/tasks.png 补丁 | 查看 | 原始文档 | blame | 历史
app/static/icons/tasks.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/qrcode.png 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/tabbar/add.png 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/tabbar/add_.png 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/tabbar/message.png 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/tabbar/message_.png 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/tabbar/task.png 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/tabbar/task_.png 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/images/wechat.png 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/static/scss/global.scss 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/store/modules/user.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/.gitignore
@@ -3,7 +3,7 @@
/unpackage/*
/node_modules/*
/uni_modules/*
######################################################################
# Development Tools
app/README.md
@@ -1,51 +1,47 @@
<p align="center">
    <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-43e3941654fa3054c9684bf53d1b1d356a1.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v1.1.0</h1>
<h4 align="center">基于UniApp开发的轻量级移动端框架</h4>
<p align="center">
    <a href="https://gitee.com/y_project/RuoYi-App/stargazers"><img src="https://gitee.com/y_project/RuoYi-App/badge/star.svg?theme=dark"></a>
    <a href="https://gitee.com/y_project/RuoYi-App"><img src="https://img.shields.io/badge/RuoYi-v1.1.0-brightgreen.svg"></a>
    <a href="https://gitee.com/y_project/RuoYi-App/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
# 若依任务调度移动应用
## 平台简介
## 项目介绍
这是一个基于uni-app开发的任务调度移动应用,包含任务管理、消息通知、微信登录等功能。
RuoYi App 移动解决方案,采用uniapp框架,一份代码多终端适配,同时支持APP、小程序、H5!实现了与[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)、[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)完美对接的移动解决方案!目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。
## 功能模块
1. **首页** - 应用入口和概览
2. **任务** - 任务列表查看和管理
3. **创建任务** - 新建任务
4. **消息** - 系统消息和通知
5. **我的** - 个人中心和设置
* 配套后端代码仓库地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 或 [RuoYi-Cloud](https://github.com/yangzongzhuan/RuoYi-Cloud) 版本。
* 应用框架基于[uniapp](https://uniapp.dcloud.net.cn/),支持小程序、H5、Android和IOS。
* 前端组件采用[uni-ui](https://github.com/dcloudio/uni-ui),全端兼容的高性能UI框架。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
## 登录功能
- 账号密码登录
- 微信一键登录(支持微信小程序)
## 运行方式
```bash
# 安装依赖
npm install
## 技术文档
# 运行到H5
npm run dev:h5
- 官网网站:[http://ruoyi.vip](http://ruoyi.vip)
- 文档地址:[http://doc.ruoyi.vip](http://doc.ruoyi.vip)
- H5页体验:[http://h5.ruoyi.vip](http://h5.ruoyi.vip)
- QQ交流群: ①133713780(满)、②146013835(满)、③189091635
- 小程序体验
# 运行到微信小程序
npm run dev:mp-weixin
<img src="https://oscimg.oschina.net/oscnet/up-26c76dc90b92acdbd9ac8cd5252f07c8ad9.jpg" alt="小程序演示"/>
# 构建生产版本
npm run build
```
## 演示图
## 目录结构
```
app/
├── api/              # 接口请求
├── components/       # 组件
├── pages/            # 页面
├── static/           # 静态资源
├── store/            # 状态管理
├── utils/            # 工具函数
└── ...
```
<table>
    <tr>
        <td><img src="https://oscimg.oschina.net/oscnet/up-21f6f842fdc94540469b4eb43fdadbaf7f8.png"/></td>
        <td><img src="https://oscimg.oschina.net/oscnet/up-a6f23cf9a371a30165e135eff6d9ae89a9d.png"/></td>
        <td><img src="https://oscimg.oschina.net/oscnet/up-ff5f62016bf6624c1ff27eee57499dccd44.png"/></td>
    </tr>
    <tr>
        <td><img src="https://oscimg.oschina.net/oscnet/up-b9a582fdb26ec69d407fabd044d2c8494df.png"/></td>
        <td><img src="https://oscimg.oschina.net/oscnet/up-96427ee08fca29d77934cfc8d1b1a637cef.png"/></td>
        <td><img src="https://oscimg.oschina.net/oscnet/up-5fdadc582d24cccd7727030d397b63185a3.png"/></td>
    </tr>
    <tr>
        <td><img src="https://oscimg.oschina.net/oscnet/up-0a36797b6bcc50c36d40c3c782665b89efc.png"/></td>
        <td><img src="https://oscimg.oschina.net/oscnet/up-d77995cc00687cedd00d5ac7d68a07ea276.png"/></td>
        <td><img src="https://oscimg.oschina.net/oscnet/up-fa8f5ab20becf59b4b38c1b92a9989e7109.png"/></td>
    </tr>
</table>
## 注意事项
1. 微信登录功能需要在微信开发者工具中测试
2. 后端接口需要根据实际情况调整
3. 图标资源已包含在static/icons目录中
app/api/wechat.js
New file
@@ -0,0 +1,36 @@
import request from '@/utils/request'
// 微信登录
export function wechatLogin(data) {
  return request({
    url: '/wechat/login',
    method: 'post',
    data: data
  })
}
// 获取微信用户信息
export function getWechatUserInfo(openid) {
  return request({
    url: '/wechat/user/' + openid,
    method: 'get'
  })
}
// 绑定微信账号
export function bindWechat(data) {
  return request({
    url: '/wechat/bind',
    method: 'post',
    data: data
  })
}
// 解绑微信账号
export function unbindWechat(data) {
  return request({
    url: '/wechat/unbind',
    method: 'post',
    data: data
  })
}
app/pages.json
@@ -5,6 +5,11 @@
      "navigationBarTitleText": "登录"
    }
  }, {
    "path": "pages/login/wechat",
    "style": {
      "navigationBarTitleText": "微信登录"
    }
  }, {
    "path": "pages/register",
    "style": {
      "navigationBarTitleText": "注册"
@@ -14,6 +19,11 @@
    "style": {
      "navigationBarTitleText": "若依移动端框架",
      "navigationStyle": "custom"
    }
  }, {
    "path": "pages/bind-vehicle",
    "style": {
      "navigationBarTitleText": "绑定车辆"
    }
  }, {
    "path": "pages/work/index",
@@ -70,6 +80,31 @@
    "style": {
      "navigationBarTitleText": "浏览文本"
    }
  }, {
    "path": "pages/task/index",
    "style": {
      "navigationBarTitleText": "任务列表"
    }
  }, {
    "path": "pages/task/create",
    "style": {
      "navigationBarTitleText": "创建任务"
    }
  }, {
    "path": "pages/task/detail",
    "style": {
      "navigationBarTitleText": "任务详情"
    }
  }, {
    "path": "pages/task/settlement",
    "style": {
      "navigationBarTitleText": "任务结算"
    }
  }, {
    "path": "pages/message/index",
    "style": {
      "navigationBarTitleText": "消息中心"
    }
  }],
  "tabBar": {
    "color": "#000000",
@@ -78,18 +113,28 @@
    "backgroundColor": "#ffffff",
    "list": [{
        "pagePath": "pages/index",
        "iconPath": "static/images/tabbar/home.png",
        "selectedIconPath": "static/images/tabbar/home_.png",
        "iconPath": "static/icons/home.png",
        "selectedIconPath": "static/icons/home-active.png",
        "text": "首页"
      }, {
        "pagePath": "pages/work/index",
        "iconPath": "static/images/tabbar/work.png",
        "selectedIconPath": "static/images/tabbar/work_.png",
        "text": "工作台"
        "pagePath": "pages/task/index",
        "iconPath": "static/icons/tasks.png",
        "selectedIconPath": "static/icons/tasks-active.png",
        "text": "任务"
      }, {
        "pagePath": "pages/task/create",
        "iconPath": "static/icons/create.png",
        "selectedIconPath": "static/icons/create-active.png",
        "text": "创建任务"
      }, {
        "pagePath": "pages/message/index",
        "iconPath": "static/icons/messages.png",
        "selectedIconPath": "static/icons/messages-active.png",
        "text": "消息"
      }, {
        "pagePath": "pages/mine/index",
        "iconPath": "static/images/tabbar/mine.png",
        "selectedIconPath": "static/images/tabbar/mine_.png",
        "iconPath": "static/icons/profile.png",
        "selectedIconPath": "static/icons/profile-active.png",
        "text": "我的"
      }
    ]
@@ -99,4 +144,4 @@
    "navigationBarTitleText": "RuoYi",
    "navigationBarBackgroundColor": "#FFFFFF"
  }
}
}
app/pages/bind-vehicle.vue
New file
@@ -0,0 +1,214 @@
<template>
  <view class="bind-vehicle-container">
    <view class="header">
      <text class="title">绑定车辆</text>
    </view>
    <!-- 扫码绑定 -->
    <view class="scan-section">
      <view class="section-title">扫一扫绑定</view>
      <view class="scan-content">
        <view class="scan-icon" @click="scanQRCode">
          <uni-icons type="scan" size="40" color="#007AFF"></uni-icons>
          <text class="scan-text">点击扫码绑定车辆</text>
        </view>
      </view>
    </view>
    <!-- 下拉选择绑定 -->
    <view class="form-section">
      <view class="section-title">选择车牌号绑定</view>
      <view class="form-item">
        <view class="form-label">车牌号</view>
        <picker mode="selector" :range="vehiclePlates" @change="onVehiclePlateChange">
          <view class="form-input picker-input">
            {{ selectedVehiclePlate || '请选择车牌号' }}
            <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
          </view>
        </picker>
      </view>
      <view class="form-actions">
        <button class="submit-btn" @click="bindVehicle" :disabled="!selectedVehiclePlate">确认绑定</button>
        <button class="cancel-btn" @click="goBack">取消</button>
      </view>
    </view>
  </view>
</template>
<script>
  export default {
    data() {
      return {
        selectedVehiclePlate: '',
        // 模拟车辆列表数据
        vehiclePlates: ['粤A12345', '粤B67890', '粤C11111', '粤D22222', '粤E33333']
      }
    },
    methods: {
      // 扫码绑定车辆
      scanQRCode() {
        // #ifdef H5
        this.$modal.showToast('H5环境暂不支持扫码功能')
        // #endif
        // #ifndef H5
        uni.scanCode({
          success: (res) => {
            console.log('扫码结果:', res)
            // 解析扫码结果,这里假设扫码结果是车牌号
            this.selectedVehiclePlate = res.result
            this.$modal.showToast('扫码成功,正在绑定车辆...')
            // 扫码成功后自动绑定
            this.bindVehicle()
          },
          fail: (err) => {
            console.log('扫码失败:', err)
            this.$modal.showToast('扫码失败,请重试')
          }
        })
        // #endif
      },
      // 车牌号选择
      onVehiclePlateChange(e) {
        this.selectedVehiclePlate = this.vehiclePlates[e.detail.value]
      },
      // 绑定车辆
      bindVehicle() {
        if (!this.selectedVehiclePlate) {
          this.$modal.showToast('请选择车牌号')
          return
        }
        // 这里可以调用API进行车辆绑定
        this.$modal.confirm('确认绑定车辆 ' + this.selectedVehiclePlate + ' 吗?').then(() => {
          this.$modal.showToast('车辆绑定成功')
          // 返回上一页
          this.$tab.navigateBack()
        }).catch(() => {
          // 用户取消操作
        })
      },
      goBack() {
        this.$tab.navigateBack()
      }
    }
  }
</script>
<style lang="scss">
  .bind-vehicle-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    min-height: 100vh;
  }
  .header {
    text-align: center;
    padding: 40rpx 0;
    .title {
      font-size: 40rpx;
      font-weight: bold;
      color: #333;
    }
  }
  .section-title {
    font-size: 32rpx;
    font-weight: bold;
    margin: 30rpx 0 20rpx 0;
    color: #333;
  }
  .scan-section {
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    .scan-content {
      text-align: center;
      padding: 40rpx 0;
      .scan-icon {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        .scan-text {
          margin-top: 20rpx;
          font-size: 28rpx;
          color: #007AFF;
        }
      }
    }
  }
  .form-section {
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    .form-item {
      margin-bottom: 40rpx;
      &:last-child {
        margin-bottom: 0;
      }
      .form-label {
        font-size: 28rpx;
        margin-bottom: 15rpx;
        color: #333;
      }
      .form-input {
        height: 70rpx;
        padding: 0 20rpx;
        border: 1rpx solid #eee;
        border-radius: 10rpx;
        font-size: 28rpx;
        &.picker-input {
          display: flex;
          align-items: center;
          justify-content: space-between;
        }
      }
    }
    .form-actions {
      display: flex;
      margin-top: 50rpx;
      .submit-btn, .cancel-btn {
        flex: 1;
        height: 80rpx;
        border-radius: 10rpx;
        font-size: 32rpx;
        margin: 0 10rpx;
      }
      .submit-btn {
        background-color: #007AFF;
        color: white;
        &[disabled] {
          background-color: #cccccc;
        }
      }
      .cancel-btn {
        background-color: #f0f0f0;
        color: #333;
      }
    }
  }
</style>
app/pages/index.vue
@@ -1,43 +1,598 @@
<template>
  <view class="content">
    <image class="logo" src="@/static/logo.png"></image>
    <view class="text-area">
      <text class="title">Hello RuoYi</text>
  <view class="home-container">
    <!-- 顶部用户信息区域 -->
    <view class="user-info-section">
      <view class="user-info-content">
        <view class="user-details">
          <view class="user-name">{{ userName || '未登录' }}</view>
          <view class="vehicle-info">
            <text v-if="boundVehicle">关联车牌号:{{ boundVehicle }}</text>
            <text v-else>未绑定车牌号</text>
          </view>
        </view>
        <button
          class="bind-vehicle-btn"
          @click="goToBindVehicle"
        >
          {{ boundVehicle ? '更换车辆' : '绑定车辆' }}
        </button>
      </view>
    </view>
    <!-- 消息入口 -->
    <view class="message-entry" @click="goToMessages">
      <view class="message-icon">
        <uni-icons type="chat" size="24" color="#007AFF"></uni-icons>
      </view>
      <view class="message-text">消息中心</view>
      <view class="unread-dot" v-if="unreadMessageCount > 0">{{ unreadMessageCount }}</view>
      <view class="arrow">
        <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
      </view>
    </view>
    <!-- 正在运行的任务标题 -->
    <view class="running-tasks-header">
      <text class="header-title">正在运行的任务</text>
    </view>
    <!-- 正在运行的任务列表 -->
    <scroll-view class="running-tasks-section" scroll-y="true">
      <view class="task-list">
        <view class="task-item" v-for="task in runningTasks" :key="task.id">
          <view class="task-main" @click="viewTaskDetail(task)">
            <view class="task-title">{{ getTaskTypeText(task.type) }} - {{ task.vehicle }}</view>
            <view class="task-info">
              <view class="info-row">
                <view class="info-item">
                  <view class="label">任务编号:</view>
                  <view class="value">{{ task.taskNo }}</view>
                </view>
                <view class="info-item">
                  <view class="label">任务状态:</view>
                  <view class="value">{{ getStatusText(task.status) }}</view>
                </view>
              </view>
              <view class="info-row">
                <view class="info-item">
                  <view class="label">出发地:</view>
                  <view class="value">{{ task.startLocation }}</view>
                </view>
                <view class="info-item">
                  <view class="label">目的地:</view>
                  <view class="value">{{ task.endLocation }}</view>
                </view>
              </view>
              <view class="info-row">
                <view class="info-item">
                  <view class="label">出发时间:</view>
                  <view class="value">{{ task.startTime }}</view>
                </view>
                <view class="info-item">
                  <view class="label">执行人员:</view>
                  <view class="value">{{ task.assignee }}</view>
                </view>
              </view>
            </view>
          </view>
          <!-- 操作按钮 -->
          <view class="task-actions">
            <button
              class="action-btn"
              :class="{ disabled: isActionDisabled(task, 'depart') }"
              @click="handleTaskAction(task, 'depart')"
              v-if="task.status !== 'completed'"
            >
              出发
            </button>
            <button
              class="action-btn"
              :class="{ disabled: isActionDisabled(task, 'arrive') }"
              @click="handleTaskAction(task, 'arrive')"
              v-if="task.status !== 'completed'"
            >
              已到达
            </button>
            <button
              class="action-btn"
              :class="{ disabled: isActionDisabled(task, 'return') }"
              @click="handleTaskAction(task, 'return')"
              v-if="task.status !== 'completed'"
            >
              返程
            </button>
            <button
              class="action-btn"
              :class="{ disabled: isActionDisabled(task, 'settle') }"
              @click="handleTaskAction(task, 'settle')"
              v-if="task.status !== 'completed'"
            >
              结算
            </button>
            <button
              class="action-btn primary"
              :class="{ disabled: isActionDisabled(task, 'complete') }"
              @click="handleTaskAction(task, 'complete')"
              v-if="task.status !== 'completed'"
            >
              已完成
            </button>
          </view>
        </view>
        <view class="no-data" v-if="runningTasks.length === 0">
          <uni-icons type="info" size="40" color="#ccc"></uni-icons>
          <text>暂无正在运行的任务</text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>
<script>
  import { mapState } from 'vuex'
  export default {
    onLoad: function() {
    data() {
      return {
        // 模拟用户绑定的车辆信息
        boundVehicle: '粤A12345', // 模拟已绑定车辆
        // 模拟消息数据
        messages: [
          {
            id: 1,
            type: 'create', // 创建成功
            content: 'TD 1011 广州天河->广州东站,时间:13:20 任务创建成功',
            time: '2023-05-15 13:20',
            read: false,
            taskId: 1
          },
          {
            id: 2,
            type: 'push', // 任务推送
            content: 'TD1021 广州天河->广州东站,时间:13:20 出发,请及时处理',
            time: '2023-05-15 13:25',
            read: false,
            taskId: 2
          },
          {
            id: 3,
            type: 'status', // 任务状态变更
            content: 'TD1021 广州天河->广州东站,时间:13:20,任务正在转运中',
            time: '2023-05-15 14:30',
            read: true,
            taskId: 2
          },
          {
            id: 4,
            type: 'create', // 创建成功
            content: 'TD 1022 深圳南山->深圳福田,时间:15:10 任务创建成功',
            time: '2023-05-15 15:10',
            read: false,
            taskId: 3
          },
          {
            id: 5,
            type: 'push', // 任务推送
            content: 'TD1023 深圳南山->深圳福田,时间:16:00 出发,请及时处理',
            time: '2023-05-15 16:00',
            read: true,
            taskId: 4
          }
        ],
        // 模拟正在运行的任务列表
        taskList: [
          {
            id: 1,
            title: '紧急维修任务',
            type: 'maintenance', // 维修保养
            startLocation: '广州市天河区XX路123号',
            endLocation: '广州市白云区YY路456号',
            startTime: '2023-05-15 15:00',
            assignee: '张三',
            status: 'pending',
            vehicle: '粤A12345',
            taskNo: 'RW20230515001'
          },
          {
            id: 2,
            title: '定期保养任务',
            type: 'maintenance', // 维修保养
            startLocation: '深圳市南山区XX路789号',
            endLocation: '深圳市福田区YY路999号',
            startTime: '2023-05-14 10:00',
            assignee: '李四',
            status: 'processing',
            vehicle: '粤B67890',
            taskNo: 'RW20230514002'
          },
          {
            id: 5,
            title: '急救转运任务',
            type: 'emergency', // 急救转运
            startLocation: '广州市越秀区医院路1号',
            endLocation: '广州市海珠区医院路2号',
            startTime: '2023-05-16 14:00',
            assignee: '张医生,李护士',
            status: 'pending',
            vehicle: '粤E33333',
            taskNo: 'RW20230516005'
          },
          {
            id: 6,
            title: '福祉车任务',
            type: 'welfare', // 福祉车
            startLocation: '广州市荔湾区社区路10号',
            endLocation: '广州市天河区养老院路20号',
            startTime: '2023-05-17 08:00',
            assignee: '王司机',
            status: 'processing',
            vehicle: '粤F44444',
            taskNo: 'RW20230517006'
          }
        ]
      }
    },
    computed: {
      ...mapState({
        userName: state => state.user.name
      }),
      // 正在运行的任务(待处理和处理中的任务)
      runningTasks() {
        return this.taskList.filter(task =>
          task.status === 'pending' || task.status === 'processing'
        );
      },
      // 未读消息数量
      unreadMessageCount() {
        return this.messages.filter(message => !message.read).length;
      }
    },
    methods: {
      // 跳转到绑定车辆页面
      goToBindVehicle() {
        // 跳转到绑定车辆的页面
        this.$tab.navigateTo('/pages/bind-vehicle');
      },
      // 跳转到消息页面
      goToMessages() {
        this.$tab.switchTab('/pages/message/index');
      },
      // 查看任务详情
      viewTaskDetail(task) {
        // 跳转到任务详情页面
        this.$tab.navigateTo(`/pages/task/detail?id=${task.id}`);
      },
      // 判断操作按钮是否禁用
      isActionDisabled(task, action) {
        // 根据任务状态和操作类型判断是否禁用
        switch (action) {
          case 'depart':
            return task.status !== 'pending';
          case 'arrive':
            return task.status !== 'processing';
          case 'return':
            return task.status !== 'processing';
          case 'settle':
            return task.status !== 'processing';
          case 'complete':
            return task.status !== 'processing';
          default:
            return false;
        }
      },
      // 处理任务操作
      handleTaskAction(task, action) {
        if (this.isActionDisabled(task, action)) {
          return;
        }
        switch (action) {
          case 'depart':
            // 出发操作,根据任务类型显示不同的确认信息
            let departMessage = '确定要标记为已出发吗?';
            if (task.type !== 'maintenance' && task.type !== 'refuel' && task.type !== 'inspection') {
              departMessage = '发出去目的地,确认?';
            }
            this.$modal.confirm(departMessage).then(() => {
              task.status = 'processing';
              this.$modal.showToast('已出发');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'arrive':
            // 已到达操作
            this.$modal.confirm('已经到达目的地,确认?').then(() => {
              this.$modal.showToast('已到达');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'return':
            // 返程操作
            this.$modal.confirm('现在已经返程中,确认?').then(() => {
              this.$modal.showToast('返程中');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'settle':
            // 结算操作,跳转到结算页面
            this.$tab.navigateTo(`/pages/task/settlement?id=${task.id}`);
            break;
          case 'complete':
            // 已完成操作
            this.$modal.confirm('任务是否已经全部完成,确认?').then(() => {
              task.status = 'completed';
              this.$modal.showToast('任务已完成');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
        }
      },
      getStatusText(status) {
        const statusMap = {
          'pending': '待处理',
          'processing': '处理中',
          'completed': '已完成'
        }
        return statusMap[status] || '未知'
      },
      getTaskTypeText(type) {
        const typeMap = {
          'maintenance': '维修保养',
          'refuel': '加油',
          'inspection': '巡检',
          'emergency': '急救转运',
          'welfare': '福祉车'
        }
        return typeMap[type] || '未知类型'
      }
    }
  }
</script>
<style>
  .content {
<style lang="scss">
  .home-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
  }
  .logo {
    height: 200rpx;
    width: 200rpx;
    margin-top: 200rpx;
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 50rpx;
  // 用户信息区域
  .user-info-section {
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    flex-shrink: 0; // 防止收缩
    .user-info-content {
      display: flex;
      justify-content: space-between;
      align-items: center;
      .user-details {
        .user-name {
          font-size: 36rpx;
          font-weight: bold;
          margin-bottom: 10rpx;
          color: #333;
        }
        .vehicle-info {
          font-size: 28rpx;
          color: #666;
        }
      }
      .bind-vehicle-btn {
        background-color: #007AFF;
        color: white;
        border-radius: 10rpx;
        padding: 15rpx 30rpx;
        font-size: 28rpx;
      }
    }
  }
  .text-area {
  // 消息入口
  .message-entry {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    position: relative;
    .message-icon {
      margin-right: 20rpx;
    }
    .message-text {
      flex: 1;
      font-size: 32rpx;
      color: #333;
    }
    .unread-dot {
      position: absolute;
      top: 15rpx;
      right: 60rpx;
      background-color: #ff4d4f;
      color: white;
      border-radius: 50%;
      width: 32rpx;
      height: 32rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 20rpx;
    }
    .arrow {
      margin-left: 20rpx;
    }
  }
  .title {
    font-size: 36rpx;
    color: #8f8f94;
  // 正在运行的任务标题
  .running-tasks-header {
    margin-bottom: 20rpx;
    flex-shrink: 0; // 防止收缩
    .header-title {
      font-size: 36rpx;
      font-weight: bold;
      color: #333;
    }
  }
</style>
  // 正在运行的任务列表
  .running-tasks-section {
    flex: 1;
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
    .task-list {
      .task-item {
        background-color: #fafafa;
        border-radius: 15rpx;
        margin-bottom: 30rpx;
        overflow: hidden;
        .task-main {
          padding: 30rpx;
          border-bottom: 1rpx solid #f0f0f0;
          .task-title {
            font-size: 32rpx;
            font-weight: bold;
            margin-bottom: 20rpx;
          }
          .task-info {
            .info-row {
              display: flex;
              margin-bottom: 15rpx;
              &:last-child {
                margin-bottom: 0;
              }
              .info-item {
                flex: 1;
                display: flex;
                .label {
                  font-size: 26rpx;
                  color: #666;
                  margin-right: 10rpx;
                  white-space: nowrap;
                }
                .value {
                  font-size: 26rpx;
                  flex: 1;
                  word-break: break-all;
                }
              }
            }
          }
        }
        .task-actions {
          display: flex;
          padding: 20rpx;
          .action-btn {
            flex: 1;
            height: 70rpx;
            border-radius: 10rpx;
            font-size: 26rpx;
            margin: 0 5rpx;
            background-color: #f0f0f0;
            color: #333;
            &.primary {
              background-color: #007AFF;
              color: white;
            }
            &.disabled {
              opacity: 0.5;
            }
            &:first-child {
              margin-left: 0;
            }
            &:last-child {
              margin-right: 0;
            }
          }
        }
      }
      .no-data {
        text-align: center;
        padding: 100rpx 0;
        color: #999;
        text {
          display: block;
          margin-top: 20rpx;
        }
      }
    }
  }
</style>
app/pages/login.vue
@@ -1,5 +1,5 @@
<template>
  <view class="normal-login-container">
  <scroll-view class="normal-login-container" scroll-y="true">
    <view class="logo-content align-center justify-center flex">
      <image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
      </image>
@@ -14,15 +14,21 @@
        <view class="iconfont icon-password icon"></view>
        <input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
      </view>
      <view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
      <view class="input-item flex align-center captcha-container" v-if="captchaEnabled">
        <view class="iconfont icon-code icon"></view>
        <input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
        <view class="login-code"> 
          <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
          <image :src="codeUrl" @click="getCode" class="login-code-img" mode="aspectFit"></image>
        </view>
      </view>
      <view class="action-btn">
        <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
      </view>
      <view class="wechat-login" @click="handleWechatLogin">
        <view class="wechat-btn">
          <image class="wechat-icon" src="/static/icons/profile.png"></image>
          <text class="wechat-text">微信一键登录</text>
        </view>
      </view>
      <view class="reg text-center" v-if="register">
        <text class="text-grey1">没有账号?</text>
@@ -34,8 +40,7 @@
        <text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
      </view>
    </view>
  </view>
  </scroll-view>
</template>
<script>
@@ -98,6 +103,40 @@
          this.pwdLogin()
        }
      },
      // 微信登录方法
      async handleWechatLogin() {
        // #ifdef MP-WEIXIN
        // 微信小程序登录
        uni.login({
          provider: 'weixin',
          success: (loginRes) => {
            console.log('微信登录成功', loginRes);
            // 获取用户信息
            uni.getUserInfo({
              provider: 'weixin',
              success: (infoRes) => {
                console.log('用户信息获取成功', infoRes);
                // 跳转到微信登录确认页面
                this.$tab.navigateTo(`/pages/login/wechat?userInfo=${encodeURIComponent(JSON.stringify(infoRes.userInfo))}`);
              },
              fail: (error) => {
                console.error('获取用户信息失败', error);
                this.$modal.msgError("获取微信用户信息失败");
              }
            });
          },
          fail: (error) => {
            console.error('微信登录失败', error);
            this.$modal.msgError("微信登录失败");
          }
        });
        // #endif
        // #ifndef MP-WEIXIN
        // H5或其他平台提示
        this.$modal.msgError("请在微信客户端中使用微信登录功能");
        // #endif
      },
      // 密码登录
      async pwdLogin() {
        this.$store.dispatch('Login', this.loginForm).then(() => {
@@ -127,6 +166,24 @@
  .normal-login-container {
    width: 100%;
    min-height: 100vh;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
    .logo-content {
      width: 100%;
@@ -158,45 +215,100 @@
        .icon {
          font-size: 38rpx;
          margin-left: 10px;
          color: #999;
        }
        .input {
          margin-left: 20rpx;
          width: 100%;
          font-size: 14px;
          line-height: 20px;
          text-align: left;
          padding-left: 15px;
          height: 45px;
          background: none;
        }
      }
      .captcha-container {
        width: 100%;
        margin: 20px auto;
        background-color: #f5f6f7;
        height: 45px;
        border-radius: 20px;
        padding-right: 10rpx;
        box-sizing: border-box;
        .icon {
          font-size: 38rpx;
          margin-left: 10px;
        }
        .input {
          margin-left: 20rpx;
          width: 60%;
          height: 45px;
          background: none;
        }
        .login-code {
          width: 30%;
          height: 45px;
          margin-left: 10rpx;
          border-left: 1px solid #e0e0e0;
          display: flex;
          align-items: center;
          justify-content: center;
          .login-code-img {
            max-width: 100%;
            max-height: 45px;
            object-fit: contain;
            cursor: pointer;
          }
        }
      }
      .login-btn {
        margin-top: 40px;
        height: 45px;
      .action-btn {
        margin: 40rpx 0;
        .login-btn {
          height: 90rpx;
          font-size: 32rpx;
        }
      }
      .reg {
        margin-top: 15px;
      .wechat-login {
        margin: 20rpx 0;
        .wechat-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          background-color: #07c160;
          height: 90rpx;
          border-radius: 20px;
          .wechat-icon {
            width: 40rpx;
            height: 40rpx;
            margin-right: 10rpx;
          }
          .wechat-text {
            color: white;
            font-size: 32rpx;
          }
        }
      }
      .xieyi {
        color: #333;
        margin-top: 20px;
      }
      .login-code {
        height: 38px;
        float: right;
        .login-code-img {
          height: 38px;
          position: absolute;
          margin-left: 10px;
          width: 200rpx;
      .reg, .xieyi {
        margin: 20rpx 0;
        .text-grey1 {
          color: #888;
        }
        .text-blue {
          margin: 0 10rpx;
          color: #007AFF;
        }
      }
    }
  }
</style>
</style>
app/pages/login/wechat.vue
New file
@@ -0,0 +1,258 @@
<template>
  <view class="wechat-login-container">
    <view class="header">
      <view class="title">微信授权登录</view>
      <view class="subtitle">即将登录到若依管理系统</view>
    </view>
    <view class="user-info-card">
      <view class="avatar-section">
        <image
          class="user-avatar"
          :src="wechatUserInfo.avatarUrl || '/static/images/profile.jpg'"
          mode="aspectFill"
        ></image>
      </view>
      <view class="info-section">
        <view class="info-item">
          <view class="label">昵称</view>
          <view class="value">{{ wechatUserInfo.nickName || '未知用户' }}</view>
        </view>
        <view class="info-item">
          <view class="label">性别</view>
          <view class="value">{{ genderText }}</view>
        </view>
        <view class="info-item">
          <view class="label">地区</view>
          <view class="value">{{ wechatUserInfo.country }} {{ wechatUserInfo.province }} {{ wechatUserInfo.city }}</view>
        </view>
      </view>
    </view>
    <view class="agreement-section">
      <label class="checkbox">
        <checkbox :checked="agreed" @click="toggleAgreement" />
        <text class="agreement-text">我已阅读并同意</text>
        <text class="link" @click="handleUserAgrement">《用户协议》</text>
        <text>和</text>
        <text class="link" @click="handlePrivacy">《隐私协议》</text>
      </label>
    </view>
    <view class="action-buttons">
      <button
        class="confirm-btn"
        :class="{ disabled: !agreed }"
        :disabled="!agreed"
        @click="confirmLogin"
      >
        确认登录
      </button>
      <button class="cancel-btn" @click="cancelLogin">取消</button>
    </view>
  </view>
</template>
<script>
  export default {
    data() {
      return {
        agreed: false,
        wechatUserInfo: {},
        globalConfig: getApp().globalData.config
      }
    },
    onLoad(options) {
      // 接收从登录页面传递的微信用户信息
      if (options.userInfo) {
        this.wechatUserInfo = JSON.parse(decodeURIComponent(options.userInfo))
        console.log('微信用户信息:', this.wechatUserInfo)
      }
    },
    computed: {
      genderText() {
        const genderMap = {
          0: '未知',
          1: '男',
          2: '女'
        }
        return genderMap[this.wechatUserInfo.gender] || '未知'
      }
    },
    methods: {
      toggleAgreement() {
        this.agreed = !this.agreed
      },
      // 隐私协议
      handlePrivacy() {
        let site = this.globalConfig.appInfo.agreements[0]
        this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
      },
      // 用户协议
      handleUserAgrement() {
        let site = this.globalConfig.appInfo.agreements[1]
        this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
      },
      // 确认登录
      async confirmLogin() {
        if (!this.agreed) {
          this.$modal.msgError("请先同意用户协议和隐私协议")
          return
        }
        this.$modal.loading("登录中...")
        // 这里应该调用后端微信登录接口
        // 示例代码,实际需要根据后端接口调整
        /*
        this.$store.dispatch('WechatLogin', {
          openid: this.wechatUserInfo.openId,
          userInfo: this.wechatUserInfo
        }).then(() => {
          this.$modal.closeLoading()
          this.loginSuccess()
        }).catch(() => {
          this.$modal.closeLoading()
          this.$modal.msgError("微信登录失败")
        })
        */
        // 临时演示用
        setTimeout(() => {
          this.$modal.closeLoading()
          this.$modal.showToast("微信登录成功")
          this.$tab.reLaunch('/pages/index')
        }, 1000)
      },
      // 取消登录
      cancelLogin() {
        this.$modal.confirm('确定要取消登录吗?').then(() => {
          this.$tab.reLaunch('/pages/login')
        }).catch(() => {
          // 取消操作
        })
      },
      // 登录成功后,处理函数
      loginSuccess(result) {
        // 设置用户信息
        this.$store.dispatch('GetInfo').then(res => {
          this.$tab.reLaunch('/pages/index')
        })
      }
    }
  }
</script>
<style lang="scss">
  .wechat-login-container {
    padding: 40rpx;
    background-color: #f5f5f5;
    min-height: 100vh;
    .header {
      text-align: center;
      margin-bottom: 60rpx;
      .title {
        font-size: 36rpx;
        font-weight: bold;
        margin-bottom: 20rpx;
      }
      .subtitle {
        font-size: 28rpx;
        color: #666;
      }
    }
    .user-info-card {
      background-color: white;
      border-radius: 20rpx;
      padding: 40rpx;
      margin-bottom: 40rpx;
      box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
      .avatar-section {
        text-align: center;
        margin-bottom: 40rpx;
        .user-avatar {
          width: 120rpx;
          height: 120rpx;
          border-radius: 50%;
          border: 4rpx solid #f0f0f0;
        }
      }
      .info-section {
        .info-item {
          display: flex;
          justify-content: space-between;
          padding: 20rpx 0;
          border-bottom: 1rpx solid #f0f0f0;
          &:last-child {
            border-bottom: none;
          }
          .label {
            font-size: 28rpx;
            color: #666;
          }
          .value {
            font-size: 28rpx;
            font-weight: bold;
          }
        }
      }
    }
    .agreement-section {
      margin-bottom: 60rpx;
      .checkbox {
        display: flex;
        align-items: center;
        .agreement-text {
          margin: 0 10rpx;
        }
        .link {
          color: #007AFF;
          margin: 0 5rpx;
        }
      }
    }
    .action-buttons {
      .confirm-btn {
        width: 100%;
        height: 80rpx;
        background-color: #07c160;
        color: white;
        border-radius: 10rpx;
        font-size: 32rpx;
        margin-bottom: 20rpx;
        &.disabled {
          background-color: #cccccc;
        }
      }
      .cancel-btn {
        width: 100%;
        height: 80rpx;
        background-color: white;
        color: #333;
        border: 1rpx solid #ddd;
        border-radius: 10rpx;
        font-size: 32rpx;
      }
    }
  }
</style>
app/pages/message/index.vue
New file
@@ -0,0 +1,239 @@
<template>
  <view class="message-container">
    <view class="message-header">
      <view class="header-title">消息中心</view>
    </view>
    <scroll-view class="message-list-scroll" scroll-y="true">
      <view class="message-list">
        <!-- 未读消息在前 -->
        <view
          class="message-item"
          v-for="message in sortedMessages"
          :key="message.id"
          @click="viewMessageDetail(message)"
        >
          <view class="message-main">
            <view class="message-title">
              <text class="title-text">{{ getMessageTypeText(message.type) }}</text>
              <view class="unread-dot" v-if="!message.read"></view>
            </view>
            <view class="message-content">{{ message.content }}</view>
            <view class="message-time">{{ message.time }}</view>
          </view>
        </view>
        <view class="no-data" v-if="sortedMessages.length === 0">
          <uni-icons type="info" size="40" color="#ccc"></uni-icons>
          <text>暂无消息</text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>
<script>
  export default {
    data() {
      return {
        // 消息列表
        messages: [
          {
            id: 1,
            type: 'create', // 创建成功
            content: 'TD 1011 广州天河->广州东站,时间:13:20 任务创建成功',
            time: '2023-05-15 13:20',
            read: false,
            taskId: 1
          },
          {
            id: 2,
            type: 'push', // 任务推送
            content: 'TD1021 广州天河->广州东站,时间:13:20 出发,请及时处理',
            time: '2023-05-15 13:25',
            read: false,
            taskId: 2
          },
          {
            id: 3,
            type: 'status', // 任务状态变更
            content: 'TD1021 广州天河->广州东站,时间:13:20,任务正在转运中',
            time: '2023-05-15 14:30',
            read: true,
            taskId: 2
          },
          {
            id: 4,
            type: 'create', // 创建成功
            content: 'TD 1022 深圳南山->深圳福田,时间:15:10 任务创建成功',
            time: '2023-05-15 15:10',
            read: false,
            taskId: 3
          },
          {
            id: 5,
            type: 'push', // 任务推送
            content: 'TD1023 深圳南山->深圳福田,时间:16:00 出发,请及时处理',
            time: '2023-05-15 16:00',
            read: true,
            taskId: 4
          }
        ]
      }
    },
    computed: {
      // 按未读/已读排序,未读的在前面
      sortedMessages() {
        return [...this.messages].sort((a, b) => {
          if (a.read === b.read) {
            // 相同状态按时间倒序
            return new Date(b.time) - new Date(a.time);
          }
          // 未读的排在前面
          return a.read ? 1 : -1;
        });
      }
    },
    methods: {
      // 获取消息类型文本
      getMessageTypeText(type) {
        const typeMap = {
          'create': '创建成功',
          'push': '任务推送',
          'status': '任务状态'
        }
        return typeMap[type] || '系统消息';
      },
      // 查看消息详情(跳转到任务详情)
      viewMessageDetail(message) {
        // 标记为已读
        message.read = true;
        // 跳转到任务详情页面
        if (message.taskId) {
          this.$tab.navigateTo(`/pages/task/detail?id=${message.taskId}`);
        } else {
          this.$modal.showToast('无法找到关联任务');
        }
      }
    }
  }
</script>
<style lang="scss">
  .message-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    height: 100vh;
    display: flex;
    flex-direction: column;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
  }
  .message-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20rpx 0;
    flex-shrink: 0; // 防止收缩
    .header-title {
      font-size: 36rpx;
      font-weight: bold;
    }
  }
  .message-list-scroll {
    flex: 1;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
  }
  .message-list {
    .message-item {
      background-color: white;
      border-radius: 15rpx;
      margin-bottom: 20rpx;
      padding: 30rpx;
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      .message-main {
        .message-title {
          display: flex;
          justify-content: space-between;
          align-items: center;
          margin-bottom: 20rpx;
          .title-text {
            font-size: 32rpx;
            font-weight: bold;
            color: #333;
          }
          .unread-dot {
            width: 16rpx;
            height: 16rpx;
            border-radius: 50%;
            background-color: #ff4d4f;
          }
        }
        .message-content {
          font-size: 28rpx;
          color: #666;
          margin-bottom: 20rpx;
          line-height: 1.5;
        }
        .message-time {
          font-size: 24rpx;
          color: #999;
          text-align: right;
        }
      }
    }
    .no-data {
      text-align: center;
      padding: 100rpx 0;
      color: #999;
      text {
        display: block;
        margin-top: 20rpx;
      }
    }
  }
</style>
app/pages/mine/index.vue
@@ -1,5 +1,5 @@
<template>
  <view class="mine-container" :style="{height: `${windowHeight}px`}">
  <view class="mine-container">
    <!--顶部个人信息栏-->
    <view class="header-section">
      <view class="flex padding justify-between">
@@ -25,7 +25,7 @@
      </view>
    </view>
    <view class="content-section">
    <scroll-view class="content-section" scroll-y="true">
      <view class="mine-actions grid col-4 text-center">
        <view class="action-item" @click="handleJiaoLiuQun">
          <view class="iconfont icon-friendfill text-pink icon"></view>
@@ -42,6 +42,34 @@
        <view class="action-item" @click="handleBuilding">
          <view class="iconfont icon-dianzan text-green icon"></view>
          <text class="text">点赞我们</text>
        </view>
      </view>
      <!-- 用户信息展示 -->
      <view class="user-info-section">
        <view class="info-item">
          <view class="info-label">用户名:</view>
          <view class="info-value">{{ name || '未登录' }}</view>
        </view>
        <view class="info-item">
          <view class="info-label">手机号码:</view>
          <view class="info-value">{{ phonenumber || '未绑定' }}</view>
        </view>
        <view class="info-item">
          <view class="info-label">绑定车辆:</view>
          <view class="info-value">{{ boundVehicle || '未绑定' }}</view>
        </view>
        <view class="info-item">
          <view class="info-label">App版本号:</view>
          <view class="info-value">{{ version || '1.0.0' }}</view>
        </view>
        <!-- 绑定/解绑车辆操作 -->
        <view class="vehicle-actions" v-if="boundVehicle && boundVehicle !== '未绑定'">
          <button class="unbind-btn" @click="unbindVehicle">取消绑定车辆</button>
        </view>
        <view class="vehicle-actions" v-else>
          <button class="bind-btn" @click="goToBindVehicle">绑定车辆</button>
        </view>
      </view>
@@ -71,18 +99,20 @@
          </view>
        </view>
      </view>
    </view>
    </scroll-view>
  </view>
</template>
<script>
  import storage from '@/utils/storage'
  import { getUserProfile } from "@/api/system/user"
  
  export default {
    data() {
      return {
        name: this.$store.state.user.name,
        phonenumber: '',
        boundVehicle: '', // 模拟绑定的车辆信息
        version: getApp().globalData.config.appInfo.version
      }
    },
@@ -94,7 +124,42 @@
        return uni.getSystemInfoSync().windowHeight - 50
      }
    },
    onLoad() {
      this.getUserInfo()
    },
    methods: {
      // 获取用户信息
      getUserInfo() {
        getUserProfile().then(response => {
          const user = response.data
          this.name = user.userName
          this.phonenumber = user.phonenumber
          // 模拟绑定车辆信息,实际项目中应从用户信息或车辆接口获取
          this.boundVehicle = '粤A12345'
        }).catch(() => {
          // 获取用户信息失败时使用默认值
          this.name = this.$store.state.user.name || '未登录'
          this.phonenumber = '未绑定'
          this.boundVehicle = '未绑定'
        })
      },
      // 跳转到绑定车辆页面
      goToBindVehicle() {
        this.$tab.navigateTo('/pages/bind-vehicle')
      },
      // 取消绑定车辆
      unbindVehicle() {
        this.$modal.confirm('是否取消绑定车辆?').then(() => {
          // 这里可以调用API取消绑定车辆
          this.boundVehicle = '未绑定'
          this.$modal.showToast('取消绑定成功')
        }).catch(() => {
          // 用户取消操作
        })
      },
      handleToInfo() {
        this.$tab.navigateTo('/pages/mine/info/index')
      },
@@ -140,13 +205,32 @@
  .mine-container {
    width: 100%;
    height: 100%;
    height: 100vh;
    display: flex;
    flex-direction: column;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
    .header-section {
      padding: 15px 15px 45px 15px;
      background-color: #3c96f3;
      color: white;
      flex-shrink: 0; // 防止收缩
      .login-tip {
        font-size: 18px;
@@ -172,8 +256,26 @@
    }
    .content-section {
      flex: 1;
      position: relative;
      top: -50px;
      // 隐藏滚动条但保持滚动功能
      ::-webkit-scrollbar {
        display: none;
        width: 0 !important;
        height: 0 !important;
        background: transparent;
      }
      // Firefox滚动条隐藏
      * {
        scrollbar-width: none; /* Firefox */
      }
      // IE/Edge滚动条隐藏
      * {
        -ms-overflow-style: none; /* IE 10+ */
      }
      .mine-actions {
        margin: 15px 15px;
@@ -193,6 +295,60 @@
          }
        }
      }
      // 用户信息展示区域
      .user-info-section {
        background-color: white;
        border-radius: 8px;
        padding: 20rpx 30rpx;
        margin: 15rpx;
        box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
        .info-item {
          display: flex;
          padding: 15rpx 0;
          border-bottom: 1rpx solid #f0f0f0;
          &:last-child {
            border-bottom: none;
          }
          .info-label {
            font-size: 28rpx;
            color: #666;
            width: 180rpx;
            flex-shrink: 0;
          }
          .info-value {
            font-size: 28rpx;
            color: #333;
            flex: 1;
          }
        }
        .vehicle-actions {
          padding: 30rpx 0 10rpx 0;
          text-align: center;
          .bind-btn, .unbind-btn {
            width: 80%;
            height: 80rpx;
            border-radius: 10rpx;
            font-size: 32rpx;
          }
          .bind-btn {
            background-color: #007AFF;
            color: white;
          }
          .unbind-btn {
            background-color: #ff4d4f;
            color: white;
          }
        }
      }
    }
  }
</style>
</style>
app/pages/mine/info/index.vue
@@ -1,13 +1,23 @@
<template>
  <view class="container">
    <uni-list>
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'person-filled'}" title="昵称" :rightText="user.nickName" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'person-filled'}" title="用户名" :rightText="user.userName" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'phone-filled'}" title="手机号码" :rightText="user.phonenumber" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'location-filled'}" title="绑定车辆" :rightText="boundVehicle || '未绑定'" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'info-filled'}" title="App版本号" :rightText="version" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'email-filled'}" title="邮箱" :rightText="user.email" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'auth-filled'}" title="岗位" :rightText="postGroup" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'staff-filled'}" title="角色" :rightText="roleGroup" />
      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'calendar-filled'}" title="创建日期" :rightText="user.createTime" />
    </uni-list>
    <!-- 绑定/解绑车辆操作 -->
    <view class="vehicle-actions" v-if="boundVehicle">
      <button class="unbind-btn" @click="unbindVehicle">取消绑定车辆</button>
    </view>
    <view class="vehicle-actions" v-else>
      <button class="bind-btn" @click="goToBindVehicle">绑定车辆</button>
    </view>
  </view>
</template>
@@ -19,7 +29,9 @@
      return {
        user: {},
        roleGroup: "",
        postGroup: ""
        postGroup: "",
        boundVehicle: "", // 模拟绑定的车辆信息
        version: getApp().globalData.config.appInfo.version || "1.0.0" // App版本号
      }
    },
    onLoad() {
@@ -32,6 +44,22 @@
          this.roleGroup = response.roleGroup
          this.postGroup = response.postGroup
        })
      },
      // 跳转到绑定车辆页面
      goToBindVehicle() {
        this.$tab.navigateTo('/pages/bind-vehicle')
      },
      // 取消绑定车辆
      unbindVehicle() {
        this.$modal.confirm('是否取消绑定车辆?').then(() => {
          // 这里可以调用API取消绑定车辆
          this.boundVehicle = ""
          this.$modal.showToast('取消绑定成功')
        }).catch(() => {
          // 用户取消操作
        })
      }
    }
  }
@@ -41,4 +69,26 @@
  page {
    background-color: #ffffff;
  }
</style>
  .vehicle-actions {
    padding: 30rpx;
    text-align: center;
    .bind-btn, .unbind-btn {
      width: 80%;
      height: 80rpx;
      border-radius: 10rpx;
      font-size: 32rpx;
    }
    .bind-btn {
      background-color: #007AFF;
      color: white;
    }
    .unbind-btn {
      background-color: #ff4d4f;
      color: white;
    }
  }
</style>
app/pages/task/create.vue
New file
@@ -0,0 +1,856 @@
<template>
  <scroll-view class="create-task-container" scroll-y="true">
    <!-- 任务类型选择界面 -->
    <view v-if="!selectedTaskCategory" class="task-category-container">
      <view class="header">
        <view class="title">选择任务类型</view>
        <view class="subtitle">请选择您要创建的任务类型</view>
      </view>
      <view class="category-list">
        <view
          class="category-item"
          v-for="(category, index) in taskCategories"
          :key="index"
          @click="selectTaskCategory(category)"
        >
          <view class="icon">
            <uni-icons :type="category.icon" size="30" :color="category.color"></uni-icons>
          </view>
          <view class="info">
            <view class="name">{{ category.name }}</view>
            <view class="desc">{{ category.description }}</view>
          </view>
          <view class="arrow">
            <uni-icons type="arrowright" size="20" color="#999"></uni-icons>
          </view>
        </view>
      </view>
    </view>
    <!-- 任务创建表单 -->
    <view v-else class="task-form-container">
      <view class="form-header">
        <view class="back-btn" @click="backToCategory">
          <uni-icons type="arrowleft" size="20"></uni-icons>
        </view>
        <view class="title">创建{{ selectedTaskCategory.name }}任务</view>
      </view>
      <view class="form-section">
        <!-- 普通任务表单 -->
        <view v-if="selectedTaskCategory.type === 'normal'">
          <view class="form-item">
            <view class="form-label">任务车辆</view>
            <picker mode="selector" :range="vehicles" @change="onVehicleChange">
              <view class="form-input picker-input">
                {{ selectedVehicle || '请选择任务车辆' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">任务类型</view>
            <input
              class="form-input"
              :value="selectedTaskCategory.name"
              disabled
            />
          </view>
          <view class="form-item">
            <view class="form-label">任务描述</view>
            <textarea
              class="form-textarea"
              placeholder="请输入任务描述"
              v-model="taskForm.description"
            />
          </view>
          <view class="form-item">
            <view class="form-label">任务出发地</view>
            <view class="form-input picker-input" @click="selectStartLocation">
              {{ taskForm.startLocation || '请选择任务出发地' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">任务目的地</view>
            <view class="form-input picker-input" @click="selectEndLocation">
              {{ taskForm.endLocation || '请选择任务目的地' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">行驶公里数</view>
            <input
              class="form-input"
              type="digit"
              placeholder="请输入行驶公里数"
              v-model="taskForm.distance"
            />
          </view>
          <view class="form-item">
            <view class="form-label">开始时间</view>
            <!-- 使用uni-datetime-picker组件 -->
            <uni-datetime-picker
              v-model="taskForm.startTime"
              type="datetime"
              :placeholder="'请选择开始时间'"
              class="form-input"
            />
          </view>
          <view class="form-item">
            <view class="form-label">结束时间</view>
            <!-- 使用uni-datetime-picker组件 -->
            <uni-datetime-picker
              v-model="taskForm.endTime"
              type="datetime"
              :placeholder="'请选择结束时间'"
              class="form-input"
            />
          </view>
          <view class="form-item">
            <view class="form-label">执行人</view>
            <input
              class="form-input"
              :value="currentUser.name"
              disabled
            />
          </view>
          <view class="form-item">
            <view class="form-label">备注</view>
            <textarea
              class="form-textarea"
              placeholder="请输入备注信息(最多200字)"
              v-model="taskForm.remark"
              maxlength="200"
            />
          </view>
        </view>
        <!-- 急救转运任务表单 -->
        <view v-else-if="selectedTaskCategory.type === 'emergency'">
          <view class="form-item">
            <view class="form-label">任务车辆</view>
            <picker mode="selector" :range="vehicles" @change="onVehicleChange">
              <view class="form-input picker-input">
                {{ selectedVehicle || '请选择任务车辆' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">执行任务人员</view>
            <view class="staff-list">
              <view class="staff-item">
                <text>{{ currentUser.name }} ({{ currentUser.position }})</text>
                <uni-icons type="checkmarkempty" size="20" color="#007AFF"></uni-icons>
              </view>
              <view
                class="staff-item"
                v-for="(staff, index) in additionalStaff"
                :key="index"
                @click="removeStaff(index)"
              >
                <text>{{ staff.name }} ({{ staff.position }})</text>
                <uni-icons type="closeempty" size="20" color="#ff4d4f"></uni-icons>
              </view>
              <view class="add-staff" @click="addStaff">
                <uni-icons type="plusempty" size="20" color="#007AFF"></uni-icons>
                <text>添加人员</text>
              </view>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">归属机构</view>
            <picker mode="selector" :range="organizations" @change="onOrganizationChange">
              <view class="form-input picker-input">
                {{ selectedOrganization || '请选择归属机构' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">任务类型</view>
            <picker mode="selector" :range="emergencyTaskTypes" @change="onEmergencyTaskTypeChange">
              <view class="form-input picker-input">
                {{ selectedEmergencyTaskType || '请选择任务类型' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">转运时间</view>
            <!-- 使用uni-datetime-picker组件 -->
            <uni-datetime-picker
              v-model="taskForm.transferTime"
              type="datetime"
              :placeholder="'请选择转运时间'"
              class="form-input"
            />
          </view>
          <view class="form-section-title">患者信息</view>
          <view class="form-item">
            <view class="form-label">联系人</view>
            <input
              class="form-input"
              placeholder="请输入联系人"
              v-model="taskForm.patient.contact"
            />
          </view>
          <view class="form-item">
            <view class="form-label">联系电话</view>
            <input
              class="form-input"
              type="number"
              placeholder="请输入联系电话"
              v-model="taskForm.patient.phone"
            />
          </view>
          <view class="form-item">
            <view class="form-label">患者姓名</view>
            <input
              class="form-input"
              placeholder="请输入患者姓名"
              v-model="taskForm.patient.name"
            />
          </view>
          <view class="form-item">
            <view class="form-label">性别</view>
            <view class="radio-group">
              <label class="radio-item">
                <radio value="male" :checked="taskForm.patient.gender === 'male'" @click="taskForm.patient.gender = 'male'" />
                <text>男</text>
              </label>
              <label class="radio-item">
                <radio value="female" :checked="taskForm.patient.gender === 'female'" @click="taskForm.patient.gender = 'female'" />
                <text>女</text>
              </label>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">患者身份证</view>
            <input
              class="form-input"
              type="idcard"
              placeholder="请输入患者身份证号"
              v-model="taskForm.patient.idCard"
            />
          </view>
          <view class="form-item">
            <view class="form-label">病情</view>
            <textarea
              class="form-textarea"
              placeholder="请输入患者病情描述"
              v-model="taskForm.patient.condition"
            />
          </view>
          <view class="form-section-title">转出医院信息</view>
          <view class="form-item">
            <view class="form-label">医院名称</view>
            <input
              class="form-input"
              placeholder="请输入转出医院名称"
              v-model="taskForm.hospitalOut.name"
            />
          </view>
          <view class="form-item">
            <view class="form-label">科室</view>
            <input
              class="form-input"
              placeholder="请输入科室"
              v-model="taskForm.hospitalOut.department"
            />
          </view>
          <view class="form-item">
            <view class="form-label">床号</view>
            <input
              class="form-input"
              placeholder="请输入床号"
              v-model="taskForm.hospitalOut.bedNumber"
            />
          </view>
          <view class="form-item">
            <view class="form-label">转出地址</view>
            <view class="form-input picker-input" @click="selectHospitalOutAddress">
              {{ taskForm.hospitalOut.address || '请选择转出地址' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </view>
          <view class="form-section-title">转入医院信息</view>
          <view class="form-item">
            <view class="form-label">医院名称</view>
            <input
              class="form-input"
              placeholder="请输入转入医院名称"
              v-model="taskForm.hospitalIn.name"
            />
          </view>
          <view class="form-item">
            <view class="form-label">科室</view>
            <input
              class="form-input"
              placeholder="请输入科室"
              v-model="taskForm.hospitalIn.department"
            />
          </view>
          <view class="form-item">
            <view class="form-label">床号</view>
            <input
              class="form-input"
              placeholder="请输入床号"
              v-model="taskForm.hospitalIn.bedNumber"
            />
          </view>
          <view class="form-item">
            <view class="form-label">转入地址</view>
            <view class="form-input picker-input" @click="selectHospitalInAddress">
              {{ taskForm.hospitalIn.address || '请选择转入地址' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">转运公里数</view>
            <input
              class="form-input"
              type="digit"
              placeholder="请输入转运公里数"
              v-model="taskForm.transferDistance"
            />
          </view>
          <view class="form-item">
            <view class="form-label">成交价</view>
            <input
              class="form-input"
              type="digit"
              placeholder="请输入成交价"
              v-model="taskForm.price"
            />
          </view>
        </view>
        <!-- 福祉车任务表单 -->
        <view v-else-if="selectedTaskCategory.type === 'welfare'">
          <view class="form-item">
            <view class="form-label">任务车辆</view>
            <picker mode="selector" :range="vehicles" @change="onVehicleChange">
              <view class="form-input picker-input">
                {{ selectedVehicle || '请选择任务车辆' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">执行任务人员</view>
            <view class="staff-list">
              <view class="staff-item">
                <text>{{ currentUser.name }} ({{ currentUser.position }})</text>
                <uni-icons type="checkmarkempty" size="20" color="#007AFF"></uni-icons>
              </view>
              <view
                class="staff-item"
                v-for="(staff, index) in additionalStaff"
                :key="index"
                @click="removeStaff(index)"
              >
                <text>{{ staff.name }} ({{ staff.position }})</text>
                <uni-icons type="closeempty" size="20" color="#ff4d4f"></uni-icons>
              </view>
              <view class="add-staff" @click="addStaff">
                <uni-icons type="plusempty" size="20" color="#007AFF"></uni-icons>
                <text>添加人员</text>
              </view>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">归属机构</view>
            <picker mode="selector" :range="organizations" @change="onOrganizationChange">
              <view class="form-input picker-input">
                {{ selectedOrganization || '请选择归属机构' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">服务时间</view>
            <!-- 使用uni-datetime-picker组件 -->
            <uni-datetime-picker
              v-model="taskForm.serviceTime"
              type="datetime"
              :placeholder="'请选择服务时间'"
              class="form-input"
            />
          </view>
          <view class="form-section-title">乘客信息</view>
          <view class="form-item">
            <view class="form-label">联系人</view>
            <input
              class="form-input"
              placeholder="请输入联系人"
              v-model="taskForm.passenger.contact"
            />
          </view>
          <view class="form-item">
            <view class="form-label">联系电话</view>
            <input
              class="form-input"
              type="number"
              placeholder="请输入联系电话"
              v-model="taskForm.passenger.phone"
            />
          </view>
          <view class="form-item">
            <view class="form-label">出发地址</view>
            <view class="form-input picker-input" @click="selectStartAddress">
              {{ taskForm.startAddress || '请选择出发地址' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">目的地址</view>
            <view class="form-input picker-input" @click="selectEndAddress">
              {{ taskForm.endAddress || '请选择目的地址' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">公里数</view>
            <input
              class="form-input"
              type="digit"
              placeholder="请输入公里数"
              v-model="taskForm.distance"
            />
          </view>
          <view class="form-item">
            <view class="form-label">成交价</view>
            <input
              class="form-input"
              type="digit"
              placeholder="请输入成交价"
              v-model="taskForm.price"
            />
          </view>
        </view>
        <view class="form-actions">
          <button class="submit-btn" @click="submitTask">保存</button>
        </view>
      </view>
    </view>
  </scroll-view>
</template>
<script>
  import { mapState } from 'vuex'
  import uniDatetimePicker from '@/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue'
  export default {
    components: {
      uniDatetimePicker
    },
    data() {
      return {
        selectedTaskCategory: null,
        selectedVehicle: '',
        selectedOrganization: '',
        selectedEmergencyTaskType: '',
        taskForm: {
          description: '',
          startLocation: '',
          endLocation: '',
          distance: '',
          startTime: '',
          endTime: '',
          remark: '',
          transferTime: '',
          patient: {
            contact: '',
            phone: '',
            name: '',
            gender: 'male',
            idCard: '',
            condition: ''
          },
          hospitalOut: {
            name: '',
            department: '',
            bedNumber: '',
            address: ''
          },
          hospitalIn: {
            name: '',
            department: '',
            bedNumber: '',
            address: ''
          },
          transferDistance: '',
          price: '',
          serviceTime: '',
          passenger: {
            contact: '',
            phone: ''
          },
          startAddress: '',
          endAddress: ''
        },
        taskCategories: [
          {
            type: 'normal',
            name: '维修保养',
            icon: 'repair',
            color: '#007AFF',
            description: '设备维修、保养等日常任务'
          },
          {
            type: 'normal',
            name: '加油',
            icon: 'fuel',
            color: '#1AAD19',
            description: '车辆加油等任务'
          },
          {
            type: 'emergency',
            name: '急救转运',
            icon: 'hospital',
            color: '#E54D42',
            description: '紧急医疗转运任务'
          },
          {
            type: 'welfare',
            name: '福祉车',
            icon: 'car',
            color: '#F37B1D',
            description: '老年人、残疾人等特殊群体用车服务'
          }
        ],
        vehicles: ['粤A12345', '粤B67890', '粤C11111', '粤D22222', '粤E33333'],
        organizations: ['广州分公司', '深圳分公司', '珠海分公司', '佛山分公司'],
        emergencyTaskTypes: ['急救转运', '航空转运'],
        additionalStaff: []
      }
    },
    computed: {
      ...mapState({
        currentUser: state => ({
          name: state.user.name || '张三',
          position: '司机'
        })
      })
    },
    methods: {
      selectTaskCategory(category) {
        this.selectedTaskCategory = category
      },
      backToCategory() {
        this.selectedTaskCategory = null
      },
      onVehicleChange(e) {
        this.selectedVehicle = this.vehicles[e.detail.value]
      },
      onOrganizationChange(e) {
        this.selectedOrganization = this.organizations[e.detail.value]
      },
      onEmergencyTaskTypeChange(e) {
        this.selectedEmergencyTaskType = this.emergencyTaskTypes[e.detail.value]
      },
      selectStartLocation() {
        this.$modal.showToast('选择出发地功能开发中')
      },
      selectEndLocation() {
        this.$modal.showToast('选择目的地功能开发中')
      },
      selectHospitalOutAddress() {
        this.$modal.showToast('选择转出医院地址功能开发中')
      },
      selectHospitalInAddress() {
        this.$modal.showToast('选择转入医院地址功能开发中')
      },
      selectStartAddress() {
        this.$modal.showToast('选择出发地址功能开发中')
      },
      selectEndAddress() {
        this.$modal.showToast('选择目的地址功能开发中')
      },
      addStaff() {
        this.$modal.showToast('添加人员功能开发中')
      },
      removeStaff(index) {
        this.additionalStaff.splice(index, 1)
      },
      submitTask() {
        this.$modal.confirm('确定要保存任务吗?').then(() => {
          this.$modal.showToast('任务保存成功')
          // 这里可以调用API保存任务
          // 保存成功后跳转到任务列表
          this.$tab.navigateTo('/pages/task/index')
        }).catch(() => {
          // 取消操作
        })
      }
    }
  }
</script>
<style lang="scss">
  .create-task-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    min-height: 100vh;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
    .task-category-container {
      .header {
        text-align: center;
        padding: 40rpx 0;
        .title {
          font-size: 40rpx;
          font-weight: bold;
          color: #333;
          margin-bottom: 20rpx;
        }
        .subtitle {
          font-size: 28rpx;
          color: #666;
        }
      }
      .category-list {
        .category-item {
          display: flex;
          align-items: center;
          background-color: white;
          border-radius: 15rpx;
          padding: 30rpx;
          margin-bottom: 20rpx;
          box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
          .icon {
            margin-right: 20rpx;
          }
          .info {
            flex: 1;
            .name {
              font-size: 32rpx;
              font-weight: bold;
              margin-bottom: 10rpx;
            }
            .desc {
              font-size: 26rpx;
              color: #666;
            }
          }
          .arrow {
            margin-left: 20rpx;
          }
        }
      }
    }
    .task-form-container {
      .form-header {
        display: flex;
        align-items: center;
        padding: 20rpx 0;
        margin-bottom: 30rpx;
        .back-btn {
          width: 60rpx;
          height: 60rpx;
          border-radius: 50%;
          background-color: #f0f0f0;
          display: flex;
          align-items: center;
          justify-content: center;
          margin-right: 20rpx;
        }
        .title {
          font-size: 36rpx;
          font-weight: bold;
          color: #333;
        }
      }
      .form-section {
        background-color: white;
        border-radius: 15rpx;
        padding: 30rpx;
        box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
        .form-section-title {
          font-size: 32rpx;
          font-weight: bold;
          margin: 40rpx 0 20rpx 0;
          padding-bottom: 10rpx;
          border-bottom: 1rpx solid #f0f0f0;
        }
        .form-item {
          margin-bottom: 40rpx;
          &:last-child {
            margin-bottom: 0;
          }
          .form-label {
            font-size: 28rpx;
            margin-bottom: 15rpx;
            color: #333;
          }
          .form-input {
            height: 70rpx;
            padding: 0 20rpx;
            border: 1rpx solid #eee;
            border-radius: 10rpx;
            font-size: 28rpx;
            &.picker-input {
              display: flex;
              align-items: center;
              justify-content: space-between;
            }
            &[disabled] {
              background-color: #f5f5f5;
              color: #999;
            }
          }
          .form-textarea {
            width: 100%;
            min-height: 150rpx;
            padding: 20rpx;
            border: 1rpx solid #eee;
            border-radius: 10rpx;
            font-size: 28rpx;
            resize: none;
          }
          .radio-group {
            display: flex;
            .radio-item {
              display: flex;
              align-items: center;
              margin-right: 30rpx;
              radio {
                margin-right: 10rpx;
              }
            }
          }
          .staff-list {
            .staff-item {
              display: flex;
              justify-content: space-between;
              align-items: center;
              padding: 20rpx;
              background-color: #f9f9f9;
              border-radius: 10rpx;
              margin-bottom: 20rpx;
            }
            .add-staff {
              display: flex;
              align-items: center;
              justify-content: center;
              padding: 20rpx;
              border: 1rpx dashed #007AFF;
              border-radius: 10rpx;
              color: #007AFF;
            }
          }
        }
        .form-actions {
          margin-top: 50rpx;
          text-align: center;
          .submit-btn {
            width: 80%;
            height: 80rpx;
            background-color: #007AFF;
            color: white;
            border-radius: 10rpx;
            font-size: 32rpx;
          }
        }
      }
    }
  }
</style>
app/pages/task/detail.vue
New file
@@ -0,0 +1,600 @@
<template>
  <view class="task-detail-container">
    <view class="task-header">
      <text class="task-title">{{ task.title }}</text>
      <view class="task-status" :class="'status-' + task.status">
        {{ getStatusText(task.status) }}
      </view>
    </view>
    <!-- 普通任务详情 -->
    <view class="task-info-section" v-if="isNormalTask">
      <view class="info-item">
        <view class="label">任务编号:</view>
        <view class="value">{{ task.taskNo }}</view>
      </view>
      <view class="info-item">
        <view class="label">任务类型:</view>
        <view class="value">{{ getTaskTypeText(task.type) }}</view>
      </view>
      <view class="info-item">
        <view class="label">车辆信息:</view>
        <view class="value">{{ task.vehicle }}</view>
      </view>
      <view class="info-item">
        <view class="label">出发地:</view>
        <view class="value">{{ task.startLocation }}</view>
      </view>
      <view class="info-item">
        <view class="label">目的地:</view>
        <view class="value">{{ task.endLocation }}</view>
      </view>
      <view class="info-item">
        <view class="label">出发时间:</view>
        <view class="value">{{ task.startTime }}</view>
      </view>
      <view class="info-item">
        <view class="label">执行人员:</view>
        <view class="value">{{ task.assignee }}</view>
      </view>
      <view class="info-item">
        <view class="label">任务描述:</view>
        <view class="value">{{ task.description || '无描述信息' }}</view>
      </view>
      <view class="info-item">
        <view class="label">行驶公里数:</view>
        <view class="value">{{ task.distance || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">结束时间:</view>
        <view class="value">{{ task.endTime || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">备注:</view>
        <view class="value">{{ task.remark || '无备注' }}</view>
      </view>
    </view>
    <!-- 急救转运任务详情 -->
    <view class="task-info-section" v-else-if="task.type === 'emergency'">
      <view class="info-item">
        <view class="label">任务编号:</view>
        <view class="value">{{ task.taskNo }}</view>
      </view>
      <view class="info-item">
        <view class="label">任务类型:</view>
        <view class="value">{{ getTaskTypeText(task.type) }}</view>
      </view>
      <view class="info-item">
        <view class="label">车辆信息:</view>
        <view class="value">{{ task.vehicle }}</view>
      </view>
      <view class="info-item">
        <view class="label">执行人员:</view>
        <view class="value">{{ task.assignee }}</view>
      </view>
      <view class="info-item">
        <view class="label">归属机构:</view>
        <view class="value">{{ task.organization || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">任务类型:</view>
        <view class="value">{{ task.emergencyTaskType || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">转运时间:</view>
        <view class="value">{{ task.transferTime || '未填写' }}</view>
      </view>
      <view class="section-title">患者信息</view>
      <view class="info-item">
        <view class="label">联系人:</view>
        <view class="value">{{ task.patient.contact || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">联系电话:</view>
        <view class="value">{{ task.patient.phone || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">患者姓名:</view>
        <view class="value">{{ task.patient.name || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">性别:</view>
        <view class="value">{{ task.patient.gender === 'male' ? '男' : '女' }}</view>
      </view>
      <view class="info-item">
        <view class="label">身份证:</view>
        <view class="value">{{ task.patient.idCard || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">病情:</view>
        <view class="value">{{ task.patient.condition || '未填写' }}</view>
      </view>
      <view class="section-title">转出医院信息</view>
      <view class="info-item">
        <view class="label">医院名称:</view>
        <view class="value">{{ task.hospitalOut.name || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">科室:</view>
        <view class="value">{{ task.hospitalOut.department || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">床号:</view>
        <view class="value">{{ task.hospitalOut.bedNumber || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">地址:</view>
        <view class="value">{{ task.hospitalOut.address || '未填写' }}</view>
      </view>
      <view class="section-title">转入医院信息</view>
      <view class="info-item">
        <view class="label">医院名称:</view>
        <view class="value">{{ task.hospitalIn.name || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">科室:</view>
        <view class="value">{{ task.hospitalIn.department || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">床号:</view>
        <view class="value">{{ task.hospitalIn.bedNumber || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">地址:</view>
        <view class="value">{{ task.hospitalIn.address || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">转运公里数:</view>
        <view class="value">{{ task.transferDistance || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">成交价:</view>
        <view class="value">¥{{ task.price || '未填写' }}</view>
      </view>
    </view>
    <!-- 福祉车任务详情 -->
    <view class="task-info-section" v-else-if="task.type === 'welfare'">
      <view class="info-item">
        <view class="label">任务编号:</view>
        <view class="value">{{ task.taskNo }}</view>
      </view>
      <view class="info-item">
        <view class="label">任务类型:</view>
        <view class="value">{{ getTaskTypeText(task.type) }}</view>
      </view>
      <view class="info-item">
        <view class="label">车辆信息:</view>
        <view class="value">{{ task.vehicle }}</view>
      </view>
      <view class="info-item">
        <view class="label">执行人员:</view>
        <view class="value">{{ task.assignee }}</view>
      </view>
      <view class="info-item">
        <view class="label">归属机构:</view>
        <view class="value">{{ task.organization || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">服务时间:</view>
        <view class="value">{{ task.serviceTime || '未填写' }}</view>
      </view>
      <view class="section-title">乘客信息</view>
      <view class="info-item">
        <view class="label">联系人:</view>
        <view class="value">{{ task.passenger.contact || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">联系电话:</view>
        <view class="value">{{ task.passenger.phone || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">出发地址:</view>
        <view class="value">{{ task.startAddress || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">目的地址:</view>
        <view class="value">{{ task.endAddress || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">公里数:</view>
        <view class="value">{{ task.distance || '未填写' }}</view>
      </view>
      <view class="info-item">
        <view class="label">成交价:</view>
        <view class="value">¥{{ task.price || '未填写' }}</view>
      </view>
    </view>
    <!-- 任务操作按钮 (任务未完成时显示) -->
    <view class="task-actions" v-if="task.status !== 'completed'">
      <button
        class="action-btn"
        :class="{ disabled: isActionDisabled('depart') }"
        @click="handleTaskAction('depart')"
      >
        出发
      </button>
      <button
        class="action-btn"
        :class="{ disabled: isActionDisabled('arrive') }"
        @click="handleTaskAction('arrive')"
      >
        已到达
      </button>
      <button
        class="action-btn"
        :class="{ disabled: isActionDisabled('return') }"
        @click="handleTaskAction('return')"
      >
        返程
      </button>
      <button
        class="action-btn"
        :class="{ disabled: isActionDisabled('settle') }"
        @click="handleTaskAction('settle')"
      >
        结算
      </button>
      <button
        class="action-btn primary"
        :class="{ disabled: isActionDisabled('complete') }"
        @click="handleTaskAction('complete')"
      >
        已完成
      </button>
    </view>
    <view class="action-section">
      <button class="back-btn" @click="goBack">返回</button>
    </view>
  </view>
</template>
<script>
  export default {
    data() {
      return {
        task: {
          id: 1,
          title: '紧急维修任务',
          type: 'maintenance',
          startLocation: '广州市天河区XX路123号',
          endLocation: '广州市白云区YY路456号',
          startTime: '2023-05-15 15:00',
          assignee: '张三',
          status: 'pending',
          vehicle: '粤A12345',
          taskNo: 'RW20230515001',
          description: '设备故障,需要紧急维修',
          // 普通任务字段
          distance: '25',
          endTime: '2023-05-15 17:00',
          remark: '维修完成后需要测试',
          // 急救转运任务字段
          organization: '广州急救中心',
          emergencyTaskType: '急救转运',
          transferTime: '2023-05-15 16:00',
          patient: {
            contact: '李四',
            phone: '13800138000',
            name: '王五',
            gender: 'male',
            idCard: '440100198001011234',
            condition: '突发心脏病'
          },
          hospitalOut: {
            name: '广州市第一人民医院',
            department: '心内科',
            bedNumber: '101',
            address: '广州市越秀区医院路1号'
          },
          hospitalIn: {
            name: '广东省人民医院',
            department: '心内科',
            bedNumber: '205',
            address: '广州市天河区医院路2号'
          },
          transferDistance: '15',
          price: '800',
          // 福祉车任务字段
          serviceTime: '2023-05-16 09:00',
          passenger: {
            contact: '赵六',
            phone: '13900139000'
          },
          startAddress: '广州市荔湾区社区路10号',
          endAddress: '广州市天河区养老院路20号'
        }
      }
    },
    computed: {
      // 判断是否为普通任务
      isNormalTask() {
        return this.task.type === 'maintenance' || this.task.type === 'refuel' || this.task.type === 'inspection';
      }
    },
    onLoad(options) {
      // 实际项目中这里会通过API获取任务详情
      // const taskId = options.id;
      // this.getTaskDetail(taskId);
    },
    methods: {
      goBack() {
        this.$tab.navigateBack();
      },
      getStatusText(status) {
        const statusMap = {
          'pending': '待处理',
          'processing': '处理中',
          'completed': '已完成'
        }
        return statusMap[status] || '未知'
      },
      getTaskTypeText(type) {
        const typeMap = {
          'maintenance': '维修保养',
          'refuel': '加油',
          'inspection': '巡检',
          'emergency': '急救转运',
          'welfare': '福祉车'
        }
        return typeMap[type] || '未知类型'
      },
      // 判断操作按钮是否禁用
      isActionDisabled(action) {
        // 根据任务状态和操作类型判断是否禁用
        switch (action) {
          case 'depart':
            return this.task.status !== 'pending';
          case 'arrive':
            return this.task.status !== 'processing';
          case 'return':
            return this.task.status !== 'processing';
          case 'settle':
            return this.task.status !== 'processing';
          case 'complete':
            return this.task.status !== 'processing';
          default:
            return false;
        }
      },
      // 处理任务操作
      handleTaskAction(action) {
        if (this.isActionDisabled(action)) {
          return;
        }
        switch (action) {
          case 'depart':
            // 出发操作,根据任务类型显示不同的确认信息
            let departMessage = '确定要标记为已出发吗?';
            if (this.task.type !== 'maintenance' && this.task.type !== 'refuel' && this.task.type !== 'inspection') {
              departMessage = '发出去目的地,确认?';
            }
            this.$modal.confirm(departMessage).then(() => {
              this.task.status = 'processing';
              this.$modal.showToast('已出发');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'arrive':
            // 已到达操作
            this.$modal.confirm('已经到达目的地,确认?').then(() => {
              this.$modal.showToast('已到达');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'return':
            // 返程操作
            this.$modal.confirm('现在已经返程中,确认?').then(() => {
              this.$modal.showToast('返程中');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'settle':
            // 结算操作,跳转到结算页面
            this.$tab.navigateTo(`/pages/task/settlement?id=${this.task.id}`);
            break;
          case 'complete':
            // 已完成操作
            this.$modal.confirm('任务是否已经全部完成,确认?').then(() => {
              this.task.status = 'completed';
              this.$modal.showToast('任务已完成');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
        }
      },
      // 模拟获取任务详情
      getTaskDetail(taskId) {
        // 这里应该调用API获取任务详情
        // 暂时使用模拟数据
      }
    }
  }
</script>
<style lang="scss">
  .task-detail-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    min-height: 100vh;
  }
  .task-header {
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    display: flex;
    justify-content: space-between;
    align-items: center;
    .task-title {
      font-size: 36rpx;
      font-weight: bold;
      color: #333;
    }
    .task-status {
      padding: 10rpx 20rpx;
      border-radius: 10rpx;
      font-size: 24rpx;
      font-weight: bold;
      &.status-pending {
        background-color: #fff3cd;
        color: #856404;
      }
      &.status-processing {
        background-color: #cce5ff;
        color: #004085;
      }
      &.status-completed {
        background-color: #d4edda;
        color: #155724;
      }
    }
  }
  .task-info-section {
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    .section-title {
      font-size: 32rpx;
      font-weight: bold;
      margin: 30rpx 0 20rpx 0;
      padding-bottom: 10rpx;
      border-bottom: 1rpx solid #f0f0f0;
      color: #333;
    }
    .info-item {
      display: flex;
      margin-bottom: 20rpx;
      padding-bottom: 20rpx;
      border-bottom: 1rpx solid #f0f0f0;
      &:last-child {
        margin-bottom: 0;
        padding-bottom: 0;
        border-bottom: none;
      }
      .label {
        font-size: 28rpx;
        color: #666;
        margin-right: 20rpx;
        white-space: nowrap;
        width: 150rpx;
      }
      .value {
        font-size: 28rpx;
        flex: 1;
        word-break: break-all;
      }
    }
  }
  .task-actions {
    display: flex;
    flex-wrap: wrap;
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    .action-btn {
      flex: 1;
      min-width: 30%;
      height: 70rpx;
      border-radius: 10rpx;
      font-size: 26rpx;
      margin: 10rpx 5rpx;
      background-color: #f0f0f0;
      color: #333;
      &.primary {
        background-color: #007AFF;
        color: white;
      }
      &.disabled {
        opacity: 0.5;
      }
    }
  }
  .action-section {
    margin-top: 40rpx;
    text-align: center;
    .back-btn {
      width: 80%;
      height: 80rpx;
      background-color: #007AFF;
      color: white;
      border-radius: 10rpx;
      font-size: 32rpx;
    }
  }
</style>
app/pages/task/index.vue
New file
@@ -0,0 +1,798 @@
<template>
  <view class="task-container">
    <!-- 任务列表头部 -->
    <view class="task-list-section">
      <view class="task-header">
        <view class="header-title">任务列表</view>
        <view class="header-actions">
          <button class="search-toggle-btn" @click="toggleSearch">
            <uni-icons :type="showSearch ? 'close' : 'search'" size="20"></uni-icons>
          </button>
          <button class="refresh-btn" @click="refreshList">
            <uni-icons type="refresh" size="20"></uni-icons>
          </button>
        </view>
      </view>
      <!-- 查询条件区域 -->
      <view class="search-section" v-show="showSearch">
        <view class="search-form">
          <view class="form-item">
            <view class="form-label">任务状态</view>
            <picker mode="selector" :range="statusOptions" @change="onStatusChange">
              <view class="form-input picker-input">
                {{ selectedStatusText || '全部状态' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">任务时间</view>
            <view class="date-range">
              <!-- 使用uni-datetime-picker组件 -->
              <uni-datetime-picker
                v-model="startDate"
                type="date"
                :placeholder="'开始时间'"
                class="date-input"
              />
              <text class="divider">至</text>
              <!-- 使用uni-datetime-picker组件 -->
              <uni-datetime-picker
                v-model="endDate"
                type="date"
                :placeholder="'结束时间'"
                class="date-input"
              />
            </view>
          </view>
          <view class="form-item">
            <view class="form-label">车牌号</view>
            <input
              class="form-input"
              placeholder="请输入车牌号"
              v-model="searchForm.vehicle"
            />
          </view>
          <view class="form-item">
            <view class="form-label">任务编号</view>
            <input
              class="form-input"
              placeholder="请输入任务编号"
              v-model="searchForm.taskNo"
            />
          </view>
          <view class="form-actions">
            <button class="search-btn" @click="handleSearch">查询</button>
            <button class="reset-btn" @click="resetSearch">重置</button>
          </view>
        </view>
      </view>
      <view class="task-filter">
        <scroll-view class="filter-tabs" scroll-x="true">
          <view
            class="filter-item"
            :class="{ active: currentFilter === 'all' }"
            @click="changeFilter('all')"
          >
            全部
          </view>
          <view
            class="filter-item"
            :class="{ active: currentFilter === 'pending' }"
            @click="changeFilter('pending')"
          >
            待处理
          </view>
          <view
            class="filter-item"
            :class="{ active: currentFilter === 'processing' }"
            @click="changeFilter('processing')"
          >
            处理中
          </view>
          <view
            class="filter-item"
            :class="{ active: currentFilter === 'completed' }"
            @click="changeFilter('completed')"
          >
            已完成
          </view>
        </scroll-view>
      </view>
      <scroll-view class="task-list-scroll" scroll-y="true">
        <view class="task-list">
          <view class="task-item" v-for="task in filteredTaskList" :key="task.id">
            <view class="task-main" @click="viewTaskDetail(task)">
              <view class="task-title">{{ getTaskTypeText(task.type) }} - {{ task.vehicle }}</view>
              <view class="task-info">
                <view class="info-row">
                  <view class="info-item">
                    <view class="label">任务编号:</view>
                    <view class="value">{{ task.taskNo }}</view>
                  </view>
                  <view class="info-item">
                    <view class="label">任务状态:</view>
                    <view class="value">{{ getStatusText(task.status) }}</view>
                  </view>
                </view>
                <view class="info-row">
                  <view class="info-item">
                    <view class="label">出发地:</view>
                    <view class="value">{{ task.startLocation }}</view>
                  </view>
                  <view class="info-item">
                    <view class="label">目的地:</view>
                    <view class="value">{{ task.endLocation }}</view>
                  </view>
                </view>
                <view class="info-row">
                  <view class="info-item">
                    <view class="label">出发时间:</view>
                    <view class="value">{{ task.startTime }}</view>
                  </view>
                  <view class="info-item">
                    <view class="label">执行人员:</view>
                    <view class="value">{{ task.assignee }}</view>
                  </view>
                </view>
              </view>
            </view>
            <!-- 操作按钮 -->
            <view class="task-actions">
              <button
                class="action-btn"
                :class="{ disabled: isActionDisabled(task, 'depart') }"
                @click="handleTaskAction(task, 'depart')"
                v-if="task.status !== 'completed'"
              >
                出发
              </button>
              <button
                class="action-btn"
                :class="{ disabled: isActionDisabled(task, 'arrive') }"
                @click="handleTaskAction(task, 'arrive')"
                v-if="task.status !== 'completed'"
              >
                已到达
              </button>
              <button
                class="action-btn"
                :class="{ disabled: isActionDisabled(task, 'return') }"
                @click="handleTaskAction(task, 'return')"
                v-if="task.status !== 'completed'"
              >
                返程
              </button>
              <button
                class="action-btn"
                :class="{ disabled: isActionDisabled(task, 'settle') }"
                @click="handleTaskAction(task, 'settle')"
                v-if="task.status !== 'completed'"
              >
                结算
              </button>
              <button
                class="action-btn primary"
                :class="{ disabled: isActionDisabled(task, 'complete') }"
                @click="handleTaskAction(task, 'complete')"
                v-if="task.status !== 'completed'"
              >
                已完成
              </button>
            </view>
          </view>
          <view class="no-data" v-if="filteredTaskList.length === 0">
            <uni-icons type="info" size="40" color="#ccc"></uni-icons>
            <text>暂无任务数据</text>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>
<script>
  import uniDatetimePicker from '@/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue'
  export default {
    components: {
      uniDatetimePicker
    },
    data() {
      return {
        // 控制查询界面显示/隐藏
        showSearch: false,
        // 查询条件
        searchForm: {
          vehicle: '',
          taskNo: ''
        },
        statusOptions: ['全部状态', '待处理', '处理中', '已完成'],
        statusValues: ['', 'pending', 'processing', 'completed'],
        selectedStatus: '',
        selectedStatusText: '',
        startDate: '',
        endDate: '',
        currentFilter: 'all',
        // 任务列表
        taskList: [
          {
            id: 1,
            title: '紧急维修任务',
            type: 'maintenance', // 维修保养
            startLocation: '广州市天河区XX路123号',
            endLocation: '广州市白云区YY路456号',
            startTime: '2023-05-15 15:00',
            assignee: '张三',
            status: 'pending',
            vehicle: '粤A12345',
            taskNo: 'RW20230515001'
          },
          {
            id: 2,
            title: '定期保养任务',
            type: 'maintenance', // 维修保养
            startLocation: '深圳市南山区XX路789号',
            endLocation: '深圳市福田区YY路999号',
            startTime: '2023-05-14 10:00',
            assignee: '李四',
            status: 'processing',
            vehicle: '粤B67890',
            taskNo: 'RW20230514002'
          },
          {
            id: 3,
            title: '设备巡检任务',
            type: 'inspection', // 巡检任务
            startLocation: '珠海市香洲区XX路321号',
            endLocation: '珠海市金湾区YY路654号',
            startTime: '2023-05-13 17:00',
            assignee: '王五',
            status: 'completed',
            vehicle: '粤C11111',
            taskNo: 'RW20230513003'
          },
          {
            id: 4,
            title: '加油任务',
            type: 'refuel', // 加油
            startLocation: '佛山市禅城区AA路555号',
            endLocation: '佛山市南海区BB路888号',
            startTime: '2023-05-12 09:00',
            assignee: '赵六',
            status: 'completed',
            vehicle: '粤D22222',
            taskNo: 'RW20230512004'
          },
          {
            id: 5,
            title: '急救转运任务',
            type: 'emergency', // 急救转运
            startLocation: '广州市越秀区医院路1号',
            endLocation: '广州市海珠区医院路2号',
            startTime: '2023-05-16 14:00',
            assignee: '张医生,李护士',
            status: 'pending',
            vehicle: '粤E33333',
            taskNo: 'RW20230516005'
          },
          {
            id: 6,
            title: '福祉车任务',
            type: 'welfare', // 福祉车
            startLocation: '广州市荔湾区社区路10号',
            endLocation: '广州市天河区养老院路20号',
            startTime: '2023-05-17 08:00',
            assignee: '王司机',
            status: 'processing',
            vehicle: '粤F44444',
            taskNo: 'RW20230517006'
          }
        ]
      }
    },
    computed: {
      filteredTaskList() {
        let filtered = this.taskList;
        // 应用筛选器
        if (this.currentFilter !== 'all') {
          filtered = filtered.filter(task => {
            if (this.currentFilter === 'pending') return task.status === 'pending';
            if (this.currentFilter === 'processing') return task.status === 'processing';
            if (this.currentFilter === 'completed') return task.status === 'completed';
            return true;
          });
        }
        // 应用状态筛选
        if (this.selectedStatus) {
          filtered = filtered.filter(task => task.status === this.selectedStatus);
        }
        // 应用车牌号筛选
        if (this.searchForm.vehicle) {
          filtered = filtered.filter(task =>
            task.vehicle.includes(this.searchForm.vehicle)
          );
        }
        // 应用任务编号筛选
        if (this.searchForm.taskNo) {
          filtered = filtered.filter(task =>
            task.taskNo.includes(this.searchForm.taskNo)
          );
        }
        // 应用时间范围筛选
        if (this.startDate) {
          filtered = filtered.filter(task =>
            task.startTime >= this.startDate
          );
        }
        if (this.endDate) {
          // 结束日期加一天,以便包含当天的数据
          const end = new Date(this.endDate);
          end.setDate(end.getDate() + 1);
          const endDateStr = end.toISOString().split('T')[0];
          filtered = filtered.filter(task =>
            task.startTime < endDateStr
          );
        }
        return filtered;
      }
    },
    methods: {
      // 切换查询界面显示/隐藏
      toggleSearch() {
        this.showSearch = !this.showSearch;
      },
      // 状态选择
      onStatusChange(e) {
        this.selectedStatus = this.statusValues[e.detail.value];
        this.selectedStatusText = this.statusOptions[e.detail.value];
      },
      // 查询
      handleSearch() {
        this.$modal.showToast('查询成功');
        console.log('查询条件:', {
          status: this.selectedStatus,
          startDate: this.startDate,
          endDate: this.endDate,
          vehicle: this.searchForm.vehicle,
          taskNo: this.searchForm.taskNo
        });
        // 查询完成后隐藏查询界面
        this.showSearch = false;
        // 这里可以调用API进行查询
      },
      // 重置查询条件
      resetSearch() {
        this.selectedStatus = '';
        this.selectedStatusText = '';
        this.startDate = '';
        this.endDate = '';
        this.searchForm.vehicle = '';
        this.searchForm.taskNo = '';
      },
      // 刷新列表
      refreshList() {
        this.$modal.showToast('列表已刷新');
        // 这里可以重新加载数据
      },
      // 筛选
      changeFilter(filter) {
        this.currentFilter = filter;
      },
      // 查看任务详情
      viewTaskDetail(task) {
        // 跳转到任务详情页面
        this.$tab.navigateTo(`/pages/task/detail?id=${task.id}`);
      },
      // 判断操作按钮是否禁用
      isActionDisabled(task, action) {
        // 根据任务状态和操作类型判断是否禁用
        switch (action) {
          case 'depart':
            return task.status !== 'pending';
          case 'arrive':
            return task.status !== 'processing';
          case 'return':
            return task.status !== 'processing';
          case 'settle':
            return task.status !== 'processing';
          case 'complete':
            return task.status !== 'processing';
          default:
            return false;
        }
      },
      // 处理任务操作
      handleTaskAction(task, action) {
        if (this.isActionDisabled(task, action)) {
          return;
        }
        switch (action) {
          case 'depart':
            // 出发操作,根据任务类型显示不同的确认信息
            let departMessage = '确定要标记为已出发吗?';
            if (task.type !== 'maintenance' && task.type !== 'refuel' && task.type !== 'inspection') {
              departMessage = '发出去目的地,确认?';
            }
            this.$modal.confirm(departMessage).then(() => {
              task.status = 'processing';
              this.$modal.showToast('已出发');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'arrive':
            // 已到达操作
            this.$modal.confirm('已经到达目的地,确认?').then(() => {
              this.$modal.showToast('已到达');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'return':
            // 返程操作
            this.$modal.confirm('现在已经返程中,确认?').then(() => {
              this.$modal.showToast('返程中');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
          case 'settle':
            // 结算操作,跳转到结算页面
            this.$tab.navigateTo(`/pages/task/settlement?id=${task.id}`);
            break;
          case 'complete':
            // 已完成操作
            this.$modal.confirm('任务是否已经全部完成,确认?').then(() => {
              task.status = 'completed';
              this.$modal.showToast('任务已完成');
              // 这里可以调用API更新任务状态
            }).catch(() => {});
            break;
        }
      },
      getStatusText(status) {
        const statusMap = {
          'pending': '待处理',
          'processing': '处理中',
          'completed': '已完成'
        }
        return statusMap[status] || '未知'
      },
      getTaskTypeText(type) {
        const typeMap = {
          'maintenance': '维修保养',
          'refuel': '加油',
          'inspection': '巡检',
          'emergency': '急救转运',
          'welfare': '福祉车'
        }
        return typeMap[type] || '未知类型'
      }
    }
  }
</script>
<style lang="scss">
  .task-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    height: 100vh;
    display: flex;
    flex-direction: column;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
  }
  // 任务列表区域
  .task-list-section {
    flex: 1;
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    display: flex;
    flex-direction: column;
  }
  .task-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20rpx 0;
    flex-shrink: 0; // 防止收缩
    .header-title {
      font-size: 36rpx;
      font-weight: bold;
    }
    .header-actions {
      display: flex;
      .search-toggle-btn, .refresh-btn {
        width: 60rpx;
        height: 60rpx;
        border-radius: 50%;
        background-color: #f0f0f0;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-left: 20rpx;
      }
    }
  }
  // 查询条件区域
  .search-section {
    background-color: #f9f9f9;
    border-radius: 15rpx;
    padding: 30rpx;
    margin: 20rpx 0;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    flex-shrink: 0; // 防止收缩
    .form-item {
      margin-bottom: 30rpx;
      &:last-child {
        margin-bottom: 0;
      }
      .form-label {
        font-size: 28rpx;
        margin-bottom: 15rpx;
        color: #333;
      }
      .form-input {
        height: 70rpx;
        padding: 0 20rpx;
        border: 1rpx solid #eee;
        border-radius: 10rpx;
        font-size: 28rpx;
        &.picker-input {
          display: flex;
          align-items: center;
          justify-content: space-between;
        }
      }
      .date-range {
        display: flex;
        align-items: center;
        .date-input {
          flex: 1;
          height: 70rpx;
          padding: 0 20rpx;
          border: 1rpx solid #eee;
          border-radius: 10rpx;
          font-size: 28rpx;
          display: flex;
          align-items: center;
        }
        .divider {
          margin: 0 20rpx;
          color: #999;
        }
      }
    }
    .form-actions {
      display: flex;
      margin-top: 20rpx;
      .search-btn, .reset-btn {
        flex: 1;
        height: 70rpx;
        border-radius: 10rpx;
        font-size: 28rpx;
        margin: 0 10rpx;
      }
      .search-btn {
        background-color: #007AFF;
        color: white;
      }
    }
  }
  .task-filter {
    margin-bottom: 30rpx;
    flex-shrink: 0; // 防止收缩
    .filter-tabs {
      white-space: nowrap;
      padding: 10rpx 0;
      // 隐藏滚动条但保持滚动功能
      ::-webkit-scrollbar {
        display: none;
        width: 0 !important;
        height: 0 !important;
        background: transparent;
      }
      // Firefox滚动条隐藏
      * {
        scrollbar-width: none; /* Firefox */
      }
      // IE/Edge滚动条隐藏
      * {
        -ms-overflow-style: none; /* IE 10+ */
      }
      .filter-item {
        display: inline-block;
        padding: 15rpx 30rpx;
        margin-right: 20rpx;
        background-color: #f5f5f5;
        border-radius: 30rpx;
        font-size: 28rpx;
        &.active {
          background-color: #007AFF;
          color: white;
        }
      }
    }
  }
  .task-list-scroll {
    flex: 1;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
  }
  .task-list {
    .task-item {
      background-color: #fafafa;
      border-radius: 15rpx;
      margin-bottom: 30rpx;
      overflow: hidden;
      .task-main {
        padding: 30rpx;
        border-bottom: 1rpx solid #f0f0f0;
        .task-title {
          font-size: 32rpx;
          font-weight: bold;
          margin-bottom: 20rpx;
        }
        .task-info {
          .info-row {
            display: flex;
            margin-bottom: 15rpx;
            &:last-child {
              margin-bottom: 0;
            }
            .info-item {
              flex: 1;
              display: flex;
              .label {
                font-size: 26rpx;
                color: #666;
                margin-right: 10rpx;
                white-space: nowrap;
              }
              .value {
                font-size: 26rpx;
                flex: 1;
                word-break: break-all;
              }
            }
          }
        }
      }
      .task-actions {
        display: flex;
        padding: 20rpx;
        .action-btn {
          flex: 1;
          height: 70rpx;
          border-radius: 10rpx;
          font-size: 26rpx;
          margin: 0 5rpx;
          background-color: #f0f0f0;
          color: #333;
          &.primary {
            background-color: #007AFF;
            color: white;
          }
          &.disabled {
            opacity: 0.5;
          }
          &:first-child {
            margin-left: 0;
          }
          &:last-child {
            margin-right: 0;
          }
        }
      }
    }
    .no-data {
      text-align: center;
      padding: 100rpx 0;
      color: #999;
      text {
        display: block;
        margin-top: 20rpx;
      }
    }
  }
</style>
app/pages/task/settlement.vue
New file
@@ -0,0 +1,747 @@
<template>
  <view class="settlement-container">
    <view class="settlement-header">
      <text class="header-title">任务结算</text>
    </view>
    <scroll-view class="settlement-content" scroll-y="true">
      <view class="task-info-section">
        <view class="info-item">
          <view class="label">任务编号:</view>
          <view class="value">{{ task.taskNo }}</view>
        </view>
        <view class="info-item">
          <view class="label">任务类型:</view>
          <view class="value">{{ getTaskTypeText(task.type) }}</view>
        </view>
        <view class="info-item">
          <view class="label">车辆信息:</view>
          <view class="value">{{ task.vehicle }}</view>
        </view>
        <view class="info-item">
          <view class="label">出发地:</view>
          <view class="value">{{ task.startLocation }}</view>
        </view>
        <view class="info-item">
          <view class="label">目的地:</view>
          <view class="value">{{ task.endLocation }}</view>
        </view>
      </view>
      <view class="amount-section">
        <view class="section-title">费用信息</view>
        <view class="amount-item">
          <view class="label">基础费用:</view>
          <view class="value">¥{{ baseAmount }}</view>
        </view>
        <view class="amount-item additional-fees" @click="showAdditionalFees">
          <view class="label">附加费用:</view>
          <view class="value">¥{{ additionalAmount }}
            <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
          </view>
        </view>
        <view class="amount-item total-amount">
          <view class="label">总费用:</view>
          <view class="value total">¥{{ totalAmount }}</view>
        </view>
      </view>
      <view class="settlement-form">
        <view class="form-item">
          <view class="form-label">结算金额</view>
          <input
            class="form-input"
            type="digit"
            placeholder="请输入结算金额"
            v-model="settlementAmount"
          />
        </view>
        <view class="form-item">
          <view class="form-label">支付方式</view>
          <picker mode="selector" :range="paymentMethods" @change="onPaymentMethodChange">
            <view class="form-input picker-input">
              {{ selectedPaymentMethod || '请选择支付方式' }}
              <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
            </view>
          </picker>
        </view>
        <view class="form-item">
          <view class="form-label">备注</view>
          <textarea
            class="form-textarea"
            placeholder="请输入备注信息"
            v-model="remark"
          />
        </view>
      </view>
      <!-- 支付二维码区域 -->
      <view v-if="showQRCode" class="qr-code-section">
        <view class="section-title">请扫码支付</view>
        <view class="qr-code-container">
          <image class="qr-code" :src="qrCodeImage" mode="widthFix"></image>
          <view class="qr-tip">请使用{{ selectedPaymentMethod }}扫码支付</view>
        </view>
        <view class="payment-status">
          <view v-if="paymentStatus === 'pending'" class="status-pending">等待支付...</view>
          <view v-if="paymentStatus === 'success'" class="status-success">
            <uni-icons type="checkmarkempty" size="24" color="#4cd964"></uni-icons>
            支付成功
          </view>
        </view>
      </view>
    </scroll-view>
    <view class="action-section">
      <button
        v-if="!showQRCode || paymentStatus === 'success'"
        class="submit-btn"
        @click="saveSettlement"
        :disabled="!canSave"
      >
        保存结算
      </button>
    </view>
    <!-- 附加费用弹窗 -->
    <uni-popup ref="additionalFeesPopup" type="bottom">
      <view class="popup-content">
        <view class="popup-header">
          <text class="header-title">附加费用</text>
          <view class="close-btn" @click="closeAdditionalFeesPopup">
            <uni-icons type="close" size="20" color="#999"></uni-icons>
          </view>
        </view>
        <!-- 简化版费用添加 -->
        <view class="simple-fee-section">
          <view class="form-item">
            <view class="form-label">费用类型</view>
            <picker mode="selector" :range="additionalFeeTypeNames" @change="onFeeTypeChange">
              <view class="form-input picker-input">
                {{ selectedFeeTypeName || '请选择费用类型' }}
                <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
              </view>
            </picker>
          </view>
          <view class="form-item">
            <view class="form-label">费用金额</view>
            <input
              class="form-input"
              type="digit"
              placeholder="请输入费用金额"
              v-model="newFeePrice"
            />
          </view>
          <view class="add-fee-btn">
            <button class="confirm-btn" @click="addCustomFee">添加费用</button>
          </view>
        </view>
        <!-- 已添加的费用列表 -->
        <view class="added-fees-list" v-if="selectedAdditionalFees.length > 0">
          <view class="section-title">已添加费用</view>
          <view
            class="fee-item"
            v-for="(fee, index) in selectedAdditionalFees"
            :key="index"
          >
            <view class="fee-info">
              <view class="fee-name">{{ fee.name }}</view>
              <view class="fee-price">¥{{ fee.price }} × {{ fee.quantity }}</view>
            </view>
            <view class="fee-total">¥{{ (fee.price * fee.quantity).toFixed(2) }}</view>
          </view>
        </view>
        <view class="popup-footer">
          <button class="confirm-btn" @click="confirmAdditionalFees">确定</button>
        </view>
      </view>
    </uni-popup>
  </view>
</template>
<script>
  export default {
    data() {
      return {
        task: {
          id: 1,
          taskNo: 'RW20230515001',
          type: 'emergency',
          vehicle: '粤A12345',
          startLocation: '广州市天河区XX路123号',
          endLocation: '广州市白云区YY路456号'
        },
        baseAmount: 1000, // 基础费用
        additionalAmount: 0, // 附加费用
        settlementAmount: '', // 结算金额
        remark: '', // 备注
        paymentMethods: ['现金', '支付宝', '微信', '挂帐'],
        selectedPaymentMethod: '',
        showQRCode: false,
        paymentStatus: 'pending', // pending, success
        qrCodeImage: '/static/images/qrcode.png', // 二维码图片路径
        additionalFeeTypes: [
          { name: '担架费', price: 200 },
          { name: '等待费', price: 100 },
          { name: '善后费', price: 150 },
          { name: '夜间服务费', price: 300 },
          { name: '长途费', price: 500 }
        ],
        selectedAdditionalFees: [], // 已选择的附加费用
        selectedFeeTypeName: '', // 选中的费用类型名称
        newFeePrice: '' // 新增费用价格
      }
    },
    computed: {
      totalAmount() {
        return this.baseAmount + this.additionalAmount;
      },
      canSave() {
        // 现金和挂帐可以直接保存,其他支付方式需要支付成功后才能保存
        if (!this.settlementAmount || !this.selectedPaymentMethod) {
          return false;
        }
        if (this.selectedPaymentMethod === '现金' || this.selectedPaymentMethod === '挂帐') {
          return true;
        }
        // 支付宝、微信等需要支付成功
        return this.paymentStatus === 'success';
      },
      // 获取费用类型名称数组
      additionalFeeTypeNames() {
        return this.additionalFeeTypes.map(fee => fee.name);
      }
    },
    onLoad(options) {
      // 实际项目中这里会通过API获取任务详情
      // const taskId = options.id;
      // this.getTaskDetail(taskId);
    },
    methods: {
      getTaskTypeText(type) {
        const typeMap = {
          'maintenance': '维修保养',
          'refuel': '加油',
          'inspection': '巡检',
          'emergency': '急救转运',
          'welfare': '福祉车'
        }
        return typeMap[type] || '未知类型'
      },
      onPaymentMethodChange(e) {
        this.selectedPaymentMethod = this.paymentMethods[e.detail.value];
        // 如果选择支付宝或微信,显示二维码
        if (this.selectedPaymentMethod === '支付宝' || this.selectedPaymentMethod === '微信') {
          this.showQRCode = true;
          this.paymentStatus = 'pending';
          // 模拟支付状态检查
          this.checkPaymentStatus();
        } else {
          this.showQRCode = false;
          this.paymentStatus = 'pending';
        }
      },
      // 费用类型选择
      onFeeTypeChange(e) {
        this.selectedFeeTypeName = this.additionalFeeTypeNames[e.detail.value];
      },
      checkPaymentStatus() {
        // 模拟支付状态检查,实际项目中应该通过API轮询检查支付状态
        setTimeout(() => {
          // 模拟支付成功
          this.paymentStatus = 'success';
        }, 5000);
      },
      showAdditionalFees() {
        this.$refs.additionalFeesPopup.open();
      },
      closeAdditionalFeesPopup() {
        this.$refs.additionalFeesPopup.close();
      },
      // 添加自定义费用
      addCustomFee() {
        if (!this.selectedFeeTypeName) {
          this.$modal.showToast('请选择费用类型');
          return;
        }
        if (!this.newFeePrice || isNaN(this.newFeePrice) || parseFloat(this.newFeePrice) <= 0) {
          this.$modal.showToast('请输入有效的费用金额');
          return;
        }
        // 查找选中的费用类型信息
        const selectedFeeType = this.additionalFeeTypes.find(fee => fee.name === this.selectedFeeTypeName);
        // 检查是否已存在同名费用
        const existingFee = this.selectedAdditionalFees.find(fee => fee.name === this.selectedFeeTypeName);
        if (existingFee) {
          existingFee.quantity += 1;
        } else {
          this.selectedAdditionalFees.push({
            name: this.selectedFeeTypeName,
            price: parseFloat(this.newFeePrice),
            quantity: 1
          });
        }
        // 清空输入框
        this.selectedFeeTypeName = '';
        this.newFeePrice = '';
        this.calculateAdditionalAmount();
        this.$modal.showToast('费用添加成功');
      },
      calculateAdditionalAmount() {
        this.additionalAmount = this.selectedAdditionalFees.reduce((total, fee) => {
          return total + (fee.price * fee.quantity);
        }, 0);
      },
      confirmAdditionalFees() {
        this.calculateAdditionalAmount();
        this.closeAdditionalFeesPopup();
      },
      saveSettlement() {
        if (!this.settlementAmount) {
          this.$modal.showToast('请输入结算金额');
          return;
        }
        if (!this.selectedPaymentMethod) {
          this.$modal.showToast('请选择支付方式');
          return;
        }
        // 检查结算金额是否合理
        if (parseFloat(this.settlementAmount) > this.totalAmount) {
          this.$modal.showToast('结算金额不能大于总费用');
          return;
        }
        this.$modal.confirm('确定要保存结算信息吗?').then(() => {
          // 这里应该调用API保存结算信息
          console.log('结算信息:', {
            taskId: this.task.id,
            baseAmount: this.baseAmount,
            additionalAmount: this.additionalAmount,
            totalAmount: this.totalAmount,
            settlementAmount: this.settlementAmount,
            paymentMethod: this.selectedPaymentMethod,
            remark: this.remark,
            additionalFees: this.selectedAdditionalFees
          });
          this.$modal.showToast('结算信息保存成功');
          // 返回任务列表
          this.$tab.navigateBack();
        }).catch(() => {
          // 取消操作
        });
      }
    }
  }
</script>
<style lang="scss">
  .settlement-container {
    padding: 20rpx;
    background-color: #f5f5f5;
    height: 100vh;
    display: flex;
    flex-direction: column;
    // 隐藏滚动条但保持滚动功能
    ::-webkit-scrollbar {
      display: none;
      width: 0 !important;
      height: 0 !important;
      background: transparent;
    }
    // Firefox滚动条隐藏
    * {
      scrollbar-width: none; /* Firefox */
    }
    // IE/Edge滚动条隐藏
    * {
      -ms-overflow-style: none; /* IE 10+ */
    }
  }
  .settlement-header {
    background-color: white;
    border-radius: 15rpx;
    padding: 30rpx;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    flex-shrink: 0;
    .header-title {
      font-size: 36rpx;
      font-weight: bold;
      color: #333;
      text-align: center;
    }
  }
  .settlement-content {
    flex: 1;
    .task-info-section {
      background-color: white;
      border-radius: 15rpx;
      padding: 30rpx;
      margin-bottom: 20rpx;
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      .info-item {
        display: flex;
        margin-bottom: 20rpx;
        padding-bottom: 20rpx;
        border-bottom: 1rpx solid #f0f0f0;
        &:last-child {
          margin-bottom: 0;
          padding-bottom: 0;
          border-bottom: none;
        }
        .label {
          font-size: 28rpx;
          color: #666;
          margin-right: 20rpx;
          white-space: nowrap;
          width: 150rpx;
        }
        .value {
          font-size: 28rpx;
          flex: 1;
          word-break: break-all;
        }
      }
    }
    .amount-section {
      background-color: white;
      border-radius: 15rpx;
      padding: 30rpx;
      margin-bottom: 20rpx;
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      .section-title {
        font-size: 32rpx;
        font-weight: bold;
        margin-bottom: 30rpx;
        color: #333;
      }
      .amount-item {
        display: flex;
        justify-content: space-between;
        margin-bottom: 20rpx;
        padding-bottom: 20rpx;
        border-bottom: 1rpx solid #f0f0f0;
        &:last-child {
          margin-bottom: 0;
          padding-bottom: 0;
          border-bottom: none;
        }
        &.additional-fees {
          .value {
            color: #007AFF;
          }
        }
        &.total-amount {
          .label {
            font-weight: bold;
          }
          .value.total {
            font-weight: bold;
            font-size: 32rpx;
            color: #e54d42;
          }
        }
        .label {
          font-size: 28rpx;
          color: #333;
        }
        .value {
          font-size: 28rpx;
          color: #333;
        }
      }
    }
    .settlement-form {
      background-color: white;
      border-radius: 15rpx;
      padding: 30rpx;
      margin-bottom: 20rpx;
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      .form-item {
        margin-bottom: 40rpx;
        &:last-child {
          margin-bottom: 0;
        }
        .form-label {
          font-size: 28rpx;
          margin-bottom: 15rpx;
          color: #333;
        }
        .form-input {
          height: 70rpx;
          padding: 0 20rpx;
          border: 1rpx solid #eee;
          border-radius: 10rpx;
          font-size: 28rpx;
          &.picker-input {
            display: flex;
            align-items: center;
            justify-content: space-between;
          }
        }
        .form-textarea {
          width: 100%;
          min-height: 150rpx;
          padding: 20rpx;
          border: 1rpx solid #eee;
          border-radius: 10rpx;
          font-size: 28rpx;
          resize: none;
        }
      }
    }
    .qr-code-section {
      background-color: white;
      border-radius: 15rpx;
      padding: 30rpx;
      margin-bottom: 20rpx;
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      text-align: center;
      .section-title {
        font-size: 32rpx;
        font-weight: bold;
        margin-bottom: 30rpx;
        color: #333;
      }
      .qr-code-container {
        margin-bottom: 30rpx;
        .qr-code {
          width: 300rpx;
          height: 300rpx;
          margin: 0 auto 20rpx;
        }
        .qr-tip {
          font-size: 26rpx;
          color: #666;
        }
      }
      .payment-status {
        .status-pending {
          font-size: 28rpx;
          color: #ff9900;
        }
        .status-success {
          font-size: 28rpx;
          color: #4cd964;
          display: flex;
          align-items: center;
          justify-content: center;
        }
      }
    }
  }
  .action-section {
    padding: 20rpx 0;
    flex-shrink: 0;
    .submit-btn {
      width: 80%;
      height: 80rpx;
      background-color: #007AFF;
      color: white;
      border-radius: 10rpx;
      font-size: 32rpx;
      margin: 0 auto;
      display: block;
      &[disabled] {
        background-color: #ccc;
      }
    }
  }
  .popup-content {
    background-color: white;
    border-top-left-radius: 20rpx;
    border-top-right-radius: 20rpx;
    padding: 30rpx;
    max-height: 80vh;
    .popup-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 30rpx;
      padding-bottom: 20rpx;
      border-bottom: 1rpx solid #f0f0f0;
      .header-title {
        font-size: 36rpx;
        font-weight: bold;
        color: #333;
      }
      .close-btn {
        padding: 10rpx;
      }
    }
    .simple-fee-section {
      padding: 20rpx 0;
      border-bottom: 1rpx solid #f0f0f0;
      margin-bottom: 20rpx;
      .form-item {
        margin-bottom: 30rpx;
        &:last-child {
          margin-bottom: 0;
        }
        .form-label {
          font-size: 28rpx;
          margin-bottom: 15rpx;
          color: #333;
        }
        .form-input {
          height: 70rpx;
          padding: 0 20rpx;
          border: 1rpx solid #eee;
          border-radius: 10rpx;
          font-size: 28rpx;
          &.picker-input {
            display: flex;
            align-items: center;
            justify-content: space-between;
          }
        }
      }
      .add-fee-btn {
        margin-top: 20rpx;
        .confirm-btn {
          width: 100%;
          height: 70rpx;
          background-color: #007AFF;
          color: white;
          border-radius: 10rpx;
          font-size: 28rpx;
        }
      }
    }
    .added-fees-list {
      margin-bottom: 30rpx;
      .section-title {
        font-size: 32rpx;
        font-weight: bold;
        margin-bottom: 20rpx;
        color: #333;
      }
      .fee-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 20rpx 0;
        border-bottom: 1rpx solid #f0f0f0;
        &:last-child {
          border-bottom: none;
        }
        .fee-info {
          .fee-name {
            font-size: 28rpx;
            color: #333;
            margin-bottom: 10rpx;
          }
          .fee-price {
            font-size: 26rpx;
            color: #666;
          }
        }
        .fee-total {
          font-size: 28rpx;
          font-weight: bold;
          color: #e54d42;
        }
      }
    }
    .popup-footer {
      .confirm-btn {
        width: 100%;
        height: 80rpx;
        background-color: #007AFF;
        color: white;
        border-radius: 10rpx;
        font-size: 32rpx;
      }
    }
  }
</style>
app/plugins/modal.js
@@ -48,6 +48,26 @@
      })
    })
  },
  // 输入框
  prompt(content, defaultValue) {
    return new Promise((resolve, reject) => {
      uni.showModal({
        title: '系统提示',
        content: content,
        editable: true,
        placeholderText: defaultValue || '',
        cancelText: '取消',
        confirmText: '确定',
        success: function(res) {
          if (res.confirm) {
            resolve(res.content)
          } else {
            reject()
          }
        }
      })
    })
  },
  // 提示信息
  showToast(option) {
    if (typeof option === "object") {
app/static/icons/README.md
New file
@@ -0,0 +1,23 @@
# 图标资源
此目录用于存放应用所需的图标资源。
## 需要的图标
### TabBar 图标
- `home.png` / `home-active.png` - 首页图标
- `tasks.png` / `tasks-active.png` - 任务图标
- `create.png` / `create-active.png` - 创建图标
- `messages.png` / `messages-active.png` - 消息图标
- `profile.png` / `profile-active.png` - 个人中心图标
## 图标规格
- 尺寸: 81px * 81px
- 格式: PNG
- 背景: 透明
- 颜色: 普通状态 #666666,激活状态 #030213
## 使用说明
图标文件需要放在此目录下,并在 `src/app.config.ts` 中正确配置路径。
app/static/icons/create-active.png
app/static/icons/create-active.svg
New file
@@ -0,0 +1,4 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <circle cx="40.5" cy="40.5" r="33.75" fill="#030213"/>
  <path d="M40.5 20.25V60.75M20.25 40.5H60.75" stroke="white" stroke-width="4" stroke-linecap="round"/>
</svg>
app/static/icons/create.png
app/static/icons/create.svg
New file
@@ -0,0 +1,4 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <circle cx="40.5" cy="40.5" r="33.75" fill="#666666"/>
  <path d="M40.5 20.25V60.75M20.25 40.5H60.75" stroke="white" stroke-width="4" stroke-linecap="round"/>
</svg>
app/static/icons/home-active.png
app/static/icons/home-active.svg
New file
@@ -0,0 +1,3 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M40.5 6.75L70.875 27.5625V67.5H54V40.5H27V67.5H10.125V27.5625L40.5 6.75Z" fill="#030213"/>
</svg>
app/static/icons/home.png
app/static/icons/home.svg
New file
@@ -0,0 +1,3 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M40.5 6.75L70.875 27.5625V67.5H54V40.5H27V67.5H10.125V27.5625L40.5 6.75Z" fill="#666666"/>
</svg>
app/static/icons/messages-active.png
app/static/icons/messages-active.svg
New file
@@ -0,0 +1,5 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M6.75 20.25H74.25C75.9069 20.25 77.25 21.5931 77.25 23.25V57.75C77.25 59.4069 75.9069 60.75 74.25 60.75H20.25L6.75 74.25V20.25Z" fill="#030213"/>
  <path d="M20.25 33.75H60.75V40.5H20.25V33.75Z" fill="white"/>
  <path d="M20.25 47.25H47.25V54H20.25V47.25Z" fill="white"/>
</svg>
app/static/icons/messages.png
app/static/icons/messages.svg
New file
@@ -0,0 +1,5 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M6.75 20.25H74.25C75.9069 20.25 77.25 21.5931 77.25 23.25V57.75C77.25 59.4069 75.9069 60.75 74.25 60.75H20.25L6.75 74.25V20.25Z" fill="#666666"/>
  <path d="M20.25 33.75H60.75V40.5H20.25V33.75Z" fill="white"/>
  <path d="M20.25 47.25H47.25V54H20.25V47.25Z" fill="white"/>
</svg>
app/static/icons/profile-active.png
app/static/icons/profile-active.svg
New file
@@ -0,0 +1,4 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <circle cx="40.5" cy="27" r="13.5" fill="#030213"/>
  <path d="M13.5 67.5C13.5 54.5 25.5 44.25 40.5 44.25C55.5 44.25 67.5 54.5 67.5 67.5V74.25H13.5V67.5Z" fill="#030213"/>
</svg>
app/static/icons/profile.png
app/static/icons/profile.svg
New file
@@ -0,0 +1,4 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <circle cx="40.5" cy="27" r="13.5" fill="#666666"/>
  <path d="M13.5 67.5C13.5 54.5 25.5 44.25 40.5 44.25C55.5 44.25 67.5 54.5 67.5 67.5V74.25H13.5V67.5Z" fill="#666666"/>
</svg>
app/static/icons/tasks-active.png
app/static/icons/tasks-active.svg
New file
@@ -0,0 +1,6 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M20.25 6.75H60.75C62.4069 6.75 63.75 8.09315 63.75 9.75V71.25C63.75 72.9069 62.4069 74.25 60.75 74.25H20.25C18.5931 74.25 17.25 72.9069 17.25 71.25V9.75C17.25 8.09315 18.5931 6.75 20.25 6.75Z" fill="#030213"/>
  <path d="M27 20.25H54V27H27V20.25Z" fill="white"/>
  <path d="M27 33.75H54V40.5H27V33.75Z" fill="white"/>
  <path d="M27 47.25H40.5V54H27V47.25Z" fill="white"/>
</svg>
app/static/icons/tasks.png
app/static/icons/tasks.svg
New file
@@ -0,0 +1,6 @@
<svg width="81" height="81" viewBox="0 0 81 81" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M20.25 6.75H60.75C62.4069 6.75 63.75 8.09315 63.75 9.75V71.25C63.75 72.9069 62.4069 74.25 60.75 74.25H20.25C18.5931 74.25 17.25 72.9069 17.25 71.25V9.75C17.25 8.09315 18.5931 6.75 20.25 6.75Z" fill="#666666"/>
  <path d="M27 20.25H54V27H27V20.25Z" fill="white"/>
  <path d="M27 33.75H54V40.5H27V33.75Z" fill="white"/>
  <path d="M27 47.25H40.5V54H27V47.25Z" fill="white"/>
</svg>
app/static/images/qrcode.png
New file
@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 200 200">
  <rect width="200" height="200" fill="#ffffff"/>
  <g fill="#000000">
    <rect x="10" y="10" width="10" height="10"/>
    <rect x="20" y="10" width="10" height="10"/>
    <rect x="30" y="10" width="10" height="10"/>
    <rect x="40" y="10" width="10" height="10"/>
    <rect x="50" y="10" width="10" height="10"/>
    <rect x="60" y="10" width="10" height="10"/>
    <rect x="70" y="10" width="10" height="10"/>
    <rect x="80" y="10" width="10" height="10"/>
    <rect x="90" y="10" width="10" height="10"/>
    <rect x="100" y="10" width="10" height="10"/>
    <rect x="110" y="10" width="10" height="10"/>
    <rect x="120" y="10" width="10" height="10"/>
    <rect x="130" y="10" width="10" height="10"/>
    <rect x="140" y="10" width="10" height="10"/>
    <rect x="150" y="10" width="10" height="10"/>
    <rect x="160" y="10" width="10" height="10"/>
    <rect x="170" y="10" width="10" height="10"/>
    <rect x="180" y="10" width="10" height="10"/>
    <rect x="10" y="20" width="10" height="10"/>
    <rect x="40" y="20" width="10" height="10"/>
    <rect x="60" y="20" width="10" height="10"/>
    <rect x="80" y="20" width="10" height="10"/>
    <rect x="100" y="20" width="10" height="10"/>
    <rect x="120" y="20" width="10" height="10"/>
    <rect x="140" y="20" width="10" height="10"/>
    <rect x="160" y="20" width="10" height="10"/>
    <rect x="180" y="20" width="10" height="10"/>
    <rect x="10" y="30" width="10" height="10"/>
    <rect x="20" y="30" width="10" height="10"/>
    <rect x="30" y="30" width="10" height="10"/>
    <rect x="40" y="30" width="10" height="10"/>
    <rect x="60" y="30" width="10" height="10"/>
    <rect x="80" y="30" width="10" height="10"/>
    <rect x="100" y="30" width="10" height="10"/>
    <rect x="120" y="30" width="10" height="10"/>
    <rect x="140" y="30" width="10" height="10"/>
    <rect x="150" y="30" width="10" height="10"/>
    <rect x="160" y="30" width="10" height="10"/>
    <rect x="180" y="30" width="10" height="10"/>
    <rect x="10" y="40" width="10" height="10"/>
    <rect x="20" y="40" width="10" height="10"/>
    <rect x="30" y="40" width="10" height="10"/>
    <rect x="40" y="40" width="10" height="10"/>
    <rect x="60" y="40" width="10" height="10"/>
    <rect x="80" y="40" width="10" height="10"/>
    <rect x="100" y="40" width="10" height="10"/>
    <rect x="120" y="40" width="10" height="10"/>
    <rect x="140" y="40" width="10" height="10"/>
    <rect x="150" y="40" width="10" height="10"/>
    <rect x="160" y="40" width="10" height="10"/>
    <rect x="180" y="40" width="10" height="10"/>
    <rect x="10" y="50" width="10" height="10"/>
    <rect x="40" y="50" width="10" height="10"/>
    <rect x="60" y="50" width="10" height="10"/>
    <rect x="80" y="50" width="10" height="10"/>
    <rect x="100" y="50" width="10" height="10"/>
    <rect x="120" y="50" width="10" height="10"/>
    <rect x="140" y="50" width="10" height="10"/>
    <rect x="160" y="50" width="10" height="10"/>
    <rect x="180" y="50" width="10" height="10"/>
    <rect x="10" y="60" width="10" height="10"/>
    <rect x="20" y="60" width="10" height="10"/>
    <rect x="30" y="60" width="10" height="10"/>
    <rect x="40" y="60" width="10" height="10"/>
    <rect x="50" y="60" width="10" height="10"/>
    <rect x="60" y="60" width="10" height="10"/>
    <rect x="70" y="60" width="10" height="10"/>
    <rect x="80" y="60" width="10" height="10"/>
    <rect x="90" y="60" width="10" height="10"/>
    <rect x="100" y="60" width="10" height="10"/>
    <rect x="110" y="60" width="10" height="10"/>
    <rect x="120" y="60" width="10" height="10"/>
    <rect x="130" y="60" width="10" height="10"/>
    <rect x="140" y="60" width="10" height="10"/>
    <rect x="150" y="60" width="10" height="10"/>
    <rect x="160" y="60" width="10" height="10"/>
    <rect x="170" y="60" width="10" height="10"/>
    <rect x="180" y="60" width="10" height="10"/>
    <rect x="40" y="70" width="10" height="10"/>
    <rect x="80" y="70" width="10" height="10"/>
    <rect x="120" y="70" width="10" height="10"/>
    <rect x="160" y="70" width="10" height="10"/>
    <rect x="10" y="80" width="10" height="10"/>
    <rect x="20" y="80" width="10" height="10"/>
    <rect x="30" y="80" width="10" height="10"/>
    <rect x="40" y="80" width="10" height="10"/>
    <rect x="50" y="80" width="10" height="10"/>
    <rect x="60" y="80" width="10" height="10"/>
    <rect x="70" y="80" width="10" height="10"/>
    <rect x="80" y="80" width="10" height="10"/>
    <rect x="90" y="80" width="10" height="10"/>
    <rect x="100" y="80" width="10" height="10"/>
    <rect x="110" y="80" width="10" height="10"/>
    <rect x="120" y="80" width="10" height="10"/>
    <rect x="130" y="80" width="10" height="10"/>
    <rect x="140" y="80" width="10" height="10"/>
    <rect x="150" y="80" width="10" height="10"/>
    <rect x="160" y="80" width="10" height="10"/>
    <rect x="170" y="80" width="10" height="10"/>
    <rect x="180" y="80" width="10" height="10"/>
    <rect x="20" y="90" width="10" height="10"/>
    <rect x="40" y="90" width="10" height="10"/>
    <rect x="60" y="90" width="10" height="10"/>
    <rect x="80" y="90" width="10" height="10"/>
    <rect x="100" y="90" width="10" height="10"/>
    <rect x="120" y="90" width="10" height="10"/>
    <rect x="140" y="90" width="10" height="10"/>
    <rect x="160" y="90" width="10" height="10"/>
    <rect x="180" y="90" width="10" height="10"/>
    <rect x="10" y="100" width="10" height="10"/>
    <rect x="20" y="100" width="10" height="10"/>
    <rect x="30" y="100" width="10" height="10"/>
    <rect x="40" y="100" width="10" height="10"/>
    <rect x="60" y="100" width="10" height="10"/>
    <rect x="80" y="100" width="10" height="10"/>
    <rect x="100" y="100" width="10" height="10"/>
    <rect x="120" y="100" width="10" height="10"/>
    <rect x="140" y="100" width="10" height="10"/>
    <rect x="150" y="100" width="10" height="10"/>
    <rect x="160" y="100" width="10" height="10"/>
    <rect x="180" y="100" width="10" height="10"/>
    <rect x="10" y="110" width="10" height="10"/>
    <rect x="40" y="110" width="10" height="10"/>
    <rect x="60" y="110" width="10" height="10"/>
    <rect x="80" y="110" width="10" height="10"/>
    <rect x="100" y="110" width="10" height="10"/>
    <rect x="120" y="110" width="10" height="10"/>
    <rect x="140" y="110" width="10" height="10"/>
    <rect x="160" y="110" width="10" height="10"/>
    <rect x="180" y="110" width="10" height="10"/>
    <rect x="10" y="120" width="10" height="10"/>
    <rect x="20" y="120" width="10" height="10"/>
    <rect x="30" y="120" width="10" height="10"/>
    <rect x="40" y="120" width="10" height="10"/>
    <rect x="60" y="120" width="10" height="10"/>
    <rect x="80" y="120" width="10" height="10"/>
    <rect x="100" y="120" width="10" height="10"/>
    <rect x="120" y="120" width="10" height="10"/>
    <rect x="140" y="120" width="10" height="10"/>
    <rect x="150" y="120" width="10" height="10"/>
    <rect x="160" y="120" width="10" height="10"/>
    <rect x="180" y="120" width="10" height="10"/>
    <rect x="10" y="130" width="10" height="10"/>
    <rect x="20" y="130" width="10" height="10"/>
    <rect x="30" y="130" width="10" height="10"/>
    <rect x="40" y="130" width="10" height="10"/>
    <rect x="60" y="130" width="10" height="10"/>
    <rect x="80" y="130" width="10" height="10"/>
    <rect x="100" y="130" width="10" height="10"/>
    <rect x="120" y="130" width="10" height="10"/>
    <rect x="140" y="130" width="10" height="10"/>
    <rect x="150" y="130" width="10" height="10"/>
    <rect x="160" y="130" width="10" height="10"/>
    <rect x="180" y="130" width="10" height="10"/>
    <rect x="10" y="140" width="10" height="10"/>
    <rect x="40" y="140" width="10" height="10"/>
    <rect x="60" y="140" width="10" height="10"/>
    <rect x="80" y="140" width="10" height="10"/>
    <rect x="100" y="140" width="10" height="10"/>
    <rect x="120" y="140" width="10" height="10"/>
    <rect x="140" y="140" width="10" height="10"/>
    <rect x="160" y="140" width="10" height="10"/>
    <rect x="180" y="140" width="10" height="10"/>
    <rect x="10" y="150" width="10" height="10"/>
    <rect x="20" y="150" width="10" height="10"/>
    <rect x="30" y="150" width="10" height="10"/>
    <rect x="40" y="150" width="10" height="10"/>
    <rect x="50" y="150" width="10" height="10"/>
    <rect x="60" y="150" width="10" height="10"/>
    <rect x="70" y="150" width="10" height="10"/>
    <rect x="80" y="150" width="10" height="10"/>
    <rect x="90" y="150" width="10" height="10"/>
    <rect x="100" y="150" width="10" height="10"/>
    <rect x="110" y="150" width="10" height="10"/>
    <rect x="120" y="150" width="10" height="10"/>
    <rect x="130" y="150" width="10" height="10"/>
    <rect x="140" y="150" width="10" height="10"/>
    <rect x="150" y="150" width="10" height="10"/>
    <rect x="160" y="150" width="10" height="10"/>
    <rect x="170" y="150" width="10" height="10"/>
    <rect x="180" y="150" width="10" height="10"/>
    <rect x="40" y="160" width="10" height="10"/>
    <rect x="80" y="160" width="10" height="10"/>
    <rect x="120" y="160" width="10" height="10"/>
    <rect x="160" y="160" width="10" height="10"/>
    <rect x="10" y="170" width="10" height="10"/>
    <rect x="20" y="170" width="10" height="10"/>
    <rect x="30" y="170" width="10" height="10"/>
    <rect x="40" y="170" width="10" height="10"/>
    <rect x="50" y="170" width="10" height="10"/>
    <rect x="60" y="170" width="10" height="10"/>
    <rect x="70" y="170" width="10" height="10"/>
    <rect x="80" y="170" width="10" height="10"/>
    <rect x="90" y="170" width="10" height="10"/>
    <rect x="100" y="170" width="10" height="10"/>
    <rect x="110" y="170" width="10" height="10"/>
    <rect x="120" y="170" width="10" height="10"/>
    <rect x="130" y="170" width="10" height="10"/>
    <rect x="140" y="170" width="10" height="10"/>
    <rect x="150" y="170" width="10" height="10"/>
    <rect x="160" y="170" width="10" height="10"/>
    <rect x="170" y="170" width="10" height="10"/>
    <rect x="180" y="170" width="10" height="10"/>
    <rect x="10" y="180" width="10" height="10"/>
    <rect x="40" y="180" width="10" height="10"/>
    <rect x="60" y="180" width="10" height="10"/>
    <rect x="80" y="180" width="10" height="10"/>
    <rect x="100" y="180" width="10" height="10"/>
    <rect x="120" y="180" width="10" height="10"/>
    <rect x="140" y="180" width="10" height="10"/>
    <rect x="160" y="180" width="10" height="10"/>
    <rect x="180" y="180" width="10" height="10"/>
    <rect x="60" y="190" width="10" height="10"/>
    <rect x="100" y="190" width="10" height="10"/>
    <rect x="140" y="190" width="10" height="10"/>
  </g>
  <text x="100" y="120" font-family="Arial" font-size="20" text-anchor="middle" fill="#000000">支付二维码</text>
</svg>
app/static/images/tabbar/add.png
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <circle cx="12" cy="12" r="10" fill="#000000"/>
  <path d="M12,7L12,17M7,12L17,12" stroke="white" stroke-width="2"/>
</svg>
app/static/images/tabbar/add_.png
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <circle cx="12" cy="12" r="10" fill="#007AFF"/>
  <path d="M12,7L12,17M7,12L17,12" stroke="white" stroke-width="2"/>
</svg>
app/static/images/tabbar/message.png
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M20,2H4A2,2 0 0,0 2,4V22L6,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2M6,9H18V11H6M14,14H6V12H14M18,8H6V6H18" fill="#000000"/>
</svg>
app/static/images/tabbar/message_.png
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M20,2H4A2,2 0 0,0 2,4V22L6,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2M6,9H18V11H6M14,14H6V12H14M18,8H6V6H18" fill="#007AFF"/>
</svg>
app/static/images/tabbar/task.png
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" fill="#000000"/>
</svg>
app/static/images/tabbar/task_.png
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" fill="#007AFF"/>
</svg>
app/static/images/wechat.png
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <rect width="24" height="24" fill="#07c160" rx="4"/>
  <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.5 14.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm-9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm9.5-5.5c-.55 0-1-.45-1-1 0-.55.45-1 1-1s1 .45 1 1-.45 1-1 1zm-10 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z" fill="white"/>
</svg>
app/static/scss/global.scss
@@ -21,6 +21,37 @@
    color: #aaa;
}
// 隐藏滚动条但保持滚动功能
::-webkit-scrollbar {
  display: none;
  width: 0 !important;
  height: 0 !important;
  background: transparent;
}
// Firefox滚动条隐藏
* {
  scrollbar-width: none; /* Firefox */
}
// IE/Edge滚动条隐藏
* {
  -ms-overflow-style: none; /* IE 10+ */
}
// 全局隐藏滚动条样式
scroll-view::-webkit-scrollbar {
  display: none;
  width: 0 !important;
  height: 0 !important;
  background: transparent;
}
scroll-view {
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE 10+ */
}
.list-cell-arrow::before {
    content: ' ';
    height: 10px;
@@ -87,4 +118,4 @@
        color: #999;
      }
    }
  }
  }
app/store/modules/user.js
@@ -2,6 +2,7 @@
import storage from '@/utils/storage'
import constant from '@/utils/constant'
import { login, logout, getInfo } from '@/api/login'
import { wechatLogin } from '@/api/wechat'
import { getToken, setToken, removeToken } from '@/utils/auth'
const baseUrl = config.baseUrl
@@ -55,6 +56,19 @@
      })
    },
    // 微信登录
    WechatLogin({ commit }, wechatData) {
      return new Promise((resolve, reject) => {
        wechatLogin(wechatData).then(res => {
          setToken(res.token)
          commit('SET_TOKEN', res.token)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 获取用户信息
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
@@ -95,4 +109,4 @@
  }
}
export default user
export default user