绿满眶商城微信小程序-uniapp
优化我的收藏,活动报名,我的活动页面,活动页面页面,新增加载更多
11个文件已修改
1140 ■■■■■ 已修改文件
api/activity.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/collect.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/common.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/userPermissions.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/mine/activity/detail.vue 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/mine/activity/myActivity.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/mine/activity/reportActivity.vue 433 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/mine/myCollect/myCollect.vue 512 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/userPermissions/addStoreMember.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/userPermissions/userPermissions.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/activity.js
@@ -18,34 +18,6 @@
    params: params,
  });
}
/**
 * 用户活动收藏列表
 * @param {Object} param
 */
export function getActivityCollectList(param){
}
/**
 * 收藏活动
 * @param {Object} param
 */
export function activityCollection(param){
}
/**
 * 取消收藏
 *
 */
export function collectCancel(param){
    return  http.request({
      url: "/lmk/my-activity/collectCancel",
      method: Method.POST,
      needToken: true,
      data:param
    });
}
/**
 * 取消报名
@@ -91,11 +63,12 @@
 * 
 * @param params
 */
 export function getActivityReportList() {
 export function getActivityReportList(param) {
  return http.request({
    url: "/lmk/activityReport",
    method: Method.GET,
    needToken: true
    needToken: true,
    params:param
  });
}
api/collect.js
@@ -20,3 +20,16 @@
    data: data
  });
}
/**
 * 获得我的收藏按传入类型 video,activity,store
 * @param {Object} param
 */
export function getMyCollectList(param){
    return http.request({
      url: "/lmk/my-collect/getMyCollectList",
      method: Method.GET,
      needToken: true,
      params: param
    });
}
api/common.js
@@ -35,15 +35,6 @@
  });
}
export function getPreviewUrl(params){
    return http.request({
      url: `${api.common}/lmk/file/preview`,
      method: Method.POST,
      data:params
    });
}
/**
 * 获取文件访问地址
 */
api/userPermissions.js
@@ -58,4 +58,12 @@
        needToken: true
    })
    
}
export function restPassword(param){
    return http.request({
        url:'/lmk/lmk-user-permissions/restPassword/'+param,
        method: Method.PUT,
        needToken: true
    })
}
pages.json
@@ -672,14 +672,8 @@
                }, {
                    "path": "activity/detail",
                    "style": {
                        "navigationBarTitleText": "活动详情"
                    }
                }, {
                    "path": "activity/myActivity",
                    "style": {
                        "navigationBarTitleText": "我的活动",
                        "navigationBarTitleText": "活动详情",
                        "enablePullDownRefresh": true, //下拉刷新
                        "navigationStyle": "custom",
                        "componentPlaceholder": {
                            "u-form": "view",
                            "u-form-item": "view",
@@ -688,7 +682,27 @@
                            "u-action-sheet": "view",
                            "u-checkbox-group": "view",
                            "u-checkbox": "view",
                            "u-navbar": "view"
                            "u-navbar": "view",
                            "u-button": "view",
                            "u-image": "view"
                        }
                    }
                }, {
                    "path": "activity/myActivity",
                    "style": {
                        "navigationBarTitleText": "我的活动",
                        "enablePullDownRefresh": true, //下拉刷新
                        "componentPlaceholder": {
                            "u-form": "view",
                            "u-form-item": "view",
                            "u-input": "view",
                            "u-icon": "view",
                            "u-action-sheet": "view",
                            "u-checkbox-group": "view",
                            "u-checkbox": "view",
                            "u-navbar": "view",
                            "u-button": "view",
                            "u-image": "view"
                        }
                    }
                }, {
@@ -696,7 +710,6 @@
                    "style": {
                        "navigationBarTitleText": "活动",
                        "enablePullDownRefresh": true, //下拉刷新
                        "navigationStyle": "custom",
                        "componentPlaceholder": {
                            "u-form": "view",
                            "u-form-item": "view",
@@ -705,13 +718,17 @@
                            "u-action-sheet": "view",
                            "u-checkbox-group": "view",
                            "u-checkbox": "view",
                            "u-navbar": "view"
                            "u-navbar": "view",
                            "u-button": "view",
                            "u-image": "view",
                            "u-loadmore": "view"
                        }
                    }
                }, {
                    "path": "myCollect/myCollect",
                    "style": {
                        "navigationBarTitleText": "我的收藏",
                        "enablePullDownRefresh": true, //下拉刷新
                        "componentPlaceholder": {
                            "u-icon": "view",
                            "u-button": "view",
@@ -721,7 +738,10 @@
                            "u-popup": "view",
                            "u-search": "view",
                            "u-loading": "view",
                            "u-navbar": "view"
                            "u-navbar": "view",
                            "u-image": "view",
                            "u-loadmore": "view"
                        }
                    }
pages/mine/activity/detail.vue
@@ -4,8 +4,12 @@
        <!-- 动态封面区域 -->
        <view class="cover-container">
          <!-- 图片类型 -->
          <block v-if="activityInfo.coverType === '图片' || activityInfo.coverType === '视频'">
            <image :src="getPreviewUrl(activityInfo.cover)" mode="aspectFill" class="activity-cover" />
          <block v-if="activityInfo.coverType === '图片'">
            <image :src="getUrl(activityInfo.cover)"  class="activity-cover" />
          </block>
          <block v-if=" activityInfo.coverType === '视频'">
              <video :src="getUrl(item.cover)"
                   @play="handleVideoPlay"  class="activity-cover"></video>
          </block>
          <!-- 文字类型 -->
          <block v-if="activityInfo.coverType === '文字'">
@@ -53,18 +57,24 @@
        </view>
        <!-- 报名状态 -->
        <view class="status-bar" :style="{ backgroundColor: statusBarColor }">
            <button class="signup-btn" @click="activityReport()" :disabled="reportBtn" >{{ reportBtn ? '已报名': '立即报名'}}</button>
            <u-button class="signup-btn" @click.stop="activityReport()" :disabled="reportBtn" >{{ reportBtn ? '已报名': '立即报名'}}</u-button>
            <u-button class="signup-btn" @click.stop="collect()">{{ isCollect ? '取消收藏' : '收藏' }}</u-button>
        </view>
    </view>
</template>
<script>
    import {getPreviewUrl} from '@/api/common.js'
    import UButton from '@/uview-components/uview-ui/components/u-button/u-button.vue';
    import {changeCollect} from '@/api/collect.js'
    import {getFilePreviewUrl} from '@/api/common.js'
    import {
        getActivityDetail,
        activityReport
    } from '@/api/activity.js';
    export default {
        components: {
            UButton
        },
        data() {
            return {
                activityInfo: {
@@ -79,12 +89,17 @@
                    activityType: '',
                    limitUserNum:'',
                },
                isCollect:false,
                reportBtn:false,
                detailId: null, // 存储接收的参数
                reportFrom: {
                    activityId: '',
                    cancel: false, //报名接口默认我false
                }
                },
                collectForm:{
                    collectType:'',
                    refId:'',
                },
            };
        },
        onLoad(options) {
@@ -96,6 +111,20 @@
            }
        },
        methods: {
            collect(){
                this.collectForm.collectType = 'activity'
                this.collectForm.refId = this.detailId
                changeCollect(this.collectForm).then(res=>{
                    if (res.statusCode === 200) {
                        this.isCollect = true;
                        uni.showToast({
                            title: res.data.msg, // 提示文字
                            icon: 'success', // 图标类型(success/loading/none)
                            mask: true // 是否显示透明蒙层(防止触摸穿透)
                        });
                    }
                })
            },
            //报名
            activityReport() {
                this.reportFrom.activityId = this.detailId
@@ -111,8 +140,10 @@
                    
                })
            },
            getPreviewUrl(params){
                return getPreviewUrl(params);
            getUrl(params){
                getFilePreviewUrl(params).then(res =>{
                    return res.data.data
                })
            },
            getActivityDetail(id) {
                uni.showLoading({
@@ -120,6 +151,7 @@
                });
                getActivityDetail(id).then(res => {
                    uni.hideLoading();
                    console.log(res.data)
                    if (res.statusCode === 200) {
                        //赋值
                        this.activityInfo.coverType = res.data.data.coverType;
@@ -132,7 +164,7 @@
                        this.activityInfo.activityType = res.data.data.activityType;
                        this.activityInfo.limitUserNum = res.data.data.limitUserNum;
                        this.reportBtn = res.data.data.isReport;
                        this.isCollect = res.data.data.isCollect;
                    }
                })
            },
@@ -277,4 +309,10 @@
        height: 40rpx;
        margin-bottom: 10rpx;
    }
    .btn-container {
        display: flex;
        justify-content: center;
        align-items: center;
        margin-top: 8px; /* 与上方标题保持间距 */
    }
</style>
pages/mine/activity/myActivity.vue
@@ -1,6 +1,5 @@
<template>
  <view class="activity-container">
    <u-navbar :is-back="true" title="我的活动" title-color="#333" back-icon-color="#333"></u-navbar>
    
    <!-- 顶部 Tab 导航 -->
    <view class="tab-nav">
@@ -29,7 +28,7 @@
            <!-- 封面区域 -->
            <view class="cover-container">
              <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                <image :src="getPreviewUrl(item.cover)" mode="aspectFill" class="activity-cover" />
                <image :src="getUrl(item.cover)" mode="aspectFill" class="activity-cover" />
              </block>
              <block v-if="item.coverType === '文字'">
                <view class="activity-cover text-cover">{{ item.cover }}</view>
@@ -82,7 +81,7 @@
          >
            <view class="cover-container">
              <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                <image :src="getPreviewUrl(item.cover)" mode="aspectFill" class="activity-cover" />
                <image :src="getUrl(item.cover)" mode="aspectFill" class="activity-cover" />
              </block>
              <block v-if="item.coverType === '文字'">
                <view class="activity-cover text-cover">{{ item.cover }}</view>
@@ -125,7 +124,7 @@
          >
            <view class="cover-container">
              <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                <image :src="getPreviewUrl(item.cover)" mode="aspectFill" class="activity-cover" />
                <image :src="getUrl(item.cover)" mode="aspectFill" class="activity-cover" />
              </block>
              <block v-if="item.coverType === '文字'">
                <view class="activity-cover text-cover">{{ item.cover }}</view>
@@ -162,7 +161,7 @@
<script>
import {getMyActivityList,collectCancel,activityCancel} from '@/api/activity.js'
import {getPreviewUrl} from '@/api/common.js'
import {getFilePreviewUrl} from '@/api/common.js'
export default {
  data() {
    return {
@@ -199,8 +198,10 @@
        }
      })
    },
    getPreviewUrl(params){
      return getPreviewUrl(params);
    getUrl(params){
      getFilePreviewUrl(params).then(res =>{
          return res.data.data
      })
    },
    switchTab(index) {
      if (this.currentTab !== index) {
pages/mine/activity/reportActivity.vue
@@ -1,47 +1,73 @@
<template>
    <view class="wrapper">
        <u-navbar :is-back="true" title="活动">
        </u-navbar>
        <!-- 内容区域 -->
        <scroll-view scroll-y class="content" :style="{ paddingBottom: safeAreaInsets.bottom + 'px' }">
        <scroll-view scroll-y class="content" style="height: 100vh;"   @scrolltolower="loadMore"
             :lower-threshold="100"
            >
            <view class="waterfall">
                <view class="column" v-for="(column, index) in columns" :key="index">
                    <!-- 遍历每列内容 -->
                    <view class="item" v-for="(item, idx) in column" :key="item.id" @click="handleItemClick(item)">
                        <!-- 图片类型 -->
                        <image v-if="item.type === '图片'" :src="item.content" mode="widthFix" class="media"
                            @load="imageLoad" :data-item="item" />
                        <image v-if="item.type === '图片'" :src="getUrl(item.cover)" mode="widthFix" class="media"
                            @load="imageLoad" :data-item="item" :style="{ height: item.height + 'px' }" />
                        <!-- 视频类型 -->
                        <video v-if="item.type === '视频'" :src="item.content" class="media" controls
                            :poster="item.poster" @play="handleVideoPlay"></video>
                        <video v-if="item.type === '视频'" :src="getUrl(item.cover)" class="media" controls
                            :poster="item.poster" @play="handleVideoPlay"
                            :style="{ height: item.height + 'px' }"></video>
                        <!-- 文字类型 -->
                        <view v-if="item.type === '文字'" class="text-content">
                            <text class="title">{{ item.cover }}</text>
                        </view>
                        <text class="title">{{ item.title }}</text>
                    </view>
                </view>
            </view>
        <!--     <view style="height: 150rpx;"></view> -->
             <!-- 改进的加载更多提示 -->
                  <view class="load-more">
                    <u-loadmore
                      v-if="mockData.length > 0"
                      :status="loading ? 'loading' : noMore ? 'nomore' : 'loadmore'"
                      :load-text="{
                        loadmore: '上拉加载更多',
                        loading: '正在加载',
                        nomore: '没有更多了'
                      }"
                    />
                  </view>
                  <view style="height:150rpx">
                  </view>
        </scroll-view>
        <custom-tabbar bgColor="#ffffff" selected="activity"></custom-tabbar>
    </view>
</template>
<script>
    import {
        changeCollect
    } from '@/api/collect.js'
    import {
        getFilePreviewUrl
    } from '@/api/common.js'
    import UButton from '@/uview-components/uview-ui/components/u-button/u-button.vue';
    import UImage from '@/uview-components/uview-ui/components/u-image/u-image.vue';
    import ULoadmore from '@/uview-components/uview-ui/components/u-loadmore/u-loadmore.vue'
    import {
        getActivityReportList,
    } from '@/api/activity.js';
    export default {
        components: {
            UImage,
            UButton,
            ULoadmore
        },
        data() {
            return {
                columns: [
@@ -50,45 +76,94 @@
                ], // 双列布局
                mockData: [],
                colHeight: [0, 0], // 记录各列高度
                baseImageHeight: 300, // 图片基础高度
                baseVideoHeight: 350, // 视频基础高度
                baseTextHeight: 120, // 文字基础高度
                query: {
                    pageNumber: 1,
                    pageSize: 8,
                },
                loading: false, // 是否正在加载
                noMore: false, // 是否没有更多数据
                total: 0 // 总数据量
            };
        },
        onLoad() {
            this.getActivityList();
            //获得userId
        },
        methods: {
            getUrl(params) {
                getFilePreviewUrl(params).then(res => {
                    return res.data.data
                })
            },
            /**
             * 下拉刷新时
             */
            onPullDownRefresh() {
                this.query.pageNumber = 1; // 重置页码
                this.noMore = false;
                this.mockData = []; // 清空数据
                this.getActivityList();
            },
            getActivityList() {
                uni.showLoading({
                    title: '加载中'
                });
                const mock = [];
                getActivityReportList().then(res => {
                    uni.hideLoading();
                    if (res.statusCode === 200) {
                        for (const value of res.data.data) {
                            const type = value.coverType;
                            const baseHeight = type === '文字' ? 120 : 350;
                            mock.push({
                                id: value.id,
                                type: type,
                                cover: value.cover,
                                height: baseHeight,
                                title: value.activityName,
                                content: value.activityContent,
                                poster: '',
                            });
                        }
                    }
                    this.mockData = mock;
                    this.layoutItems();
                })
            loadMore() {
              // 显示加载状态
              this.loading = true;
              // 延迟执行让UI有反应时间
              setTimeout(() => {
                this.query.pageNumber += 1;
                this.getActivityList();
              }, 300);
            },
            async getActivityList() {
              try {
                const res = await getActivityReportList(this.query);
                this.loading = false;
                if (res.statusCode === 200) {
                  const newData = res.data.data.map(value => ({
                    id: value.id,
                    type: value.coverType,
                    cover: value.cover,
                    height: value.coverType === '图片' ? this.baseImageHeight :
                           value.coverType === '视频' ? this.baseVideoHeight : this.baseTextHeight,
                    title: value.activityName,
                    content: value.activityContent,
                    poster: '',
                  }));
                  // 更新总数据量
                  this.total = res.data.total || 0;
                  // 追加或替换数据
                  this.mockData = this.query.pageNumber === 1
                    ? newData
                    : [...this.mockData, ...newData];
                  // 判断是否还有更多数据
                  this.noMore = newData.length < this.query.pageSize ||
                               this.mockData.length >= this.total;
                  // 布局更新
                  this.$nextTick(() => {
                    this.layoutItems();
                  });
                }
              } catch (error) {
                console.error('加载失败:', error);
                // 失败时回退页码
                if (this.query.pageNumber > 1) {
                  this.query.pageNumber -= 1;
                }
              } finally {
                this.loading = false;
                uni.hideLoading();
                uni.stopPullDownRefresh();
              }
            },
            // 图片加载完成回调
            layoutItems() {
@@ -100,19 +175,9 @@
                this.mockData.forEach(item => {
                    const minIndex = this.colHeight.indexOf(Math.min(...this.colHeight));
                    this.columns[minIndex].push(item);
                    // 文字类型不需要计算图片高度
                    if (item.type !== 'text') {
                        this.colHeight[minIndex] += item.height + 40; // 40为间距
                    } else {
                        // 文字类型固定高度计算(根据字体大小和行数)
                        const lineHeight = 40; // 假设每行40rpx
                        const lines = Math.ceil(uni.getSystemInfoSync().windowWidth / 345 * 0.8); // 响应式行数
                        this.colHeight[minIndex] += lineHeight * lines + 40;
                    }
                    this.columns[minIndex].push(item); //获得高度更小的 放入元素
                    this.colHeight[minIndex] += item.height + 40; // 40为间距
                });
                console.log(this.colHeight)
            },
            // 图片加载回调
            imageLoad(e) {
@@ -124,176 +189,200 @@
                const item = e.currentTarget.dataset.item;
                // 重新计算实际显示高度
                const viewWidth = 345;
                const viewWidth = uni.upx2px(345); // 将rpx转换为px
                const viewHeight = viewWidth * ratio;
                const index = this.columns[0].findIndex(i => i.id === item.id) ||
                    this.columns[1].findIndex(i => i.id === item.id);
                if (index !== -1) {
                    const colIndex = this.colHeight[0] < this.colHeight[1] ? 0 : 1;
                    this.colHeight[colIndex] -= item.height;
                    this.colHeight[colIndex] += viewHeight;
                    item.height = viewHeight;
                }
                // 更新item高度
                item.height = viewHeight;
                // 重新计算列高度
                this.recalculateColumns();
            },
            // 重新计算列高度
            recalculateColumns() {
                this.colHeight = [0, 0];
                this.columns.forEach((column, colIndex) => {
                    column.forEach(item => {
                        this.colHeight[colIndex] += item.height + 40; // 40为间距
                    });
                });
            },
            handleItemClick(item) {
                console.log(item)
                uni.navigateTo({
                    url: `/pages/mine/activity/detail?id=${item.id}` // 参数通过 URL 传递
                });
            }
        },
        }
    }
</script>
<style lang="scss">
    /* 新增加载更多样式 */
        .load-more {
            padding: 20rpx 0;
            text-align: center;
            color: #999;
            font-size: 26rpx;
            background-color: #f7f8fa;
        }
    .btn-container {
        display: flex;
        justify-content: center;
        align-items: center;
        margin-top: 8px;
        /* 与上方标题保持间距 */
    }
    /* 全局样式优化 */
    .wrapper {
      height: 100vh;
      display: flex;
      flex-direction: column;
      background-color: #f7f8fa;
        height: 100vh;
        display: flex;
        flex-direction: column;
        background-color: #f7f8fa;
    }
    /* 导航栏优化 */
    .u-navbar {
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
        box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
    }
    /* 内容区域优化 */
    .content {
      flex: 1;
      overflow: hidden;
      padding: 0 20rpx;
      box-sizing: border-box;
      /* 确保可以滚动 */
      -webkit-overflow-scrolling: touch;
    }
    /* 瀑布流布局优化 */
    .waterfall {
      display: flex;
      padding: 20rpx 0;
      gap: 20rpx;
        display: flex;
        padding: 20rpx 0;
        gap: 20rpx;
    }
    .column {
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 20rpx;
        flex: 1;
        display: flex;
        flex-direction: column;
        gap: 20rpx;
    }
    /* 卡片项优化 */
    .item {
      background: #fff;
      border-radius: 16rpx;
      overflow: hidden;
      box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
      transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
      &:active {
        transform: scale(0.98);
        box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12);
      }
        background: #fff;
        border-radius: 16rpx;
        overflow: hidden;
        box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        &:active {
            transform: scale(0.98);
            box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12);
        }
    }
    /* 媒体内容样式 */
    .media {
      width: 100%;
      display: block;
      border-radius: 16rpx 16rpx 0 0;
      background-color: #f5f5f5;
      &[mode="widthFix"] {
        height: auto;
      }
        width: 100%;
        display: block;
        border-radius: 16rpx 16rpx 0 0;
        background-color: #f5f5f5;
        &[mode="widthFix"] {
            height: auto;
        }
    }
    /* 视频特殊样式 */
    video.media {
      object-fit: cover;
        object-fit: cover;
    }
    /* 文字内容样式 */
    .text-content {
      padding: 24rpx;
      background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
      min-height: 160rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      .title {
        color: #fff;
        font-size: 32rpx;
        font-weight: 500;
        line-height: 1.4;
        text-align: center;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 3;
        overflow: hidden;
      }
        padding: 24rpx;
        background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
        min-height: 160rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        .title {
            color: #fff;
            font-size: 32rpx;
            font-weight: 500;
            line-height: 1.4;
            text-align: center;
            display: -webkit-box;
            -webkit-box-orient: vertical;
            -webkit-line-clamp: 3;
            overflow: hidden;
        }
    }
    /* 标题样式优化 */
    .title {
      padding: 20rpx 24rpx;
      font-size: 28rpx;
      color: #333;
      line-height: 1.5;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
      font-weight: 500;
      &:not(.text-content .title) {
        border-top: 1rpx solid #f0f0f0;
      }
        padding: 20rpx 24rpx;
        font-size: 28rpx;
        color: #333;
        line-height: 1.5;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        overflow: hidden;
        font-weight: 500;
        &:not(.text-content .title) {
            border-top: 1rpx solid #f0f0f0;
        }
    }
    /* 加载动画 */
    @keyframes fadeInUp {
      from {
        opacity: 0;
        transform: translateY(20rpx);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
        from {
            opacity: 0;
            transform: translateY(20rpx);
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }
    .item {
      animation: fadeInUp 0.4s ease forwards;
      opacity: 0;
      @for $i from 1 through 10 {
        &:nth-child(#{$i}) {
          animation-delay: $i * 0.05s;
        }
      }
        animation: fadeInUp 0.4s ease forwards;
        opacity: 0;
        @for $i from 1 through 10 {
            &:nth-child(#{$i}) {
                animation-delay: $i * 0.05s;
            }
        }
    }
    /* 空状态样式 */
    .empty-state {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 60vh;
      text-align: center;
      image {
        width: 240rpx;
        opacity: 0.6;
        margin-bottom: 30rpx;
      }
      text {
        color: #c0c4cc;
        font-size: 28rpx;
      }
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 60vh;
        text-align: center;
        image {
            width: 240rpx;
            opacity: 0.6;
            margin-bottom: 30rpx;
        }
        text {
            color: #c0c4cc;
            font-size: 28rpx;
        }
    }
</style>
pages/mine/myCollect/myCollect.vue
@@ -10,103 +10,147 @@
        </view>
        <!-- 视频列表 -->
        <view class="activity-list" >
        <view class="activity-list">
            <view v-if="currentTab === 0">
                <view v-if="signedActivities.length > 0">
                    <view v-for="(item, idx) in signedActivities" :key="idx" class="activity-item">
                        <!-- 封面区域 -->
                        <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                            <image :src="getPreviewUrl(item.cover)" mode="aspectFill" class="activity-cover" />
                        </block>
                        <block v-if="item.coverType === '文字'">
                            <view class="activity-cover">{{ item.cover }}</view>
                        </block>
                <scroll-view scroll-y class="activity-list" style="height: 100vh;" @scrolltolower="loadMore"
                    :lower-threshold="100">
                    <view v-if="videoCollects.length > 0">
                        <view v-for="(item, idx) in videoCollects" :key="idx" class="video-item">
                            <!-- 视频封面+播放按钮 -->
                            <view class="video-cover-container">
                                <image :src="item.coverUrl" mode="aspectFill" class="video-cover" />
                                <view class="play-icon">
                                    <u-icon name="play-circle-fill" size="60" color="#fff"></u-icon>
                                </view>
                                <view class="video-duration" v-if="item.duration">{{ item.duration }}</view>
                            </view>
                        <!-- 活动信息 -->
                        <view class="activity-info">
                            <view class="activity-title">{{ item.activityName }}</view>
                            <view class="activity-meta">
                                <text class="activity-time">{{ item.startTime }} - {{ item.endTime }}</text>
                                <text class="activity-location">{{ item.activityLocation || '暂无' }}</text>
                            <!-- 视频信息 -->
                            <view class="video-info">
                                <view class="video-title">{{ item.authorName || '未知作者' }}</view>
                                <view class="video-meta">
                                    <text class="video-weight" v-if="item.weight > 0">
                                        <u-icon name="thumb-up-fill" size="24" color="#999"></u-icon>
                                        {{ item.weight }}
                                    </text>
                                </view>
                            </view>
                            <!-- 操作按钮 -->
                            <view class="video-actions">
                                <button class="cancel-btn" @click.stop="handleCancelCollection(item,'video')">
                                    取消收藏
                                </button>
                            </view>
                        </view>
                        <!-- 操作区域 -->
                        <view class="action-container">
                            <button class="cancel-btn" @click="handleCancelCollection(item)"
                                hover-class="cancel-btn-hover">
                                取消收藏
                            </button>
                        </view>
                    </view>
                </view>
                    <view v-else class="empty-tip">
                        <text>暂无收藏视频</text>
                    </view>
                    <view class="load-more">
                        <u-loadmore v-if="videoCollects.length > 0"
                            :status="loading ? 'loading' : noMore ? 'nomore' : 'loadmore'" :load-text="{
                      loadmore: '上拉加载更多',
                      loading: '正在加载',
                      nomore: '没有更多了'
                    }" />
                    </view>
                </scroll-view>
            </view>
            <view v-if="currentTab === 1">
                <!-- 已结束活动列表 -->
                <view v-if="endedActivities.length > 0">
                    <view v-for="(item, idx) in endedActivities" :key="idx" class="activity-item">
                        <!-- 封面区域 -->
                        <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                            <image :src="getPreviewUrl(item.cover)" mode="aspectFill" class="activity-cover" />
                        </block>
                        <block v-if="item.coverType === '文字'">
                            <view class="activity-cover">{{ item.cover }}</view>
                        </block>
                <scroll-view scroll-y class="activity-list" style="height: 100vh;" @scrolltolower="loadMore"
                    :lower-threshold="100">
                    <view v-if="goodsCollects.length > 0">
                        <view v-for="(item, idx) in goodsCollects" :key="idx" class="activity-item">
                            <!-- 封面区域 -->
                            <block>
                                <image :src="item.original" mode="aspectFill" class="activity-cover" />
                            </block>
                        <!-- 活动信息 -->
                        <view class="activity-info">
                            <view class="activity-title">{{ item.activityName }}</view>
                            <view class="activity-meta">
                                <text class="activity-time">{{ item.startTime }} - {{ item.endTime }}</text>
                                <text class="activity-location">{{ item.activityLocation || '暂无' }}</text>
                            <!-- 活动信息 -->
                            <view class="activity-info">
                                <view class="activity-title">{{ item.goodsName }}</view>
                                <view class="activity-meta">
                                    <text class="activity-time">价格:{{ item.price }}元</text>
                                    <text class="activity-location">{{ item.storeName || '暂无' }}</text>
                                </view>
                            </view>
                        </view>
                        <!-- 操作区域 -->
                        <view class="action-container">
                            <button class="cancel-btn" @click="handleCancelCollection(item)"
                                hover-class="cancel-btn-hover">
                                取消收藏
                            </button>
                        </view>
                            <!-- 操作区域 -->
                            <view class="action-container">
                                <button class="cancel-btn" @click="handleCancelCollection(item,'goods')"
                                    hover-class="cancel-btn-hover">
                                    取消收藏
                                </button>
                            </view>
                        </view>
                    </view>
                </view>
                    <view v-else class="empty-tip">
                        <text>暂无收藏商品</text>
                    </view>
                    <view class="load-more">
                        <u-loadmore v-if="goodsCollects.length > 0"
                            :status="loading ? 'loading' : noMore ? 'nomore' : 'loadmore'" :load-text="{
                      loadmore: '上拉加载更多',
                      loading: '正在加载',
                      nomore: '没有更多了'
                    }" />
                    </view>
                </scroll-view>
            </view>
            <view v-if="currentTab === 2">
                <!-- 已取消活动列表 -->
                <view v-if="canceledActivities.length > 0">
                    <view v-for="(item, idx) in endedActivities" :key="idx" class="activity-item">
                        <!-- 封面区域 -->
                        <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                            <image :src="getPreviewUrl(item.cover)" mode="aspectFill" class="activity-cover" />
                        </block>
                        <block v-if="item.coverType === '文字'">
                            <view class="activity-cover">{{ item.cover }}</view>
                        </block>
                        <!-- 活动信息 -->
                        <view class="activity-info">
                            <view class="activity-title">{{ item.activityName }}</view>
                            <view class="activity-meta">
                                <text class="activity-time">{{ item.startTime }} - {{ item.endTime }}</text>
                                <text class="activity-location">{{ item.activityLocation || '暂无' }}</text>
                <scroll-view scroll-y class="activity-list" style="height: 80vh;" @scrolltolower="loadMore"
                    :lower-threshold="100">
                    <view v-if="activityCollects.length > 0">
                        <view v-for="(item, idx) in activityCollects" :key="idx" class="activity-item">
                            <!-- 封面区域 -->
                            <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                                <image :src="item.cover" mode="aspectFill" class="activity-cover" />
                            </block>
                            <block v-if="item.coverType === '文字'">
                                <view class="activity-cover  text-cover">{{ item.cover }}</view>
                            </block>
                            <!-- 活动信息 -->
                            <view class="activity-info">
                                <view class="activity-title">{{ item.activityName }}</view>
                                <view class="activity-meta">
                                    <text class="activity-time">{{ item.startTime }}</text>
                                    <text class="activity-time"> {{ item.endTime }}</text>
                                    <text class="activity-location">{{ item.activityLocation || '暂无' }}</text>
                                </view>
                            </view>
                            <!-- 操作区域 -->
                            <view class="action-container">
                                <button class="cancel-btn" @click="handleCancelCollection(item,'activity')"
                                    hover-class="cancel-btn-hover">
                                    取消收藏
                                </button>
                            </view>
                        </view>
                        <!-- 操作区域 -->
                        <view class="action-container">
                            <button class="cancel-btn" @click="handleCancelCollection(item)"
                                hover-class="cancel-btn-hover">
                                取消收藏
                            </button>
                        </view>
                    </view>
                </view>
                    <view v-else class="empty-tip">
                        <text>暂无收藏活动</text>
                    </view>
                    <view class="load-more">
                        <u-loadmore v-if="activityCollects.length > 0"
                            :status="loading ? 'loading' : noMore ? 'nomore' : 'loadmore'" :load-text="{
                      loadmore: '上拉加载更多',
                      loading: '正在加载',
                      nomore: '没有更多了'
                    }" />
                    </view>
                    <view style="height: 150rpx"></view>
                </scroll-view>
            </view>
@@ -120,88 +164,300 @@
</template>
<script>
    import ULoadmore from '@/uview-components/uview-ui/components/u-loadmore/u-loadmore.vue'
    import UImage from '@/uview-components/uview-ui/components/u-image/u-image.vue';
    import {
        getMyActivityList,
        activityCancel
    } from '@/api/activity.js'
    import {
        getPreviewUrl
        getFilePreviewUrl
    } from '@/api/common.js'
    import {
        changeCollect,
        getMyCollectList
    } from '@/api/collect.js'
    import {
        ifError
    } from 'assert'
    export default {
        components: {
            UImage,
            ULoadmore
        },
        data() {
            return {
                total: 0,
                loading: false,
                noMore: {
                    video: false,
                    goods: false,
                    activity: false
                },
                currentTab: 0, // 当前选中的tab索引
                tabs: ['视频', '商品', '活动'],
                //
                //
                videoCollects: [], // 收藏视频列表
                storeCollects: [], // 收藏商品列表
                goodsCollects: [], // 收藏商品列表
                activityCollects: [], // 收藏活动列表
                query: {
                    id: '',
                    status: '',
                    cancel: false,
                collectForm: {
                    collectType: '',
                    refId: '',
                },
                query: {
                    type: 'video',
                    pageNumber: 1,
                    pageSize: 5,
                }
            }
        },
        onLoad(){
        onLoad() {
            this.currentTab = 0;
            //TODO 未登录需要id,测试用写死\
            this.switchTab(this.currentTab);
            this.getintit()
        },
        methods: {
            handleCancelCollection(id) {
            /**
             * 下拉刷新时
             */
            onPullDownRefresh() {
                this.currentTab = 0;
                this.query.pageNumber = 1; // 重置页码
                this.noMore = false;
                this.videoCollects = [];
                this.goodsCollects = []; // 收藏商品列表
                this.activityCollects = []; // 收藏活动列表// 清空数据
                this.getintit();
            },
            getPreviewUrl(params) {
                // return getPreviewUrl(params);
                return '';
            loadMore() {
                this.loading = true;
                this.query.pageNumber += 1;
                // 延迟执行让UI有反应时间
                setTimeout(() => {
                    this.query.pageNumber += 1;
                    this.getintit();
                }, 300);
            },
            handleCancelCollection(item, type) {
                console.log(item)
                this.collectForm.collectType = type;
                this.collectForm.refId = item.id;
                changeCollect(this.collectForm).then(res => {
                    if (res.statusCode === 200) {
                        uni.showToast({
                            title: res.data.msg, // 提示文字
                            icon: 'none', // 图标类型(success/loading/none)
                            mask: true // 是否显示透明蒙层(防止触摸穿透)
                        });
                        this.getintit();
                    }
                })
            },
            getUrl(params) {
                getFilePreviewUrl(params).then(res => {
                    return res.data.data
                })
            },
            // 切换tab
            switchTab(index) {
                if (this.currentTab !== index) {
                    this.currentTab = index
                    //切换时页码归0
                    this.query.pageNumber = 0;
                    // 清空数据
                    this.videoCollects = [];
                    this.goodsCollects = [];
                    this.activityCollects = [];
                    // 实际项目中可以在这里添加加载数据的逻辑
                    if (this.currentTab === 0) {
                        //加载视频列表
                        this.getMyCollectionVideoList();
                    } else if (this.currentTab === 1) {
                        //加载商品列表
                        this.getMyCollectionStoreList();
                    } else if (this.currentTab === 2) {
                        //加载活动列表
                        this.getMyCollectActivityList();
                    }
                    this.getintit()
                }
            },
            async getintit() {
                uni.showLoading({
                    title: '加载中'
                });
                if (this.currentTab === 0) {
                    this.query.type = 'video';
                    getMyCollectList(this.query).then(res => {
                        uni.hideLoading();
                        this.loading = false;
                        if (res.statusCode === 200) {
                            const newData = res.data.data
                            this.total = res.data.total || 0;
                            // 追加或替换数据
                            this.videoCollects = this.query.pageNumber === 1 ?
                                newData :
                                [...this.videoCollects, ...newData];
                            // 判断是否还有更多数据
                            this.noMore = newData.length < this.query.pageSize ||
                                this.videoCollects.length >= this.total;
                        }
                    })
                } else if (this.currentTab === 1) {
                    this.query.type = 'goods';
                    getMyCollectList(this.query).then(res => {
                        uni.hideLoading();
                        this.loading = false;
                        if (res.statusCode === 200) {
                            const newData = res.data.data
                            this.total = res.data.total || 0;
                            this.goodsCollects = this.query.pageNumber === 1 ?
                                newData :
                                [...this.goodsCollects, ...newData];
                            // 判断是否还有更多数据
                            this.noMore = newData.length < this.query.pageSize ||
                                this.goodsCollects.length >= this.total;
                        }
                    })
                } else if (this.currentTab === 2) {
                    this.query.type = 'activity';
                    getMyCollectList(this.query).then(res => {
                        uni.hideLoading();
                        this.loading = false;
                        if (res.statusCode === 200) {
                            const newData = res.data.data
                            this.total = res.data.total || 0;
                            this.activityCollects = this.query.pageNumber === 1 ?
                                newData :
                                [...this.activityCollects, ...newData];
                            this.noMore = newData.length < this.query.pageSize ||
                                this.activityCollects.length >= this.total;
                        }
                    })
                }
            },
            getMyCollectionStoreList() {
                uni.showLoading({
                    title: '加载中'
                });
                uni.hideLoading();
            },
            getMyCollectionVideoList() {
                uni.showLoading({
                    title: '加载中'
                });
                uni.hideLoading();
            },
            getMyCollectActivityList() {
                uni.showLoading({
                    title: '加载中'
                });
                uni.hideLoading();
            },
            }
        }
    }
</script>
<style lang="scss">
    .text-cover {
        display: flex;
        align-items: center;
        justify-content: center;
        background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
        color: #fff;
        font-size: 28rpx;
        padding: 16rpx;
        line-height: 1.4;
    }
    /* 视频列表专用样式 */
    .video-item {
        display: flex;
        padding: 24rpx 0;
        border-bottom: 1rpx solid #f5f5f5;
        align-items: center;
        &:last-child {
            border-bottom: none;
        }
    }
    .video-cover-container {
        position: relative;
        width: 240rpx;
        height: 160rpx;
        border-radius: 12rpx;
        overflow: hidden;
        margin-right: 24rpx;
        flex-shrink: 0;
        .video-cover {
            width: 100%;
            height: 100%;
        }
        .play-icon {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            opacity: 0.9;
        }
        .video-duration {
            position: absolute;
            right: 8rpx;
            bottom: 8rpx;
            background: rgba(0, 0, 0, 0.6);
            color: #fff;
            font-size: 20rpx;
            padding: 4rpx 12rpx;
            border-radius: 20rpx;
        }
    }
    .video-info {
        flex: 1;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        height: 160rpx;
        .video-title {
            font-size: 30rpx;
            color: #333;
            font-weight: bold;
            display: -webkit-box;
            -webkit-box-orient: vertical;
            -webkit-line-clamp: 2;
            overflow: hidden;
        }
        .video-meta {
            display: flex;
            justify-content: space-between;
            font-size: 24rpx;
            color: #999;
            .video-weight {
                display: flex;
                align-items: center;
            }
        }
    }
    .video-actions {
        margin-left: 20rpx;
        flex-shrink: 0;
        .cancel-btn {
            background: #f5f5f5;
            color: #666;
            border: none;
            font-size: 24rpx;
            padding: 8rpx 20rpx;
            border-radius: 20rpx;
            &:active {
                background: #eee;
            }
        }
    }
    .empty-tip {
        text-align: center;
        padding: 100rpx 0;
        image {
            width: 300rpx;
            margin-bottom: 30rpx;
            opacity: 0.6;
        }
        text {
            display: block;
            font-size: 28rpx;
            color: #999;
        }
    }
    .activity-container {
        padding: 20rpx;
        background-color: #f5f5f5;
pages/userPermissions/addStoreMember.vue
@@ -14,12 +14,8 @@
                <u-form-item label="电话" prop="mobile" borderBottom required="true">
                    <u-input v-model="form.mobile" placeholder="请输入手机号码" border="none" type="number" />
                </u-form-item>
                <!-- 旧密码  -->
                <u-form-item label="旧密码" prop="password" borderBottom required="true" v-if="false">
                    <u-input v-model="form.oldPassword" placeholder="请输入密码" border="none" type="password" />
                </u-form-item>
                <!-- 密码 -->
                <u-form-item label="新密码" prop="password" borderBottom required="true">
                <u-form-item label="密码" prop="password" borderBottom required="true" v-if="!form.id">
                    <u-input v-model="form.password" placeholder="请输入密码" border="none" type="password" />
                </u-form-item>
pages/userPermissions/userPermissions.vue
@@ -23,6 +23,7 @@
                </view>
                <!-- 操作按钮区域 -->
                <view class="action-buttons">
                    <u-button type="primary" size="mini" @click.stop="restPassword(user.memberId)" class="edit-btn">重置密码</u-button>
                    <u-button type="primary" size="mini" @click.stop="navigateToDetail(user.id)" class="edit-btn">修改</u-button>
                    <u-button type="error" size="mini" @click.stop="deleteUser(user.id)"
                        class="delete-btn">删除</u-button>
@@ -51,7 +52,8 @@
        getPage,
        del,
        add,
        update
        update,
        restPassword
    } from "@/api/userPermissions.js"
    import UIcon from '@/uview-components/uview-ui/components/u-icon/u-icon.vue';
    import UButton from '@/uview-components/uview-ui/components/u-button/u-button.vue';
@@ -95,6 +97,17 @@
            this.getPage()
        },
        methods: {
            restPassword(id){
                restPassword(id).then(res=>{
                    if(res.statusCode === 200){
                        uni.showToast({
                            title: res.data.msg, // 提示文字
                            icon: 'none', // 图标类型(success/loading/none)
                            mask: true // 是否显示透明蒙层(防止触摸穿透)
                        });
                    }
                })
            },
            async getPage() {
                //
                uni.showLoading({