|
@@ -0,0 +1,265 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="page-container">
|
|
|
|
|
+ <view class="waterfall-box">
|
|
|
|
|
+ <view class="column">
|
|
|
|
|
+ <view class="card-item" v-for="(item, index) in leftList" :key="item.id" @click="goDetail(item)">
|
|
|
|
|
+ <CardContent :item="item" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="column">
|
|
|
|
|
+ <view class="card-item" v-for="(item, index) in rightList" :key="item.id" @click="goDetail(item)">
|
|
|
|
|
+ <CardContent :item="item" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+// 为了代码整洁,我们将卡片内部逻辑抽离成局部组件,或者直接写在上面
|
|
|
|
|
+// 这里为了演示方便,我直接在 methods 里处理数据分发
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ components: {
|
|
|
|
|
+ // 内部组件:卡片内容
|
|
|
|
|
+ CardContent: {
|
|
|
|
|
+ props: ['item'],
|
|
|
|
|
+ template: `
|
|
|
|
|
+ <view class="card-inner">
|
|
|
|
|
+ <view class="media-cover">
|
|
|
|
|
+ <u-image
|
|
|
|
|
+ v-if="item.type === 'image' && item.urls.length > 0"
|
|
|
|
|
+ :src="item.urls[0]"
|
|
|
|
|
+ width="100%"
|
|
|
|
|
+ mode="widthFix"
|
|
|
|
|
+ radius="8px 8px 0 0"
|
|
|
|
|
+ ></u-image>
|
|
|
|
|
+
|
|
|
|
|
+ <u-image
|
|
|
|
|
+ v-else-if="item.type === 'video'"
|
|
|
|
|
+ :src="item.videoPoster"
|
|
|
|
|
+ width="100%"
|
|
|
|
|
+ mode="widthFix"
|
|
|
|
|
+ radius="8px 8px 0 0"
|
|
|
|
|
+ ></u-image>
|
|
|
|
|
+
|
|
|
|
|
+ <view v-else class="text-cover" :style="{background: item.bgColor || '#f0f2f5'}">
|
|
|
|
|
+ <text class="only-text">{{ item.content }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="icon-layer">
|
|
|
|
|
+ <u-icon v-if="item.type === 'video'" name="play-circle-fill" color="#fff" size="28"></u-icon>
|
|
|
|
|
+ <view v-if="item.type === 'image' && item.urls.length > 1" class="img-count">
|
|
|
|
|
+ <u-icon name="photo" color="#fff" size="12"></u-icon>
|
|
|
|
|
+ <text class="count-num">{{ item.urls.length }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="info-box">
|
|
|
|
|
+ <view class="title u-line-2" v-if="item.type !== 'text'">
|
|
|
|
|
+ {{ item.content || '分享生活' }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="user-row">
|
|
|
|
|
+ <view class="left-user">
|
|
|
|
|
+ <u-avatar :src="item.avatar" size="20"></u-avatar>
|
|
|
|
|
+ <text class="name u-line-1">{{ item.nickname }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="right-like">
|
|
|
|
|
+ <u-icon :name="item.isLiked ? 'heart-fill' : 'heart'" :color="item.isLiked ? '#ff2442' : '#999'" size="16"></u-icon>
|
|
|
|
|
+ <text class="num">{{ item.likeCount }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ `
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ allList: [], // 原始数据
|
|
|
|
|
+ leftList: [], // 左列数据
|
|
|
|
|
+ rightList: [] // 右列数据
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ onLoad() {
|
|
|
|
|
+ this.loadData();
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ // 模拟获取数据
|
|
|
|
|
+ loadData() {
|
|
|
|
|
+ // 模拟API返回的数据
|
|
|
|
|
+ const apiData = [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1, type: 'image', content: '周末去露营啦,风景真的超级好!推荐大家来这里打卡。',
|
|
|
|
|
+ urls: ['https://cdn.uviewui.com/uview/album/1.jpg', 'https://cdn.uviewui.com/uview/album/2.jpg'],
|
|
|
|
|
+ avatar: 'https://cdn.uviewui.com/uview/album/1.jpg', nickname: '露营达人', likeCount: 120, isLiked: true
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2, type: 'video', content: '这是我新拍的Vlog,剪辑了好久,希望大家喜欢!',
|
|
|
|
|
+ videoPoster: 'https://cdn.uviewui.com/uview/album/2.jpg', videoUrl: '...',
|
|
|
|
|
+ avatar: 'https://cdn.uviewui.com/uview/album/2.jpg', nickname: 'Vlog博主', likeCount: 45, isLiked: false
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3, type: 'image', content: '今天吃的好丰盛',
|
|
|
|
|
+ urls: ['https://cdn.uviewui.com/uview/album/3.jpg'], // 单图
|
|
|
|
|
+ avatar: 'https://cdn.uviewui.com/uview/album/3.jpg', nickname: '吃货小美', likeCount: 8, isLiked: false
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 4, type: 'text', content: '今天心情稍微有点低落,但是看到彩虹了!',
|
|
|
|
|
+ bgColor: '#ff9a9e', // 纯文字给个背景色
|
|
|
|
|
+ avatar: 'https://cdn.uviewui.com/uview/album/4.jpg', nickname: '心情日记', likeCount: 2, isLiked: false
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 5, type: 'image', content: '高纵横比的图片测试,瀑布流效果。',
|
|
|
|
|
+ urls: ['https://cdn.uviewui.com/uview/swiper/1.jpg'], // 这是一个长图
|
|
|
|
|
+ avatar: 'https://cdn.uviewui.com/uview/album/5.jpg', nickname: '摄影师', likeCount: 300, isLiked: true
|
|
|
|
|
+ }
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ this.distributeData(apiData);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 关键:左右分列逻辑
|
|
|
|
|
+ distributeData(list) {
|
|
|
|
|
+ list.forEach((item, index) => {
|
|
|
|
|
+ // 简单方案:根据索引奇偶数分发
|
|
|
|
|
+ // 进阶方案:应该计算左右两列当前的累积高度,哪边矮由于哪边(需要异步获取图片高度,较复杂)
|
|
|
|
|
+ if (index % 2 === 0) {
|
|
|
|
|
+ this.leftList.push(item);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.rightList.push(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ goDetail(item) {
|
|
|
|
|
+ console.log('点击了卡片', item.id);
|
|
|
|
|
+ // 跳转到详情页,详情页通常是全屏轮播图或者类似抖音的上下滑动
|
|
|
|
|
+ // uni.navigateTo({ url: `/pages/moments/detail?id=${item.id}` });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.page-container {
|
|
|
|
|
+ background-color: #f7f7f7; // 浅灰底色
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.waterfall-box {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.column {
|
|
|
|
|
+ width: 48%; // 左右各占约一半
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-item {
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ overflow: hidden; // 圆角遮罩
|
|
|
|
|
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 以下样式会被 CardContent 组件继承(如果是分开的文件需注意scoped)
|
|
|
|
|
+.card-inner {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.media-cover {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ min-height: 200rpx; // 防止加载时塌陷
|
|
|
|
|
+
|
|
|
|
|
+ // 纯文字模式样式
|
|
|
|
|
+ .text-cover {
|
|
|
|
|
+ height: 300rpx;
|
|
|
|
|
+ padding: 30rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+
|
|
|
|
|
+ .only-text {
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .icon-layer {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 10rpx;
|
|
|
|
|
+ right: 10rpx;
|
|
|
|
|
+ z-index: 2;
|
|
|
|
|
+
|
|
|
|
|
+ .img-count {
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .count-num {
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ margin-left: 6rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.info-box {
|
|
|
|
|
+ padding: 16rpx;
|
|
|
|
|
+
|
|
|
|
|
+ .title {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ margin-bottom: 16rpx;
|
|
|
|
|
+ // u-line-2 是 uView 内置的多行省略样式,如果未生效请手动添加css
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .user-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .left-user {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+
|
|
|
|
|
+ .name {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ margin-left: 10rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .right-like {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .num {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ margin-left: 6rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|