安全教育平台微信小程序(hbuilder打开后运行到微信开发者程序)
祖安之光
2025-11-05 f4ed2c4a1412f7256614e04e18683ca15a89bb25
新增
7 files modified
6 files added
2184 ■■■■ changed files
api/review.js 16 ●●●●● patch | view | raw | blame | history
common/constant.js 4 ●●●● patch | view | raw | blame | history
pages.json 102 ●●●●● patch | view | raw | blame | history
pages/index/index.vue 18 ●●●●● patch | view | raw | blame | history
pages/menuPage/index.vue 172 ●●●●● patch | view | raw | blame | history
pages/review/index.vue 305 ●●●●● patch | view | raw | blame | history
pages/review/signPage.vue 388 ●●●●● patch | view | raw | blame | history
pages/tabBar/count/count.vue 14 ●●●● patch | view | raw | blame | history
pages/tabBar/count/countDetail.vue 2 ●●● patch | view | raw | blame | history
pages/tabBar/current/detail.vue 95 ●●●● patch | view | raw | blame | history
pages/tabBar/firstPage/firstPage.vue 1068 ●●●● patch | view | raw | blame | history
static/educate.png patch | view | raw | blame | history
static/review.png patch | view | raw | blame | history
api/review.js
New file
@@ -0,0 +1,16 @@
import {service} from '../common/request.js';
export function getProjectList(id) {
  return service({
        url: '/app/item/review/approvalList?userId=' + id ,
        method: 'GET'
    })
}
export function postSignaure(data) {
  return service({
        url: '/app/item/review/approval',
        method: 'POST',
        data
    })
}
common/constant.js
@@ -2,8 +2,8 @@
let VUE_APP_BASE_URL= null;
if (process.env.NODE_ENV == 'development') {
    // VUE_APP_BASE_URL = 'http://106.15.95.149:8056/api'
    // VUE_APP_BASE_URL = 'http://192.168.2.30:8056/api'
    VUE_APP_BASE_URL = 'https://reagent.sinanoaq.cn/exam'
    VUE_APP_BASE_URL = 'http://192.168.2.28:8056/api'
    // VUE_APP_BASE_URL = 'https://reagent.sinanoaq.cn/exam'
}else {
    // VUE_APP_BASE_URL = 'http://192.168.2.15:8082'
    // 正式环境
pages.json
@@ -7,16 +7,37 @@
            }
        },
        {
            "path" : "pages/tabBar/firstPage/firstPage",
            "style" :
            {
                "navigationBarTitleText": "首页"
            "path": "pages/menuPage/index",
            "style": {
                "navigationBarTitleText": "主页面",
                "navigationStyle": "custom"
            }
        },
        {
            "path" : "pages/tabBar/firstPage/exam",
            "style" :
            {
            "path": "pages/review/index",
            "style": {
                "navigationBarTitleText": "项目审批",
                "enablePullDownRefresh": false,
                "navigationStyle": "custom"
            }
        },
        {
            "path": "pages/review/signPage",
            "style": {
                "navigationBarTitleText": "电子签名"
            }
        },
        {
            "path": "pages/tabBar/firstPage/firstPage",
            "style": {
                "navigationBarTitleText": "首页",
                "navigationStyle": "custom",
                "enablePullDownRefresh": false
            }
        },
        {
            "path": "pages/tabBar/firstPage/exam",
            "style": {
                "navigationBarTitleText": "题目内容",
                "enablePullDownRefresh": false,
                "app-plus": {
@@ -25,20 +46,18 @@
            }
        },
        {
            "path" : "pages/tabBar/count/count",
            "style" :
            {
                "navigationBarTitleText": "我的",
                "enablePullDownRefresh": false,
            "path": "pages/tabBar/count/count",
            "style": {
                "navigationBarTitleText": "我的",
                "enablePullDownRefresh": false,
                "app-plus": {
                    "scrollIndicator": "none"
                }
            }
            }
        },
        {
            "path" : "pages/tabBar/count/countDetail",
            "style" :
            {
            "path": "pages/tabBar/count/countDetail",
            "style": {
                "navigationBarTitleText": "我的成绩",
                "enablePullDownRefresh": false,
                "app-plus": {
@@ -47,9 +66,8 @@
            }
        },
        {
            "path" : "pages/tabBar/current/current",
            "style" :
            {
            "path": "pages/tabBar/current/current",
            "style": {
                "navigationBarTitleText": "课程",
                "enablePullDownRefresh": false,
                "app-plus": {
@@ -58,9 +76,8 @@
            }
        },
        {
            "path" : "pages/tabBar/current/detail",
            "style" :
            {
            "path": "pages/tabBar/current/detail",
            "style": {
                "navigationBarTitleText": "课程详情",
                "enablePullDownRefresh": false,
                "app-plus": {
@@ -69,20 +86,18 @@
            }
        },
        {
            "path" : "pages/tabBar/wearhouse/wearhouse",
            "style" :
            {
                "navigationBarTitleText": "刷题",
                "enablePullDownRefresh": false,
            "path": "pages/tabBar/wearhouse/wearhouse",
            "style": {
                "navigationBarTitleText": "刷题",
                "enablePullDownRefresh": false,
                "app-plus": {
                    "scrollIndicator": "none"
                }
            }
            }
        },
        {
            "path" : "pages/tabBar/wearhouse/questions",
            "style" :
            {
            "path": "pages/tabBar/wearhouse/questions",
            "style": {
                "navigationBarTitleText": "题目内容",
                "enablePullDownRefresh": false,
                "app-plus": {
@@ -97,16 +112,16 @@
        "borderStyle": "black",
        "backgroundColor": "#ffffff",
        "list": [{
            "pagePath": "pages/tabBar/firstPage/firstPage",
            "iconPath": "static/home.png",
            "selectedIconPath": "static/home_sel.png",
            "text": "首页"
        },{
            "pagePath": "pages/tabBar/current/current",
            "iconPath": "/static/notice.png",
            "selectedIconPath": "/static/notice-sel.png",
            "text": "课程"
        },{
                "pagePath": "pages/tabBar/firstPage/firstPage",
                "iconPath": "static/home.png",
                "selectedIconPath": "static/home_sel.png",
                "text": "首页"
            }, {
                "pagePath": "pages/tabBar/current/current",
                "iconPath": "/static/notice.png",
                "selectedIconPath": "/static/notice-sel.png",
                "text": "课程"
            }, {
                "pagePath": "pages/tabBar/wearhouse/wearhouse",
                "iconPath": "/static/wearhouse.png",
                "selectedIconPath": "/static/wearhouse_sel.png",
@@ -117,7 +132,8 @@
                "iconPath": "/static/my.png",
                "selectedIconPath": "/static/my_sel.png",
                "text": "我的"
            }]
            }
        ]
    },
    "globalStyle": {
        "navigationBarTextStyle": "black",
@@ -126,4 +142,4 @@
        "backgroundColor": "#f5f7fa"
    },
    "uniIdRouter": {}
}
}
pages/index/index.vue
@@ -4,7 +4,7 @@
    <view class="main">
      <view class="header">
        <view class="titleFirst">
          你好~<br/>欢迎来到安全教育平台</view>
          你好~<br/>欢迎来到体系合规化平台</view>
      </view>
      <!-- <view class="form-area"> -->
        <u--form :model="form" ref="uForm" class="form" >
@@ -129,22 +129,18 @@
                login(data).then(res => {
                    if (res.code === 200) {
                        t.isLogining = false;
                         //登录成功后
                         //设置别名
                        // jpushModule.setAlias({
                        //     'alias': this.phone,
                        //     'sequence': 1
                        // })
                        uni.setStorageSync("name", t.form.username);
                        uni.setStorageSync("pwd", t.form.password);
                        uni.setStorageSync("tk", res.data.token);
                        uni.setStorageSync("uid",res.data.id);
                        uni.setStorageSync('user', res.data);
                        // t.$store.commit('setRoleId', 'user_leader');
                        uni.switchTab({
                            url: '/pages/tabBar/firstPage/firstPage'
                        // uni.switchTab({
                        //     url: '/pages/tabBar/firstPage/firstPage'
                        // })
                        uni.navigateTo({
                          url: '/pages/menuPage/index'
                        })
        //                 }
                    }else{
                        uni.showToast({
                            icon: "none",
pages/menuPage/index.vue
New file
@@ -0,0 +1,172 @@
<template>
    <view>
        <!-- 自定义导航栏 -->
        <view class="custom-navbar">
            <view class="navbar-content">
                <text class="navbar-title">主页面</text>
            </view>
        </view>
        <view class="page-content" :style="{ paddingTop: navbarHeight + 'px' }">
            <view class="navBtn" @click="toReview()">
              <u-image radius="16px" width="140rpx" height="140rpx" :show-loading="true" :src="reviewIcon" mode="aspectFill">
              </u-image>
              <view class="cardTit">
                项目审批
              </view>
            </view>
            <view class="navBtn" @click="toEducate()">
              <u-image radius="16px" width="140rpx" height="140rpx" :show-loading="true" :src="educateIcon" mode="aspectFill">
              </u-image>
              <view class="cardTit">
                安全教育
              </view>
            </view>
            <view class="loginBtn">
              <u-button @click="loginOut" type="primary" text="退出登录" shape="circle"></u-button>
            </view>
        </view>
    </view>
</template>
<script>
    import VUE_APP_BASE_URL from 'common/constant.js'
    import {loginOut} from "../../api"
    import reviewIcon from '../../static/review.png'
    import educateIcon from '../../static/educate.png'
    export default {
        components: {},
        data() {
            return {
                navbarHeight: 0,
                reviewIcon: reviewIcon,
                educateIcon: educateIcon
            }
        },
        onLoad() {
            this.getNavbarHeight()
        },
        onShow() {
        },
        created() {
        },
        mounted() {
        },
        methods: {
            getNavbarHeight() {
                const systemInfo = uni.getSystemInfoSync()
                const statusBarHeight = systemInfo.statusBarHeight
                const navbarHeight = 44
                this.navbarHeight = statusBarHeight + navbarHeight
            },
            toReview() {
                uni.navigateTo({
                    url: '/pages/review/index'
                })
            },
            toEducate() {
                uni.switchTab({
                    url: '/pages/tabBar/firstPage/firstPage'
                })
            },
            loginOut(){
              uni.showModal({
                title: '提示',
                content: '是否确认退出该账号?',
                success: async function (res) {
                  if (res.confirm) {
                    loginOut().then(res=>{
                      if(res.code == 200){
                        uni.showToast({
                          title: '账户已退出',
                          duration: 800
                        })
                        setTimeout(()=>{
                          uni.clearStorageSync();
                          uni.clearStorage();
                          uni.navigateTo({
                            url: '/pages/index/index'
                          })
                        },800)
                      }
                    })
                  } else if (res.cancel) {
                    console.log('用户点击取消');
                  }
                }
              })
            }
        }
    }
</script>
<style lang="scss" scoped>
    .custom-navbar {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 999;
        background: #ffffff;
        .navbar-content {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 44px;
            padding-top: var(--status-bar-height);
        }
        .navbar-title {
            font-size: 16px;
            font-weight: bold;
            color: #333;
        }
    }
    .page-content {
      min-height: 100vh;
      box-sizing: border-box;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 0 15px;
      .navBtn{
          width: 100%;
          padding: 15px 30px;
          box-sizing: border-box;
          margin-top: 15px;
          background: #fff;
          border-radius: 20rpx;
          display: flex;
          align-items: center;
          box-shadow: 0 10rpx 20rpx rgba(0,0,0,.05);
          transition: box-shadow .15s ease !important;
          &:active{
            box-shadow: none;
          }
          .cardTit{
            font-size: 36rpx;
            font-weight: bold;
            margin-left: 30px;
          }
      }
      .loginBtn{
        width: 100%;
        position: fixed;
        bottom: 90px;
        ::v-deep .u-button{
            width: 80%;
        }
      }
    }
</style>
pages/review/index.vue
New file
@@ -0,0 +1,305 @@
<template>
    <view>
        <view class="custom-navbar">
            <view class="navbar-content">
                <view class="nav-left" @click="handleBack">
                    <view class="back-btn">
                        <text class="back-text">返回</text>
                    </view>
                </view>
                <text class="navbar-title">项目审批</text>
                <view class="nav-right"></view>
            </view>
        </view>
        <view class="page-content" :style="{ paddingTop: navbarHeight + 'px'}">
            <view class="card-t">
                <span class="card-t-l">项目审批</span>
                <div>
                    <u-tabs :list="examSelect" :current="currentTab" @click="changeState"></u-tabs>
                </div>
            </view>
            <view>
                <view class="cardList" v-if="projectList && projectList.length>0">
                    <scroll-view scroll-y="true" class="scroll-Y" @scrolltoupper="upper" @scrolltolower="lower"
                        lower-threshold="150" @scroll="scrollView" style="height:100%">
                        <view style="padding: 0 0 20px">
                            <view class="card-i" v-for="(item,index) in projectList" :key="index">
                                <view class="card-line">
                                    <view class="card-tit">项目名称</view>
                                    <view class="card-cont">{{item.fileName}}</view>
                                </view>
                                <view class="card-line">
                                    <view class="card-tit">项目阶段</view>
                                    <view class="card-cont">{{item.stage}}</view>
                                </view>
                                <view class="card-line">
                                    <u-button text="项目文档" type="primary" @click="viewProject(item)"></u-button>
                                    <u-button text="电子签名" type="primary" @click="toSign(item)"></u-button>
                                </view>
                            </view>
                        </view>
                    </scroll-view>
                </view>
                <u-empty v-else text="暂无记录" mode="data"></u-empty>
            </view>
        </view>
    </view>
</template>
<script>
    import {
        getProjectList
    } from '../../api/review.js'
    import VUE_APP_BASE_URL from "../../common/constant";
    export default {
        components: {},
        data() {
            return {
                navbarHeight: 0,
                currentTab: 0,
                examSelect: [{
                    name: "待审批"
                }],
                projectList: []
            }
        },
        onLoad() {
            //获取手机状态栏高度
            this.getNavbarHeight()
            this.getList()
        },
        onShow() {
        },
        created() {},
        methods: {
            getNavbarHeight() {
                const systemInfo = uni.getSystemInfoSync()
                const statusBarHeight = systemInfo.statusBarHeight
                const navbarHeight = 44
                this.navbarHeight = statusBarHeight + navbarHeight
            },
            handleBack() {
                uni.navigateTo({
                    url: '/pages/menuPage/index'
                })
            },
            changeState(e) {
                this.currentTab = e.index
            },
            viewProject(item) {
                wx.showLoading({
                    title: '文件获取中...',
                    mask: true
                })
                wx.downloadFile({
                    url: VUE_APP_BASE_URL + '/' + item.filePath,
                    success: function(res) {
                        if (res.statusCode !== 200 || !res.tempFilePath) {
                            wx.hideLoading()
                            uni.showToast({
                                icon: 'none',
                                title: '文件下载失败',
                                duration: 2000
                            })
                            return
                        }
                        var filePath = res.tempFilePath
                        wx.openDocument({
                            filePath: filePath,
                            showMenu: true,
                            success: function(res) {
                                wx.hideLoading()
                            },
                            fail: function(res) {
                                uni.showToast({
                                    icon: 'none',
                                    duration: 2000,
                                    position: 'top',
                                    title: `打开文件失败: ${res.errMsg || '未知错误'}`
                                });
                                wx.hideLoading()
                            }
                        })
                    },
                    fail: function(res) {
                        uni.showToast({
                            icon: 'none',
                            duration: 2000,
                            position: 'top',
                            title: `下载失败: ${res.errMsg || '网络错误'}`
                        });
                        wx.hideLoading()
                    }
                })
            },
            toSign(item) {
                uni.navigateTo({
                    url: `/pages/review/signPage?id=` + encodeURIComponent(JSON.stringify(item.id))
                })
            },
            async getList() {
                const res = await getProjectList(uni.getStorageSync('uid'))
                if (res.code == 200) {
                    this.projectList = res.data || []
                } else {
                    uni.$u.toast(res.message)
                }
            },
            upper(e) {
                // console.log(e)
            },
            lower(e) {
                //并且让页码+1,调用获取数据的方法获取第二页数据
                this.classParams.pageNum++
                if (this.classParams.pageNum > this.totalPage) {
                    uni.$u.toast('已加载全部数据')
                    return
                }
                //此处调用自己获取数据列表的方法
                this.getClass()
            },
            scrollView(e) {
                // console.log(e)
            }
        }
    }
</script>
<style lang="scss" scoped>
    .custom-navbar {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 999;
        background: #ffffff;
        .navbar-content {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 44px;
            padding-top: var(--status-bar-height);
        }
        .nav-left {
            flex-shrink: 0;
            width: 80px;
            padding-left: 15px;
            box-sizing: border-box;
        }
        .back-btn {
            display: flex;
            align-items: center;
            padding: 8px 12px 8px 0;
        }
        .back-text {
            font-size: 16px;
            color: #333;
        }
        .navbar-title {
            flex: 1;
            font-size: 16px;
            text-align: center;
            font-weight: bold;
            color: #333;
        }
        .nav-right {
            flex-shrink: 0;
            width: 80px;
        }
    }
    .page-content {
        min-height: calc(100vh - 44px);
        box-sizing: border-box;
        padding: 0 15px;
        .card-t {
            width: 100%;
            padding: 0 6rpx;
            box-sizing: border-box;
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 20rpx;
            .card-t-l {
                font-size: 36rpx;
                font-weight: bold;
            }
            .card-t-r {
                color: #999;
                cursor: pointer;
                font-size: 28rpx;
            }
            .uni-stat__select {
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 2rpx 20rpx;
                border: 1rpx solid #ccc;
                border-radius: 99rpx;
                background: #fff;
                cursor: pointer;
                text {
                    color: #999;
                    font-size: 28rpx;
                    margin-right: 6rpx;
                }
            }
        }
        .cardList {
            width: 100%;
            height: calc(100vh - 130px);
            box-sizing: border-box;
            .card-i {
                width: 100%;
                background: #fff;
                border-radius: 20rpx;
                padding: 15px;
                margin-bottom: 15px;
                box-sizing: border-box;
                box-shadow: 4px 4px 12px rgba(150, 150, 150, .05);
                .card-line {
                    margin-bottom: 15px;
                    display: flex;
                    align-items: flex-start;
                    &:last-of-type {
                        margin-bottom: 0;
                    }
                    .card-tit {
                        flex-shrink: 0;
                        width: 140rpx;
                    }
                    .u-button {
                        margin-right: 30px;
                        &:last-of-type {
                            margin-right: 0;
                        }
                    }
                }
            }
        }
    }
</style>
pages/review/signPage.vue
New file
@@ -0,0 +1,388 @@
<template>
    <view class="signature-container">
        <view class="canvas-container">
            <canvas canvas-id="signatureCanvas" id="signatureCanvas" class="signature-canvas"
                @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd"
                disable-scroll></canvas>
            <view class="toast" v-if="toastVisible">请签写您的名字</view>
        </view>
        <view class="controls">
            <view></view>
            <view class="btn-group">
                <button class="btn btn-clear" @tap="clearCanvas" :disabled="uploading">清空</button>
                <button class="btn btn-confirm" @tap="saveSignature" :disabled="uploading || !hasDrawn">
                    {{ uploading ? '上传中...' : '确定' }}
                </button>
            </view>
        </view>
    </view>
</template>
<script>
    import VUE_APP_BASE_URL from "../../common/constant";
    import {
        postSignaure
    } from "../../api/review.js"
    export default {
        name: 'SignaturePad',
        data() {
            return {
                canvas: null,
                ctx: null,
                isDrawing: false,
                hasDrawn: false,
                lastX: 0,
                lastY: 0,
                toastVisible: true,
                systemInfo: null,
                uploading: false,
                id: null
            }
        },
        onReady() {
            this.initCanvas();
        },
        onLoad(e) {
            this.getSystemInfo();
            this.id = e.id && JSON.parse(decodeURIComponent(e.id))
        },
        onUnload() {
            this.cleanup();
        },
        methods: {
            getSystemInfo() {
                const that = this;
                wx.getSystemInfo({
                    success(res) {
                        that.systemInfo = res;
                        that.initCanvas();
                    }
                });
            },
            initCanvas() {
                if (!this.systemInfo) return;
                this.ctx = wx.createCanvasContext('signatureCanvas', this);
                this.ctx.setLineCap('round');
                this.ctx.setLineJoin('round');
                this.ctx.setStrokeStyle('#1A1A1A');
                this.ctx.setLineWidth(2);
                this.ctx.setFillStyle('#1A1A1A');
                this.clearCanvas();
            },
            handleTouchStart(e) {
                if (!this.ctx) return;
                const touch = e.touches[0];
                this.isDrawing = true;
                [this.lastX, this.lastY] = [touch.x, touch.y];
                this.drawDot(this.lastX, this.lastY);
                this.hideToast();
                this.hasDrawn = true;
            },
            handleTouchMove(e) {
                if (!this.isDrawing || !this.ctx) return;
                const touch = e.touches[0];
                const x = touch.x;
                const y = touch.y;
                // 绘制线条
                this.ctx.beginPath();
                this.ctx.moveTo(this.lastX, this.lastY);
                this.ctx.lineTo(x, y);
                this.ctx.stroke();
                this.ctx.draw(true);
                this.drawDot(x, y);
                [this.lastX, this.lastY] = [x, y];
            },
            handleTouchEnd() {
                this.isDrawing = false;
            },
            drawDot(x, y) {
                if (!this.ctx) return;
                this.ctx.beginPath();
                this.ctx.arc(x, y, 1, 0, 2 * Math.PI);
                this.ctx.fill();
                this.ctx.draw(true);
            },
            clearCanvas() {
                if (!this.ctx) return;
                this.ctx.clearRect(0, 0, 1000, 1000);
                this.ctx.setFillStyle('#F5F7FB');
                this.ctx.fillRect(0, 0, 1000, 1000);
                this.ctx.draw(true);
                this.hasDrawn = false;
                this.showToast();
            },
            showToast() {
                this.toastVisible = true;
            },
            hideToast() {
                this.toastVisible = false;
            },
            async saveSignature() {
                if (!this.hasDrawn) {
                    wx.showToast({
                        title: '您还未签名!',
                        icon: 'none',
                        duration: 2000
                    });
                    return;
                }
                if (this.uploading) return;
                this.uploading = true;
                try {
                    const tempFilePath = await this.canvasToTempFile();
                    const uploadResult = await this.uploadToServer(tempFilePath);
                    await this.handleUploadSuccess(uploadResult);
                } catch (error) {
                    this.handleUploadError(error);
                } finally {
                    this.uploading = false;
                }
            },
            canvasToTempFile() {
                return new Promise((resolve, reject) => {
                    wx.canvasToTempFilePath({
                        canvasId: 'signatureCanvas',
                        quality: 1,
                        fileType: 'png',
                        success: (res) => {
                            resolve(res.tempFilePath);
                        },
                        fail: (err) => {
                            reject(new Error('生成图片失败:' + JSON.stringify(err)));
                        }
                    }, this);
                });
            },
            uploadToServer(tempFilePath) {
                return new Promise((resolve, reject) => {
                    wx.uploadFile({
                        url: `${VUE_APP_BASE_URL}/system/common/uploadFile`, // 替换为你的上传接口
                        filePath: tempFilePath,
                        name: 'file',
                        formData: {},
                        header: {
                            'Authorization': uni.getStorageSync('tk'), // 如果有token认证
                            'Content-Type': 'multipart/form-data'
                        },
                        success: (res) => {
                            if (res.statusCode === 200) {
                                try {
                                    const data = JSON.parse(res.data);
                                    resolve(data);
                                } catch (e) {
                                    reject(new Error('解析响应数据失败'));
                                }
                            } else {
                                reject(new Error(`上传失败,状态码:${res.statusCode}`));
                            }
                        },
                        fail: (err) => {
                            reject(new Error('网络请求失败:' + JSON.stringify(err)));
                        }
                    });
                });
            },
            async handleUploadSuccess(res) {
                try {
                    if (!res.data || !res.data.path) {
                        throw new Error('未获取到文件路径');
                    }
                    const filePath = res.data.path;
                    const submitResult = await this.submitSignatureInfo(filePath);
                    this.handleFinalSuccess(submitResult);
                } catch (error) {
                    throw new Error('提交签名信息失败:' + error.message);
                }
            },
            async submitSignatureInfo(path) {
                const res = await postSignaure({
                    id: this.id,
                    sign: path
                })
                if (res.code == 200) {
                    return res
                } else {
                    reject(new Error(`提交失败,状态码:${res.code}`));
                }
            },
            handleFinalSuccess(result) {
                wx.showToast({
                    title: '签名提交成功',
                    icon: 'success',
                    duration: 2000
                });
                setTimeout(() => {
                    // const pages = getCurrentPages();
                    // if (pages.length > 1) {
                    //     const prevPage = pages[pages.length - 2];
                    //     if (prevPage) {
                    //         prevPage.needRefresh = true;
                    //     }
                    //     wx.navigateBack({
                    //         delta: 1,
                    //         success: () => {
                    //             console.log('返回成功,页面应该刷新');
                    //         }
                    //     });
                    // } else {
                        wx.reLaunch({
                            url: '/pages/review/index'
                        });
                    // }
                }, 1500);
            },
            handleUploadError(error) {
                console.error('上传失败:', error);
                wx.showToast({
                    title: '上传失败,请重试',
                    icon: 'none',
                    duration: 2000
                });
            },
            cleanup() {
                this.ctx = null;
                this.isDrawing = false;
            }
        }
    }
</script>
<style scoped>
    .signature-container {
        width: 100vw;
        height: 100vh;
        display: flex;
        flex-direction: column;
        background: #ffffff;
    }
    .canvas-container {
        flex: 1;
        position: relative;
        background: #F5F7FB;
        border: 1px solid rgba(0, 0, 0, 0.08);
        border-radius: 4px;
        margin: 10px;
        overflow: hidden;
    }
    .signature-canvas {
        width: 100%;
        height: 100%;
        background: #F5F7FB;
    }
    .toast {
        position: absolute;
        top: 50%;
        left: 40%;
        transform: translate(-50%, -50%);
        transform: rotate(90deg);
        font-size: 40rpx;
        color: rgba(58, 65, 85, 0.4);
        pointer-events: none;
        z-index: 100;
    }
    .controls {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 15px 32px;
        background: #ffffff;
        border-top: 1px solid #e5e7eb;
    }
    .btn {
        padding: 8px 20px;
        border: none;
        border-radius: 4px;
        font-size: 14px;
        cursor: pointer;
        transition: all 0.3s ease;
    }
    .btn-clear {
        background: rgba(103, 149, 255, 0.2);
        color: #3670F5;
        margin-right: 12px;
    }
    .btn-clear:hover {
        background: rgba(103, 149, 255, 0.3);
    }
    .btn-confirm {
        background: #3670F5;
        color: #FFFFFF;
    }
    .btn-confirm:hover {
        background: #2563eb;
    }
    .btn-group {
        display: flex;
        gap: 10px;
    }
    /* 微信小程序适配样式 */
    button {
        margin: 0;
        padding: 8px 20px;
        border-radius: 4px;
        font-size: 14px;
    }
    button::after {
        border: none;
    }
    /* 横竖屏适配 */
    @media (orientation: landscape) {
        .signature-container {
            flex-direction: column;
        }
        .canvas-container {
            height: calc(100vh - 80px);
        }
    }
    @media (orientation: portrait) {
        .signature-container {
            flex-direction: column;
        }
        .canvas-container {
            height: calc(100vh - 120px);
        }
    }
</style>
pages/tabBar/count/count.vue
@@ -63,7 +63,11 @@
            return {
                page: 'pages/tabBar/count/count',
                statusBarHeight: '',
        user: {},
        user: {
            name: '',
            phone: '',
            company: {},
        },
        examParams: {
          pageNum: 1,
          pageSize: 10,
@@ -75,11 +79,13 @@
            
        },
    created(){
        this.getUserDetail()
    },
        onShow(){
            this.getUserDetail()
        },
        onLoad() {
            this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
      this.getUserInfo()
        },
        methods: {
@@ -89,7 +95,7 @@
                });
            },
      getUserInfo(){
      getUserDetail(){
        getUserInfo(uni.getStorageSync('uid')).then(res => {
          if(res.code == 200) {
            this.user = res.data
pages/tabBar/count/countDetail.vue
@@ -21,7 +21,7 @@
                    丨
                  <view>{{item.passed == 0?'不合格':'合格'}}</view>
                </view>
                <u-button @tap.stop="reExam(item)" class="re-exam-button" plain type="warning" text="重新考试" shape="circle" size="small"></u-button>
                <u-button @tap.native.stop="reExam(item)" class="re-exam-button" plain type="warning" text="重新考试" shape="circle" size="small"></u-button>
              </view>
            </view>
          </scroll-view>
pages/tabBar/current/detail.vue
@@ -237,22 +237,58 @@
            this.detail = res.data
            this.showDetail = true
            if(this.detail.resourceType == 1){
                wx.showLoading({
                  title: '课程获取中...',
                  mask: true
                })
              this.$nextTick(() => {
                // this.videoUrl = this.videoBaseUrl + this.detail.resourcePath
                if (!this.detail.resourcePath) {
                  wx.hideLoading()
                  uni.showToast({
                    icon: 'none',
                    title: '文件路径不存在',
                    duration: 2000
                  })
                  return
                }
                this.videoUrl = this.detail.resourcePath
                this.moduleKey++
                this.$nextTick(() => {
                  if(isClick == true){
                      wx.hideLoading()
                    uni.createVideoContext('myVideo', this).play();
                  }
                  wx.hideLoading()
                })
              })
              wx.hideLoading()
            }else{
              const t = this
              uni.downloadFile({
                url: this.detail.resourcePath,
                // url: 'http://106.15.95.149:8056/api/images/20250718/00571736c0c741e895318c2edd8a3f9d.PDF',
              wx.showLoading({
                title: '课程获取中...',
                mask: true
              })
                if (!t.detail.resourcePath) {
                  wx.hideLoading()
                  uni.showToast({
                    icon: 'none',
                    title: '文件路径不存在',
                    duration: 2000
                  })
                  return
                }
              wx.downloadFile({
                url: t.detail.resourcePath,
                success: function (res) {
                  if (res.statusCode !== 200 || !res.tempFilePath) {
                    wx.hideLoading()
                    uni.showToast({
                      icon: 'none',
                      title: '文件下载失败',
                      duration: 2000
                    })
                    return
                  }
                  const data = {
                    chapterId: chapterId,
                    courseId: courseId,
@@ -262,29 +298,60 @@
                  }
                  postNewStudy(data).then(re=>{
                    if(re.code == 200){
                      this.studyId = re.data
                      this.handleUpdate(3)
                      t.studyId = re.data
                      t.handleUpdate(3)
                    }else{
                      uni.$u.toast(res.message)
                      uni.showToast({
                          icon: 'none',
                          duration: 2000,
                          position: 'top',
                          title: `${res.message}`
                      });
                    }
                  })
                  }).catch(err => {
                    console.error('学习记录提交失败:', err)
                  })
                  var filePath = res.tempFilePath
                  uni.openDocument({
                  wx.openDocument({
                    filePath: filePath,
                    showMenu: true,
                    success: function (res) {
                      console.log('打开文档成功');
                    }
                      wx.hideLoading()
                    },
                    fail: function(res){
                        uni.showToast({
                            icon: 'none',
                            duration: 2000,
                            position: 'top',
                            title: `打开文件失败: ${res.errMsg || '未知错误'}`
                        });
                        wx.hideLoading()
                    }
                  })
                }
                },
                fail: function(res){
                    uni.showToast({
                        icon: 'none',
                        duration: 2000,
                        position: 'top',
                        title: `下载失败: ${res.errMsg || '网络错误'}`
                    });
                    wx.hideLoading()
                }
              })
            }
          }
        }else{
          uni.$u.toast(res.message)
        }
      })
      }).catch(err => {
        console.error('获取课程详情失败:', err)
        uni.showToast({
          icon: 'none',
          title: '网络请求失败',
          duration: 2000
        })
      })
    },
    goBack(){
pages/tabBar/firstPage/firstPage.vue
@@ -1,161 +1,195 @@
<template>
    <!-- 自定义导航栏 -->
    <view>
        <!-- 自定义导航栏 -->
<!--         <view class="navBarBox fix">
            <view class="statusBar" :style="{ paddingTop: statusBarHeight + 'px' }"></view>
            <view class="navBar">
                <view class="barText">首页</view>
        <view class="custom-navbar">
            <view class="navbar-content">
                <view class="nav-left" @click="handleBack">
                    <view class="back-btn">
                        <text class="back-text">返回</text>
                    </view>
                </view>
                <text class="navbar-title">安全教育</text>
                <view class="nav-right"></view>
            </view>
        </view> -->
    <view style="width: 100%;padding: 0 15px;box-sizing: border-box;margin: 20px 0">
      <u-swiper :list="swiperList" indicatorMode="dot" bgColor="#f5f7fa" circular height="160" indicator previousMargin="30" nextMargin="30"></u-swiper>
    </view>
    <scroll-view scroll-y="true" class="scroll-Y" @scrolltoupper="upper"
                 @scrolltolower="lower" lower-threshold="150" @scroll="scrollView" style="height:calc(100vh - 180px)">
    <view class="m-p-15">
      <view class="card" style="width:100%">
        <view class="card-t">
          <span class="card-t-l">我的线上课程</span>
          <span class="card-t-r" @click="toCourses">查看全部</span>
        </view>
        <view class="card-c" v-if="Array.isArray(classList) && classList.length>0">
          <view class="card-i" v-for="(item,index) in classList" :key="index">
            <u-image radius="16px" width="200rpx" height="200rpx" :show-loading="true" :src="getImageUrl(item.course.logo)" mode="aspectFill">
            </u-image>
            <!--            <view class="card-i-t">目前已学:{{item.totalProgress}}分钟</view>-->
            <view class="card-i-r">
              <view class="card-i-r-t">
                <view>{{item.phaseName + '-' + item.course.name}}</view>
                <span style="color: #999;font-size: 12px;display: flex;align-items: center"><u-icon name="account" color="#999" size="18" style="margin-bottom: 0;margin-right: 2px"></u-icon>{{item.createName +'('+ item.createTime +')'}}</span>
              </view>
              <view class="card-i-r-b">
                <u-tag icon="clock" :text="secondsToHms(item.course.period)" type="success" size="mini" shape="circle" plain plainFill></u-tag>
                <u-button class="study-button" type="primary" text="开始学习" shape="circle" size="small" @click="toStudy(item)"></u-button>
              </view>
            </view>
          </view>
        </view>
        <view class="card-c" v-else>
            <span style="font-size: 28rpx;color: #999;">暂无课程信息</span>
        </view>
      </view>
    </view>
    <view class="m-p-15">
      <view class="card" style="width:100%">
        <view class="card-t">
          <span class="card-t-l">我的考试</span>
<!--          <uni-data-select
        <view class="page-content" :style="{ paddingTop: navbarHeight + 'px' }">
            <view style="width: 100%;padding: 0 15px;box-sizing: border-box;margin: 20px 0">
                <u-swiper :list="swiperList" indicatorMode="dot" bgColor="#f5f7fa" circular height="160" indicator
                    previousMargin="30" nextMargin="30"></u-swiper>
            </view>
            <scroll-view scroll-y="true" class="scroll-Y" @scrolltoupper="upper" @scrolltolower="lower"
                lower-threshold="150" @scroll="scrollView" style="height:calc(100vh - 244px)">
                <view class="m-p-15">
                    <view class="card" style="width:100%">
                        <view class="card-t">
                            <span class="card-t-l">我的线上课程</span>
                            <span class="card-t-r" @click="toCourses">查看全部</span>
                        </view>
                        <view class="card-c" v-if="Array.isArray(classList) && classList.length>0">
                            <view class="card-i" v-for="(item,index) in classList" :key="index">
                                <u-image radius="16px" width="200rpx" height="200rpx" :show-loading="true"
                                    :src="getImageUrl(item.course.logo)" mode="aspectFill">
                                </u-image>
                                <!--            <view class="card-i-t">目前已学:{{item.totalProgress}}分钟</view>-->
                                <view class="card-i-r">
                                    <view class="card-i-r-t">
                                        <view>{{item.phaseName + '-' + item.course.name}}</view>
                                        <span
                                            style="color: #999;font-size: 12px;display: flex;align-items: center"><u-icon
                                                name="account" color="#999" size="18"
                                                style="margin-bottom: 0;margin-right: 2px"></u-icon>{{item.createName +'('+ item.createTime +')'}}</span>
                                    </view>
                                    <view class="card-i-r-b">
                                        <u-tag icon="clock" :text="secondsToHms(item.course.period)" type="success"
                                            size="mini" shape="circle" plain plainFill></u-tag>
                                        <u-button class="study-button" type="primary" text="开始学习" shape="circle"
                                            size="small" @click="toStudy(item)"></u-button>
                                    </view>
                                </view>
                            </view>
                        </view>
                        <view class="card-c" v-else>
                            <span style="font-size: 28rpx;color: #999;">暂无课程信息</span>
                        </view>
                    </view>
                </view>
                <view class="m-p-15">
                    <view class="card" style="width:100%">
                        <view class="card-t">
                            <span class="card-t-l">我的考试</span>
                            <!--          <uni-data-select
              v-model="examParams.state"
              placeholder="完成状态"
              :localdata="examSelect"
              :clear="true"
              @change="changeExam"
          ></uni-data-select> -->
          <div>
<!--               <span @click="showSelect = true" class="uni-stat__select"><text>{{examSelect[0][examParams.state].text}}</text><u-icon name="arrow-down" color="#999" size="14"></u-icon></span>
                            <div>
                                <!--               <span @click="showSelect = true" class="uni-stat__select"><text>{{examSelect[0][examParams.state].text}}</text><u-icon name="arrow-down" color="#999" size="14"></u-icon></span>
              <u-picker :show="showSelect" :columns="examSelect" @confirm="confirm" @cancel="showSelect = false" keyName="text"></u-picker> -->
              <u-tabs :list="examSelect" :current="currentTab" @click="changeState"></u-tabs>
          </div>
        </view>
        <view class="card-c card-d" v-if="examList && examList.length>0">
            <view class="paper-card" v-for="(item,index) in examList" :key="index" @click="toExam(item)">
              <view class="paper-card-t">
                  <!-- <span :class="item.state == 0?'blue':item.state == 1?'red':'green'">[{{item.state == 0?'待考试':item.state == 1?'待批阅':'批阅完成'}}]</span> -->
                  {{item.examPaper.name}}</view>
              <view class="tag-area">
                 <u-tag :text="item.examPaper.categoryName" plain size="mini"></u-tag>
                 <u-tag :text="item.examPaper.limited == 1?'时长:' + item.examPaper.limitTime + '分钟':'不限时'" type="warning" plain size="mini"></u-tag>
              </view>
              <view class="paper-card-b">
                <view style="font-size: 12px;margin-top: 5px;color: #999">
                  <view style="margin-bottom: 2px">创建人:{{item.createName}}</view>
                  <view>截止日期:{{item.examPaper.deadline.substring(0,10)}}</view>
                </view>
<!--                <view class="btn-area" v-if="item.state == 0">
                                <u-tabs :list="examSelect" :current="currentTab" @click="changeState"></u-tabs>
                            </div>
                        </view>
                        <view class="card-c card-d" v-if="examList && examList.length>0">
                            <view class="paper-card" v-for="(item,index) in examList" :key="index"
                                @click="toExam(item)">
                                <view class="paper-card-t">
                                    <!-- <span :class="item.state == 0?'blue':item.state == 1?'red':'green'">[{{item.state == 0?'待考试':item.state == 1?'待批阅':'批阅完成'}}]</span> -->
                                    {{item.examPaper.name}}
                                </view>
                                <view class="tag-area">
                                    <u-tag :text="item.examPaper.categoryName" plain size="mini"></u-tag>
                                    <u-tag
                                        :text="item.examPaper.limited == 1?'时长:' + item.examPaper.limitTime + '分钟':'不限时'"
                                        type="warning" plain size="mini"></u-tag>
                                </view>
                                <view class="paper-card-b">
                                    <view style="font-size: 12px;margin-top: 5px;color: #999">
                                        <view style="margin-bottom: 2px">创建人:{{item.createName}}</view>
                                        <view>截止日期:{{item.examPaper.deadline.substring(0,10)}}</view>
                                    </view>
                                    <!--                <view class="btn-area" v-if="item.state == 0">
                  <u-button @click="toExam(item,1)" class="exam-button" type="primary" text="开始考试" shape="circle" size="small"></u-button>
                </view> -->
                <view class="btn-area" v-if="item.state == 2">
                    <u-button @tap.stop="reExam(item)" class="exam-button" type="primary" text="重新考试" shape="circle" size="small"></u-button>
                    <!-- <u-button @click="toExam(item,2)" class="re-exam-button" type="primary" text="查看" shape="circle" size="small"></u-button> -->
                                    <view class="btn-area" v-if="item.state == 2">
                                        <u-button @tap.native.stop="reExam(item)" class="exam-button" type="primary"
                                            text="重新考试" shape="circle" size="small"></u-button>
                                        <!-- <u-button @click="toExam(item,2)" class="re-exam-button" type="primary" text="查看" shape="circle" size="small"></u-button> -->
                                    </view>
                                </view>
                            </view>
                        </view>
                        <view class="card-c card-d" v-else>
                            <u-empty text="该状态暂无记录" mode="data"></u-empty>
                        </view>
                    </view>
                </view>
              </view>
            </view>
        </view>
        <view class="card-c card-d" v-else>
          <u-empty text="该状态暂无记录" mode="data"></u-empty>
        </view>
      </view>
    </view>
    </scroll-view>
            </scroll-view>
        </view>
    </view>
</template>
<script>
    import {getClassList, getExamList, getSwiperList} from '../../../api/index.js'
    import {
        getClassList,
        getExamList,
        getSwiperList
    } from '../../../api/index.js'
    import VUE_APP_BASE_URL from 'common/constant.js'
    import {postEndExam, postAgainExam} from "../../../api/wearhouse";
    import {
        postEndExam,
        postAgainExam
    } from "../../../api/wearhouse";
    export default {
        components:{},
        components: {},
        data() {
            return {
                showSelect:false,
        swiperList: [
          '/static/defaultCover.jpg','/static/defaultCover.jpg','/static/defaultCover.jpg'
        ],
        titleList:[
            {
                label: '默认排序',
                value: 1,
            }
        ],
        classList: [],
        examList: [],
        totalPage: 0,
        page: 'pages/tabBar/firstPage/firstPage',
        statusBarHeight: '',
        classParams:{
          pageNum: 1,
          pageSize: 3,
        },
        examParams: {
          pageNum: 1,
          pageSize: 10,
          state: 0
        },
        currentTab: 0,
        examSelect: [
          { name: "待考试" },
          { name: "批阅完成" }
        ],
                navbarHeight: 0,
                showSelect: false,
                swiperList: [
                    '/static/defaultCover.jpg', '/static/defaultCover.jpg', '/static/defaultCover.jpg'
                ],
                titleList: [{
                    label: '默认排序',
                    value: 1,
                }],
                classList: [],
                examList: [],
                totalPage: 0,
                page: 'pages/tabBar/firstPage/firstPage',
                statusBarHeight: '',
                classParams: {
                    pageNum: 1,
                    pageSize: 3,
                },
                examParams: {
                    pageNum: 1,
                    pageSize: 10,
                    state: 0
                },
                currentTab: 0,
                examSelect: [{
                        name: "待考试"
                    },
                    {
                        name: "批阅完成"
                    }
                ],
                role: '',
                realname:''
                realname: ''
            }
        },
        onLoad() {
            //获取手机状态栏高度
            this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
      this.examList = []
      this.classParams.pageNum = 1
      this.examParams.pageNum = 1
      this.getSwiper()
      this.getClass()
      this.getExamList()
            this.getNavbarHeight()
            this.examList = []
            this.classParams.pageNum = 1
            this.examParams.pageNum = 1
            this.getSwiper()
            this.getClass()
            this.getExamList()
        },
        onShow(){
            // this.role = uni.getStorageSync('roleName');
            // this.realname = uni.getStorageSync('user').realName
      // this.tabBarLists = uni.getStorageSync('tabBarList');
        onShow() {
        },
    created(){
    },
        created() {},
        methods: {
            getNavbarHeight() {
                const systemInfo = uni.getSystemInfoSync()
                const statusBarHeight = systemInfo.statusBarHeight
                const navbarHeight = 44
                this.navbarHeight = statusBarHeight + navbarHeight
            },
            handleBack() {
                uni.navigateTo({
                    url: '/pages/menuPage/index'
                })
            },
            loginOut() {
                uni.clearStorageSync();
                uni.clearStorage();
@@ -164,414 +198,456 @@
                })
            },
      confirm(e) {
        this.examParams.state = e.value[0].value
        this.examParams.pageNum = 1
        this.getExamList()
        this.showSelect = false
      },
            confirm(e) {
                this.examParams.state = e.value[0].value
                this.examParams.pageNum = 1
                this.getExamList()
                this.showSelect = false
            },
        changeState(e){
            this.currentTab = e.index
            this.examParams.state = e.index == 0?0:2
            this.examParams.pageNum = 1
            this.getExamList()
        },
            changeState(e) {
                this.currentTab = e.index
                this.examParams.state = e.index == 0 ? 0 : 2
                this.examParams.pageNum = 1
                this.getExamList()
            },
      async getSwiper(){
        const res = await getSwiperList()
        if(res.code == 200){
          let list = res.data.list || []
          if(list.length>0){
            this.swiperList = list.map(i=>VUE_APP_BASE_URL + '/api/' + i.imgUrl) || []
          }
        }else{
          uni.$u.toast(res.message)
        }
      },
            async getSwiper() {
                const res = await getSwiperList()
                if (res.code == 200) {
                    let list = res.data.list || []
                    if (list.length > 0) {
                        this.swiperList = list.map(i => VUE_APP_BASE_URL + '/api/' + i.imgUrl) || []
                    }
                } else {
                    uni.$u.toast(res.message)
                }
            },
      getClass(){
        getClassList(this.classParams).then(res => {
                    if(res.code == 200) {
                        if(res.data && res.data.list.length > 0){
            getClass() {
                getClassList(this.classParams).then(res => {
                    if (res.code == 200) {
                        if (res.data && res.data.list.length > 0) {
                            this.classList = res.data.list
                        }else {
              this.classList = []
                        } else {
                            this.classList = []
                        }
                    }else{
                    } else {
                        uni.$u.toast(res.message)
                    }
                })
            },
      getExamList(){
        getExamList(this.examParams).then(res => {
          if(res.code == 200) {
            let list = res.data.list?res.data.list: [];
            if (res.data.pageNum != 1) {
              this.examList = this.examList.concat(list)
            } else {
              this.examList = res.data.list
            }
            this.totalPage = res.data.totalPage
          }else{
            uni.$u.toast(res.message)
          }
        })
      },
            getExamList() {
                getExamList(this.examParams).then(res => {
                    if (res.code == 200) {
                        let list = res.data.list ? res.data.list : [];
                        if (res.data.pageNum != 1) {
                            this.examList = this.examList.concat(list)
                        } else {
                            this.examList = res.data.list
                        }
                        this.totalPage = res.data.totalPage
                    } else {
                        uni.$u.toast(res.message)
                    }
                })
            },
      getImageUrl(logo) {
        return logo ? VUE_APP_BASE_URL  + '/api/' + logo : '/static/defaultCover.jpg';
      },
            getImageUrl(logo) {
                return logo ? VUE_APP_BASE_URL + '/api/' + logo : '/static/defaultCover.jpg';
            },
      toStudy(item) {
        uni.setStorageSync("prevPage", '/pages/tabBar/firstPage/firstPage');
        uni.navigateTo({
          url: `/pages/tabBar/current/detail?bank=` + encodeURIComponent(JSON.stringify(item))
        })
      },
            toStudy(item) {
                uni.setStorageSync("prevPage", '/pages/tabBar/firstPage/firstPage');
                uni.navigateTo({
                    url: `/pages/tabBar/current/detail?bank=` + encodeURIComponent(JSON.stringify(item))
                })
            },
      toExam(item){
          console.log(item,'item')
          if(item.state == 0 && item.examPaper.deadline){
            const deadline = item.examPaper.deadline
            const deadlineTime = new Date(deadline).getTime()
            const currentTime = new Date().getTime()
            if (currentTime > deadlineTime) {
              uni.$u.toast('已超过考试截止时间,不可重新考试')
              return
            }
          }
        if(item.state == 0){
          uni.showModal({
            title: '提示',
            content: item.examPaper.limited == 1?'该考试限制时长为:' + item.examPaper.limitTime + '分钟,进入后开始计时,计时结束自动交卷,是否继续?':'是否确认考试?',
            success: function (res) {
              if (res.confirm) {
                uni.setStorageSync("prevPage", '/pages/tabBar/firstPage/firstPage');
                uni.navigateTo({
                  url: `/pages/tabBar/firstPage/exam?bank=` + encodeURIComponent(JSON.stringify(item)) + `&type=` + encodeURIComponent(JSON.stringify(1))
                })
              } else if (res.cancel) {
                console.log('用户点击取消');
              }
            }
          })
        }else{
          uni.setStorageSync("prevPage", '/pages/tabBar/firstPage/firstPage');
          uni.navigateTo({
            url: `/pages/tabBar/firstPage/exam?bank=` + encodeURIComponent(JSON.stringify(item)) + `&type=` + encodeURIComponent(JSON.stringify(2))
          })
        }
      },
        async reExam(item){
            const t = this
            if(item.examPaper.deadline){
                const deadline = item.examPaper.deadline
                const deadlineTime = new Date(deadline).getTime()
                const currentTime = new Date().getTime()
                if (currentTime > deadlineTime) {
                  uni.$u.toast('已超过考试截止时间,不可重新考试')
                  return
            toExam(item) {
                if (item.state == 0 && item.examPaper.deadline) {
                    const deadline = item.examPaper.deadline
                    const deadlineTime = new Date(deadline).getTime()
                    const currentTime = new Date().getTime()
                    if (currentTime > deadlineTime) {
                        uni.$u.toast('已超过考试截止时间,不可重新考试')
                        return
                    }
                }
            }
            uni.showModal({
              title: '提示',
              content: '是否重新考试?',
              success: async function (res) {
                if (res.confirm) {
                  const res = await postAgainExam({paperId: item.paperId,studentId: uni.getStorageSync('uid')})
                  if(res.code == 200){
                      uni.$u.toast(res.message)
                    t.currentTab = 0
                    t.examParams.state = 0
                      t.examParams.pageNum = 1
                      t.getExamList()
                  }else{
                      uni.$u.toast(res.message)
                  }
                } else if (res.cancel) {
                  console.log('用户点击取消');
                }
              }
            })
        },
                if (item.state == 0) {
                    uni.showModal({
                        title: '提示',
                        content: item.examPaper.limited == 1 ? '该考试限制时长为:' + item.examPaper.limitTime +
                            '分钟,进入后开始计时,计时结束自动交卷,是否继续?' : '是否确认考试?',
                        success: function(res) {
                            if (res.confirm) {
                                uni.setStorageSync("prevPage", '/pages/tabBar/firstPage/firstPage');
                                uni.navigateTo({
                                    url: `/pages/tabBar/firstPage/exam?bank=` + encodeURIComponent(JSON
                                        .stringify(item)) + `&type=` + encodeURIComponent(JSON
                                        .stringify(1))
                                })
                            } else if (res.cancel) {
                                console.log('用户点击取消');
                            }
                        }
                    })
                } else {
                    uni.setStorageSync("prevPage", '/pages/tabBar/firstPage/firstPage');
                    uni.navigateTo({
                        url: `/pages/tabBar/firstPage/exam?bank=` + encodeURIComponent(JSON.stringify(item)) +
                            `&type=` + encodeURIComponent(JSON.stringify(2))
                    })
                }
            },
      secondsToHms(seconds) {
        seconds = Number(seconds);
        const h = Math.floor(seconds / 3600);
        const m = Math.floor(seconds % 3600 / 60);
        const s = Math.floor(seconds % 3600 % 60);
            async reExam(item) {
                const t = this
                if (item.examPaper.deadline) {
                    const deadline = item.examPaper.deadline
                    const deadlineTime = new Date(deadline).getTime()
                    const currentTime = new Date().getTime()
                    if (currentTime > deadlineTime) {
                        uni.$u.toast('已超过考试截止时间,不可重新考试')
                        return
                    }
                }
                uni.showModal({
                    title: '提示',
                    content: '是否重新考试?',
                    success: async function(res) {
                        if (res.confirm) {
                            const res = await postAgainExam({
                                paperId: item.paperId,
                                studentId: uni.getStorageSync('uid')
                            })
                            if (res.code == 200) {
                                uni.$u.toast(res.message)
                                t.currentTab = 0
                                t.examParams.state = 0
                                t.examParams.pageNum = 1
                                t.getExamList()
                            } else {
                                uni.$u.toast(res.message)
                            }
                        } else if (res.cancel) {
                            console.log('用户点击取消');
                        }
                    }
                })
            },
        const hDisplay = h > 0 ? String(h).padStart(2, '0') : '00';
        const mDisplay = m > 0 ? String(m).padStart(2, '0') : '00';
        const sDisplay = s > 0 ? String(s).padStart(2, '0') : '00';
        return `${hDisplay}:${mDisplay}:${sDisplay}`;
      },
            secondsToHms(seconds) {
                seconds = Number(seconds);
                const h = Math.floor(seconds / 3600);
                const m = Math.floor(seconds % 3600 / 60);
                const s = Math.floor(seconds % 3600 % 60);
      upper(e) {
        // console.log(e)
      },
      lower(e) {
        //并且让页码+1,调用获取数据的方法获取第二页数据
        this.examParams.pageNum++
        //此处调用自己获取数据列表的方法
        if (this.examParams.pageNum > this.totalPage){
          uni.$u.toast('已加载全部数据')
          return
        }
        this.getExamList()
      },
      scrollView(e) {
        // console.log(e)
      },
                const hDisplay = h > 0 ? String(h).padStart(2, '0') : '00';
                const mDisplay = m > 0 ? String(m).padStart(2, '0') : '00';
                const sDisplay = s > 0 ? String(s).padStart(2, '0') : '00';
                return `${hDisplay}:${mDisplay}:${sDisplay}`;
            },
      toCourses(){
        uni.switchTab({
          url: '/pages/tabBar/current/current'
        })
      },
            upper(e) {
                // console.log(e)
            },
            lower(e) {
                //并且让页码+1,调用获取数据的方法获取第二页数据
                this.examParams.pageNum++
                //此处调用自己获取数据列表的方法
                if (this.examParams.pageNum > this.totalPage) {
                    uni.$u.toast('已加载全部数据')
                    return
                }
                this.getExamList()
            },
            scrollView(e) {
                // console.log(e)
            },
            toCourses() {
                uni.switchTab({
                    url: '/pages/tabBar/current/current'
                })
            },
        }
    }
</script>
<style lang="scss">
.navBarBox .navBar {
    background-color:#fff;
      height: 50px;
      display: flex;
      flex-direction: row;
      justify-content: center;
      align-items: center;
  box-shadow: 0 3px 12px rgba(0,0,0,0.05);
}
.fix{
    position: sticky;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    z-index: 1;
}
.barText{
  /* text-align: center; */
  font-size: 16px;
  font-weight: 600;
  flex: 2;
  margin-left: 45%;
}
.statusBar{
    background-color:lightgrey;
}
.m-p-15{
  width: 100%;
  box-sizing: border-box;
  padding: 0 15px;
}
.card{
  width: 100%;
  margin-bottom: 40rpx;
<style lang="scss" scoped>
    .custom-navbar {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 999;
        background: #ffffff;
        border-bottom: 1px solid #f0f0f0;
  .card-t{
    width: 100%;
    padding: 0 6rpx;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20rpx;
    .card-t-l{
      font-size: 36rpx;
      font-weight: bold;
    }
    .card-t-r{
      color: #999;
      cursor: pointer;
      font-size: 28rpx;
    }
    .uni-stat__select{
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 2rpx 20rpx;
        border: 1rpx solid #ccc;
        border-radius: 99rpx;
        background: #fff;
        cursor: pointer;
        text{
            color: #999;
            font-size: 28rpx;
            margin-right: 6rpx;
        .navbar-content {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 44px;
            padding-top: var(--status-bar-height);
        }
    }
  }
  .card-c{
    background: #fff;
    border-radius: 20rpx;
    padding: 15px;
        .nav-left {
            flex-shrink: 0;
            width: 80px;
            padding-left: 15px;
            box-sizing: border-box;
        }
    ::v-deep .u-empty{
      margin-top: 40rpx !important;
        .back-btn {
            display: flex;
            align-items: center;
            padding: 8px 12px 8px 0;
        }
        .back-text {
            font-size: 16px;
            color: #000000;
        }
        .navbar-title {
            flex: 1;
            font-size: 16px;
            text-align: center;
            font-weight: bold;
            color: #000000;
        }
        .nav-right {
            flex-shrink: 0;
            width: 80px;
        }
    }
    .card-i{
      padding-bottom: 15px;
      margin-bottom: 15px;
      border-bottom: 1px solid #f0f0f0;
      position: relative;
      display: flex;
      align-items: flex-start;
      box-sizing: border-box;
      &:last-of-type{
        margin-bottom: 0;
        padding-bottom: 0;
        border-bottom: none;
      }
      .card-i-t{
        position: absolute;
        width: 250rpx;
        height: 30px;
        border-radius: 8rpx;
        line-height: 30px;
        color: #fff;
        padding: 0 10rpx;
        box-sizing: border-box;
        left: 0;
        bottom: 0;
        background: rgba(0,0,0,.4);
      }
      .card-i-r{
        width: 100%;
        height: 200rpx;
        margin-left: 20rpx;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        .card-i-r-t{
          view{
            font-size: 36rpx;
            margin-bottom: 10rpx;
            font-family: "PingFang SC";
            font-weight: 800;
            overflow: hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
          }
        }
        .card-i-r-b{
          width: 100%;
          display: flex;
          align-items: center;
          justify-content: space-between;
    .page-content {
        min-height: calc(100vh - 44px);
    }
          ::v-deep .u-button{
            width: 220rpx;
            margin: 0;
            box-shadow: 3px 3px 12px rgba(51,133,217,.3), -2px -2px 6px #fff;
            border: 1px solid rgba(255,255,255,.8);
          }
        }
      }
    }
  }
  .card-d{
    background: none;
    padding: 0 0 15px;
    .paper-card{
      background: #fff;
      margin-bottom: 15px;
      padding: 15px;
      box-sizing: border-box;
      position: relative;
      border-radius: 20rpx;
    .m-p-15 {
        width: 100%;
        box-sizing: border-box;
        padding: 0 15px;
    }
      &:last-of-type{
        margin-bottom: 0;
      }
    .card {
        width: 100%;
        margin-bottom: 40rpx;
      .paper-card-t{
        font-size: 36rpx;
        margin-bottom: 10rpx;
        font-family: "PingFang SC";
        font-weight: 800;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        .blue{
          font-size: 32rpx;
          color: #0f7ff9
        }
        .green{
          font-size: 32rpx;
          color: #5ac725
        }
        .red{
          font-size: 32rpx;
          color: #f56c6c
        }
      }
        .tag-area{
        .card-t {
            width: 100%;
            display: flex;
            ::v-deep .u-tag-wrapper{
                margin-right: 10rpx;
            }
        }
      .paper-card-b{
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        .btn-area{
            padding: 0 6rpx;
            box-sizing: border-box;
            display: flex;
            align-items: center;
            ::v-deep .u-button{
                width: 220rpx;
                margin: 0;
                box-shadow: 3px 3px 12px rgba(51,133,217,.3), -2px -2px 6px #fff;
                border: 1px solid rgba(255,255,255,.8);
            justify-content: space-between;
            margin-bottom: 20rpx;
            .card-t-l {
                font-size: 36rpx;
                font-weight: bold;
            }
        }
        .btn-area2{
            display: flex;
            align-items: center;
            ::v-deep .u-button{
                width: 150rpx;
                margin: 0;
                &:last-of-type{
                    box-shadow: 3px 3px 12px rgba(51,133,217,.3), -2px -2px 6px #fff;
                    border: 1px solid rgba(255,255,255,.8);
                    margin-left: 20rpx;
            .card-t-r {
                color: #999;
                cursor: pointer;
                font-size: 28rpx;
            }
            .uni-stat__select {
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 2rpx 20rpx;
                border: 1rpx solid #ccc;
                border-radius: 99rpx;
                background: #fff;
                cursor: pointer;
                text {
                    color: #999;
                    font-size: 28rpx;
                    margin-right: 6rpx;
                }
            }
        }
      }
    }
  }
}
.badge span{
    text-align: center;
    width: 100%;
}
        .card-c {
            background: #fff;
            border-radius: 20rpx;
            padding: 15px;
            ::v-deep .u-empty {
                margin-top: 40rpx !important;
            }
            .card-i {
                padding-bottom: 15px;
                margin-bottom: 15px;
                border-bottom: 1px solid #f0f0f0;
                position: relative;
                display: flex;
                align-items: flex-start;
                box-sizing: border-box;
                &:last-of-type {
                    margin-bottom: 0;
                    padding-bottom: 0;
                    border-bottom: none;
                }
                .card-i-t {
                    position: absolute;
                    width: 250rpx;
                    height: 30px;
                    border-radius: 8rpx;
                    line-height: 30px;
                    color: #fff;
                    padding: 0 10rpx;
                    box-sizing: border-box;
                    left: 0;
                    bottom: 0;
                    background: rgba(0, 0, 0, .4);
                }
                .card-i-r {
                    width: 100%;
                    height: 200rpx;
                    margin-left: 20rpx;
                    display: flex;
                    flex-direction: column;
                    justify-content: space-between;
                    .card-i-r-t {
                        view {
                            font-size: 36rpx;
                            margin-bottom: 10rpx;
                            font-family: "PingFang SC";
                            font-weight: 800;
                            overflow: hidden;
                            text-overflow: ellipsis;
                            display: -webkit-box;
                            -webkit-line-clamp: 2;
                            -webkit-box-orient: vertical;
                        }
                    }
                    .card-i-r-b {
                        width: 100%;
                        display: flex;
                        align-items: center;
                        justify-content: space-between;
                        ::v-deep .u-button {
                            width: 220rpx;
                            margin: 0;
                            box-shadow: 3px 3px 12px rgba(51, 133, 217, .3), -2px -2px 6px #fff;
                            border: 1px solid rgba(255, 255, 255, .8);
                        }
                    }
                }
            }
        }
        .card-d {
            background: none;
            padding: 0 0 15px;
            .paper-card {
                background: #fff;
                margin-bottom: 15px;
                padding: 15px;
                box-sizing: border-box;
                position: relative;
                border-radius: 20rpx;
                &:last-of-type {
                    margin-bottom: 0;
                }
                .paper-card-t {
                    font-size: 36rpx;
                    margin-bottom: 10rpx;
                    font-family: "PingFang SC";
                    font-weight: 800;
                    white-space: nowrap;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    .blue {
                        font-size: 32rpx;
                        color: #0f7ff9
                    }
                    .green {
                        font-size: 32rpx;
                        color: #5ac725
                    }
                    .red {
                        font-size: 32rpx;
                        color: #f56c6c
                    }
                }
                .tag-area {
                    width: 100%;
                    display: flex;
                    ::v-deep .u-tag-wrapper {
                        margin-right: 10rpx;
                    }
                }
                .paper-card-b {
                    display: flex;
                    align-items: flex-end;
                    justify-content: space-between;
                    .btn-area {
                        display: flex;
                        align-items: center;
                        ::v-deep .u-button {
                            width: 220rpx;
                            margin: 0;
                            box-shadow: 3px 3px 12px rgba(51, 133, 217, .3), -2px -2px 6px #fff;
                            border: 1px solid rgba(255, 255, 255, .8);
                        }
                    }
                    .btn-area2 {
                        display: flex;
                        align-items: center;
                        ::v-deep .u-button {
                            width: 150rpx;
                            margin: 0;
                            &:last-of-type {
                                box-shadow: 3px 3px 12px rgba(51, 133, 217, .3), -2px -2px 6px #fff;
                                border: 1px solid rgba(255, 255, 255, .8);
                                margin-left: 20rpx;
                            }
                        }
                    }
                }
            }
        }
    }
    .badge span {
        text-align: center;
        width: 100%;
    }
</style>
static/educate.png
static/review.png