saveComment.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. <template>
  2. <view class="content">
  3. <view class="top-fixed">
  4. <!-- <view class="nav-title">
  5. <text>{{tabIndex === 0 ? '我的评论' : '我的收藏'}}</text>
  6. </view> -->
  7. <view class="tabs-container"
  8. :style="{ '--underline-width': underlineWidth + 'rpx', '--underline-left': underlineLeft + 'rpx', '--underline-opacity': underlineOpacity }">
  9. <view v-for="(tab, index) in mtabs" :key="index" class="tab-item"
  10. :class="tabIndex === index ? 'active' : ''" @click="tabChange(index)" :id="'tab-' + index">
  11. <text :class="tabIndex === index ? 'active-text' : ''">{{ tab }}</text>
  12. </view>
  13. <view class="nav-underline"></view>
  14. </view>
  15. </view>
  16. <mescroll-uni ref="mescrollRef" style="background-color: #FAFAFA;" :top="mescrollTopY" top="24"
  17. :down="downOption" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick"
  18. @init="mescrollInit">
  19. <view class="comment-list" style="padding: 0 24rpx;">
  20. <view class="comment-item" v-for="(item, index) in dataList" :key="index" @click="handleDetail(item)">
  21. <view class="comment-header">
  22. </view>
  23. <image v-if="item.imgsUrl" class="comment-image" :src="item.imgsUrl" mode="aspectFill"></image>
  24. <video v-else="item.videoUrl" class="comment-video" :src="item.videoUrl" object-fit="fill" show-progress="false"></video>
  25. <view class="comment-footer">
  26. <view class="like-count">
  27. <image class="like-icon" src="https://fs-1319721001.cos.ap-chongqing.myqcloud.com/images/like_icon.png"></image>
  28. <text>{{ item.likeCount || 0 }}</text>
  29. </view>
  30. </view>
  31. </view>
  32. </view>
  33. </mescroll-uni>
  34. </view>
  35. </template>
  36. <script>
  37. import { getMyComments, getMyCollection } from '@/api/life'
  38. export default {
  39. components: {
  40. },
  41. data() {
  42. return {
  43. tabTop: 0,
  44. mtabs: ["我的收藏", "我的评论"],
  45. downOption: { //下拉刷新
  46. use: true,
  47. auto: false
  48. },
  49. upOption: {
  50. auto: false, // 不自动加载
  51. page: {
  52. num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
  53. size: 20 // 每页数据的数量
  54. },
  55. textNoMore: "已经到底了",
  56. noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
  57. toTop: {
  58. width: 0,
  59. },
  60. empty: {
  61. tip: '~ 暂无数据 ~', // 提示
  62. btnText: '刷新重试',
  63. icon: "/static/images/icon_img_empty.png"
  64. },
  65. },
  66. dataList: [],
  67. tabIndex: 0,
  68. mescrollTopY: 0,
  69. mescroll: null,
  70. tabPositions: [] // 存储每个tab的位置信息
  71. }
  72. },
  73. computed: {
  74. // 计算下划线的宽度
  75. underlineWidth() {
  76. if (this.tabPositions.length === 0 || this.tabIndex >= this.tabPositions.length) {
  77. return 0;
  78. }
  79. const position = this.tabPositions[this.tabIndex];
  80. return Math.round(position.width * 2);
  81. },
  82. // 计算下划线的左侧位置
  83. underlineLeft() {
  84. if (this.tabPositions.length === 0 || this.tabIndex >= this.tabPositions.length) {
  85. return 0;
  86. }
  87. const position = this.tabPositions[this.tabIndex];
  88. const left = position.left + position.width / 2;
  89. return Math.round(left * 2);
  90. },
  91. // 计算下划线的透明度
  92. underlineOpacity() {
  93. if (this.tabPositions.length === 0 || this.tabIndex >= this.tabPositions.length) {
  94. return 0;
  95. }
  96. return 1;
  97. }
  98. },
  99. onLoad(options) {
  100. this.tabIndex = Number(options.tabIndex || 0)
  101. uni.$on("refreshPage", res => {
  102. this.refreshPage();
  103. })
  104. },
  105. onUnload() {
  106. uni.$off("refreshPage")
  107. },
  108. mounted(option) {
  109. const system = uni.getSystemInfoSync();
  110. let navigationBarHeight = 44, tabHei = 88, searchBarHei = 44;
  111. let tempHei = navigationBarHeight + system.statusBarHeight;
  112. tempHei = tempHei + uni.upx2px(tabHei);
  113. this.mescrollTopY = tempHei + "px";
  114. // 初始化tab位置
  115. this.initTabPositions();
  116. },
  117. methods: {
  118. handleDetail(item) {
  119. console.log("跳转视频",item)
  120. uni.navigateTo({
  121. url: "/pages_life/video?resourceId=" + item.resourceId
  122. })
  123. },
  124. /*下拉刷新的回调 */
  125. downCallback() {
  126. this.refreshPage();
  127. },
  128. /*上拉加载的回调*/
  129. upCallback(page) {
  130. /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
  131. const params = {
  132. page: page.num,
  133. pageSize: page.size
  134. };
  135. try {
  136. // 直接调用对应的函数,避免使用变量
  137. if (this.tabIndex === 0) {
  138. // 调用我的评论接口
  139. getMyComments(params).then(res => {
  140. if (res.code == 200) {
  141. setTimeout(() => {
  142. const list = res.data && res.data.list ? res.data.list : [];
  143. // 转换数据格式,确保有必要的字段
  144. const formattedList = list.map(item => ({
  145. ...item,
  146. cover: item.imgsUrl || item.videoUrl || '',
  147. likeNum: item.likeCount || item.likeNum || item.likes || 0
  148. }));
  149. const total = res.data && res.data.total ? res.data.total : 0;
  150. const pages = Math.ceil(total / page.size);
  151. this.mescroll.endByPage(formattedList.length, pages);
  152. // 不累加数据,直接替换
  153. this.dataList = formattedList;
  154. },
  155. 300
  156. );
  157. } else {
  158. this.dataList = []; //追加新数据
  159. this.mescroll.endByPage(0, 1);
  160. }
  161. }).catch(err => {
  162. console.error('获取评论失败:', err);
  163. this.dataList = []; //追加新数据
  164. //联网失败, 结束加载
  165. this.mescroll.endErr();
  166. });
  167. } else {
  168. // 调用我的收藏接口
  169. getMyCollection(params).then(res => {
  170. if (res.code == 200) {
  171. setTimeout(() => {
  172. const list = res.data && res.data.list ? res.data.list : [];
  173. // 转换数据格式,确保有必要的字段
  174. const formattedList = list.map(item => ({
  175. ...item,
  176. cover: item.imgsUrl || item.videoUrl || '',
  177. likeNum: item.likeCount || item.likeNum || item.likes || 0
  178. }));
  179. const total = res.data && res.data.total ? res.data.total : 0;
  180. const pages = Math.ceil(total / page.size);
  181. this.mescroll.endByPage(formattedList.length, pages);
  182. // 不累加数据,直接替换
  183. this.dataList = formattedList;
  184. },
  185. 300
  186. );
  187. } else {
  188. this.dataList = []; //追加新数据
  189. this.mescroll.endByPage(0, 1);
  190. }
  191. }).catch(err => {
  192. console.error('获取收藏失败:', err);
  193. this.dataList = []; //追加新数据
  194. //联网失败, 结束加载
  195. this.mescroll.endErr();
  196. });
  197. }
  198. } catch (error) {
  199. console.error('错误:', error);
  200. this.dataList = [];
  201. this.mescroll.endErr();
  202. }
  203. },
  204. refreshPage() {
  205. if (this.mescroll) {
  206. this.mescroll.hideTopBtn();
  207. this.mescroll.resetUpScroll(true);
  208. // 手动触发上拉加载,获取第一页数据
  209. this.mescroll.triggerUpScroll();
  210. } else {
  211. // 如果 mescroll 还未初始化,延迟后重试
  212. setTimeout(() => {
  213. this.refreshPage();
  214. }, 100);
  215. }
  216. },
  217. tabChange(index) {
  218. console.log("qxj tabChange");
  219. this.tabIndex = index;
  220. this.refreshPage();
  221. // 更新tab位置
  222. this.initTabPositions();
  223. },
  224. // 初始化tab位置
  225. initTabPositions() {
  226. let positions = [];
  227. // 确保mtabs有数据
  228. if (this.mtabs.length === 0) {
  229. console.log('No tabs data available');
  230. return;
  231. }
  232. Promise.all(
  233. this.mtabs.map((_, index) =>
  234. new Promise((resolve) => {
  235. uni
  236. .createSelectorQuery()
  237. .in(this)
  238. .select(`#tab-${index}`)
  239. .boundingClientRect((data) => {
  240. if (data) {
  241. positions.push({ left: data.left, width: data.width });
  242. } else {
  243. // 兜底默认宽度
  244. positions.push({ left: index * 150, width: 150 });
  245. }
  246. resolve();
  247. })
  248. .exec();
  249. })
  250. )
  251. ).then(() => {
  252. // 所有节点查询完毕,一次性赋值
  253. this.tabPositions = positions;
  254. // console.log('Calculated tab positions:', this.tabPositions);
  255. });
  256. },
  257. //点击空布局按钮的回调
  258. emptyClick() {
  259. this.refreshPage();
  260. },
  261. // 初始化mescroll
  262. mescrollInit(mescroll) {
  263. this.mescroll = mescroll;
  264. // 初始化时触发一次加载
  265. if (!this.dataList.length) {
  266. setTimeout(() => {
  267. this.mescroll.triggerUpScroll();
  268. }, 100);
  269. }
  270. }
  271. }
  272. }
  273. </script>
  274. <style lang="scss" scoped>
  275. page {
  276. height: 100%;
  277. background-color: #f5f5f5;
  278. }
  279. .content {
  280. height: 100%;
  281. position: relative;
  282. }
  283. .top-fixed {
  284. width: 100%;
  285. position: absolute;
  286. top: 0;
  287. left: 0;
  288. z-index: 10;
  289. height: 88rpx;
  290. background-color: #fff;
  291. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
  292. }
  293. .tabs-container {
  294. height: 100%;
  295. display: flex;
  296. align-items: center;
  297. position: relative;
  298. .tab-item {
  299. flex: 1;
  300. height: 100%;
  301. display: flex;
  302. align-items: center;
  303. justify-content: center;
  304. position: relative;
  305. &.active {
  306. text {
  307. color: #333333;
  308. font-weight: bold;
  309. }
  310. }
  311. text {
  312. font-size: 34rpx;
  313. color: #4D4D4D;
  314. line-height: 90rpx;
  315. }
  316. }
  317. .nav-underline {
  318. position: absolute;
  319. height: 6rpx;
  320. border-radius: 2rpx;
  321. top: 84rpx;
  322. width: var(--underline-width, 0);
  323. left: var(--underline-left, 0);
  324. opacity: var(--underline-opacity, 0);
  325. visibility: visible;
  326. transition: all 0.3s ease;
  327. background-color: #02B176;
  328. transform: translateX(-50%);
  329. }
  330. }
  331. .sticky-tabs {
  332. display: none;
  333. }
  334. .comment-list {
  335. margin-top: 88rpx;
  336. display: flex;
  337. flex-wrap: wrap;
  338. padding: 20rpx 0;
  339. gap: 16rpx;
  340. }
  341. .comment-item {
  342. position: relative;
  343. width: calc(33% - 10rpx);
  344. background-color: #fff;
  345. border-radius: 16rpx;
  346. margin-bottom: 0;
  347. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
  348. height: 280rpx;
  349. display: flex;
  350. flex-direction: column;
  351. overflow: hidden;
  352. .comment-image {
  353. width: 100%;
  354. height: 100%;
  355. border-radius: 10rpx;
  356. margin-bottom: 12rpx;
  357. object-fit: cover;
  358. }
  359. .comment-video{
  360. width: 100%;
  361. height: 100%;
  362. border-radius: 10rpx;
  363. margin-bottom: 12rpx;
  364. object-fit: cover;
  365. }
  366. .comment-header {
  367. display: flex;
  368. justify-content: flex-end;
  369. .item-type-tag {
  370. font-size: 18rpx;
  371. color: #999999;
  372. padding: 4rpx 8rpx;
  373. background-color: #f5f5f5;
  374. border-radius: 4rpx;
  375. }
  376. }
  377. .comment-footer {
  378. position: absolute;
  379. bottom: 0;
  380. left: 0;
  381. width: 100%;
  382. display: flex;
  383. align-items: center;
  384. justify-content: center;
  385. margin-top: auto;
  386. .like-count {
  387. width: 100%;
  388. padding: 8rpx 0;
  389. box-sizing: border-box;
  390. display: flex;
  391. align-items: center;
  392. justify-content: center;
  393. background-color: rgba(0, 0, 0, 0.4);
  394. .like-icon {
  395. width: 24rpx;
  396. height: 24rpx;
  397. margin-right: 4rpx;
  398. }
  399. text {
  400. font-size: 22rpx;
  401. color: #666666;
  402. }
  403. }
  404. }
  405. }
  406. </style>