|
|
|
<template>
|
|
|
|
<view class="page-container">
|
|
|
|
<!-- 导航栏组件 -->
|
|
|
|
<headerVue fixed></headerVue>
|
|
|
|
|
|
|
|
<!-- 灵动岛组件 -->
|
|
|
|
<DynamicIsland
|
|
|
|
ref="dynamicIsland"
|
|
|
|
:page-id="'timeShopBank_page'"
|
|
|
|
:style-type="'timeShop'"
|
|
|
|
/>
|
|
|
|
<view class="desc-box">
|
|
|
|
<view class=""> 欢迎来到「旅行时间行」,你的精神财富储蓄所。 </view>
|
|
|
|
<view class="">
|
|
|
|
在这里,你的每一次人文漫游、每一次灵感闪现,都值得被郑重记录。我们鼓励你分享高质量的图文笔记,将旅途中的美与感动,化为这座精神星球上的璀璨星辰。
|
|
|
|
</view>
|
|
|
|
<view class="">
|
|
|
|
为他人的美好驻足、点赞、留言,每一次真诚的互动,都是在为你的时间银行存入一笔宝贵的「精神货币」。这些资产不仅可以兑换独家福利与实体好物,更能为你解锁专属的荣誉身份,让你成为这座星球上最闪耀的共创者。
|
|
|
|
</view>
|
|
|
|
<view class=""> 即刻发布你的第一篇笔记,开启你的财富积累之旅吧! </view>
|
|
|
|
</view>
|
|
|
|
|
|
|
|
<image
|
|
|
|
style="width: 700rpx; height: 14rpx; margin: 20rpx auto; display: block"
|
|
|
|
:src="showImg('/uploads/20250829/f7214bc2a4f4e236561de893ca7b9113.png')"
|
|
|
|
></image>
|
|
|
|
|
|
|
|
<!-- Tab切换组件 -->
|
|
|
|
<view class="tab-container">
|
|
|
|
<view class="tab-wrapper">
|
|
|
|
<view
|
|
|
|
v-for="(tab, index) in tabs"
|
|
|
|
:key="index"
|
|
|
|
class="tab-item"
|
|
|
|
:class="{ active: currentTab === index }"
|
|
|
|
@click="switchTab(index)"
|
|
|
|
>
|
|
|
|
<text class="tab-text">{{ tab.name }}</text>
|
|
|
|
<view v-if="currentTab === index" class="tab-indicator"></view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 内容区域 -->
|
|
|
|
<view class="content-area">
|
|
|
|
<view v-if="currentTab == 0" class="notes-content">
|
|
|
|
<WaterfallLayout
|
|
|
|
:items="waterfallItems"
|
|
|
|
:column-count="2"
|
|
|
|
:column-gap="16"
|
|
|
|
:item-gap="16"
|
|
|
|
@item-click="handleItemClick"
|
|
|
|
@like-change="handleNoteLikeChange"
|
|
|
|
style="margin-top: 20rpx"
|
|
|
|
/>
|
|
|
|
</view>
|
|
|
|
<!-- <view v-if="currentTab === 0" class="follow-content recommend-content">
|
|
|
|
<text class="coming-soon">笔记功能开发中...</text>
|
|
|
|
</view> -->
|
|
|
|
<!-- 关注tab内容 -->
|
|
|
|
<view v-if="currentTab == 1" class="follow-content">
|
|
|
|
<!-- <FollowTab /> -->
|
|
|
|
<text class="coming-soon">关注功能开发中...</text>
|
|
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 推荐tab内容 -->
|
|
|
|
<view v-if="currentTab == 2" class="notes-content">
|
|
|
|
<WaterfallLayout
|
|
|
|
:items="waterfallItems"
|
|
|
|
:column-count="2"
|
|
|
|
:column-gap="16"
|
|
|
|
:item-gap="16"
|
|
|
|
@item-click="handleItemClick"
|
|
|
|
@like-change="handleNoteLikeChange"
|
|
|
|
style="margin-top: 20rpx"
|
|
|
|
/>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
<!-- <view class="fab-container">
|
|
|
|
<image
|
|
|
|
@click="goToPublish"
|
|
|
|
:src="
|
|
|
|
showImg('/uploads/20250825/7ea7864b8abb89c3dd7834f025e49b3f.png')
|
|
|
|
"
|
|
|
|
style="width: 91rpx; height: 91rpx"
|
|
|
|
></image>
|
|
|
|
</view> -->
|
|
|
|
<!-- 控制按钮 -->
|
|
|
|
<!-- <view class="controls">
|
|
|
|
<button @click="addRandomItem" class="control-btn primary">
|
|
|
|
添加项目
|
|
|
|
</button>
|
|
|
|
<button @click="clearAllItems" class="control-btn danger">清空</button>
|
|
|
|
</view> -->
|
|
|
|
|
|
|
|
<!-- 底部占位区域,防止内容被TabBar遮挡 -->
|
|
|
|
<view class="tab-bar-placeholder"></view>
|
|
|
|
|
|
|
|
<!-- 底部TabBar组件 -->
|
|
|
|
<CustomTabBar :currentTab="2" />
|
|
|
|
</view>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import WaterfallLayout from "@/components/WaterfallLayout.vue";
|
|
|
|
import headerVue from "@/components/header.vue";
|
|
|
|
import CustomTabBar from "@/components/CustomTabBar.vue";
|
|
|
|
import DynamicIsland from "@/components/DynamicIsland.vue";
|
|
|
|
import FollowTab from "./components/FollowTab.vue";
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: "TimeShopBank",
|
|
|
|
components: {
|
|
|
|
WaterfallLayout,
|
|
|
|
headerVue,
|
|
|
|
CustomTabBar,
|
|
|
|
DynamicIsland,
|
|
|
|
FollowTab,
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
currentTab: 2, // 默认选中"笔记"
|
|
|
|
tabs: [
|
|
|
|
{ name: "笔记", id: "notes" },
|
|
|
|
{ name: "关注", id: "follow" },
|
|
|
|
{ name: "推荐", id: "recommend" },
|
|
|
|
],
|
|
|
|
waterfallItems: [],
|
|
|
|
// 分页相关数据
|
|
|
|
pageNum: 1,
|
|
|
|
pageSize: 10,
|
|
|
|
loading: false,
|
|
|
|
hasMore: true,
|
|
|
|
autoAddEnabled: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
onLoad() {
|
|
|
|
this.userInfo =
|
|
|
|
(uni.getStorageSync("userInfo") &&
|
|
|
|
JSON.parse(uni.getStorageSync("userInfo"))) ||
|
|
|
|
this.$store.state.user.userInfo ||
|
|
|
|
{};
|
|
|
|
// 加载推荐列表数据
|
|
|
|
this.getRecommendList(1);
|
|
|
|
|
|
|
|
// 监听笔记点赞变更事件
|
|
|
|
uni.$on("note-like-change", this.handleNoteLikeChange);
|
|
|
|
},
|
|
|
|
onShow() {
|
|
|
|
if (this.userInfo && this.userInfo.id) {
|
|
|
|
this.getUserInfo();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// 页面卸载时移除事件监听
|
|
|
|
onUnload() {
|
|
|
|
uni.$off("note-like-change", this.handleNoteLikeChange);
|
|
|
|
},
|
|
|
|
|
|
|
|
// 页面滚动到底部时触发
|
|
|
|
onReachBottom() {
|
|
|
|
this.loadMoreItems();
|
|
|
|
},
|
|
|
|
|
|
|
|
// 页面滚动时触发 - 用于灵动岛样式切换
|
|
|
|
onPageScroll(e) {
|
|
|
|
// 只触发带页面ID的事件,避免不同页面间的状态冲突
|
|
|
|
uni.$emit("pageScroll_timeShopBank_page", e.scrollTop);
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
getUserInfo() {
|
|
|
|
this.Post({}, "/framework/user/getInfo", "DES").then((res) => {
|
|
|
|
res.data.token = this.userInfo.token;
|
|
|
|
uni.setStorageSync("userInfo", JSON.stringify(res.data));
|
|
|
|
this.userInfo = res.data;
|
|
|
|
this.$nextTick(() => {
|
|
|
|
this.$refs.dynamicIsland.getUserInfo();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// 获取推荐列表数据
|
|
|
|
getRecommendList(type = 1) {
|
|
|
|
if (this.loading) return;
|
|
|
|
|
|
|
|
this.loading = true;
|
|
|
|
const params = {
|
|
|
|
pageNum: this.pageNum,
|
|
|
|
pageSize: this.pageSize,
|
|
|
|
type: type, // 0: 笔记tab, 1: 推荐tab
|
|
|
|
};
|
|
|
|
|
|
|
|
this.Post(params, "/framework/note/list", "DES")
|
|
|
|
.then((res) => {
|
|
|
|
if (res.code === 200 && res.rows) {
|
|
|
|
const newItems = res.rows || [];
|
|
|
|
|
|
|
|
if (this.pageNum === 1) {
|
|
|
|
// 首次加载或刷新
|
|
|
|
this.waterfallItems = newItems;
|
|
|
|
} else {
|
|
|
|
// 加载更多
|
|
|
|
this.waterfallItems.push(...newItems);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 判断是否还有更多数据
|
|
|
|
this.hasMore = newItems.length === this.pageSize;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
console.error("获取推荐列表失败:", error);
|
|
|
|
uni.showToast({
|
|
|
|
title: "加载失败,请重试",
|
|
|
|
icon: "none",
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
this.loading = false;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
// 清空所有项目
|
|
|
|
clearAllItems() {
|
|
|
|
uni.showModal({
|
|
|
|
title: "确认清空",
|
|
|
|
content: "确定要清空所有项目吗?",
|
|
|
|
success: (res) => {
|
|
|
|
if (res.confirm) {
|
|
|
|
this.waterfallItems = [];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// 处理项目点击
|
|
|
|
handleItemClick(item) {
|
|
|
|
console.log(item);
|
|
|
|
// 跳转到笔记详情页面
|
|
|
|
uni.navigateTo({
|
|
|
|
url: `/pages/notes/detail?id=${item.id}`,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// 跳转到发布页面
|
|
|
|
goToPublish() {
|
|
|
|
uni.navigateTo({
|
|
|
|
url: "/pages/notes/publish",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// 处理项目添加
|
|
|
|
handleItemAdded(item) {},
|
|
|
|
|
|
|
|
// 处理自动添加请求
|
|
|
|
handleAutoAddRequest() {
|
|
|
|
this.loadMoreItems();
|
|
|
|
},
|
|
|
|
|
|
|
|
// 加载更多项目(到达底部时调用)
|
|
|
|
loadMoreItems() {
|
|
|
|
if (!this.loading && this.hasMore) {
|
|
|
|
this.pageNum++;
|
|
|
|
// 根据当前tab传递不同的type: 0-笔记, 1-推荐
|
|
|
|
const type = this.currentTab === 0 ? 0 : 1;
|
|
|
|
this.getRecommendList(type);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// Tab切换方法
|
|
|
|
switchTab(index) {
|
|
|
|
this.currentTab = index;
|
|
|
|
// 根据不同tab加载不同内容
|
|
|
|
this.loadTabContent(this.tabs[index].id);
|
|
|
|
},
|
|
|
|
|
|
|
|
// 加载tab对应的内容
|
|
|
|
loadTabContent(tabId) {
|
|
|
|
// 这里可以根据不同的tabId加载不同的数据
|
|
|
|
if (tabId === "notes") {
|
|
|
|
// 笔记tab: 重置分页数据并加载笔记列表
|
|
|
|
this.pageNum = 1;
|
|
|
|
this.waterfallItems = [];
|
|
|
|
this.hasMore = true;
|
|
|
|
this.getRecommendList(0);
|
|
|
|
}
|
|
|
|
// 推荐tab不需要重新加载数据,保持现有数据
|
|
|
|
},
|
|
|
|
|
|
|
|
// 处理笔记点赞变更事件
|
|
|
|
handleNoteLikeChange(data) {
|
|
|
|
console.log(data, "data");
|
|
|
|
if (!data || !data.noteId) return;
|
|
|
|
|
|
|
|
// 在瀑布流中查找并更新对应的笔记
|
|
|
|
const updateItemInList = (items) => {
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
if (items[i].id == data.noteId) {
|
|
|
|
// 更新点赞状态和数量
|
|
|
|
items[i].userLiked = data.isLiked;
|
|
|
|
items[i].likeCount = data.likeCount;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// 尝试在当前显示的笔记列表中更新
|
|
|
|
const updated = updateItemInList(this.waterfallItems);
|
|
|
|
console.log(updated, "updated");
|
|
|
|
// 如果找到并更新了笔记,强制视图更新
|
|
|
|
if (updated) {
|
|
|
|
// 使用Vue的响应式更新机制
|
|
|
|
this.$forceUpdate();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<style>
|
|
|
|
page {
|
|
|
|
background-color: white;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.page-container {
|
|
|
|
min-height: 100vh;
|
|
|
|
background: white;
|
|
|
|
color: #333;
|
|
|
|
}
|
|
|
|
|
|
|
|
.controls {
|
|
|
|
display: flex;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
gap: 20rpx;
|
|
|
|
justify-content: center;
|
|
|
|
margin: 40rpx 0;
|
|
|
|
padding: 0 20rpx;
|
|
|
|
}
|
|
|
|
|
|
|
|
.control-btn {
|
|
|
|
padding: 24rpx 48rpx;
|
|
|
|
border-radius: 48rpx;
|
|
|
|
font-size: 28rpx;
|
|
|
|
border: none;
|
|
|
|
cursor: pointer;
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
background: white;
|
|
|
|
color: #333;
|
|
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
|
|
|
font-weight: 500;
|
|
|
|
}
|
|
|
|
|
|
|
|
.control-btn.primary {
|
|
|
|
background: #ff4757;
|
|
|
|
color: white;
|
|
|
|
}
|
|
|
|
|
|
|
|
.control-btn.danger {
|
|
|
|
background: #ff6b6b;
|
|
|
|
color: white;
|
|
|
|
}
|
|
|
|
|
|
|
|
.control-btn:active {
|
|
|
|
transform: scale(0.95);
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab-bar-placeholder {
|
|
|
|
height: 120rpx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tab切换样式 */
|
|
|
|
.tab-container {
|
|
|
|
background: #ffffff;
|
|
|
|
padding: 0 32rpx;
|
|
|
|
margin-top: 20rpx;
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab-wrapper {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: space-around;
|
|
|
|
height: 88rpx;
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab-item {
|
|
|
|
flex: 1;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
height: 100%;
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab-text {
|
|
|
|
font-size: 32rpx;
|
|
|
|
color: #999999;
|
|
|
|
font-weight: 400;
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab-item.active .tab-text {
|
|
|
|
color: #333333;
|
|
|
|
font-weight: 600;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tab-indicator {
|
|
|
|
position: absolute;
|
|
|
|
bottom: 0;
|
|
|
|
left: 50%;
|
|
|
|
transform: translateX(-50%);
|
|
|
|
width: 80rpx;
|
|
|
|
height: 4rpx;
|
|
|
|
background: #33fefe;
|
|
|
|
border-radius: 3rpx;
|
|
|
|
animation: slideIn 0.3s ease;
|
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes slideIn {
|
|
|
|
from {
|
|
|
|
width: 0;
|
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
to {
|
|
|
|
width: 80rpx;
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 悬浮发布按钮 */
|
|
|
|
.fab-container {
|
|
|
|
position: fixed;
|
|
|
|
bottom: 250rpx;
|
|
|
|
right: 40rpx;
|
|
|
|
z-index: 999;
|
|
|
|
}
|
|
|
|
|
|
|
|
.fab-btn {
|
|
|
|
width: 80rpx;
|
|
|
|
height: 80rpx;
|
|
|
|
border-radius: 40rpx;
|
|
|
|
background: linear-gradient(135deg, #ff4757, #ff6b7a);
|
|
|
|
color: #fff;
|
|
|
|
border: none;
|
|
|
|
box-shadow: 0 8rpx 24rpx rgba(255, 71, 87, 0.4);
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
transform: scale(0.95);
|
|
|
|
box-shadow: 0 4rpx 12rpx rgba(255, 71, 87, 0.3);
|
|
|
|
}
|
|
|
|
|
|
|
|
.fab-icon {
|
|
|
|
font-size: 48rpx;
|
|
|
|
font-weight: 300;
|
|
|
|
line-height: 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 内容区域样式 */
|
|
|
|
.content-area {
|
|
|
|
flex: 1;
|
|
|
|
overflow: hidden;
|
|
|
|
padding: 10rpx 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
.notes-content {
|
|
|
|
position: relative;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.follow-content {
|
|
|
|
height: 100%;
|
|
|
|
overflow-y: auto;
|
|
|
|
}
|
|
|
|
|
|
|
|
.recommend-content {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
height: 400rpx;
|
|
|
|
|
|
|
|
.coming-soon {
|
|
|
|
font-size: 28rpx;
|
|
|
|
color: #999;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.desc-box {
|
|
|
|
padding: 0 20rpx;
|
|
|
|
color: #616161;
|
|
|
|
margin: 30rpx 0;
|
|
|
|
font-size: 24rpx;
|
|
|
|
padding: 0 40rpx;
|
|
|
|
view {
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
&:nth-child(1) {
|
|
|
|
font-size: 24rpx;
|
|
|
|
font-weight: bold;
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 自定义样式已移至WaterfallLayout组件内部 */
|
|
|
|
</style>
|