yfx
2026-03-19 0cf1e9bc1e8fc6ac9b6ff0d81acf93307f91bad0
137弹框协议
1 files added
329 ■■■■■ changed files
components/textBox/index.vue 329 ●●●●● patch | view | raw | blame | history
components/textBox/index.vue
New file
@@ -0,0 +1,329 @@
<script>
export default {
  name: "ProtocolPopup",
  props:{
    code:{ type:String, default:'' },
    title:{ type:String, default:'' },
    getPlatformProtocolNewList:{
      type:Array,
      default:() => []
    }
  },
  data(){
    return {
      list: [],
      text:'',
      titleText:'',
      flag:false,
      ing:0,
      scrollTop: 0,
      reachBottom:false,
      readingTimer:null,
      readTime:500,
      tiems:5,
      timer:null, // 倒计时
    }
  },
  created() {
    const _this = this;
    // 多协议
    if(this.getPlatformProtocolNewList.length){
      const cache = uni.getStorageSync('protocol_read_cache') || {};
      this.list = this.getPlatformProtocolNewList.map((item, index) => ({
        ...item,
        // isRead: cache[index]?.isRead || false
        isRead:  false
      }));
      this.ing = cache.ing || 0;
      this.updateCurrent();
      this.checkAllRead();
      return;
    }
    // 单协议
    uni.request({
      url: this.$webHost + `/api/v2/protocolInfo/getProtocol?appId=xxx&code=${this.code}`,
      success: (res) => {
        _this.text = res.data.data.content;
        _this.titleText = res.data.data.name;
        _this.flag = false;
      }
    })
  },
  methods:{
    open(){
      this.$refs.boxCnt.open();
      this.startCountDown();
    },
    closePopup() {
      this.$refs.boxCnt.close()
    },
    // ✅ 倒计时
    startCountDown(){
      this.tiems = 5;
      if(this.timer){
        clearInterval(this.timer);
      }
      this.timer = setInterval(() => {
        if(this.tiems > 0){
          this.tiems--;
        }else{
          clearInterval(this.timer);
          this.timer = null;
        }
      },1000);
    },
    // 更新当前协议
    updateCurrent(){
      this.text = this.list[this.ing].content;
      this.titleText = this.list[this.ing].name;
      this.scrollTop = 0;
      this.reachBottom = false;
      if(this.readingTimer){
        clearTimeout(this.readingTimer);
      }
      // 每个协议重新倒计时
      this.startCountDown();
    },
    // 滚动到底
    scrolltolower(){
      if(!this.list.length){
        this.flag = true;
        return;
      }
      if(this.reachBottom) return;
      this.reachBottom = true;
      if(this.readingTimer){
        clearTimeout(this.readingTimer);
      }
      this.readingTimer = setTimeout(() => {
        this.list[this.ing].isRead = true;
        this.saveCache();
        const allRead = this.list.every(item => item.isRead);
        if (allRead) {
          this.flag = true;
        } else {
        }
      }, this.readTime);
    },
    // scroll 兜底
    onScroll(e){
      const { scrollTop, scrollHeight, deltaY } = e.detail;
      if(scrollTop + deltaY >= scrollHeight - 100){
        this.scrolltolower();
      }
    },
    // 缓存
    saveCache(){
      const cache = {
        ing: this.ing,
        ...this.list.reduce((acc, item, index) => {
          acc[index] = { isRead: item.isRead };
          return acc;
        }, {})
      };
      uni.setStorageSync('protocol_read_cache', cache);
    },
    // 判断是否全部读完
    checkAllRead(){
      this.flag = this.list.every(item => item.isRead);
    },
    // 点击确认
    loadPopup(){
      console.log(this.flag);
      if(!this.flag){
        if(this.getPlatformProtocolNewList.length && this.reachBottom){
          if(this.readingTimer){
            clearTimeout(this.readingTimer);
          }
          this.scrollTop = 0;
          this.ing++;
          if(this.ing === this.getPlatformProtocolNewList.length){
            this.flag = true;
            uni.removeStorageSync('protocol_read_cache');
            this.$refs.boxCnt.close();
            this.$emit('confirm');
            return;
          }
          this.updateCurrent();
        } else {
          uni.showToast({
            title: '请先阅读完本协议内容',
            icon: 'none',
          })
        }
        return;
      }
      if(!this.flag || this.tiems > 0) return;
      uni.removeStorageSync('protocol_read_cache');
      this.$refs.boxCnt.close();
      this.$emit('confirm');
    }
  },
  beforeDestroy(){
    if(this.timer){
      clearInterval(this.timer);
    }
    if(this.readingTimer){
      clearTimeout(this.readingTimer);
    }
  }
}
</script>
<template>
  <uv-popup
      ref="boxCnt"
      z-index="999"
      :round="16"
      :safe-area-inset-bottom="true"
      :closeOnClickOverlay="false"
  >
    <view class="box">
      <!-- 标题 -->
      <view class="title">
        {{ titleText }}
      </view>
      <!-- 进度 -->
      <view v-if="list.length" class="progress">
        {{ ing + 1 }} / {{ list.length }}
      </view>
      <view v-if="!reachBottom" class="scroll-tip">
        ↓ 请滑动阅读全部内容
      </view>
      <!-- 内容 -->
      <scroll-view
          :key="ing"
          scroll-y
          :scroll-top="scrollTop"
          @scroll="onScroll"
          @scrolltolower="scrolltolower"
          lower-threshold="50"
          style="height: 70vh;"
          :show-scrollbar="true"
      >
        <view class="text-container">
          <rich-text :nodes="text"></rich-text>
        </view>
      </scroll-view>
      <!-- 按钮 -->
      <view style="margin-top: 20rpx;">
        <uv-button
            :disabled="!flag || tiems > 0"
            @click="loadPopup"
            v-if="code"
            :custom-style="{ color: '#FF1472',border:'none' }"
        >
          确定
          <text v-show="tiems">({{ tiems }}S)</text>
        </uv-button>
        <uv-button
            :disabled="tiems > 0 || !reachBottom "
            @click="loadPopup"
            v-if="getPlatformProtocolNewList.length"
            :custom-style="{ color: '#FF1472',border:'none' }"
        >
          {{ flag ||  getPlatformProtocolNewList.length === ing + 1 ? '确定' : '下一步' }}
          <text v-show="tiems">({{ tiems }}S)</text>
        </uv-button>
      </view>
    </view>
  </uv-popup>
</template>
<style scoped lang="scss">
.box{
  padding: 40rpx;
  width:80vw;
  margin: auto;
  border-radius: 40rpx;
  background: white;
}
.title{
  text-align: center;
  font-weight: 800;
  margin-bottom: 10rpx;
  font-size: 34rpx;
}
.progress{
  text-align: center;
  font-size: 24rpx;
  color: #999;
  margin-bottom: 10rpx;
}
.text-container {
  font-size: 28rpx;
  line-height: 1.8;
  color: #666;
}
.uni-scroll-view::-webkit-scrollbar {
  width: 6px;
}
.uni-scroll-view::-webkit-scrollbar-thumb {
  background: rgba(0,0,0,0.3);
  border-radius: 10px;
}
.uni-scroll-view::-webkit-scrollbar-track {
  background: transparent;
}
.scroll-tip{
  text-align:center;
  color:#999;
  font-size:24rpx;
  margin:10rpx 0;
}
</style>