tj
2025-05-21 7abdb24968bcb88f177851a30e41f282b9189a98
初步框架
已删除5个文件
已修改9个文件
已添加37个文件
5334 ■■■■ 文件已修改
App.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/comment/comment-item.vue 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/comment/comment-popup.vue 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/comment/comment-sub-item.vue 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/footer/customer-footer.vue 139 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/setting/setting-popup.vue 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/title/section-title-btn.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home-main.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/scene-museum-card.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
readme 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
static/common/left-arrow-dark.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/left-arrow-light.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/marker.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/order.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/right-arrow-dark.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/right-arrow-light.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/share2.png 补丁 | 查看 | 原始文档 | blame | 历史
static/common/smile.png 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-detail copy.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-detail.vue 428 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-list.vue 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-official-detail-delivery.vue 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-official-detail.vue 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-official-timeline.vue 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/mine/index copy 2.vue 416 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/mine/index copy.vue 397 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/mine/index.vue 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
theme.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/changelog.md 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/components/waterfall/js/utils.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/components/waterfall/waterfall-col.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/components/waterfall/waterfall-item.vue 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/components/waterfall/waterfall-list.vue 343 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/mock-data/waterfall-list.js 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/package.json 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/pages/waterfall/waterfall.vue 363 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/readme.md 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/static/waterfall/0.jpg 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/static/waterfall/1.jpg 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/static/waterfall/2.jpg 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/static/waterfall/empty.png 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/helang-waterfall/static/waterfall/fail.png 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-nav-bar/changelog.md 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue 357 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-nav-bar/package.json 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uni-nav-bar/readme.md 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App.vue
@@ -72,7 +72,6 @@
    }
</script>
</style>
<style lang="scss">
    /*每个页面公共css */
    @import '@/uni_modules/uni-scss/index.scss';
components/comment/comment-item.vue
对比新文件
@@ -0,0 +1,153 @@
<template>
  <view class="comment-item">
    <view class="comment-item-header">
      <view class="comment-item-header-left">
        <view class="avatar">
          <u-avatar :src="avatar" size="50" shape="circle" />
        </view>
        <view class="comment-content">
          <view class="author">
            <text class="nickname">{{ nickname }}</text>
            <view v-if="isAuthor" class="author-flag">作者</view>
          </view>
          <view class="comment-content-text">
            <text>{{ content }}</text>
            <u-album :urls="images" multipleSize="180" singleSize="500"></u-album>
            <text class="date">{{ date }}</text>
            <text class="address">{{ address }}</text>
            <text class="reply" @click="onReply">回复</text>
          </view>
        </view>
        <view class="comment-opeartor">
          <u-icon name="heart" size="30" />
          <view class="comment-opeartor-heart-number">{{ likes }}</view>
        </view>
      </view>
    </view>
    <view style="margin-left: 60rpx; margin-top: 10rrx;">
      <comment-sub-item avatar="https://img.yzcdn.cn/vant/cat.jpeg" nickname="图墙精选" :isAuthor="true"
        content="如果路线里全是常规景区,没一个特殊的点位,那就是普通团,除非他的住宿安排的很好。。要不然和普通团没任何区别,都是去景区挤,然后上车赶路" :images="urls2" date="2天前"
        address="湖北" :likes="30" @reply="showCommentLayer" />
    </view>
  </view>
</template>
<script>
export default {
  name: 'CommentItem',
  props: {
    avatar: String,
    nickname: String,
    isAuthor: Boolean,
    content: String,
    images: {
      type: Array,
      default: () => [],
    },
    date: String,
    address: String,
    likes: Number,
  },
  data() {
    return {
      urls2: [
        "https://img.yzcdn.cn/vant/cat.jpeg",
        "https://img.yzcdn.cn/vant/cat.jpeg",
        "https://img.yzcdn.cn/vant/cat.jpeg",
        "https://img.yzcdn.cn/vant/cat.jpeg",
        "https://img.yzcdn.cn/vant/cat.jpeg",
      ],
    }
  },
  methods: {
    onReply() {
      this.$emit('reply');
    },
  },
};
</script>
<style lang="scss" scoped>
.comment-item {
  .comment-item-header {
    display: flex;
    align-items: center;
    .comment-item-header-left {
      display: flex;
      width: 100%;
      .comment-content {
        width: 92%;
        display: flex;
        flex-direction: column;
        padding-left: 10rpx;
        .author {
          display: flex;
          align-items: center;
          .nickname {
            font-size: 30rpx;
            color: #858585;
          }
          .author-flag {
            font-size: 20rpx;
            color: #ff4d4f;
            font-weight: bold;
            font-size: 18rpx;
            border-radius: 50rpx;
            padding: 5rpx 10rpx 5rpx 10rpx;
            background-color: rgba(219, 19, 22, 0.219);
            width: 40rpx;
            margin-left: 10rpx;
            height: 25rpx;
            // border: 1px solid #ff4d4f;
          }
        }
        .comment-content-text {
          margin-top: 10rpx;
          font-size: 26rpx;
          letter-spacing: 1rpx;
          line-height: 1.5;
          .date {
            font-size: 20rpx;
            padding: 10rpx;
            color: #858585;
          }
          .address {
            font-size: 20rpx;
            padding: 10rpx;
            color: #858585;
          }
          .reply {
            font-size: 24rpx;
            padding: 10rpx;
          }
        }
      }
      .comment-opeartor {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        // margin-left: 10rpx;
        .comment-opeartor-heart-number {
          width: 100%;
          text-align: center;
          font-size: 20rpx;
        }
      }
    }
  }
}
</style>
components/comment/comment-popup.vue
对比新文件
@@ -0,0 +1,166 @@
<template>
  <u-popup :show="value" mode="bottom" @open="handleOpen" @close="handleClose" closeOnClickOverlay>
    <view class="comment-popup">
      <u-textarea v-model="commentContent" placeholder="请输入内容" count :focus="isFocus" :cursor-spacing="150"
        style="background-color: #999999;"></u-textarea>
      <view class="comment-btn-view">
        <view class="comment-btn-icon">
          <u-icon name="@" size="60" color="#999999"></u-icon>
          <u-icon name="/static/common/smile.png" size="60" color="#999999"></u-icon>
          <u-icon name="photo" size="60" color="#999999" @click="chooseImage"></u-icon>
          <u-icon name="plus-circle" size="60" color="#999999"></u-icon>
        </view>
        <view class="comment-btn">
          <u-button :disabled="!canSend" type="error" shape="circle" text="发送" size="mini"
            @click="sendComment"></u-button>
        </view>
      </view>
      <!-- 只有在图片选择后才显示上传组件 -->
      <view class="comment-image-upload" v-if="fileList.length > 0">
        <u-upload :file-list="fileList" name="file" :auto-upload="false" :max-count="9" @afterRead="afterRead"
          @delete="handleDelete" upload-text="上传图片" width="150" height="150" :cursor-spacing="150"
          multiple="true"></u-upload>
      </view>
    </view>
  </u-popup>
</template>
<script>
export default {
  name: "CommentPopup",
  props: {
    value: Boolean
  },
  data() {
    return {
      commentContent: "",
      isFocus: false,
      focusLock: false,
      fileList: [],
      uploadUrl: "https://your-api.com/upload", // 替换为你的上传接口
    };
  },
  watch: {
    value(newVal) {
      if (!newVal) {
        this.isFocus = false;
      }
    }
  },
  computed: {
    canSend() {
      return this.commentContent.trim() !== '' || this.fileList.length > 0;
    }
  },
  methods: {
    handleOpen() {
      if (this.focusLock) return;
      this.focusLock = true;
      setTimeout(() => {
        this.isFocus = true;
        this.focusLock = false;
      }, 300);
    },
    handleClose() {
      this.isFocus = false;
      this.$emit("input", false);
    },
    chooseImage() {
      uni.chooseImage({
        count: 9 - this.fileList.length,
        sizeType: ["original", "compressed"],
        sourceType: ["album", "camera"],
        success: (res) => {
          console.log("选择的图片路径:", res.tempFilePaths);
          res.tempFilePaths.forEach(filePath => {
            const fileItem = {
              url: filePath,
              status: 'ready'
            };
            this.fileList.push(fileItem);
            this.uploadImage(filePath); // 调用上传
          });
        },
        fail: (err) => {
          console.log("选择失败", err);
        }
      });
    },
    uploadImage(filePath) {
      uni.uploadFile({
        url: this.uploadUrl,
        filePath,
        name: 'file',
        success: (res) => {
          console.log('上传成功', res);
          // 你可以解析返回值,并更新 fileList 中对应的项
        },
        fail: (err) => {
          console.error('上传失败', err);
        }
      });
    },
    afterRead(file) {
      // 理论上这个不会触发了,因为你已经自己上传了
    },
    handleDelete(index, file) {
      this.fileList.splice(index, 1);
    },
    sendComment() {
      if (!this.canSend) return;
      console.log("发送评论:", this.commentContent);
      console.log("附带图片:", this.fileList.map(f => f.url));
      // 这里处理评论内容和图片上传路径的提交逻辑
      // 例如向后端接口提交
      // 清空状态
      this.commentContent = "";
      this.fileList = [];
      this.handleClose();
    }
  }
};
</script>
<style scoped>
.comment-popup {
  padding: 20rpx;
}
.comment-btn-view {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 20rpx;
}
.comment-btn-icon {
  display: flex;
  gap: 20rpx;
}
.comment-btn {
  flex-shrink: 0;
}
.comment-image-upload {
  margin-top: 20rpx;
}
.horizontal-scroll {
  white-space: nowrap;
  overflow-x: auto;
  padding: 10rpx 0;
}
.scroll-content {
  display: inline-block;
}
</style>
components/comment/comment-sub-item.vue
对比新文件
@@ -0,0 +1,139 @@
<template>
    <view class="comment-item">
      <view class="comment-item-header">
        <view class="comment-item-header-left">
          <view class="avatar">
            <u-avatar :src="avatar" size="40" shape="circle" />
          </view>
          <view class="comment-content">
            <view class="author">
              <text class="nickname">{{ nickname }}</text>
              <view v-if="isAuthor" class="author-flag">作者</view>
            </view>
            <view class="comment-content-text">
              <text>{{ content }}</text>
              <u-album :urls="images" multipleSize="180" singleSize="500"></u-album>
              <text class="date">{{ date }}</text>
              <text class="address">{{ address }}</text>
              <text class="reply" @click="onReply">回复</text>
            </view>
          </view>
          <view class="comment-opeartor">
            <u-icon name="heart" size="30" />
            <view class="comment-opeartor-heart-number">{{ likes }}</view>
          </view>
        </view>
      </view>
    </view>
  </template>
  <script>
  export default {
    name: 'CommentItem',
    props: {
      avatar: String,
      nickname: String,
      isAuthor: Boolean,
      content: String,
      images: {
        type: Array,
        default: () => [],
      },
      date: String,
      address: String,
      likes: Number,
    },
    methods: {
      onReply() {
        this.$emit('reply');
      },
    },
  };
  </script>
  <style lang="scss" scoped>
  .comment-item {
        .comment-item-header {
            display: flex;
            align-items: center;
            .comment-item-header-left {
                display: flex;
                width: 100%;
                .comment-content {
                    width: 92%;
                    display: flex;
                    flex-direction: column;
                    padding-left: 10rpx;
                    .author {
                        display: flex;
                        align-items: center;
                        .nickname {
                            font-size: 30rpx;
                            color: #858585;
                        }
                        .author-flag {
                            font-size: 20rpx;
                            color: #ff4d4f;
                            font-weight: bold;
                            font-size: 18rpx;
                            border-radius: 50rpx;
                            padding: 5rpx 10rpx 5rpx 10rpx;
                            background-color: rgba(219, 19, 22, 0.219);
                            width: 40rpx;
                            margin-left: 10rpx;
                            height: 25rpx;
                            // border: 1px solid #ff4d4f;
                        }
                    }
                    .comment-content-text {
                        margin-top: 10rpx;
                        font-size: 26rpx;
                        letter-spacing: 1rpx;
                        line-height: 1.5;
                        .date {
                            font-size: 20rpx;
                            padding: 10rpx;
                            color: #858585;
                        }
                        .address {
                            font-size: 20rpx;
                            padding: 10rpx;
                            color: #858585;
                        }
                        .reply {
                            font-size: 24rpx;
                            padding: 10rpx;
                        }
                    }
                }
                .comment-opeartor {
                    display: flex;
                    flex-direction: column;
                    align-items: flex-start;
                    // margin-left: 10rpx;
                    .comment-opeartor-heart-number {
                        width: 100%;
                        text-align: center;
                        font-size: 20rpx;
                    }
                }
            }
        }
    }
  </style>
components/footer/customer-footer.vue
@@ -1,9 +1,10 @@
<template>
    <view class="footer flex customer-footer flex-wrap-normal" style="">
        <view v-for="(item,index) in tabBar" :key="index" class="footer-item">
            <view class="item" :class="flg==index?'cur':''" @click="go(index,item)">
        <view v-for="(item, index) in tabBar" :key="index" class="footer-item">
            <!-- <view class="item" :class="flg == index ? 'cur' : ''" @click="go(index, item)"> -->
            <view class="item" :class="currentIndex === index ? 'cur' : ''" @click="go(index, item)">
                <!-- <view v-if="index==0">
            <!-- <view v-if="index==0">
                    <image src="../../static/images/customer/footer/footer-home-1.png" class="footer-icon"
                        v-if="flg==0"></image>
                    <image src="../../static/images/customer/footer/footer-home-0.png" class="footer-icon"
@@ -30,85 +31,85 @@
                        v-if="flg!=3"></image>
                </view> -->
                <view>
                    {{item.text}}
                </view>
            <view>
                {{ item.text }}
            </view>
        </view>
    </view>
    </view>
</template>
<script>
    export default {
        data() {
            return {
                tabBar: [{
export default {
    data() {
        return {
            tabBar: [{
                        "text": "首页",
                        "pagePath": '/pages/home/home'
                    },
                    {
                        "text": "影视地图",
                        "pagePath": '/sub-pages/film-map/index'
                    },
                    {
                        "text": "行程规划",
                        "pagePath": '/sub-pages/customer/shopping/shopping'
                    },
                    {
                        "text": "社区",
                        "pagePath": '/pages/user/supplier-user'
                    },
                    {
                        "text": "我的",
                        "pagePath": '/pages/user/supplier-user'
                    }
                ]
            }
        },
        props: {
            flg: {
                type: String,
                "text": "首页",
                "pagePath": '/pages/home/home'
            },
            bussincess: {
                type: Boolean,
                default () {
                    return false
                }
            }
        },
        created() {},
        methods: {
            {
            go(index, item) {
                debugger;
                uni.navigateTo({
                        url: item.pagePath
                    })
                // if (index === 1 || index === 2) {
                //     //交易大厅和购物车,这2个可以用跳转方式
                //     uni.navigateTo({
                //         url: item.pagePath
                //     })
                // } else {
                //     uni.reLaunch({
                //         url: item.pagePath
                //     });
                // }
                "text": "影视地图",
                "pagePath": '/sub-pages/film-map/index'
            },
            {
                "text": "我的种草",
                "pagePath": '/sub-pages/customer/shopping/shopping'
            },
            {
                "text": "社区",
                "pagePath": '/pages/user/supplier-user'
            },
            {
                "text": "我的",
                "pagePath": '/sub-pages/mine/index'
            }
            ]
        }
    },
    props: {
        flg: {
            type: String,
        },
        bussincess: {
            type: Boolean,
            default() {
                return false
            }
        }
    },
    created() { },
    computed: {
        currentPath() {
            const pages = getCurrentPages();
            const currentPage = pages[pages.length - 1];
            return '/' + currentPage.route;
        },
        currentIndex() {
            return this.tabBar.findIndex(item => this.normalizePath(item.pagePath) === this.normalizePath(this.currentPath));
        }
    },
    methods: {
        normalizePath(path) {
            return path.replace(/^\/+/, '/');
        },
        go(index, item) {
            // uni.navigateTo({
            //         url: item.pagePath
            //     })
            uni.reLaunch({
                url: item.pagePath
            })
        },
    }
}
</script>
<style lang="scss">
    @import "./main.scss";
@import "./main.scss";
</style>
components/setting/setting-popup.vue
对比新文件
@@ -0,0 +1,167 @@
<template>
  <u-popup :show="value" mode="left" @open="handleOpen" @close="handleClose" closeOnClickOverlay
    customStyle="background-color: #FAFAFA;">
    <scroll-view scroll-y class="setting-scroll">
      <view class="setting-page">
        <u-cell-group :border="false" :customStyle="cellGroupStyle">
          <u-cell title="发现好友" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
        </u-cell-group>
        <u-cell-group :border="false" :customStyle="cellGroupStyle">
          <u-cell title="创作者中心" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
        </u-cell-group>
        <u-cell-group :border="false" :customStyle="cellGroupStyle">
          <u-cell title="我的草稿" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
          <u-cell title="我的评论" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
          <u-cell title="浏览记录" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
        </u-cell-group>
        <u-cell-group :border="false" :customStyle="cellGroupStyle">
          <u-cell title="订单" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
          <u-cell title="购物车" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
          <u-cell title="钱包" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
          <u-cell title="小程序" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
          <u-cell title="社区公约" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
        </u-cell-group>
        <u-cell-group :border="false" :customStyle="cellGroupStyle">
          <u-cell title="小程序" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
        </u-cell-group>
        <u-cell-group :border="false" :customStyle="cellGroupStyle">
          <u-cell title="社区公约" url="/pages/componentsB/tag/tag" :border="false">
            <u-icon slot="icon" :size="btnSize" name="search"></u-icon>
          </u-cell>
        </u-cell-group>
      </view>
    </scroll-view>
    <view class="opeartor-view">
      <u-grid :border="false" @click="click">
        <u-grid-item v-for="(baseListItem, baseListIndex) in baseList" :key="baseListIndex"
          @click="handleGridClick(baseListItem, baseListIndex)">
          <u-icon :customStyle="{ paddingTop: 20 + 'rpx' }" :name="baseListItem.name" :size="30"></u-icon>
          <text class="grid-text">{{ baseListItem.title }}</text>
        </u-grid-item>
      </u-grid>
    </view>
  </u-popup>
</template>
<script>
export default {
  name: "SettingPopup",
  props: {
    value: Boolean
  },
  data() {
    return {
      cellGroupStyle: 'background-color: #ffffff; margin-bottom: 20rpx; border-radius: 16rpx;',
      btnSize: 50,
      baseList: [{
        name: 'scan',
        title: '扫一扫'
      },
      {
        name: 'kefu-ermai',
        title: '帮助与客服'
      },
      {
        name: 'setting',
        title: '设置'
      },
      ]
    };
  },
  watch: {
    value(newVal) {
      if (!newVal) {
      }
    }
  },
  computed: {
  },
  methods: {
    handleGridClick(item, index) {
      console.log('点击了:', item, '索引为:', index);
      // 举例:根据 title 跳转
      if (item.title === '订单') {
        uni.navigateTo({
          url: '/pages/order/index'
        });
      } else if (item.title === '购物车') {
        uni.navigateTo({
          url: '/pages/cart/index'
        });
      } else {
        // 通用跳转
        uni.navigateTo({
          url: item.url || '/pages/default/index'
        });
      }
    },
    handleOpen() { },
    handleClose() {
      this.$emit("input", false);
    },
  }
};
</script>
<style lang="scss" scoped>
.setting-page {
  width: 400rpx;
  padding: 20rpx;
  box-sizing: border-box;
  overflow-y: auto;
  height: calc(100vh - 200rpx);
}
.cell-group {
  margin-bottom: 20rpx;
  background-color: #FFFFFF;
}
.opeartor-view {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  padding: 10rpx;
  box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
  z-index: 10;
  box-sizing: border-box;
  height: 200rpx;
  .grid-text {
    font-size: 26rpx;
    color: #909399;
    padding: 10rpx 0 20rpx 0rpx;
    /* #ifndef APP-PLUS */
    box-sizing: border-box;
    /* #endif */
  }
}
</style>
components/title/section-title-btn.vue
对比新文件
@@ -0,0 +1,105 @@
<template>
<view class="section">
  <u-row>
    <u-col span="6">
      <view class="row">
        <u-icon v-if="iconName" :name="iconName" :size="iconSize" :color="iconColor" />
        <text class="theme-text left-text">{{ title }}</text>
      </view>
    </u-col>
    <u-col span="3"></u-col>
    <u-col span="3">
      <view class="row-right" @click="go(goUrl)">
        <text class="theme-text right-text ">{{ optitle }} </text>
        <u-icon name="arrow-rightward" size="25" :color="'var(--icon-color)'" />
      </view>
    </u-col>
  </u-row>
</view>
</template>
<script>
export default {
  name: 'SectionTitle',
  props: {
    iconName: {
      type: String,
      default: ''
    },
    iconSize: {
      type: [String, Number],
      default: 40
    },
    iconColor: {
      type: String,
      default: 'yellow'
    },
    title: {
      type: String,
      default: ''
    },
    optitle: {
      type: String,
      default: ''
    },
    opIconColor: {
      type: String,
      default: 'white'
    },
    goUrl: {
      type: String,
      default: ''
    }
  },
  methods: {
    go(url) {
      if (url) {
        uni.navigateTo({
          url
        });
      }
    }
  }
}
</script>
<style scoped>
.section{
  margin-top: 30rpx;
}
.space-between {
  justify-content: space-between;
}
.row {
  display: flex;
  align-items: center;
  padding: 20rpx 0;
}
.row-right{
  display: flex;
  align-items: center;
  padding: 20rpx 0;
  justify-content: end;
}
.left-text{
  font-size: 40rpx;
  font-weight: bold;
  color: var(--icon-color);
  letter-spacing: 5rpx;
}
.right-text{
  font-size: 25rpx;
  margin-right: 10rpx;
}
.theme-text {
  margin-left: 10rpx;
}
</style>
main.js
@@ -63,6 +63,18 @@
import sectionTitle from '@/components/title/section-title'
Vue.component('section-title', sectionTitle)
import sectionTitleBtn from '@/components/title/section-title-btn'
Vue.component('section-title-btn', sectionTitleBtn)
// 评论组件
import commentTtem from '@/components/comment/comment-item'
Vue.component('comment-item', commentTtem)
import commentsubTtem from '@/components/comment/comment-sub-item'
Vue.component('comment-sub-item', commentsubTtem)
import commentPopup from '@/components/comment/comment-popup'
Vue.component('comment-popup', commentPopup)
import topTabs from '@/components/top-tabs.vue'
Vue.component('top-tabs', topTabs)
package-lock.json
@@ -6,7 +6,7 @@
        "": {
            "dependencies": {
                "moment": "^2.30.1",
                "uview-ui": "^2.0.38"
                "uview-ui": "^2.0.36"
            },
            "devDependencies": {
                "sass": "^1.77.6",
@@ -409,9 +409,9 @@
            "dev": true
        },
        "node_modules/@types/node": {
            "version": "22.15.17",
            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz",
            "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==",
            "version": "22.15.18",
            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz",
            "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==",
            "dev": true,
            "peer": true,
            "dependencies": {
@@ -802,9 +802,9 @@
            }
        },
        "node_modules/electron-to-chromium": {
            "version": "1.5.152",
            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz",
            "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==",
            "version": "1.5.155",
            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
            "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
            "dev": true,
            "peer": true
        },
@@ -1239,9 +1239,9 @@
            "peer": true
        },
        "node_modules/sass": {
            "version": "1.88.0",
            "resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
            "integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
            "version": "1.89.0",
            "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz",
            "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==",
            "dev": true,
            "dependencies": {
                "chokidar": "^4.0.0",
@@ -1392,14 +1392,14 @@
            }
        },
        "node_modules/terser": {
            "version": "5.39.0",
            "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
            "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
            "version": "5.39.2",
            "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
            "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
            "dev": true,
            "peer": true,
            "dependencies": {
                "@jridgewell/source-map": "^0.3.3",
                "acorn": "^8.8.2",
                "acorn": "^8.14.0",
                "commander": "^2.20.0",
                "source-map-support": "~0.5.20"
            },
@@ -1909,9 +1909,9 @@
            "dev": true
        },
        "@types/node": {
            "version": "22.15.17",
            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz",
            "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==",
            "version": "22.15.18",
            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz",
            "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==",
            "dev": true,
            "peer": true,
            "requires": {
@@ -2225,9 +2225,9 @@
            "optional": true
        },
        "electron-to-chromium": {
            "version": "1.5.152",
            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz",
            "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==",
            "version": "1.5.155",
            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
            "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
            "dev": true,
            "peer": true
        },
@@ -2552,9 +2552,9 @@
            "peer": true
        },
        "sass": {
            "version": "1.88.0",
            "resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
            "integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
            "version": "1.89.0",
            "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz",
            "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==",
            "dev": true,
            "requires": {
                "@parcel/watcher": "^2.4.1",
@@ -2645,14 +2645,14 @@
            "peer": true
        },
        "terser": {
            "version": "5.39.0",
            "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
            "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
            "version": "5.39.2",
            "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
            "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
            "dev": true,
            "peer": true,
            "requires": {
                "@jridgewell/source-map": "^0.3.3",
                "acorn": "^8.8.2",
                "acorn": "^8.14.0",
                "commander": "^2.20.0",
                "source-map-support": "~0.5.20"
            }
pages.json
@@ -27,6 +27,47 @@
                    }
                }
            ]
        },
        {
            "root": "sub-pages/film-list",
            "pages": [
                {
                    "path": "film-list",
                    "style": {
                        "navigationBarTitleText": "",
                        "enablePullDownRefresh": true
                    }
                }
                ,{
                    "path": "film-detail",
                    "style": {
                        "navigationBarTitleText": "",
                        "enablePullDownRefresh": true
                        // ,"navigationStyle": "custom"
                    }
                }
                ,{
                    "path": "film-official-detail",
                    "style": {
                        "navigationBarTitleText": "",
                        "enablePullDownRefresh": true
                        // ,"navigationStyle": "custom"
                    }
                }
            ]
        },
        {
            "root": "sub-pages/mine",
            "pages": [
                {
                    "path": "index",
                    "style": {
                        "navigationBarTitleText": "",
                        "enablePullDownRefresh": true
                    }
                }
            ]
        }
    ],
    "globalStyle": {
pages/home/home-main.vue
@@ -60,7 +60,11 @@
    },
    methods: {
        startExplore() {
            this.showVideo = true;
            // this.showVideo = true;
            // 改成跳转到具体页面
            uni.navigateTo({
                url: '/sub-pages/film-list/film-list'
            });
        },
        onVideoEnd() {
            // 切换下一个视频
pages/home/home.vue
@@ -4,14 +4,39 @@
        <view class="main-content">
            <!-- 主要内容展示 -->
            <HomeMain></HomeMain>
            <!-- <view class="row">
                <u-icon name="star" size="40" color="yellow" />
                <text class="theme-text">内容精选</text>
            </view> -->
            <section-title title="内容精选" optitle="查看全部" goUrl="/pages/home/home-more" />
            <trip-card v-for="(item, index) in tripCardList" :key="index" :tag="item.tag" :title="item.title"
                :subtitle="item.subtitle" :score="item.score" :imageUrl="item.imageUrl" :detailUrl="item.detailUrl" />
            <!--
            <section-title-btn title="内容精选" optitle="查看全部" goUrl="/pages/home/home-more" /> -->
            <view class="section">
                <u-row>
                    <u-col span="6">
                        <view class="row">
                            <!-- <u-icon v-if="iconName" :name="iconName" :size="iconSize" :color="iconColor" /> -->
                            <text class="theme-text left-text">内容精选</text>
                        </view>
                    </u-col>
                    <u-col span="3"></u-col>
                    <u-col span="3">
                        <view class="row-right">
                            <u-icon name="/static/common/left-arrow-dark.png" size="80" @click="prevPage" />
                            <u-icon name="/static/common/right-arrow-dark.png" size="80" @click="nextPage" />
                        </view>
                    </u-col>
                </u-row>
            </view>
            <view class="trip-card-swiper">
                <swiper :current="currentPage" @change="onSwiperChange" circular style="min-height: 1500rpx;">
                    <swiper-item v-for="(group, pageIndex) in pagedTripCards" :key="pageIndex">
                        <trip-card v-for="(item, index) in group" :key="index" :tag="item.tag" :title="item.title"
                            :subtitle="item.subtitle" :score="item.score" :imageUrl="item.imageUrl"
                            :detailUrl="item.detailUrl" />
                    </swiper-item>
                </swiper>
            </view>
            <!-- <trip-card v-for="(item, index) in visibleTripCards" :key="index" :tag="item.tag" :title="item.title"
                :subtitle="item.subtitle" :score="item.score" :imageUrl="item.imageUrl" :detailUrl="item.detailUrl" /> -->
            <section-title title="全球影视地标" optitle="查看全部" goUrl="/pages/home/home-more" />
            <global-geo />
@@ -53,6 +78,22 @@
                return state.currentInfo || {}
            },
        }),
        visibleTripCards() {
            const start = this.currentPage * this.pageSize;
            const end = start + this.pageSize;
            return this.tripCardList.slice(start, end);
        },
        totalPages() {
            return Math.ceil(this.tripCardList.length / this.pageSize);
        },
        pagedTripCards() {
            const pageSize = 3;
            const pages = [];
            for (let i = 0; i < this.tripCardList.length; i += pageSize) {
                pages.push(this.tripCardList.slice(i, i + pageSize));
            }
            return pages;
        },
    },
    components: {
        HomeMain,
@@ -91,8 +132,34 @@
                    score: 4.7,
                    imageUrl: 'https://ai-public.mastergo.com/ai/img_res/f2262cda01488f5f44f6eabf63e5dc5a.jpg',
                    detailUrl: '/pages/detail?id=125'
                },
                {
                    tag: '史诗邮轮2',
                    title: '《权力的游戏》君临城朝圣之旅',
                    subtitle: '克罗地亚杜布罗夫尼克5日深度游',
                    score: 4.9,
                    imageUrl: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg',
                    detailUrl: '/pages/detail?id=123'
                },
                {
                    tag: '经典路线2',
                    title: '冰岛极光探索之旅',
                    subtitle: '冬季极地风光6日深度体验',
                    score: 4.8,
                    imageUrl: 'https://ai-public.mastergo.com/ai/img_res/1a65e5ff6452efb1e6db001fcefd0b42.jpg',
                    detailUrl: '/pages/detail?id=124'
                },
                {
                    tag: '文化探秘2',
                    title: '日本京都文化巡游',
                    subtitle: '探访古都名刹5日行程',
                    score: 4.7,
                    imageUrl: 'https://ai-public.mastergo.com/ai/img_res/f2262cda01488f5f44f6eabf63e5dc5a.jpg',
                    detailUrl: '/pages/detail?id=125'
                }
            ],
            currentPage: 0,
            pageSize: 3,
            cardList: [
                {
                    image: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg',
@@ -149,7 +216,22 @@
        this.theme = theme
    },
    methods: {
        nextPage() {
            if (this.currentPage < this.totalPages - 1) {
                this.currentPage++;
            }
            // else{
            //     this.currentPage = 0;
            // }
        },
        prevPage() {
            if (this.currentPage > 0) {
                this.currentPage--;
            }
        },
        onSwiperChange(e) {
            this.currentPage = e.detail.current;
        },
    }
}
@@ -157,7 +239,7 @@
<style scoped>
.main-content {
    margin: 40rpx;
    margin: 30rpx;
}
.row {
@@ -166,4 +248,44 @@
    margin-top: 20rpx;
    margin-bottom: 20rpx;
}
</style>
<style scoped lang="scss">
.section {
    // margin-top: 30rpx;
    .space-between {
        justify-content: space-between;
    }
    .row {
        display: flex;
        align-items: center;
        padding: 20rpx 0;
    }
    .row-right {
        display: flex;
        align-items: center;
        padding: 20rpx 0;
        // justify-content: end;
        justify-content: space-between;
    }
    .left-text {
        font-size: 40rpx;
        font-weight: bold;
        color: var(--icon-color);
        letter-spacing: 5rpx;
    }
    .right-text {
        font-size: 25rpx;
        margin-right: 10rpx;
    }
    .theme-text {
        margin-left: 10rpx;
    }
}
</style>
pages/home/scene-museum-card.vue
@@ -35,6 +35,7 @@
      default: ""
    }
  }
}
</script>
readme
对比新文件
@@ -0,0 +1,5 @@
swiper-list 左右滑动切换顶部选项卡
https://ext.dcloud.net.cn/plugin?id=2128
uni-swiper-dot 轮播图指示点
https://ext.dcloud.net.cn/plugin?id=284
static/common/left-arrow-dark.png
static/common/left-arrow-light.png
static/common/marker.png
static/common/order.png
static/common/right-arrow-dark.png
static/common/right-arrow-light.png
static/common/share2.png
static/common/smile.png
sub-pages/film-list/film-detail copy.vue
对比新文件
@@ -0,0 +1,92 @@
<template>
    <view :class="['app', theme]">
        <!-- <u-swiper :list="list6" height="500" @change="e => currentNum = e.current" :autoplay="false"
            indicatorStyle="right: 20px;top: 20px;"
        >
            <view slot="indicator" class="indicator-num">
                <text class="indicator-num__text">{{ currentNum + 1 }}/{{ list6.length }}</text>
            </view>
        </u-swiper> -->
        <view>
            <u-swiper :list="list6" height="500" @change="e => currentNum = e.current" :autoplay="false">
                <view slot="indicator" class="indicator">
                    <view class="indicator__dot" v-for="(item, index) in list6" :key="index"
                        :class="[index === currentNum && 'indicator__dot--active']">
                    </view>
                </view>
            </u-swiper>
        </view>
    </view>
</template>
<script>
export default {
    components: {
    },
    data() {
        return {
            current: 0,
            currentNum: 0,
            list6: [
                "https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg",
                "https://img.yzcdn.cn/vant/cat.jpeg",
                "https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg",
            ],
            list7: [
                "",
                "",
                "",
            ],
        }
    },
    onLoad(options) {
        // 尝试读取缓存设置的主题
        console.log("theme", uni.getStorageSync('theme'))
        const theme = uni.getStorageSync('theme') || 'light'
        this.theme = theme
    },
    methods: {
    }
}
</script>
<style lang="scss">
.indicator {
    @include flex(row);
    justify-content: center;
    &__dot {
        height: 6px;
        width: 6px;
        border-radius: 100px;
        background-color: rgba(255, 255, 255, 0.35);
        margin: 0 5px;
        transition: background-color 0.3s;
        &--active {
            background-color: red;
        }
    }
}
.indicator-num {
    padding: 2px 0;
    background-color: rgba(0, 0, 0, 0.35);
    border-radius: 100px;
    width: 35px;
    @include flex;
    justify-content: center;
    &__text {
        color: #ffffff;
        font-size: 12px;
    }
}
</style>
sub-pages/film-list/film-detail.vue
对比新文件
@@ -0,0 +1,428 @@
<template>
    <!-- <view :class="['app', theme]"> -->
    <view>
        <view class="page">
            <u-sticky bgColor="#fff">
                <view class="card-footer">
                    <view class="user-info">
                        <u-avatar :src="user.avatar" size="60" shape="circle" />
                        <view class="user-text">
                            <text class="nickname">{{ user.username }}</text>
                            <!-- <text class="date">{{ item.date }}</text> -->
                        </view>
                    </view>
                    <view class="opera-info">
                        <button class="custom-btn">关注</button>
                        <u-icon name="/static/common/share2.png" size="40" color="#999" />
                    </view>
                </view>
            </u-sticky>
            <scroll-view class="content" scroll-y>
                <view class="swiper-container">
                    <swiper :current="currentNum" @change="onSwiperChange" circular :autoplay="false"
                        class="custom-swiper">
                        <swiper-item v-for="(item, index) in list6" :key="index" class="swiper-item">
                            <image :src="item" mode="aspectFill" class="swiper-image" @tap="previewImage(index)" />
                        </swiper-item>
                    </swiper>
                    <!-- 自定义右上角索引 -->
                    <view class="indicator-num">
                        <text class="indicator-num__text">
                            {{ currentNum + 1 }}/{{ list6.length }}
                        </text>
                    </view>
                </view>
                <view class="article-content">
                    <view class="title content-item">
                        <text>我是标题标题我是标题标题我是标题标题我是标题标题</text>
                    </view>
                    <view class="content-item">
                        <rich-text :nodes="desc"></rich-text>
                    </view>
                    <view class="annotation content-item">
                        <text>4分钟前 美国</text>
                    </view>
                </view>
                <u-line></u-line>
                <view class="comment">
                    <view class="writer-view">
                        <u-icon name="chat-fill" size="60" @click="showCommentLayer"></u-icon>
                        <view class="comment-operation" @click="showCommentLayer">
                            <u-text size="12px" text="说点什么......" margin="0rpx 0rpx 0rpx 20rpx"
                                color="#B9B9B9"></u-text>
                        </view>
                    </view>
                    <comment-item avatar="https://img.yzcdn.cn/vant/cat.jpeg" nickname="图墙精选" :isAuthor="true"
                        content="如果路线里全是常规景区,没一个特殊的点位,那就是普通团,除非他的住宿安排的很好。。要不然和普通团没任何区别,都是去景区挤,然后上车赶路" :images="urls2"
                        date="2天前" address="湖北" :likes="30" @reply="showCommentLayer" />
                </view>
            </scroll-view>
            <!-- 固定底部输入框 -->
            <view class="comment-box">
                <view class="input-row">
                    <view class="comment-input" @click="showCommentLayer">
                        <u-text size="12px" text="说点什么......" margin="0rpx 0rpx 0rpx 20rpx" color="#B9B9B9"
                            confirmType="send"></u-text>
                    </view>
                    <u-icon name="heart" size="60" color="#B9B9B9" label="11"></u-icon>
                    <u-icon name="star" size="60" color="#B9B9B9" label="22"></u-icon>
                    <u-icon name="chat" size="60" color="#B9B9B9" label="33"></u-icon>
                </view>
            </view>
        </view>
        <!-- <u-popup :show="commentShow" mode="bottom" @close="close" @open="open" closeOnClickOverlay="true">
            <view class="comment-popup">
                <u--textarea v-model="commentContent" placeholder="请输入内容" count focus="true"></u--textarea>
                <view class="comment-btn-view">
                    <view class="comment-btn-icon">
                        <u-icon name=" " size="40" color="#999999" label="@" @click="close"></u-icon>
                        <u-icon name="/static/common/smile.png" size="40" color="#999999" @click="close"></u-icon>
                        <u-icon name="photo" size="40" color="#999999" @click="close"></u-icon>
                        <u-icon name="plus-circle" size="40" color="#999999" @click="close"></u-icon>
                    </view>
                    <view class="comment-btn">
                        <u-button type="error" shape="circle" text="发送" size="mini" disabled></u-button>
                    </view>
                </view>
            </view>
        </u-popup> -->
        <comment-popup v-model="commentShow" />
    </view>
</template>
<script>
export default {
    components: {
    },
    data() {
        return {
            commentShow: false,
            commentContent: '',
            value1: 0,
            current: 0,
            currentNum: 0,
            desc: `😭……
刚从新疆旅游回来,真的踩了好多坑!!打算假期去新疆的姐妹们一定要做好攻略再来!!
真的会少踩很多坑!熬夜整理这篇作为普通游客的避坑建议,一定要先看看!!
.
✅【新疆全年玩法】
🚩11-3月:北疆喀纳斯、禾木玩雪;南疆慕士塔格冰川、喀什古城
🚩4-5月:伊犁土尔根杏花沟;南疆帕米尔杏花、大西沟杏花、库里塔格沙漠
🚩6-8月:伊犁那拉提草原、喀拉峻草原、赛里木湖、独库公路、喀纳斯
🚩9-10月:北疆喀纳斯、禾木;南疆胡杨林、罗布人村寨
.
❇️【自驾or跟团】
✔️新疆每天路程比较远,自驾可能有点累,而且景点比较分散,可以选择报个小团,都是年轻人,玩的也自由。
❌不建议大团喔,人多且很多时间在等人,并且有好看的风景不能随走随停!
.
🚘【路线推荐】
✨D1:乌市—S201沙漠公路—乌伦古湖—五彩滩一布尔津
✨D2:布尔津—禾木桥—禾木村—禾木星空—住禾木
✨D3:禾木一观鱼亭一神仙湾月亮湾卧龙湾一住贾登峪/喀纳斯
✨D4:贾登峪一乌尔禾魔鬼城—乌尔禾
✨D5:乌尔禾一赛里木湖一果子沟大桥—清水河
✨D6:清水河—琼库什台-住特克斯
✨D7:琼库什台—那拉提
✨D8:那拉提—独库公路北段—唐布拉百里画廊—乌鲁木齐<br/>
.
#新疆是个好地方 #新疆旅行攻略 #新疆旅游 #不翻车旅行 #新疆小团 #新疆定制游 #旅游不踩坑 #新疆环线`,
            user: {
                id: 3,
                username: '图墙精选',
                avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
            },
            list6: [
                "https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg",
                "https://img.yzcdn.cn/vant/cat.jpeg",
                "https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg",
            ],
            inputValue: '',
            urls2: [
                "https://img.yzcdn.cn/vant/cat.jpeg"
            ],
        }
    },
    onLoad(options) {
        // 尝试读取缓存设置的主题
        console.log("theme", uni.getStorageSync('theme'))
        const theme = uni.getStorageSync('theme') || 'light'
        this.theme = theme
    },
    methods: {
        onSwiperChange(e) {
            this.currentNum = e.detail.current;
        },
        previewImage(index) {
            uni.previewImage({
                current: this.list6[index],
                urls: this.list6
            });
        },
        click1(e) { console.log('click1', e); },
        submitComment() {
            if (!this.inputValue) return;
            this.comments.push(this.inputValue);
            this.inputValue = '';
        },
        showCommentLayer(e) {
            this.commentShow = true
        },
        open() {
            // console.log('open');
        },
        close() {
            this.commentShow = false
            // console.log('close');
        }
    }
}
</script>
<style scoped>
.page {
    display: flex;
    flex-direction: column;
    height: 100vh;
    background-color: #fff;
}
.swiper-container {
    position: relative;
    width: 100%;
    height: 500rpx;
}
.custom-swiper {
    width: 100%;
    height: 100%;
}
.swiper-item {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
}
.swiper-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.indicator-num {
    position: absolute;
    top: 20rpx;
    right: 20rpx;
    padding: 4rpx 12rpx;
    background-color: rgba(0, 0, 0, 0.4);
    border-radius: 20rpx;
}
.indicator-num__text {
    color: #fff;
    font-size: 24rpx;
}
</style>
<style lang="scss" scoped>
.card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10rpx;
    background-color: #FFFFFF;
}
.user-info {
    display: flex;
    align-items: center;
    .user-text {
        font-size: 30rpx;
        line-height: 30px;
        margin-left: 10rpx;
        .nickname {
            font-weight: bold;
            display: block;
            color: #646464;
            letter-spacing: 2rpx;
        }
        .date {
            font-size: 12px;
            color: #aaa;
            margin-top: 10rpx;
        }
    }
}
.opera-info {
    display: flex;
    align-items: center;
    text {
        margin-left: 10rpx;
        font-size: 12px;
    }
}
.content {
    .annotation {
        margin-top: 10rpx;
        font-size: 20rpx;
        color: #646464;
    }
}
.comment {
    margin: 10rpx;
    // border-top: 1rpx solid #EBEBEB;
    .writer-view {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 30rpx;
        .comment-operation {
            font-size: 18rpx;
            background-color: #F4F4F6;
            height: 30px;
            line-height: 30px;
            border-radius: 20px;
            width: 92%;
        }
    }
}
.article-content {
    padding: 10rpx;
    font-size: 32rpx;
    letter-spacing: 1rpx;
    .title {
        font-size: 36rpx;
        font-weight: bold;
    }
    .content-item {
        margin-bottom: 20rpx;
    }
}
.comment-box {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 20rpx 20rpx;
    background-color: #fff;
    box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
    z-index: 999;
    height: 100rpx;
    .input-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: 80%;
        .comment-input {
            width: 300rpx;
            background-color: #F4F4F6;
            border-radius: 50rpx;
            height: 60rpx;
            display: flex;
            align-items: center;
        }
    }
    .flex-input {
        flex: 1;
        margin-right: 10rpx;
    }
}
.comment-popup {
    margin: 20rpx;
    .comment-btn-view {
        margin-top: 20rpx;
        display: flex;
        justify-content: space-between;
    }
    .comment-btn-icon{
        display: flex;
    }
    .comment-btn{
    }
}
</style>
<style scoped>
.custom-btn {
    /* background-color: #ff4d4f; */
    margin: 0 10rpx;
    /* 自定义背景色 */
    color: #ff4d4f;
    /* 字体颜色 */
    font-size: 20rpx;
    /* 字体大小 */
    border-radius: 50rpx;
    /* 圆角 */
    padding: 10rpx 20rpx;
    /* 内边距 */
    border: 1px solid #ff4d4f;
    /* 去除边框 */
    line-height: 1;
}
.custom-btn::after {
    border: none;
    /* 去除点击阴影边框 */
}
</style>
<style scoped>
.page {
    height: 100vh;
    display: flex;
    flex-direction: column;
}
.content {
    flex: 1;
    overflow-y: auto;
    padding-bottom: 80rpx;
    /* 留出评论区高度,避免遮挡 */
}
</style>
sub-pages/film-list/film-list.vue
对比新文件
@@ -0,0 +1,244 @@
<template>
  <view :class="['app', theme]">
    <!-- <view class="page"> -->
    <view style="padding-top: 10rpx ;">
      <u-search height="70" placeholder="搜索" searchIconSize="30" margin="0rpx 30rpx 0rpx 30rpx" v-model="search"
        :show-action="true" @search="onSearch" @custom="onSearch" @clickIcon="onSearch" />
    </view>
    <!-- <u-tabs :list="tabList" @click="click"></u-tabs> -->
    <u-tabs :list="tabList" @click="click">
      <view slot="right" style="padding-left: 4px;" @tap="$u.toast('插槽被点击')">
        <u-icon name="list" size="40" bold></u-icon>
      </view>
    </u-tabs>
    <view class="list-view">
      <waterfall ref="helangWaterfall" :col="2">
        <template v-slot:default="{ list, colWidth }">
          <waterfall-col v-for="(col, colIndex) in list" :key="colIndex" :colWidth="colWidth">
            <waterfall-item v-for="(item, itemIndex) in col" :key="item.__waterfall_renderId" :colIndex="colIndex"
              :reportHeightTime="item.__waterfall_reportHeightTime" @height="onRenderHeight">
              <!-- 自定义内容 -->
              <view class="card" @click="handleDetailClick(item)">
                <image :src="item.imgurl" mode="widthFix" class="card-image" style="width: 100%;" />
                <view class="card-title">
                  <u--text :lines="2" size="14px" :text="item.title" :bold="true"></u--text>
                </view>
                <view class="card-footer">
                  <view class="user-info">
                    <u-avatar :src="item.avatar" size="40" shape="circle" />
                    <view class="user-text">
                      <text class="nickname">{{ item.username }}</text>
                      <!-- <text class="date">{{ item.date }}</text> -->
                    </view>
                  </view>
                  <view class="opera-info">
                    <u-icon name="heart" size="30" color="#999" />
                    <text>{{ item.likes }}</text>
                  </view>
                </view>
              </view>
            </waterfall-item>
          </waterfall-col>
        </template>
      </waterfall>
    </view>
  </view>
</template>
<script>
import Waterfall from '@/uni_modules/helang-waterfall/components/waterfall/waterfall-list';
import WaterfallCol from '@/uni_modules/helang-waterfall/components/waterfall/waterfall-col';
import WaterfallItem from '@/uni_modules/helang-waterfall/components/waterfall/waterfall-item';
export default {
  components: {
    Waterfall,
    WaterfallCol,
    WaterfallItem,
  },
  data() {
    return {
      theme: 'light', // 默认 light,可切换为 dark
      windowHeight: uni.getSystemInfoSync().windowHeight,
      search: '',
      tabList: [{
        name: '关注',
      }, {
        name: '推荐',
      }, {
        name: '电影'
      }, {
        name: '科技'
      }, {
        name: '音乐'
      }, {
        name: '美食'
      }, {
        name: '文化'
      }, {
        name: '财经'
      }, {
        name: '手工'
      }],
      items: [
        {
          id: 1,
          title: 'iPhone 高清全屏壁纸,划走就后悔',
          url: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg',
          imgurl: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg',
          username: '草莓喵喵',
          avatar: '/static/avatar1.jpg',
          date: '2024-12-01',
          likes: 1397,
        },
        {
          id: 2,
          title: '高清 4K 全屏手机壁纸 #高质量壁纸高清 4K 全屏手机壁纸 #高质量壁纸高清 4K 全屏手机壁纸 #高质量壁纸',
          url: 'https://img.yzcdn.cn/vant/cat.jpeg',
          imgurl: 'https://img.yzcdn.cn/vant/cat.jpeg',
          username: '4K wallpaper',
          avatar: '/static/avatar2.jpg',
          date: '02-24',
          likes: 167,
        },
        {
          id: 3,
          title: 'iPhone 实况动态壁纸 #动态壁纸',
          url: '/static/wallpaper3.jpg',
          imgurl: 'https://img.yzcdn.cn/vant/cat.jpeg',
          username: '图墙精选',
          avatar: '/static/avatar3.jpg',
          date: '03-01',
          likes: 980,
        },
        {
          id: 4,
          title: '高清小清新壁纸 浪漫的人都有自己的海',
          url: '/static/wallpaper4.jpg',
          imgurl: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg',
          username: 'wallpaper',
          avatar: '/static/avatar4.jpg',
          date: '04-10',
          likes: 456,
        }
      ]
    }
  },
  onLoad(options) {
    // 尝试读取缓存设置的主题
    console.log("theme", uni.getStorageSync('theme'))
    const theme = uni.getStorageSync('theme') || 'light'
    this.theme = theme
    this.$refs.helangWaterfall.render(this.items, true);
  },
  methods: {
    onSearch(value) {
      debugger;
      uni.showToast({
        title: `搜索:${value}`,
        icon: 'none'
      });
    },
    handleDetailClick(item) {
      if (item.id === 1) {
        uni.navigateTo({
          url: '/sub-pages/film-list/film-official-detail?id=' + item.id
        })
      } else {
        uni.navigateTo({
          url: '/sub-pages/film-list/film-detail?id=' + item.id
        })
      }
    },
    onRenderHeight({ colIndex, height, reportHeightTimeChange }) {
      this.$refs.helangWaterfall.reportHeight({
        colIndex,
        height,
        reportHeightTimeChange
      })
    },
  }
}
</script>
<style scoped>
.page {
  background-color: #F5F5F5;
}
.list-view {
  margin-top: 10px;
}
.filter-bar {
  flex-wrap: wrap;
}
</style>
<style lang="scss">
.card {
  border-radius: 10rpx;
  background-color: #FFFFFF;
  font-size: 14px;
  line-height: 20px;
  color: rgb(51, 51, 51);
  // border: 1rpx solid rgb(51, 51, 51);
  .card-image {
    border-radius: inherit;
  }
  .card-title {
    padding: 10rpx;
    // font-size: 14px;
    font-weight: 500;
  }
  .card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10rpx;
  }
  .user-info {
    display: flex;
    align-items: center;
    .user-text {
      font-size: 18rpx;
      line-height: 14px;
      margin-left: 10rpx;
      .nickname {
        font-weight: bold;
        display: block;
        color: #646464;
      }
      .date {
        font-size: 12px;
        color: #aaa;
        margin-top: 10rpx;
      }
    }
  }
  .opera-info {
    display: flex;
    align-items: center;
    text {
      margin-left: 10rpx;
      font-size: 12px;
    }
  }
}
</style>
sub-pages/film-list/film-official-detail-delivery.vue
对比新文件
@@ -0,0 +1,114 @@
<template>
  <view class="timeline">
    <view class="timeline-item" v-for="(item, index) in timeList" :key="index">
      <!-- 左边时间点 -->
      <view class="timeline-left">
        <view class="dot"></view>
        <!-- 连线,最后一个不显示 -->
        <view class="line" v-if="index !== timeList.length - 1"></view>
      </view>
      <!-- 右边内容区域 -->
      <view class="timeline-content">
        <!-- 你可以在这里自定义内容 -->
        <view class="title">
          <text>{{ item.title }}</text>
          <text class="time">{{ item.time }}</text>
        </view>
        <view class="desc">
          {{ item.description }}
        </view>
        <view class="extra" v-if="item.button">
          <u-button type="primary" size="mini" @click="onAction(item)">处理</u-button>
        </view>
      </view>
    </view>
  </view>
</template>
<script>
export default {
  data() {
    return {
      timeList: [
        {
          time: '2025-05-01',
          title: '创建订单',
          description: '用户提交订单信息,等待支付。',
          button: true,
        },
        {
          time: '2025-05-02',
          title: '订单支付',
          description: '订单已支付,准备发货。',
        },
        {
          time: '2025-05-03',
          title: '发货完成',
          description: '物流:顺丰快递,单号 12345678',
        },
      ]
    }
  },
  methods: {
    onAction(item) {
      console.log('点击处理', item);
    }
  }
}
</script>
<style scoped>
.timeline {
  padding: 30rpx;
  position: relative;
}
.timeline-item {
  display: flex;
  position: relative;
  padding-bottom: 40rpx;
}
.timeline-left {
  width: 40rpx;
  position: relative;
  display: flex;
  justify-content: center;
}
.dot {
  width: 16rpx;
  height: 16rpx;
  background-color: #2979ff;
  border-radius: 50%;
  margin-top: 4rpx;
}
.line {
  position: absolute;
  top: 24rpx;
  width: 2rpx;
  height: calc(100% - 24rpx);
  background-color: #dcdfe6;
}
.timeline-content {
  flex: 1;
  padding-left: 20rpx;
}
.title {
  font-weight: bold;
  font-size: 28rpx;
  color: #333;
  display: flex;
  justify-content: space-between;
}
.time {
  font-size: 24rpx;
  color: #999;
}
.desc {
  margin-top: 10rpx;
  font-size: 26rpx;
  color: #666;
}
.extra {
  margin-top: 10rpx;
}
</style>
sub-pages/film-list/film-official-detail.vue
对比新文件
@@ -0,0 +1,273 @@
<template>
  <view class="container">
    <view class="movie-title">
      <view><u-text text="破.地狱" size="30"></u-text></view>
      <view><u-text text="The Last Dance" size="20"></u-text></view>
    </view>
    <scroll-view scroll-y class="content">
      <view class="movie-info">
        <image class="movie-poster" src="https://ai-public.mastergo.com/ai/img_res/1ef11f7a594da679d35cddf809a63ec7.jpg"
          mode="aspectFill" />
        <view class="movie-details">
          <view class="scene-number">
            <text>片场</text>
            <text>17</text>
          </view>
          <view class="movie-tags">
            <text class="tag">剧情/家庭</text>
            <text class="tag">中国香港</text>
            <text class="tag">2024</text>
          </view>
          <u-button text="more" size="normal" icon="play-right-fill" plain type="info"></u-button>
        </view>
      </view>
      <view class="movie-desc">
        <text class="desc-text">聚焦香港殡葬文化下的人际关系和生死观念,多数取景地都在殡仪服务扎堆的红磡。</text>
        <text class="desc-text">主要拍摄的殡仪馆外景是万国殡仪馆,不过根据电影官方发布的制作花絮,殡仪馆内景大厅是另外搭建的。</text>
        <text
          class="desc-text">道生接手的"长生店"是位于九龙城福佬村道的百年老字号合昌殡仪,"Hello文"吃宵夜的地方在九龙湾的康乐茶居,火化延姐的地方在葵涌火葬场,甄小姐存户的东华义庄是香港目前唯一仍在运营的义庄。</text>
      </view>
      <view class="map-container">
        <map style="width: 100%; height: 500rpx;" :latitude="latitude" :longitude="longitude" :markers="markers"
          scale="16" show-location></map>
      </view>
      <view>
        <u-tabs v-if="tabList.length" :list="tabList" :current="current" @change="onTabChange" @click="onTabChange"
          lineColor="#2979ff" activeStyle="color: #2979ff">
          <view slot="left" style="padding-left: 4px;" @tap="">
            <u-icon name="/static/common/order.png" size="40" bold></u-icon>
          </view>
          <view slot="right" style="padding-left: 4px;" @tap="$u.toast('插槽被点击')">
            <u-icon name="list" size="40" bold></u-icon>
          </view>
        </u-tabs>
        <swiper class="swiper-box" :current="current" @change="onSwiperChange" duration="300">
          <swiper-item v-for="(item, index) in tabList" :key="index" class="swiper-item">
            <film-official-timeline></film-official-timeline>
          </swiper-item>
        </swiper>
      </view>
      <view class="comment-section">
        <u-button class="comment-btn" type="primary" plain>我要发言</u-button>
      </view>
    </scroll-view>
    <view class="back-to-top" @click="scrollToTop">
      <u-icon name="arrow-up" size="24" color="#666666" />
    </view>
  </view>
</template>
<script>
import FilmOfficialTimeline from './film-official-timeline.vue';
export default {
  components: {
    FilmOfficialTimeline
  },
  onLoad(option) {
    // 获取当前位置
    uni.getLocation({
      type: 'gcj02',
      success: (res) => {
        this.latitude = res.latitude
        this.longitude = res.longitude
        this.markers = [
          {
            id: 1,
            latitude: res.latitude,
            longitude: res.longitude,
            title: "我的位置",
            iconPath: "/static/common/marker.png", // 自定义图标路径
            width: 30,
            height: 30
          }
        ]
      }
    }),
      uni.getSystemInfo({
        success: (e) => {
          // #ifdef MP-WEIXIN
          let custom = uni.getMenuButtonBoundingClientRect();
          this.offsetHeight = custom.bottom + custom.top - e.statusBarHeight + 68;
          // #endif
          console.log('height', this.offsetHeight)
        },
      });
  },
  data() {
    return {
      offsetHeight: 0,
      latitude: 0,
      longitude: 0,
      markers: [],
      current: 0,
      tabList: [
        { name: '剧情' },
        { name: '距离' },
        { name: '城市' },
      ],
    };
  },
  methods: {
    goBack() {
      uni.navigateBack();
    },
    scrollToTop() {
      uni.pageScrollTo({
        scrollTop: 0,
        duration: 300
      });
    }
    , onTabChange(item) {
      this.current = item.index; // 切换 swiper 面板
    },
    onSwiperChange(e) {
      this.current = e.detail.current; // 同步 tabs
    },
  }
};
</script>
<style lang="scss">
::v-deep .u-sticky {
  z-index: 999 !important;
  position: sticky;
  left: 0;
}
</style>
<style lang="scss" scoped>
.container {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: #ffffff;
  .movie-title {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    .center-text {
      width: 100%;
      text-align: center;
      display: block;
    }
  }
}
.content {
  flex: 1;
  overflow: auto;
}
.movie-info {
  padding: 32rpx;
  display: flex;
  gap: 32rpx;
}
.movie-poster {
  width: 400rpx;
  height: 500rpx;
  border-radius: 16rpx;
  flex-shrink: 0;
}
.movie-details {
  flex: 1;
}
.scene-number {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-size: 32px;
  color: #333333;
  font-weight: bold;
  margin-bottom: 16rpx;
}
.movie-tags {
  display: flex;
  flex-direction: column;
  gap: 16rpx;
  margin-bottom: 32rpx;
}
.tag {
  font-size: 14px;
  color: #666666;
}
.movie-desc {
  padding: 32rpx;
  border-top: 1px dashed #f5f5f5;
  border-bottom: 1px dashed #f5f5f5;
}
.desc-text {
  display: block;
  font-size: 14px;
  color: #666666;
  line-height: 1.6;
  margin-bottom: 24rpx;
}
.map-container {
  padding: 32rpx;
}
.map-image {
  width: 100%;
  border-radius: 16rpx;
}
.comment-section {
  padding: 32rpx;
}
.comment-btn {
  width: 100% !important;
  height: 88rpx;
  line-height: 88rpx;
  background-color: #f5f5f5 !important;
  color: #666666 !important;
  font-size: 14px;
}
.back-to-top {
  position: fixed;
  right: 32rpx;
  bottom: 32rpx;
  width: 80rpx;
  height: 80rpx;
  border-radius: 40rpx;
  background-color: #ffffff;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
}
.swiper-box {
  min-height: 50vh;
  /* 或者你希望的高度 */
  .swiper-item {
    height: 100%;
    overflow: auto;
    /* 允许滚动内容 */
  }
}
</style>
sub-pages/film-list/film-official-timeline.vue
对比新文件
@@ -0,0 +1,141 @@
<template>
  <view class="timeline-container">
    <view v-for="(item, index) in timeline" :key="index" class="timeline-item">
      <view class="timeline-time">{{ item.time }}</view>
      <view class="timeline-content">
        <view class="info">
          <text class="title">{{ item.title }}</text>
          <text class="desc">{{ item.subtitle }}</text>
          <text class="desc">{{ item.location }}</text>
          <text class="desc">{{ item.description }}</text>
        </view>
        <image :src="item.image" class="thumbnail" mode="aspectFill"></image>
      </view>
    </view>
  </view>
</template>
<script>
export default {
  name: "Timeline",
  data() {
    return {
      timeline: [
        {
          time: "01:21",
          title: "万国殡仪馆",
          subtitle: "International Funeral Parlour",
          location: "中国香港",
          description: "万国殡仪馆",
          image: "https://dummyimage.com/120x70/cccccc/000000&text=Image1"
        },
        {
          time: "03:11",
          title: "锦记粥粉面饭",
          subtitle: "Kum Kee Restaurant",
          location: "中国香港",
          description: "道生见明叔的地方",
          image: "https://dummyimage.com/120x70/cccccc/000000&text=Image2"
        },
        {
          time: "08:57",
          title: "康乐茶居",
          subtitle: "Hong Lok Tea House",
          location: "中国香港",
          description: "Hello文道生吃宵夜",
          image: "https://dummyimage.com/120x70/cccccc/000000&text=Image3"
        },
        {
          time: "10:45",
          title: "阳光洗衣便利店 金巴…",
          subtitle: "Sunshine Laundry (Kimberly St…",
          location: "中国香港",
          description: "洗衣店",
          image: "https://dummyimage.com/120x70/cccccc/000000&text=Image4"
        }
      ]
    };
  }
};
</script>
<style scoped>
.timeline-container {
  padding: 20rpx;
  background-color: #ffffff;
}
.timeline-title {
  text-align: center;
  font-size: 36rpx;
  margin-bottom: 30rpx;
  font-weight: bold;
}
.tabs {
  display: flex;
  justify-content: center;
  margin-bottom: 20rpx;
}
.tab-text {
  font-size: 28rpx;
  margin: 0 20rpx;
  color: #999;
}
.tab-text.active {
  color: #000;
  border-bottom: 4rpx solid #000;
  padding-bottom: 6rpx;
}
.tab-text.disabled {
  color: #ccc;
}
.timeline-item {
  display: flex;
  margin-bottom: 40rpx;
}
.timeline-time {
  color: #C1A14E;
  font-size: 30rpx;
  width: 100rpx;
  flex-shrink: 0;
}
.timeline-content {
  border-left: 2rpx solid #f0f0f0;
  padding-left: 20rpx;
  flex: 1;
  display: flex;
  justify-content: space-between;
}
.info {
  flex: 1;
}
.title {
  font-size: 30rpx;
  font-weight: bold;
  display: block;
}
.desc {
  font-size: 24rpx;
  color: #666;
  display: block;
  margin-top: 6rpx;
}
.thumbnail {
  width: 240rpx;
  height: 140rpx;
  margin-left: 20rpx;
  border-radius: 8rpx;
}
</style>
sub-pages/mine/index copy 2.vue
对比新文件
@@ -0,0 +1,416 @@
<template>
    <view class="page">
        <view class="profile-section" :style="backgroundStyle">
            <view class="top-bar">
                <u-icon name="list" size="60" color="#D0E1E9" />
                <view class="top-bar-right">
                    <!-- <u-icon name="edit-pen" size="60" color="#ffffff" class="icon-margin" /> -->
                    <u-sticky><u-icon name="share" size="60" color="#D0E1E9" /></u-sticky>
                </view>
            </view>
            <view class="profile-content">
                <view class="profile-header">
                    <view class="avatar-container">
                        <image class="avatar"
                            src="https://ai-public.mastergo.com/ai/img_res/e8ae645c666c247b895b488e60b048f5.jpg"
                            mode="aspectFill" />
                        <view class="add-icon">
                            <u-icon name="plus" size="20" color="#333" />
                        </view>
                    </view>
                    <view class="user-info">
                        <view class="username">小红薯 6786F040</view>
                        <view class="user-id">小红书号:95619601810</view>
                        <view class="location">IP 属地:江苏</view>
                    </view>
                </view>
                <view class="edit-profile">点击这里,填写简介</view>
                <view class="stats">
                    <view class="stat-row">
                        <view class="stat-item">
                            <text class="number">0</text>
                            <text class="label">关注</text>
                        </view>
                        <view class="stat-item">
                            <text class="number">0</text>
                            <text class="label">粉丝</text>
                        </view>
                        <view class="stat-item">
                            <text class="number">1</text>
                            <text class="label">获赞与收藏</text>
                        </view>
                    </view>
                    <view class="action-buttons">
                        <u-button text="编辑资料" size="mini" type="info" plain hairline
                            :custom-style="{ backgroundColor: 'transparent', borderColor: '#B1ABA9' }"></u-button>
                        <u-icon name="setting" size="40"></u-icon>
                    </view>
                </view>
            </view>
            <scroll-view class="function-cards" scroll-x="true" show-scrollbar="false">
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">购物车</text>
                        <text class="card-subtitle">查看推荐好物</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">创作灵感</text>
                        <text class="card-subtitle">学创作找灵感</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">浏览记录</text>
                        <text class="card-subtitle">看过的笔记</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">浏览记录</text>
                        <text class="card-subtitle">看过的笔记</text>
                    </view>
                </view>
            </scroll-view>
        </view>
            <view class="tab-section">
                <u-sticky>
                <u-tabs :list="tabList" :current="current" @change="onTabChange" lineColor="#2979ff"
                    activeStyle="color: #2979ff"></u-tabs>
                </u-sticky>
                <swiper class="swiper-box" :current="current" @change="onSwiperChange" duration="300">
                    <swiper-item v-for="(item, index) in tabList" :key="index">
                        <view class="empty-state">
                            <image class="empty-icon"
                                src="https://ai-public.mastergo.com/ai/img_res/fc3cb775274ef0f5e58ac01687a9c121.jpg"
                                mode="aspectFit" />
                            <text class="empty-text">快去发布你的笔记吧</text>
                            <u-button class="publish-btn" size="mini" type="warning" shape="circle">去发布</u-button>
                        </view>
                    </swiper-item>
                </swiper>
            </view>
        <common-footer flg="0"></common-footer>
    </view>
</template>
<script>
export default {
    data() {
        return {
            backgroundUrl2: 'https://ai-public.mastergo.com/ai/img_res/25f5105167f00aff148acc9bb83aed54.jpg', // 可能是动态获取的图片 URL
            backgroundUrl: '',
            current: 0,
            tabList: [
                { name: '笔记' },
                { name: '收藏' },
                { name: '赞过' },
            ],
        }
    },
    computed: {
        backgroundStyle() {
            return this.backgroundUrl
                ? `background: url(${this.backgroundUrl}) center center / cover no-repeat;`
                : 'background-color: #1e1e1e;';
        }
    },
    methods: {
        getBackgroundStyle() {
            if (this.backgroundUrl) {
                return `background: url(${this.backgroundUrl}) center center / cover no-repeat;`;
            } else {
                return 'background-color: #f5f5f5;';
            }
        },
        onTabChange(index) {
            this.current = index; // 切换 swiper 面板
        },
        onSwiperChange(e) {
            this.current = e.detail.current; // 同步 tabs
        },
    },
}
</script>
<style>
.page {
  height: 100vh;
  overflow-y: auto;
}
.top-bar {
    position: relative;
    z-index: 3;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24rpx 32rpx;
}
.top-bar-right {
    display: flex;
    align-items: center;
}
.icon-margin {
    margin-right: 32rpx;
}
.profile-section {
    position: relative;
    /* height: 680rpx; */
}
.bg-image {
    position: absolute;
    width: 100%;
    height: 100%;
    z-index: 1;
}
.profile-content {
    position: relative;
    z-index: 2;
    padding: 30rpx;
}
.profile-header {
    display: flex;
    align-items: flex-start;
    margin-bottom: 24rpx;
}
.avatar-container {
    position: relative;
    margin-right: 24rpx;
}
.avatar {
    width: 120rpx;
    height: 120rpx;
    border-radius: 60rpx;
    border: 2px solid #ffffff;
}
.add-icon {
    position: absolute;
    right: -8rpx;
    bottom: -8rpx;
    width: 40rpx;
    height: 40rpx;
    background-color: #FFE411;
    border-radius: 20rpx;
    display: flex;
    align-items: center;
    justify-content: center;
}
.user-info {
    flex: 1;
}
.username {
    font-size: 30rpx;
    color: #FCFFFE;
    font-weight: bold;
    margin-bottom: 8rpx;
}
.user-id,
.location {
    font-size: 26rpx;
    color: #666666;
    margin-bottom: 4rpx;
}
.edit-profile {
    color: #999999;
    font-size: 26rpx;
    margin-bottom: 20rpx;
}
.stats {
    display: flex;
    /* margin-bottom: 20rpx; */
    align-items: center;
    justify-content: space-between;
}
.stat-row {
    display: flex;
}
.stat-item {
    margin-right: 48rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.stat-item:last-child {
    margin-right: auto;
}
.number {
    font-size: 30rpx;
    color: #A19B99;
    font-weight: bold;
    margin-bottom: 4rpx;
}
.label {
    font-size: 24rpx;
    color: #B1ABA9;
}
.action-buttons {
    display: flex;
    gap: 16rpx;
}
.edit-btn {
    padding: 0 24rpx !important;
    background-color: #f5f5f5 !important;
    color: #333333 !important;
    border: none !important;
}
.settings-btn {
    width: 80rpx !important;
    background-color: #f5f5f5 !important;
    color: #333333 !important;
    border: none !important;
    padding: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
}
.function-cards {
    /* padding: 32rpx 0; */
    white-space: nowrap;
    overflow: auto;
    padding-bottom: 20rpx;
}
.card {
    display: inline-block;
    width: 180rpx;
    height: 80rpx;
    margin-left: 20rpx;
    background-color: rgba(60, 60, 60, 0.4);
    /* 灰色 + 20% 透明度 */
    border-radius: 16rpx;
    padding: 10rpx;
    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.card:last-child {
    margin-right: 24rpx;
}
.card-content {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}
.card-title {
    font-size: 30rpx;
    color: #E7E1DF;
    font-weight: bold;
}
.card-subtitle {
    font-size: 20rpx;
    color: #BEBBB8;
}
.tab-section {
    padding: 0 32rpx;
}
.tab-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 48rpx;
}
.tab {
    position: relative;
    padding: 16rpx 0;
    display: flex;
    align-items: center;
}
.tab.active::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 48rpx;
    height: 4rpx;
    background-color: #333;
    border-radius: 2rpx;
}
.tab-text {
    font-size: 16px;
    color: #333;
    margin-right: 8rpx;
}
.tab-count {
    font-size: 16px;
    color: #999;
}
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 48rpx 0;
    min-height: 600rpx;
}
.empty-icon {
    width: 200rpx;
    height: 200rpx;
    margin-bottom: 24rpx;
}
.empty-text {
    font-size: 14px;
    color: #999;
    /* margin-bottom: 24rpx; */
}
.publish-btn {
    background-color: #ffe411 !important;
    color: #333 !important;
    border: none !important;
    border-radius: 32rpx !important;
    padding: 0 48rpx !important;
}
</style>
<style lang="scss" scoped>
.swiper-box {
    // min-height: 600rpx;
    /* 或 100vh,或具体 px */
    height: 800px;
}
</style>
sub-pages/mine/index copy.vue
对比新文件
@@ -0,0 +1,397 @@
<template>
    <view class="page">
        <view class="profile-section" :style="backgroundStyle">
            <view class="top-bar">
                <u-icon name="list" size="60" color="#D0E1E9" />
                <view class="top-bar-right">
                    <!-- <u-icon name="edit-pen" size="60" color="#ffffff" class="icon-margin" /> -->
                    <u-icon name="share" size="60" color="#D0E1E9" />
                </view>
            </view>
            <view class="profile-content">
                <view class="profile-header">
                    <view class="avatar-container">
                        <image class="avatar"
                            src="https://ai-public.mastergo.com/ai/img_res/e8ae645c666c247b895b488e60b048f5.jpg"
                            mode="aspectFill" />
                        <view class="add-icon">
                            <u-icon name="plus" size="20" color="#333" />
                        </view>
                    </view>
                    <view class="user-info">
                        <view class="username">小红薯 6786F040</view>
                        <view class="user-id">小红书号:95619601810</view>
                        <view class="location">IP 属地:江苏</view>
                    </view>
                </view>
                <view class="edit-profile">点击这里,填写简介</view>
                <view class="stats">
                    <view class="stat-row">
                        <view class="stat-item">
                            <text class="number">0</text>
                            <text class="label">关注</text>
                        </view>
                        <view class="stat-item">
                            <text class="number">0</text>
                            <text class="label">粉丝</text>
                        </view>
                        <view class="stat-item">
                            <text class="number">1</text>
                            <text class="label">获赞与收藏</text>
                        </view>
                    </view>
                    <view class="action-buttons">
                        <u-button text="编辑资料" size="mini" type="info" plain hairline
                            :custom-style="{ backgroundColor: 'transparent', borderColor: '#B1ABA9' }"></u-button>
                        <u-icon name="setting"></u-icon>
                    </view>
                </view>
            </view>
            <scroll-view class="function-cards" scroll-x="true" show-scrollbar="false">
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">购物车</text>
                        <text class="card-subtitle">查看推荐好物</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">创作灵感</text>
                        <text class="card-subtitle">学创作找灵感</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">浏览记录</text>
                        <text class="card-subtitle">看过的笔记</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">浏览记录</text>
                        <text class="card-subtitle">看过的笔记</text>
                    </view>
                </view>
            </scroll-view>
        </view>
        <view class="tab-section">
            <view class="tab-header">
                <view class="tab active">
                    <text class="tab-text">笔记</text>
                    <text class="tab-count">0</text>
                </view>
                <view class="tab">
                    <text class="tab-text">收藏</text>
                    <text class="tab-count">0</text>
                </view>
                <view class="tab">
                    <text class="tab-text">赞过</text>
                    <text class="tab-count">0</text>
                </view>
            </view>
            <view class="empty-state">
                <image class="empty-icon"
                    src="https://ai-public.mastergo.com/ai/img_res/fc3cb775274ef0f5e58ac01687a9c121.jpg"
                    mode="aspectFit" />
                <text class="empty-text">快去发布你的笔记吧</text>
                <u-button class="publish-btn" size="mini" type="warning" shape="circle">去发布</u-button>
            </view>
        </view>
        <common-footer flg="0"></common-footer>
    </view>
</template>
<script>
export default {
    data() {
        return {
            backgroundUrl2: 'https://ai-public.mastergo.com/ai/img_res/25f5105167f00aff148acc9bb83aed54.jpg', // 可能是动态获取的图片 URL
            backgroundUrl: '',
        }
    },
    computed: {
        backgroundStyle() {
            return this.backgroundUrl
                ? `background: url(${this.backgroundUrl}) center center / cover no-repeat;`
                : 'background-color: #1e1e1e;';
        }
    },
    methods: {
        getBackgroundStyle() {
            if (this.backgroundUrl) {
                return `background: url(${this.backgroundUrl}) center center / cover no-repeat;`;
            } else {
                return 'background-color: #f5f5f5;';
            }
        }
    },
}
</script>
<style>
.page {
    height: 100%;
    /* background-color: #ffffff; */
}
.top-bar {
    position: relative;
    z-index: 3;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24rpx 32rpx;
}
.top-bar-right {
    display: flex;
    align-items: center;
}
.icon-margin {
    margin-right: 32rpx;
}
.profile-section {
    position: relative;
    height: 680rpx;
}
.bg-image {
    position: absolute;
    width: 100%;
    height: 100%;
    z-index: 1;
}
.profile-content {
    position: relative;
    z-index: 2;
    padding: 30rpx;
}
.profile-header {
    display: flex;
    align-items: flex-start;
    margin-bottom: 24rpx;
}
.avatar-container {
    position: relative;
    margin-right: 24rpx;
}
.avatar {
    width: 120rpx;
    height: 120rpx;
    border-radius: 60rpx;
    border: 2px solid #ffffff;
}
.add-icon {
    position: absolute;
    right: -8rpx;
    bottom: -8rpx;
    width: 40rpx;
    height: 40rpx;
    background-color: #FFE411;
    border-radius: 20rpx;
    display: flex;
    align-items: center;
    justify-content: center;
}
.user-info {
    flex: 1;
}
.username {
    font-size: 30rpx;
    color: #FCFFFE;
    font-weight: bold;
    margin-bottom: 8rpx;
}
.user-id,
.location {
    font-size: 26rpx;
    color: #666666;
    margin-bottom: 4rpx;
}
.edit-profile {
    color: #999999;
    font-size: 26rpx;
    margin-bottom: 32rpx;
}
.stats {
    display: flex;
    margin-bottom: 32rpx;
    align-items: center;
    justify-content: space-between;
}
.stat-row{
    display: flex;
}
.stat-item {
    margin-right: 48rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.stat-item:last-child {
    margin-right: auto;
}
.number {
    font-size: 30rpx;
    color: #A19B99;
    font-weight: bold;
    margin-bottom: 4rpx;
}
.label {
    font-size: 24rpx;
    color: #B1ABA9;
}
.action-buttons {
    display: flex;
    gap: 16rpx;
}
.edit-btn {
    padding: 0 24rpx !important;
    background-color: #f5f5f5 !important;
    color: #333333 !important;
    border: none !important;
}
.settings-btn {
    width: 80rpx !important;
    background-color: #f5f5f5 !important;
    color: #333333 !important;
    border: none !important;
    padding: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
}
.function-cards {
    /* padding: 32rpx 0; */
    white-space: nowrap;
    overflow: auto;
}
.card {
    display: inline-block;
    width: 180rpx;
    height: 80rpx;
    margin-left: 20rpx;
    background-color: rgba(60, 60, 60, 0.4);
    /* 灰色 + 20% 透明度 */
    border-radius: 16rpx;
    padding: 24rpx;
    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.card:last-child {
    margin-right: 24rpx;
}
.card-content {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}
.card-title {
    font-size: 30rpx;
    color: #E7E1DF;
    font-weight: bold;
}
.card-subtitle {
    font-size: 20rpx;
    color: #BEBBB8;
}
.tab-section {
    padding: 0 32rpx;
}
.tab-header {
    display: flex;
    justify-content: space-around;
    margin-bottom: 48rpx;
}
.tab {
    position: relative;
    padding: 16rpx 0;
    display: flex;
    align-items: center;
}
.tab.active::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 48rpx;
    height: 4rpx;
    background-color: #333;
    border-radius: 2rpx;
}
.tab-text {
    font-size: 16px;
    color: #333;
    margin-right: 8rpx;
}
.tab-count {
    font-size: 16px;
    color: #999;
}
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 48rpx 0;
}
.empty-icon {
    width: 200rpx;
    height: 200rpx;
    margin-bottom: 24rpx;
}
.empty-text {
    font-size: 14px;
    color: #999;
    margin-bottom: 24rpx;
}
.publish-btn {
    background-color: #ffe411 !important;
    color: #333 !important;
    border: none !important;
    border-radius: 32rpx !important;
    padding: 0 48rpx !important;
}
</style>
sub-pages/mine/index.vue
对比新文件
@@ -0,0 +1,424 @@
<template>
    <view class="page">
        <view class="profile-section" :style="backgroundStyle">
            <view class="top-bar">
                <u-icon name="list" size="60" color="#D0E1E9" @click="onSettingClick" />
                <view class="top-bar-right">
                    <!-- <u-icon name="edit-pen" size="60" color="#ffffff" class="icon-margin" /> -->
                    <u-icon name="share" size="60" color="#D0E1E9" />
                </view>
            </view>
            <view class="profile-content">
                <view class="profile-header">
                    <view class="avatar-container">
                        <image class="avatar"
                            src="https://ai-public.mastergo.com/ai/img_res/e8ae645c666c247b895b488e60b048f5.jpg"
                            mode="aspectFill" />
                        <view class="add-icon">
                            <u-icon name="plus" size="20" color="#333" />
                        </view>
                    </view>
                    <view class="user-info">
                        <view class="username">小红薯 6786F040</view>
                        <view class="user-id">小红书号:95619601810</view>
                        <view class="location">IP 属地:江苏</view>
                    </view>
                </view>
                <view class="edit-profile">点击这里,填写简介</view>
                <view class="stats">
                    <view class="stat-row">
                        <view class="stat-item">
                            <text class="number">0</text>
                            <text class="label">关注</text>
                        </view>
                        <view class="stat-item">
                            <text class="number">0</text>
                            <text class="label">粉丝</text>
                        </view>
                        <view class="stat-item">
                            <text class="number">1</text>
                            <text class="label">获赞与收藏</text>
                        </view>
                    </view>
                    <view class="action-buttons">
                        <u-button text="编辑资料" size="mini" type="info" plain hairline
                            :custom-style="{ backgroundColor: 'transparent', borderColor: '#B1ABA9' }"></u-button>
                        <u-icon name="setting" size="40"></u-icon>
                    </view>
                </view>
            </view>
            <scroll-view class="function-cards" scroll-x="true" show-scrollbar="false">
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">购物车</text>
                        <text class="card-subtitle">查看推荐好物</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">创作灵感</text>
                        <text class="card-subtitle">学创作找灵感</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">浏览记录</text>
                        <text class="card-subtitle">看过的笔记</text>
                    </view>
                </view>
                <view class="card">
                    <view class="card-content">
                        <text class="card-title">浏览记录</text>
                        <text class="card-subtitle">看过的笔记</text>
                    </view>
                </view>
            </scroll-view>
        </view>
            <view class="tab-section">
                <u-sticky>
                <u-tabs :list="tabList" :current="current" @change="onTabChange" lineColor="#2979ff"
                    activeStyle="color: #2979ff"></u-tabs>
                </u-sticky>
                <swiper class="swiper-box" :current="current" @change="onSwiperChange" duration="300">
                    <swiper-item v-for="(item, index) in tabList" :key="index">
                        <view class="empty-state">
                            <image class="empty-icon"
                                src="https://ai-public.mastergo.com/ai/img_res/fc3cb775274ef0f5e58ac01687a9c121.jpg"
                                mode="aspectFit" />
                            <text class="empty-text">快去发布你的笔记吧</text>
                            <u-button class="publish-btn" size="mini" type="warning" shape="circle">去发布</u-button>
                        </view>
                    </swiper-item>
                </swiper>
            </view>
        <common-footer flg="0"></common-footer>
        <setting-popup v-model="settingShow" />
    </view>
</template>
<script>
import SettingPopup from '@/components/setting/setting-popup.vue'
export default {
    components: {
        SettingPopup
    },
    data() {
        return {
            backgroundUrl2: 'https://ai-public.mastergo.com/ai/img_res/25f5105167f00aff148acc9bb83aed54.jpg', // 可能是动态获取的图片 URL
            backgroundUrl: '',
            current: 0,
            tabList: [
                { name: '笔记' },
                { name: '收藏' },
                { name: '赞过' },
            ],
            settingShow:false,
        }
    },
    computed: {
        backgroundStyle() {
            return this.backgroundUrl
                ? `background: url(${this.backgroundUrl}) center center / cover no-repeat;`
                : 'background-color: #1e1e1e;';
        }
    },
    methods: {
        onSettingClick(){
            this.settingShow = true;
        },
        getBackgroundStyle() {
            if (this.backgroundUrl) {
                return `background: url(${this.backgroundUrl}) center center / cover no-repeat;`;
            } else {
                return 'background-color: #f5f5f5;';
            }
        },
        onTabChange(item) {
            this.current = item.index; // 切换 swiper 面板
        },
        onSwiperChange(e) {
            this.current = e.detail.current; // 同步 tabs
        },
    },
}
</script>
<style>
.page {
  height: 100vh;
  overflow-y: auto;
}
.top-bar {
    position: relative;
    z-index: 3;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24rpx 32rpx;
}
.top-bar-right {
    display: flex;
    align-items: center;
}
.icon-margin {
    margin-right: 32rpx;
}
.profile-section {
    position: relative;
    /* height: 680rpx; */
}
.bg-image {
    position: absolute;
    width: 100%;
    height: 100%;
    z-index: 1;
}
.profile-content {
    position: relative;
    z-index: 2;
    padding: 30rpx;
}
.profile-header {
    display: flex;
    align-items: flex-start;
    margin-bottom: 24rpx;
}
.avatar-container {
    position: relative;
    margin-right: 24rpx;
}
.avatar {
    width: 120rpx;
    height: 120rpx;
    border-radius: 60rpx;
    border: 2px solid #ffffff;
}
.add-icon {
    position: absolute;
    right: -8rpx;
    bottom: -8rpx;
    width: 40rpx;
    height: 40rpx;
    background-color: #FFE411;
    border-radius: 20rpx;
    display: flex;
    align-items: center;
    justify-content: center;
}
.user-info {
    flex: 1;
}
.username {
    font-size: 30rpx;
    color: #FCFFFE;
    font-weight: bold;
    margin-bottom: 8rpx;
}
.user-id,
.location {
    font-size: 26rpx;
    color: #666666;
    margin-bottom: 4rpx;
}
.edit-profile {
    color: #999999;
    font-size: 26rpx;
    margin-bottom: 20rpx;
}
.stats {
    display: flex;
    /* margin-bottom: 20rpx; */
    align-items: center;
    justify-content: space-between;
}
.stat-row {
    display: flex;
}
.stat-item {
    margin-right: 48rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.stat-item:last-child {
    margin-right: auto;
}
.number {
    font-size: 30rpx;
    color: #A19B99;
    font-weight: bold;
    margin-bottom: 4rpx;
}
.label {
    font-size: 24rpx;
    color: #B1ABA9;
}
.action-buttons {
    display: flex;
    gap: 16rpx;
}
.edit-btn {
    padding: 0 24rpx !important;
    background-color: #f5f5f5 !important;
    color: #333333 !important;
    border: none !important;
}
.settings-btn {
    width: 80rpx !important;
    background-color: #f5f5f5 !important;
    color: #333333 !important;
    border: none !important;
    padding: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
}
.function-cards {
    /* padding: 32rpx 0; */
    white-space: nowrap;
    overflow: auto;
    padding-bottom: 20rpx;
}
.card {
    display: inline-block;
    width: 180rpx;
    height: 80rpx;
    margin-left: 20rpx;
    background-color: rgba(60, 60, 60, 0.4);
    /* 灰色 + 20% 透明度 */
    border-radius: 16rpx;
    padding: 10rpx;
    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.card:last-child {
    margin-right: 24rpx;
}
.card-content {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}
.card-title {
    font-size: 30rpx;
    color: #E7E1DF;
    font-weight: bold;
}
.card-subtitle {
    font-size: 20rpx;
    color: #BEBBB8;
}
.tab-section {
    padding: 0 32rpx;
}
.tab-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 48rpx;
}
.tab {
    position: relative;
    padding: 16rpx 0;
    display: flex;
    align-items: center;
}
.tab.active::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 48rpx;
    height: 4rpx;
    background-color: #333;
    border-radius: 2rpx;
}
.tab-text {
    font-size: 16px;
    color: #333;
    margin-right: 8rpx;
}
.tab-count {
    font-size: 16px;
    color: #999;
}
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 48rpx 0;
    min-height: 600rpx;
}
.empty-icon {
    width: 200rpx;
    height: 200rpx;
    margin-bottom: 24rpx;
}
.empty-text {
    font-size: 14px;
    color: #999;
    /* margin-bottom: 24rpx; */
}
.publish-btn {
    background-color: #ffe411 !important;
    color: #333 !important;
    border: none !important;
    border-radius: 32rpx !important;
    padding: 0 48rpx !important;
}
</style>
<style lang="scss" scoped>
.swiper-box {
    // min-height: 600rpx;
    /* 或 100vh,或具体 px */
    min-height: 800px;
}
</style>
theme.scss
@@ -3,7 +3,7 @@
    transition: background-color 0.3s;
  
    &.light {
      background-color: #ffffff;
      background-color: #F5F5F5;
      color: #000000;
      --icon-color: #000000;
      --section-title:  #000000;
uni_modules/helang-waterfall/changelog.md
对比新文件
@@ -0,0 +1,31 @@
## 1.2.4(2024-11-24)
- [优化] 新增 `udpate` 动态更新数据方法
## 1.2.3(2024-11-02)
- [修复] 组件重新渲染时未重置待渲染位置的问题
- [优化] 图片宽度支持 px 单位
## 1.2.2(2024-10-18)
- [修复] 解决了因为数据id重复导致的 小程序平台  waterfall-item 组件不会重新渲染的问题
## 1.2.1(2024-10-07)
全新升级为组件+插槽的方案实现,极大的简便了组件的使用和内容调整。
## 1.1.6(2022-10-24)
修复 初始化渲染变量取值错误问题
## 1.1.5(2022-10-24)
初始化渲染增加状态条件判断
## 1.1.4(2022-10-24)
修改空数据提示
## 1.1.3(2022-10-24)
1、增加内容插槽
2、删除状态文本属性
3、组件创建时可触发渲染条件
## 1.1.2(2022-09-26)
修改了开启布局的判断条件
## 1.1.1(2022-08-28)
1、加强组件化封装
2、 完善注释和优化使用逻辑
## 1.1.0(2022-08-22)
重写渲染列表逻辑
## 1.0.1(2021-06-08)
修改插入方向计算方式
uni_modules/helang-waterfall/components/waterfall/js/utils.js
对比新文件
@@ -0,0 +1,7 @@
export const wait = (time = 100)=>{
    return new Promise((resolve)=>{
        setTimeout(()=>{
            resolve();
        },time);
    })
}
uni_modules/helang-waterfall/components/waterfall/waterfall-col.vue
对比新文件
@@ -0,0 +1,50 @@
<template>
    <view class="waterfall-col" :style="{width:colWidth}">
        <slot name="default"></slot>
    </view>
</template>
<script>
    export default {
        name:"helangWaterfallCol",
        options:{
            virtualHost: true
        },
        components: {
        },
        props:{
            // 栏目宽度
            colWidth:{
                type: String,
                default:''
            },
        },
        watch:{
        },
        computed:{
        },
        data() {
            return {
            }
        },
        mounted() {
        },
        methods: {
        }
    }
</script>
<style lang="scss" scoped>
    .waterfall-col{
        padding:0 10rpx;
        box-sizing: border-box;
    }
</style>
uni_modules/helang-waterfall/components/waterfall/waterfall-item.vue
对比新文件
@@ -0,0 +1,86 @@
<template>
    <view class="waterfall-item">
        <slot name="default"></slot>
    </view>
</template>
<script>
    import { wait } from './js/utils.js';
    export default {
        name:"helangWaterfallItem",
        options:{
            virtualHost: true
        },
        props:{
            colIndex:{
                type:Number,
                default:0
            },
            reportHeightTime:{
                type:Number | String | undefined | null,
                default:''
            }
        },
        watch:{
            "$props.reportHeightTime"(newValue, oldValue){
                if(newValue !== oldValue){
                    this.postHeight({
                        reportHeightTimeChange:true
                    });
                }
            }
        },
        data() {
            return {
            };
        },
        mounted() {
            this.postHeight();
        },
        methods:{
            async postHeight(params){
                const { reportHeightTimeChange = false } = params ?? {};
                // 添加一个异步的宏任务,部分低性能的移动设备会因为渲染问题导致上报的高度存在错误问题
                await wait(10);
                // 当前高度
                const currentHeight = await this.getHeight();
                // 显示高度
                const displayHeight = this.displayHeight || 0;
                // 上报高度
                let height = currentHeight - displayHeight;
                // 上报高度为0时,取消上报。防止用户调用 update 函数时错误的reportHeight参数情况
                if(height === 0){
                    return;
                }
                this.$emit("height",{
                    colIndex:this.$props.colIndex,
                    height,
                    reportHeightTimeChange
                });
                // 缓存渲染高度
                this.displayHeight = currentHeight;
            },
            getHeight(){
                const query = uni.createSelectorQuery().in(this);
                return new Promise((resolve,reject)=>{
                    query.select('.waterfall-item').boundingClientRect((data) => {
                        // 节点高度
                        const height = Math.floor(data.height) || 0;
                        resolve(height);
                    }).exec();
                })
            }
        }
    }
</script>
<style lang="scss" scoped>
    .waterfall-item{
        & + .waterfall-item{
            margin-top:20rpx;
        }
    }
</style>
uni_modules/helang-waterfall/components/waterfall/waterfall-list.vue
对比新文件
@@ -0,0 +1,343 @@
<template>
    <view>
        <view class="waterfall-box" :class="{'hidden':hideList}">
            <view class="h-flex-x helang-waterfall-slot">
                <slot name="default" v-bind:colWidth="colWidth" v-bind:list="renderList"></slot>
            </view>
        </view>
        <slot name="tips"></slot>
        <image
            class="load-image"
            v-for="(item,index) in waitRenderList"
            :key="index"
            v-if="item.__waterfall_imageHeight === undefined"
            :src="item | imageLoadUrl"
            :data-index="index"
            @load="handleImageLoad"
            @error="handleImageLoad"
        ></image>
    </view>
</template>
<script>
    // 字符串前缀
    const prefixStr = '__waterfall_';
    // 图片高度键名
    const IMAGE_HEIGHT_KEY = `${prefixStr}imageHeight`;
    // 图片缓存键名
    const IMAGE_CACHE_KEY = `${prefixStr}cacheImageHeight`;
    // 是否渲染
    const ITEM_SHOW_KEY = `${prefixStr}show`;
    // 渲染ID
    const RENDER_ID = `${prefixStr}renderId`;
    // 上报高度时间
    const REPORT_TIME = `${prefixStr}reportHeightTime`;
    // 图片链接键名
    let IMAGE_URL_KEY = '';
    // 图片PX宽度
    let IMAGE_PX_WIDTH = 0;
    // 渲染状态
    const RENDER_STATUS = {
        // 加载中
        LOADING: 'RENDER_LOADING',
        // 渲染开始
        START:'RENDER_START',
        // 渲染结束
        END:'RENDER_END'
    };
    export default {
        name:"helangWaterfallList",
        options:{
            virtualHost: true
        },
        components: {
        },
        props:{
            // 分栏
            col:{
                type: Number,
                default:2
            },
            // 图片宽度
            imageWidth:{
                type: Number | String,
                default:305
            },
            // 缓存高度(加速渲染)
            cacheImageHeight:{
                type: Boolean,
                default:true
            },
            // 图片链接键名
            imageKey:{
                type: String,
                default:'url'
            },
            // 隐藏列表
            hideList:{
                type: Boolean,
                default:false
            },
        },
        filters:{
            imageLoadUrl(data){
                return data[IMAGE_URL_KEY] ?? "";
            }
        },
        watch:{
        },
        computed:{
            colWidth(){
                return `${1 / this.$props.col * 100}%`;
            }
        },
        data() {
            return {
                // 待渲染列表
                waitRenderList:[],
                // 渲染列表
                renderList:[],
                // 渲染列表高度
                renderListHeight:[]
            }
        },
        created() {
            this.initRenderList();
            // 图片高度缓存
            if(!uni[IMAGE_CACHE_KEY]){
                uni[IMAGE_CACHE_KEY] = {};
            }
            IMAGE_URL_KEY = this.$props.imageKey;
            IMAGE_PX_WIDTH = this.getImagePxWidth();
        },
        mounted() {
        },
        methods: {
            // 初始化渲染列表
            initRenderList(){
                let list = [];
                let heights = [];
                for(let i = 0; i < this.$props.col; i++){
                    list.push([]);
                    heights.push(0);
                }
                this.renderList = list;
                this.renderListHeight = heights;
                this.waitPushIndex = 0;
            },
            // 获取图片的 px 宽度
            getImagePxWidth(){
                const width = this.$props.imageWidth;
                // 是否为px单位
                if(/^\d+px$/i.test(String(width))){
                    return parseInt(width);
                }else{
                    return uni.upx2px(parseInt(width) || 0)
                }
            },
            // 处理图片加载
            handleImageLoad(e){
                const { width = 0, height = 0 } = e.detail;
                const { index } = e.currentTarget.dataset;
                const renderHeight = width === 0 ? 0 : parseInt(IMAGE_PX_WIDTH / width * height);
                this.addWaitRenderListImageHeight(index, renderHeight);
                if(this.$props.cacheImageHeight){
                    const imageUrl = this.waitRenderList[index][IMAGE_URL_KEY];
                    if(imageUrl.length < 300){
                        uni[IMAGE_CACHE_KEY][imageUrl] = renderHeight;
                    }
                }
                if(this.checkImageHaveHeight()){
                    this.emitRenderStatusChange(RENDER_STATUS.START);
                    this.addRenderData();
                }
            },
            // 检查图片是否已全部包含高度
            checkImageHaveHeight(){
                const index = this.waitRenderList.findIndex((item)=>{
                    return item[IMAGE_HEIGHT_KEY] === undefined;
                })
                return index === -1 ? true : false;
            },
            // 待渲染列表添加图片高度数据
            addWaitRenderListImageHeight(index,height){
                this.waitRenderList[index][IMAGE_HEIGHT_KEY] = height;
            },
            // 查找最小高度的列的索引
            findSmallColIndex(){
                let col = 0;
                let size = this.renderListHeight[col];
                this.renderListHeight.forEach((item,index)=>{
                    if(item < size){
                        col = index;
                        size = item;
                    }
                });
                return col;
            },
            // 追加渲染数据
            pushRender(data){
                const index = this.findSmallColIndex();
                const nowDate = Date.now();
                this.renderList[index].push({
                    ...data,
                    [IMAGE_HEIGHT_KEY]: `${data[IMAGE_HEIGHT_KEY]}px`,
                    [RENDER_ID]: `${prefixStr}${nowDate}`,
                    [REPORT_TIME]: `${prefixStr}${nowDate}`
                });
            },
            // 添加所有渲染数据,方案存在问题暂
            addAllRenderData(){
                this.waitPushIndex = 0;
                const add = ()=>{
                    const current = this.waitRenderList[this.waitPushIndex];
                    if(current === undefined){
                        this.waitRenderList = [];
                        return;
                    }
                    this.pushRender(current);
                    this.waitPushIndex++;
                    add();
                };
                add();
            },
            // 渲染状态变更
            emitRenderStatusChange(status){
                this.$emit("statusChange",status);
            },
            // 提交高度
            reportHeight(params){
                const { colIndex, height, reportHeightTimeChange } = params;
                this.renderListHeight[colIndex]+=height;
                if(reportHeightTimeChange){
                    return;
                }
                this.addRenderData();
            },
            // 添加一条渲染数据
            addRenderData(){
                const index = this.waitPushIndex ?? 0;
                const current = this.waitRenderList[index];
                if(current === undefined){
                    this.emitRenderStatusChange(RENDER_STATUS.END);
                    return;
                }
                this.pushRender(current);
                this.waitPushIndex = index + 1;
                if(this.waitPushIndex >= this.waitRenderList.length){
                    this.waitPushIndex = 0;
                    this.waitRenderList = [];
                }
            },
            // 启动渲染
            render(list = [],reset = false){
                if(!list?.length > 0){
                    console.log('河浪瀑布流插件提示:当前数据为空,不会触发列表渲染');
                    return;
                }
                if(reset){
                    this.$nextTick(()=>{
                        this.initRenderList();
                    });
                    // this.initRenderList();
                }
                this.waitRenderList = list.map((item)=>{
                    const imageUrl = item[IMAGE_URL_KEY];
                    const imageHeight = uni[IMAGE_CACHE_KEY][imageUrl];
                    const show = this.$props.sameHeight ? true:false;
                    return {
                        ...item,
                        [IMAGE_HEIGHT_KEY]: imageHeight,
                        // 该属性暂未使用,预留字段
                        [ITEM_SHOW_KEY]: show
                    }
                });
                this.emitRenderStatusChange(RENDER_STATUS.LOADING);
                if(this.checkImageHaveHeight()){
                    this.emitRenderStatusChange(RENDER_STATUS.START);
                    this.addRenderData();
                }
            },
            // 更新
            update(params){
                const { colIndex, itemIndex, data = {}, reportHeight = false } = params;
                // 当需要重新上报高度时,更新渲染时间
                if(reportHeight){
                    const nowDate = Date.now();
                    data[REPORT_TIME] = `${prefixStr}${nowDate}`;
                }
                this.$set(this.renderList[colIndex], itemIndex, data);
            }
        }
    }
</script>
<style>
    /* 小程序模式下插槽会增加一个 <scoped-slots-default> DOM节点 */
    .helang-waterfall-slot > scoped-slots-default{
        width: 100%;
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: flex-start;
        align-items: flex-start;
        align-content: flex-start;
    }
</style>
<style lang="scss" scoped>
    .waterfall-box {
        padding: 0 10rpx;
        box-sizing: border-box;
        &.hidden{
            display: none;
        }
        .list-item{
            margin-bottom: 0;
            // 设置透明,默认是可视的
            opacity: 0;
            // 默认超出隐藏,不影响加载中的文字显示效果
            overflow: hidden;
            height: 0;
            &.show{
                margin-bottom: 20rpx;
                opacity: 1;
                overflow: auto;
                height: auto;
            }
        }
    }
    .h-flex-x {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: flex-start;
        align-items: flex-start;
        align-content: flex-start;
    }
    .load-image{
        display: none !important;
    }
</style>
uni_modules/helang-waterfall/mock-data/waterfall-list.js
对比新文件
@@ -0,0 +1,40 @@
let list = (page)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(() => {
            // 生成随机数方法
            let random = (min = 0, max) => {
                return Math.floor(Math.random() * max) + min;
            }
            // 待选的图片数据
            let imgs = [];
            // 待选的标题数据
            let titles = [
                '桃花坞里桃花庵,桃花庵里桃花仙;',
                '桃花仙人种桃树,又摘桃花卖酒钱。',
                '酒醒只在花前坐,酒醉还来花下眠;半醒半醉日复日,花落花开年复年。',
                '但愿老死花酒间,不愿鞠躬车马前;',
                '车尘马足富者趣,酒盏花枝贫者缘。若将富贵比贫贱,',
                '一在平地一在天;若将贫贱比车马,他得驱驰我得闲。',
                '别人笑我太疯癫,我笑他人看不穿;不见五陵豪杰墓,无花无酒锄作田。'
            ];
            let res = [];
            let t = `?t=${new Date().getTime()}`;
            for (let i = 0; i < 10; i++) {
                res.push({
                    id:`${Date.now()}_${i+1}`,
                    imgUrl:`/uni_modules/helang-waterfall/static/waterfall/${random(0,3)}.jpg`,
                    title: titles[random(0, titles.length)],
                    money: random(9, 9999),
                    label:'官方自营',
                    shop:'唐诗三百首旗舰店'
                })
            }
            resolve(res);
        }, 1000);
    })
}
export default {
    getList:list
}
uni_modules/helang-waterfall/package.json
对比新文件
@@ -0,0 +1,77 @@
{
  "id": "helang-waterfall",
  "displayName": "瀑布流布局-waterfall",
  "version": "1.2.4",
  "description": "一款简单又好用的瀑布流布局组件,全新升级为组件+插槽的方案实现,极大的简便了组件的使用和内容调整。",
  "keywords": [
    "瀑布流",
    "布局",
    "列表",
    "waterfall"
],
  "repository": "https://gitee.com/myDarling/uniapp-extend",
  "engines": {
    "HBuilderX": "^4.0"
  },
"dcloudext": {
    "sale": {
      "regular": {
        "price": "0.00"
      },
      "sourcecode": {
        "price": "0.00"
      }
    },
    "contact": {
      "qq": ""
    },
    "declaration": {
      "ads": "无",
      "data": "无",
      "permissions": "无"
    },
    "npmurl": "",
    "type": "uniapp-template-page"
  },
  "uni_modules": {
    "dependencies": [],
    "encrypt": [],
    "platforms": {
      "cloud": {
        "tcb": "y",
        "aliyun": "y",
        "alipay": "n"
      },
      "client": {
        "App": {
          "app-vue": "y",
          "app-nvue": "u"
        },
        "H5-mobile": {
          "Safari": "y",
          "Android Browser": "y",
          "微信浏览器(Android)": "y",
          "QQ浏览器(Android)": "y"
        },
        "H5-pc": {
          "Chrome": "y",
          "IE": "y",
          "Edge": "y",
          "Firefox": "y",
          "Safari": "y"
        },
        "小程序": {
          "微信": "y",
          "阿里": "y",
          "百度": "y",
          "字节跳动": "y",
          "QQ": "y"
        },
        "快应用": {
          "华为": "y",
          "联盟": "y"
        }
      }
    }
  }
}
uni_modules/helang-waterfall/pages/waterfall/waterfall.vue
对比新文件
@@ -0,0 +1,363 @@
<template>
    <view style="padding: 20rpx 0;">
        <waterfall
            ref="helangWaterfall"
            :hideList="hideList"
            imageKey="imgUrl"
            @statusChange="handleStatusChange"
        >
            <template v-slot:default="{ list, colWidth }">
                <waterfall-col
                    v-for="(colItem,colIndex) in list"
                    :key="colIndex"
                    :colWidth="colWidth"
                >
                    <template>
                        <waterfall-item
                            v-for="(item,index) in colItem"
                            :key="item.__waterfall_renderId"
                            :colIndex="colIndex"
                            :reportHeightTime="item.__waterfall_reportHeightTime"
                            @height="onRenderHeight"
                        >
                            <view class="content" @click="handleClick" :data-col_index="colIndex" :data-item_index="index">
                                <image
                                    class="image"
                                    mode="aspectFill"
                                    :src="item.imgUrl"
                                    :style="{height:item.__waterfall_imageHeight}"
                                >
                                    <!-- 必须给图片的高度设置为 __waterfall_imageHeight 属性的高 -->
                                </image>
                                <view class="title">{{item.title}}</view>
                                <view class="label ellipsis-1">{{item.label}}</view>
                                <view class="money ellipsis-1">价格:{{item.money}}元</view>
                                <view class="shop ellipsis-1">{{item.shop}}</view>
                                <!-- 这个节点真实项目中可删除 -->
                                <view
                                    v-if="(colIndex === 0 && index === 0) || (colIndex === 1 && index === 1)"
                                    style="margin-top: 8px;border: red solid 1px;padding: 8px;"
                                >
                                    <view style="font-size: 12px;color: red;margin-bottom: 4px;">
                                        次节点为演示 update 函数动态更新数据的能力,
                                        真是项目删除即可
                                    </view>
                                    <view>
                                        <button
                                            size="mini"
                                            style="margin-right: 4px;"
                                            :type="item.star ? 'primary':'warn'"
                                            @tap.stop="handleStar({
                                                colIndex,
                                                itemIndex:index,
                                                data:item
                                            })"
                                        >{{item.star ? '已':''}}收藏</button>
                                    </view>
                                    <view>
                                        <view
                                            v-if="item.activeHeight"
                                            :style="{height:item.activeHeight+'px'}"
                                            style="background-color: rgba(255, 0, 0, 0.4);font-size: 12px;"
                                        >当前随机高度:{{ item.activeHeight }}</view>
                                        <button size="mini"
                                            @tap.stop="handleActiveHeight({
                                                colIndex,
                                                itemIndex:index,
                                                data:item
                                            })"
                                        >随机高度</button>
                                    </view>
                                </view>
                            </view>
                        </waterfall-item>
                    </template>
                </waterfall-col>
            </template>
            <template #tips>
                <view class="load-txt">
                    <view v-if="hideList">
                        <template v-if="waterfall.status === 'fail'">
                            <image src="/uni_modules/helang-waterfall/static/waterfall/fail.png" mode="aspectFit"></image>
                            <view>数据异常</view>
                        </template>
                        <template v-else-if="waterfall.status === 'empty'">
                            <image src="/uni_modules/helang-waterfall/static/waterfall/empty.png" mode="aspectFit"></image>
                            <view>暂无内容</view>
                        </template>
                    </view>
                    <view v-else style="padding-top: 30rpx;">加载中</view>
                </view>
            </template>
        </waterfall>
        <!-- 演示多种效果 -->
        <view class="status-change" @click="onStatusChange">设置</view>
    </view>
</template>
<script>
    // 瀑布流组件
    import Waterfall from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-list";
    import WaterfallCol from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-col";
    import WaterfallItem from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-item";
    // 列表接口模拟数据,真是项目不需要
    import mockData from '../../mock-data/waterfall-list.js'
    export default {
        components: {
            "waterfall": Waterfall,
            "waterfall-col": WaterfallCol,
            "waterfall-item": WaterfallItem
        },
        computed:{
            hideList(){
                return ['fail','empty'].includes(this.waterfall.status);
            }
        },
        data() {
            return {
                // 异步请求相关
                ajax: {
                    // 是否可以加载
                    load: true,
                    // 每页的请求条件
                    rows:10,
                    // 页码
                    page:1,
                    // 数据列表
                    dataList:[]
                },
                // 瀑布流组件相关
                waterfall:{
                    // 组件状态
                    status:'',
                    // 是否渲染完成
                    renderEnd:true
                }
            }
        },
        onReady() {
            this.getList();
        },
        // 触底触发
        onReachBottom() {
            // 渲染完成,才允许请求下一页
            if(this.waterfall.renderEnd){
                this.getList();
            }
        },
        // 下拉刷新
        onPullDownRefresh(){
            // 正常情况下接口返回应该很会很快。故意延迟调用,让用户有在刷新的体验感
            setTimeout(()=>{
                this.ajax.page = 1;
                this.ajax.load = true;
                this.getList(this.ajax.page);
            },800);
        },
        methods: {
            // 监听渲染高度,此事件为必用
            onRenderHeight(e){
                const { colIndex, height, reportHeightTimeChange } = e;
                // 上报当前高度,上报后会自动触发下一项数据的渲染
                this.$refs.helangWaterfall.reportHeight({
                    colIndex,
                    height,
                    reportHeightTimeChange
                });
            },
            // 获取数据
            getList() {
                // 请求未完成前禁止发起请求
                if (!this.ajax.load) {
                    return;
                }
                this.ajax.load = false;
                // 请求数据, mockData.getList 示例一个 ajax 请求
                mockData.getList({
                    pageNum:this.ajax.page,
                    pageSize:this.ajax.rows
                }).then(res=>{
                    // 获取到的数据,请注意数据结构
                    console.log(res);
                    // 第一页数据执行以下代码
                    if(this.ajax.page == 1){
                        // 关闭下拉
                        uni.stopPullDownRefresh();
                    }
                    // 数据无效时处理
                    if(!res || res.length < 1){
                        // 设置提示内容
                        this.waterfall.status = this.ajax.page === 1 ? 'fail' : 'empty';
                        return;
                    }
                    // 当前页面页面为1时,表示列表重绘,场景一般是 刷新/搜索 时
                    const reset  = this.ajax.page === 1 ? true : false;
                    // 若是重绘列表的话,需要取消 `hideList`属性,隐藏状态下会引发列表尺寸获取错误的问题
                    if(reset){
                        this.waterfall.status = '';
                    }
                    // 调用 render 方法,使组件开始进入渲染阶段
                    this.$refs.helangWaterfall.render(res,reset);
                    this.ajax.load = true;
                    this.ajax.page++;
                })
            },
            // 导航状态切换演示监听
            onStatusChange(){
                const vlaues = ['','fail','empty'];
                const labels = ['正常展示', '加载异常', '无结果'];
                uni.showActionSheet({
                    itemList: labels,
                    success: (res)=> {
                        this.waterfall.status = vlaues[res.tapIndex];
                    }
                });
            },
            // 瀑布流组件点击事件
            handleClick(e){
                const { col_index, item_index } = e?.currentTarget?.dataset;
                uni.showToast({
                    title:`第${col_index+1}栏的${item_index+1}个被点击`,
                    icon:'none'
                });
            },
            // 处理瀑布流渲染状态
            handleStatusChange(status){
                if(status === 'RENDER_END'){
                    this.waterfall.renderEnd = true;
                }else{
                    this.waterfall.renderEnd = false;
                }
            },
            // 收藏
            handleStar(params){
                const { colIndex, itemIndex, data } = params;
                // 切换收藏状态
                const star = !data?.star;
                this.$refs.helangWaterfall.update({
                    colIndex,
                    itemIndex,
                    data:{
                        ...data,
                        star
                        // 此处因为收藏操作不会改变渲染高度,所以不需要传递reportHeight参数
                    }
                })
            },
            // 收藏
            handleActiveHeight(params){
                const { colIndex, itemIndex, data } = params;
                const activeHeight = Math.floor(Math.random() * 100) + 50;
                this.$refs.helangWaterfall.update({
                    colIndex,
                    itemIndex,
                    data:{
                        ...data,
                        activeHeight
                    },
                    // 因为改变高度后会于原来的高度产生差值,需要重新上报高度
                    // update 方法的 reportHeight 参数设置为 true
                    reportHeight:true
                });
            },
        }
    }
</script>
<style lang="scss">
    page {
        background-color: #f3f3f3;
    }
    .content{
        padding: 20rpx;
        background-color: white;
        border-radius: 4px;
        line-height: 1.3;
        .image{
            display: block;
            width: 100%;
            height: auto;
        }
        .ellipsis-1{
            white-space:nowrap;        // 强制一行显示
            overflow:hidden;          // 超出隐藏
            text-overflow:ellipsis;   // 省略号
        }
        .title{
            font-size: 28rpx;
            color:#666;
            margin: 20rpx 0;
        }
        .shop{
            font-size: 24rpx;
            color:#666;
            margin-top: 20rpx;
        }
        .label{
            padding: 10rpx 20rpx;
            border-radius: 100px;
            background-color: #F00;
            color: white;
            font-size: 24rpx;
            display: inline-block;
            line-height: 1;
        }
        .money{
            font-size: 28rpx;
            color:#F00;
            margin-top: 10rpx;
        }
    }
    .load-txt{
        padding:  0;
        text-align: center;
        color: #999;
        font-size: 24rpx;
    }
    .load-icon{
        width: 300rpx;
        height: 300rpx;
        margin: 0 auto 20rpx auto;
        display: block;
    }
    .status-change{
        position: fixed;
        left: 10rpx;
        top: 60%;
        width: 80rpx;
        height: 80rpx;
        z-index: 100;
        font-size: 24rpx;
        border-radius: 50%;
        background-color: #f00;
        color: #fff;
        line-height: 1;
        opacity: .7;
        box-shadow: 0 0 10px black;
        display: flex;
        flex-direction: row;
        flex-wrap: nowrap;
        justify-content: center;
        align-items: center;
        align-content: center;
    }
</style>
uni_modules/helang-waterfall/readme.md
对比新文件
@@ -0,0 +1,142 @@
# 瀑布流 helang-waterfall
## 插件简介
> 这是一款简单又好用的瀑布流布局组件,从`1.2.1`版本开始全新升级为组件+插槽的方案实现,极大的简便了组件的使用和内容调整。
#### <font color="red">源码中有详细的注释,请各位开发者按个人需求自行修改。</font>
### <font color="red">对使用有疑问,可以先导入 [示例项目] 学习 页面与组件的使用流程</font>
## 组件使用
### 示例源码
> 示例源码较多,请下载示例项目查看
### 组件清单
```javascript
// 引用组件
/* 瀑布流组件 */
// 容器组件
import Waterfall from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-list";
// 分栏组件
import WaterfallCol from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-col";
// 内容组件
import WaterfallItem from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-item";
export default {
    // ...
    // 注册组件
    components: {
        "waterfall": Waterfall,
        "waterfall-col": WaterfallCol,
        "waterfall-item": WaterfallItem
    },
    // ...
}
```
### 容器组件 waterfall-list
#### 插槽
插槽名称 | 透传数据 | 说明
--------|------|------
default | 详情见default插槽透传数据说明 |瀑布流列表内容,放置 `waterfall-col` 组件
tips | - | 瀑布流列表下方提示信息
#### default插槽透传数据说明
- 数据结构:`{ list,colWidth }`
- 字段说明:
    1. list:渲染的数据列表,数据增加了 `__waterfall_renderId`、`__waterfall_imageHeight` 属性
        1. `__waterfall_renderId` 用于&lt;waterfall-item&gt;组件循环渲染的 `key`
        2. `__waterfall_reportHeightTime` 用于&lt;waterfall-item&gt;组件监听是否需要重新上报高度的时机
        3. `__waterfall_imageHeight` 用于&lt;image&gt;标签的 `style高度`
    2. colWidth:栏目的宽度,用于 &lt;waterfall-col&gt; 组件的`colWidth`属性
####  属性
属性名 | 类型 | 默认值 | 说明
--------|------|------|------
col | Number | 2 | 列表分栏数量,`不可动态修改`
imageWidth | Number / String | 305 | 列表中图片渲染的宽度,可支持 px 和 rpx单位的宽度,Number类型为rpx单位,若需要px单位则可写`152px`, `不可动态修改`,305是示例列表的图片渲染宽度
imageKey | String | url | 数据中图片的key值,默认为`url`
hideList | Boolean | false | 是否隐藏列表,可隐藏列表部分来展示更多的提示内容(如:空列表时)
cacheImageHeight | Boolean | true | 是否缓存图片高度,能减少图片信息的加载过程提高渲染效率。
#### 方法
- 源码演示
```javascript
// 组件绑定 ref
<waterfall ref="helangWaterfall">...</waterfall>
// 通过 ref 调用组件中的方案
// 渲染
this.$refs.helangWaterfall.render(数据Array,是否重绘Boolean);
// 上报当前高度
this.$refs.helangWaterfall.reportHeight({
    colIndex: 栏目序号Number,
    height: 高度Number,
    reportHeightTimeChange: 是否需要重新上报高度Boolean
});
// 更新数据
this.$refs.helangWaterfall.update({
    colIndex: 栏目序号Number,
    itemIndex: 渲染项序号Number,
    data: 新的数据Object,切记需要保留之前的属性,仅做新属性的覆盖,参考示例项目,
    reportHeight: 是否上报高度Boolean
});
```
- 详细说明
方法名称 | 参数 | 说明
--------|------|------
render | (数据Array,是否重绘Boolean) | 开启瀑布流渲染
reportHeight | ({<br />&nbsp;&nbsp;colIndex: 栏目序号Number,<br />&nbsp;&nbsp;height: 高度Number,<br />&nbsp;&nbsp;reportHeightTimeChange: 是否需要重新上报高度Boolean<br />}) | 汇报当前项的渲染高度,数据从`<waterfall-item @height="onRenderHeight">` 获取
update | ({<br />&nbsp;&nbsp;colIndex: 栏目序号Number,<br />&nbsp;&nbsp;itemIndex: 渲染项序号Number,<br />&nbsp;&nbsp;data: 新的数据Object,切记需要保留之前的属性,仅做新属性的覆盖,参考示例项目<br />&nbsp;&nbsp;reportHeight: 是否上报高度Boolean,用于改变数据会导致节点渲染高度发生改变时使用(如有添加、展开、删除节点的操作时)<br />}) | 更新数据,可参考示例项目的收藏和改变高度的功能实现
#### 事件
事件名称 | 参数 | 说明
--------|------|------
@statusChange | (渲染状态值String) | 监听瀑布流组件的渲染状态,状态有:`RENDER_LOADING(加载中)`、`RENDER_START(渲染开始)`、`RENDER_END(渲染结束)`,<br />可让业务代码配合拦截部分操作减少渲染过程中的BUG发生
### 分栏组件 waterfall-col
####  属性
属性名 | 类型 | 默认值 | 说明
--------|------|------|------
colWidth | String | '' | 栏目宽度,数据来源于 `waterfall-lsit` 的插槽透传的`colWidth`属性
### 内容组件 waterfall-item
####  属性
属性名 | 类型 | 默认值 | 说明
--------|------|------|------
colIndex | Number | 0 | 栏目索引,用于汇报渲染高度时使用
reportHeightTime | Number / String / undefined / null | 0 | 高度上报时间,用于监听是否数据发生更新
####  事件
方法名 | 回调参数 | 说明
--------|------|------
@height | ({colIndex, height,reportHeightTimeChange}) | 监听当前内容渲染后的高度,<br />`colIndex`:栏目索引、<br />`height`:渲染的高度值、<br />`reportHeightTimeChange`:是否需要重新上报高度Boolean
## 常见问题解答
### 1. 列表渲染速度太慢,怎么优化?
瀑布流布局最大的难点是在于图片加载是一个异步的过程,所以本插件也是利用了图片加载这个事件来触发组件初始化完成的。为了更好的加载体验和降低用户的用户的流量消耗,推荐使用`缩略图`来做为列表中的显示(也应当这么做,不只是在瀑布流布局中),这也是做好一个产品最基本的优化要求。
## 友情提示:
1. 当前项目源码使用了 ES6 和 scss 请添加运行依赖
2. [插件ZIP]只包含模板文件和数据文件,不包含项目运行依赖(如 pages.json)
3. [示例项目ZIP]是完整的项目文件,可下载后导入 HBuilderX 中直接运行体验
4. 文件下载在当前页面的上部靠右位置,有 [下载插件ZIP] [下载示例项目ZIP] 按钮,点击即可下载
uni_modules/helang-waterfall/static/waterfall/0.jpg
uni_modules/helang-waterfall/static/waterfall/1.jpg
uni_modules/helang-waterfall/static/waterfall/2.jpg
uni_modules/helang-waterfall/static/waterfall/empty.png
uni_modules/helang-waterfall/static/waterfall/fail.png
uni_modules/uni-nav-bar/changelog.md
文件已删除
uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue
文件已删除
uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue
文件已删除
uni_modules/uni-nav-bar/package.json
文件已删除
uni_modules/uni-nav-bar/readme.md
文件已删除