my-dynamics.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. <template>
  2. <view class="my-dynamics-page">
  3. <!-- 顶部导航栏 -->
  4. <view class="top-nav">
  5. <view class="back-btn" @click="goBack">
  6. <text class="back-icon">‹</text>
  7. </view>
  8. <view class="nav-title">我的动态</view>
  9. <view class="nav-right"></view>
  10. </view>
  11. <!-- 用户信息区域 -->
  12. <view class="user-info-section">
  13. <image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
  14. <view class="user-details">
  15. <text class="nickname">{{ userInfo.nickname }}</text>
  16. <text class="dynamic-count">{{ userInfo.dynamicCount }} 动态</text>
  17. </view>
  18. </view>
  19. <!-- 标签页导航 -->
  20. <view class="tab-nav">
  21. <view
  22. class="tab-item"
  23. :class="{ active: activeTab === 'dynamic' }"
  24. @click="switchTab('dynamic')"
  25. >
  26. <text>动态</text>
  27. </view>
  28. <view
  29. class="tab-item"
  30. :class="{ active: activeTab === 'interaction' }"
  31. @click="switchTab('interaction')"
  32. >
  33. <text>互动</text>
  34. </view>
  35. <view
  36. class="tab-item"
  37. :class="{ active: activeTab === 'browse' }"
  38. @click="switchTab('browse')"
  39. >
  40. <text>浏览记录</text>
  41. </view>
  42. </view>
  43. <!-- 互动子标签 -->
  44. <view class="sub-tab-nav" v-if="activeTab === 'interaction'">
  45. <view
  46. class="sub-tab-item"
  47. :class="{ active: activeSubTab === 'like' }"
  48. @click="switchSubTab('like')"
  49. >
  50. <text>点赞</text>
  51. </view>
  52. <view
  53. class="sub-tab-item"
  54. :class="{ active: activeSubTab === 'collect' }"
  55. @click="switchSubTab('collect')"
  56. >
  57. <text>收藏</text>
  58. </view>
  59. </view>
  60. <!-- 内容区域 -->
  61. <view class="content-area">
  62. <!-- 动态列表 -->
  63. <view v-if="activeTab === 'dynamic'" class="dynamic-list">
  64. <view class="empty-tip" v-if="dynamicList.length === 0">
  65. <text>暂无动态</text>
  66. </view>
  67. <view class="dynamic-item" v-for="item in dynamicList" :key="item.dynamicId">
  68. <!-- 操作菜单按钮 -->
  69. <view class="dynamic-menu" @click.stop="showActionMenu(item)">
  70. <text class="menu-icon">⋯</text>
  71. </view>
  72. <view @click="goToDetail(item.dynamicId)">
  73. <!-- 动态内容 -->
  74. <view class="dynamic-content">
  75. <text class="dynamic-text">{{ item.content }}</text>
  76. </view>
  77. <!-- 动态图片 -->
  78. <view class="dynamic-images" v-if="item.mediaUrls">
  79. <image
  80. class="dynamic-image"
  81. v-for="(img, index) in getMediaUrls(item.mediaUrls)"
  82. :key="index"
  83. :src="img"
  84. mode="aspectFill"
  85. @click.stop="previewImage(getMediaUrls(item.mediaUrls), index)"
  86. ></image>
  87. </view>
  88. <!-- 动态信息 -->
  89. <view class="dynamic-info">
  90. <text class="dynamic-time">{{ formatTime(item.createdAt) }}</text>
  91. <view class="dynamic-stats">
  92. <text class="stat-item">{{ item.likeCount || 0 }} 赞</text>
  93. <text class="stat-item">{{ item.commentCount || 0 }} 评论</text>
  94. </view>
  95. </view>
  96. </view>
  97. </view>
  98. </view>
  99. <!-- 互动列表 -->
  100. <view v-else-if="activeTab === 'interaction'" class="interaction-list">
  101. <view class="empty-tip" v-if="interactionList.length === 0">
  102. <text>暂无互动</text>
  103. </view>
  104. <view class="interaction-item" v-for="item in interactionList" :key="item.dynamicId">
  105. <!-- 操作菜单按钮 -->
  106. <view class="interaction-menu" @click.stop="showInteractionMenu(item)">
  107. <text class="menu-icon">⋯</text>
  108. </view>
  109. <view @click="goToDetail(item.dynamicId)">
  110. <!-- 用户信息 -->
  111. <view class="user-info">
  112. <image class="avatar" :src="getAvatar(item)" mode="aspectFill"></image>
  113. <view class="user-details">
  114. <text class="nickname">{{ getNickname(item) }}</text>
  115. </view>
  116. </view>
  117. <!-- 互动内容 -->
  118. <view class="interaction-content">
  119. <text class="interaction-text">{{ item.content }}</text>
  120. </view>
  121. <!-- 互动图片 -->
  122. <view class="interaction-images" v-if="item.mediaUrls">
  123. <image
  124. class="interaction-image"
  125. v-for="(img, index) in getMediaUrls(item.mediaUrls)"
  126. :key="index"
  127. :src="img"
  128. mode="aspectFill"
  129. @click.stop="previewImage(getMediaUrls(item.mediaUrls), index)"
  130. ></image>
  131. </view>
  132. <!-- 互动信息 -->
  133. <view class="interaction-info">
  134. <text class="interaction-time">{{ formatTime(item.createdAt) }}</text>
  135. <view class="interaction-stats">
  136. <text class="stat-item">{{ item.likeCount || 0 }} 赞</text>
  137. <text class="stat-item">{{ item.commentCount || 0 }} 评论</text>
  138. </view>
  139. </view>
  140. </view>
  141. </view>
  142. </view>
  143. <!-- 浏览记录列表 -->
  144. <view v-else-if="activeTab === 'browse'" class="browse-list">
  145. <view class="empty-tip" v-if="browseList.length === 0">
  146. <text>暂无浏览记录</text>
  147. </view>
  148. <view class="browse-item" v-for="item in browseList" :key="item.dynamicId" @click="goToDetail(item.dynamicId)">
  149. <!-- 用户信息 -->
  150. <view class="user-info">
  151. <image class="avatar" :src="getAvatar(item)" mode="aspectFill"></image>
  152. <view class="user-details">
  153. <text class="nickname">{{ getNickname(item) }}</text>
  154. </view>
  155. </view>
  156. <!-- 浏览内容 -->
  157. <view class="browse-content">
  158. <text class="browse-text">{{ item.content }}</text>
  159. </view>
  160. <!-- 浏览图片 -->
  161. <view class="browse-images" v-if="item.mediaUrls">
  162. <image
  163. class="browse-image"
  164. v-for="(img, index) in getMediaUrls(item.mediaUrls)"
  165. :key="index"
  166. :src="img"
  167. mode="aspectFill"
  168. @click.stop="previewImage(getMediaUrls(item.mediaUrls), index)"
  169. ></image>
  170. </view>
  171. <!-- 浏览信息 -->
  172. <view class="browse-info">
  173. <text class="browse-time">{{ formatTime(item.createdAt) }}</text>
  174. <view class="browse-stats">
  175. <text class="stat-item">{{ item.likeCount || 0 }} 赞</text>
  176. <text class="stat-item">{{ item.commentCount || 0 }} 评论</text>
  177. </view>
  178. </view>
  179. </view>
  180. </view>
  181. </view>
  182. <!-- 发布动态按钮 -->
  183. <view class="publish-btn" @click="goPublish">
  184. <text class="publish-icon">+</text>
  185. </view>
  186. </view>
  187. </template>
  188. <script>
  189. import api from '../../utils/api.js'
  190. export default {
  191. data() {
  192. return {
  193. // 用户信息
  194. userInfo: {
  195. avatar: 'https://via.placeholder.com/100',
  196. nickname: '用户',
  197. dynamicCount: 0
  198. },
  199. // 活跃标签页
  200. activeTab: 'dynamic',
  201. // 活跃子标签(互动下的点赞/收藏)
  202. activeSubTab: 'like',
  203. // 动态列表数据
  204. dynamicList: [],
  205. // 互动列表数据
  206. interactionList: [],
  207. // 浏览记录列表数据
  208. browseList: [],
  209. // 默认头像
  210. defaultAvatar: 'https://via.placeholder.com/100x100.png?text=头像'
  211. }
  212. },
  213. onLoad() {
  214. // 页面加载时获取用户信息和动态数据
  215. this.loadUserInfo();
  216. this.loadDynamicData();
  217. },
  218. methods: {
  219. // 返回上一页
  220. goBack() {
  221. uni.navigateBack();
  222. },
  223. // 获取头像
  224. getAvatar(item) {
  225. if (item && item.user && item.user.avatarUrl) {
  226. return item.user.avatarUrl;
  227. }
  228. return this.defaultAvatar;
  229. },
  230. // 获取昵称
  231. getNickname(item) {
  232. if (item && item.user && item.user.nickname) {
  233. return item.user.nickname;
  234. }
  235. return '匿名用户';
  236. },
  237. // 切换标签页
  238. switchTab(tab) {
  239. this.activeTab = tab;
  240. // 根据标签页加载对应数据
  241. if (tab === 'dynamic') {
  242. this.loadDynamicData();
  243. } else if (tab === 'interaction') {
  244. this.loadInteractionData();
  245. } else if (tab === 'browse') {
  246. this.loadBrowseData();
  247. }
  248. },
  249. // 切换互动子标签
  250. switchSubTab(subTab) {
  251. if (this.activeSubTab === subTab) {
  252. return; // 如果点击的是当前标签,不重复加载
  253. }
  254. this.activeSubTab = subTab;
  255. // 先清空列表
  256. this.interactionList = [];
  257. // 根据子标签加载对应数据
  258. this.loadInteractionData();
  259. },
  260. // 加载用户信息
  261. loadUserInfo() {
  262. // 从本地存储获取用户信息
  263. const userInfo = uni.getStorageSync('userInfo');
  264. if (userInfo) {
  265. this.userInfo = {
  266. avatar: userInfo.avatarUrl || userInfo.avatar || 'https://via.placeholder.com/100',
  267. nickname: userInfo.nickname || '用户',
  268. dynamicCount: 0 // 后续从接口获取
  269. };
  270. // 加载用户动态数量
  271. this.loadUserDynamicCount();
  272. }
  273. },
  274. // 加载用户动态数量
  275. loadUserDynamicCount() {
  276. const userInfo = uni.getStorageSync('userInfo');
  277. if (userInfo && userInfo.userId) {
  278. // 获取用户动态数量
  279. api.dynamic.getUserDynamics(userInfo.userId, { pageNum: 1, pageSize: 1 }).then(res => {
  280. if (res && res.total) {
  281. this.userInfo.dynamicCount = res.total;
  282. }
  283. }).catch(err => {
  284. console.error('获取用户动态数量失败:', err);
  285. });
  286. }
  287. },
  288. // 加载动态数据
  289. loadDynamicData() {
  290. const userInfo = uni.getStorageSync('userInfo');
  291. if (userInfo && userInfo.userId) {
  292. // 获取用户发布的动态列表
  293. api.dynamic.getUserDynamics(userInfo.userId, {
  294. pageNum: 1,
  295. pageSize: 10
  296. }).then(res => {
  297. if (res && res.records) {
  298. this.dynamicList = res.records;
  299. }
  300. }).catch(err => {
  301. console.error('获取用户动态列表失败:', err);
  302. });
  303. }
  304. },
  305. // 加载互动数据
  306. loadInteractionData() {
  307. const userInfo = uni.getStorageSync('userInfo');
  308. if (!userInfo || !userInfo.userId) {
  309. console.warn('用户信息不存在,无法加载互动数据');
  310. this.interactionList = [];
  311. return;
  312. }
  313. // 先清空列表,避免显示旧数据
  314. this.interactionList = [];
  315. if (this.activeSubTab === 'like') {
  316. // 获取用户点赞的动态列表
  317. console.log('开始加载点赞列表,userId:', userInfo.userId);
  318. api.dynamic.getLikedList(userInfo.userId, 1, 10).then(res => {
  319. console.log('点赞列表API返回数据:', res);
  320. if (res) {
  321. // 处理返回数据,支持多种可能的数据结构
  322. const records = res.records || res.list || (Array.isArray(res) ? res : []);
  323. this.interactionList = records || [];
  324. console.log('设置点赞列表,数量:', this.interactionList.length);
  325. } else {
  326. this.interactionList = [];
  327. console.log('点赞列表返回数据为空');
  328. }
  329. }).catch(err => {
  330. console.error('获取用户点赞列表失败:', err);
  331. this.interactionList = [];
  332. uni.showToast({
  333. title: '加载点赞列表失败',
  334. icon: 'none'
  335. });
  336. });
  337. } else if (this.activeSubTab === 'collect') {
  338. // 获取用户收藏的动态列表
  339. console.log('开始加载收藏列表,userId:', userInfo.userId);
  340. api.dynamic.getFavoritesList(userInfo.userId, 1, 10).then(res => {
  341. console.log('收藏列表API返回数据:', res);
  342. if (res) {
  343. // 处理返回数据,支持多种可能的数据结构
  344. const records = res.records || res.list || (Array.isArray(res) ? res : []);
  345. this.interactionList = records || [];
  346. console.log('设置收藏列表,数量:', this.interactionList.length);
  347. } else {
  348. this.interactionList = [];
  349. console.log('收藏列表返回数据为空');
  350. }
  351. }).catch(err => {
  352. console.error('获取用户收藏列表失败:', err);
  353. this.interactionList = [];
  354. uni.showToast({
  355. title: '加载收藏列表失败',
  356. icon: 'none'
  357. });
  358. });
  359. }
  360. },
  361. // 加载浏览记录数据
  362. loadBrowseData() {
  363. const userInfo = uni.getStorageSync('userInfo');
  364. if (userInfo && userInfo.userId) {
  365. // 获取用户浏览记录列表
  366. api.dynamic.getBrowseHistoryList(userInfo.userId, 1, 10).then(res => {
  367. if (res && res.records) {
  368. this.browseList = res.records;
  369. }
  370. }).catch(err => {
  371. console.error('获取用户浏览记录失败:', err);
  372. });
  373. }
  374. },
  375. // 跳转到发布动态页面
  376. goPublish() {
  377. uni.navigateTo({
  378. url: '/pages/plaza/publish'
  379. });
  380. },
  381. // 跳转到动态详情页
  382. goToDetail(dynamicId) {
  383. uni.navigateTo({
  384. url: `/pages/plaza/detail?id=${dynamicId}`
  385. });
  386. },
  387. // 格式化时间
  388. formatTime(timeStr) {
  389. if (!timeStr) return '';
  390. const date = new Date(timeStr);
  391. const now = new Date();
  392. const diff = now - date;
  393. const minute = 60 * 1000;
  394. const hour = minute * 60;
  395. const day = hour * 24;
  396. const month = day * 30;
  397. const year = day * 365;
  398. if (diff < minute) {
  399. return '刚刚';
  400. } else if (diff < hour) {
  401. return Math.floor(diff / minute) + '分钟前';
  402. } else if (diff < day) {
  403. return Math.floor(diff / hour) + '小时前';
  404. } else if (diff < month) {
  405. return Math.floor(diff / day) + '天前';
  406. } else if (diff < year) {
  407. return Math.floor(diff / month) + '个月前';
  408. } else {
  409. return Math.floor(diff / year) + '年前';
  410. }
  411. },
  412. // 处理媒体URL,支持数组、JSON字符串、逗号分隔等多种格式
  413. getMediaUrls(mediaUrls) {
  414. if (!mediaUrls) return [];
  415. const isLikelyImage = (u) => {
  416. if (typeof u !== 'string' || !u.trim()) return false;
  417. const url = u.trim();
  418. const hasExt = /(\.png|\.jpg|\.jpeg|\.gif|\.webp|\.bmp)(\?|$)/i.test(url);
  419. const isHttp = /^https?:\/\//i.test(url);
  420. return hasExt || isHttp;
  421. };
  422. try {
  423. if (Array.isArray(mediaUrls)) {
  424. return mediaUrls.filter(isLikelyImage);
  425. }
  426. if (typeof mediaUrls === 'string') {
  427. const s = mediaUrls.trim();
  428. // JSON数组字符串: ["url1", "url2"]
  429. if (s.startsWith('[')) {
  430. const arr = JSON.parse(s);
  431. return Array.isArray(arr) ? arr.filter(isLikelyImage) : [];
  432. }
  433. // 逗号分隔或带引号
  434. return s.split(',')
  435. .map(x => x.trim().replace(/^\[|\]$/g, '').replace(/^['"]|['"]$/g, ''))
  436. .filter(isLikelyImage);
  437. }
  438. } catch (e) {
  439. // ignore parse error
  440. console.error('解析媒体URL失败:', e);
  441. }
  442. return [];
  443. },
  444. // 预览图片
  445. previewImage(urls, current) {
  446. uni.previewImage({
  447. urls: urls,
  448. current: current,
  449. longPressActions: {
  450. itemList: ['保存图片'],
  451. success: function(data) {
  452. console.log('长按图片操作结果:', data);
  453. },
  454. fail: function(err) {
  455. console.error('长按图片操作失败:', err);
  456. }
  457. }
  458. });
  459. },
  460. // 显示操作菜单
  461. showActionMenu(item) {
  462. uni.showActionSheet({
  463. itemList: ['编辑动态', '删除动态'],
  464. success: (res) => {
  465. if (res.tapIndex === 0) {
  466. // 编辑动态
  467. this.editDynamic(item);
  468. } else if (res.tapIndex === 1) {
  469. // 删除动态
  470. this.deleteDynamic(item);
  471. }
  472. }
  473. });
  474. },
  475. // 编辑动态
  476. editDynamic(item) {
  477. // 跳转到编辑页面,传递动态信息
  478. uni.navigateTo({
  479. url: `/pages/plaza/publish?edit=true&dynamicId=${item.dynamicId}&content=${encodeURIComponent(item.content || '')}&mediaUrls=${encodeURIComponent(JSON.stringify(item.mediaUrls || []))}&mediaType=${item.mediaType || 0}&visibility=${item.visibility || 1}`
  480. });
  481. },
  482. // 删除动态
  483. deleteDynamic(item) {
  484. uni.showModal({
  485. title: '确认删除',
  486. content: '确定要删除这条动态吗?删除后无法恢复。',
  487. confirmText: '删除',
  488. confirmColor: '#E91E63',
  489. success: async (res) => {
  490. if (res.confirm) {
  491. try {
  492. const userInfo = uni.getStorageSync('userInfo');
  493. if (!userInfo || !userInfo.userId) {
  494. uni.showToast({
  495. title: '请先登录',
  496. icon: 'none'
  497. });
  498. return;
  499. }
  500. uni.showLoading({
  501. title: '删除中...'
  502. });
  503. await api.dynamic.deleteUserDynamic(item.dynamicId, userInfo.userId);
  504. uni.hideLoading();
  505. uni.showToast({
  506. title: '删除成功',
  507. icon: 'success'
  508. });
  509. // 重新加载动态列表
  510. this.loadDynamicData();
  511. } catch (error) {
  512. uni.hideLoading();
  513. console.error('删除动态失败:', error);
  514. uni.showToast({
  515. title: error.message || '删除失败',
  516. icon: 'none'
  517. });
  518. }
  519. }
  520. }
  521. });
  522. },
  523. // 显示互动操作菜单
  524. showInteractionMenu(item) {
  525. const menuItems = [];
  526. if (this.activeSubTab === 'like') {
  527. menuItems.push('取消点赞');
  528. } else if (this.activeSubTab === 'collect') {
  529. menuItems.push('取消收藏');
  530. }
  531. if (menuItems.length === 0) return;
  532. uni.showActionSheet({
  533. itemList: menuItems,
  534. success: (res) => {
  535. if (res.tapIndex === 0) {
  536. if (this.activeSubTab === 'like') {
  537. this.cancelLike(item);
  538. } else if (this.activeSubTab === 'collect') {
  539. this.cancelFavorite(item);
  540. }
  541. }
  542. }
  543. });
  544. },
  545. // 取消点赞
  546. async cancelLike(item) {
  547. try {
  548. const userInfo = uni.getStorageSync('userInfo');
  549. if (!userInfo || !userInfo.userId) {
  550. uni.showToast({
  551. title: '请先登录',
  552. icon: 'none'
  553. });
  554. return;
  555. }
  556. uni.showLoading({
  557. title: '处理中...'
  558. });
  559. await api.dynamic.unlike(item.dynamicId, userInfo.userId);
  560. uni.hideLoading();
  561. uni.showToast({
  562. title: '已取消点赞',
  563. icon: 'success'
  564. });
  565. // 通知其他页面更新状态(如首页、详情页)
  566. uni.$emit('dynamic-updated', {
  567. dynamicId: item.dynamicId,
  568. isLiked: false,
  569. likeCount: Math.max(0, (item.likeCount || 0) - 1)
  570. });
  571. // 从列表中移除该项
  572. const index = this.interactionList.findIndex(i => i.dynamicId === item.dynamicId);
  573. if (index !== -1) {
  574. this.interactionList.splice(index, 1);
  575. }
  576. } catch (error) {
  577. uni.hideLoading();
  578. console.error('取消点赞失败:', error);
  579. uni.showToast({
  580. title: error.message || '操作失败',
  581. icon: 'none'
  582. });
  583. }
  584. },
  585. // 取消收藏
  586. async cancelFavorite(item) {
  587. try {
  588. const userInfo = uni.getStorageSync('userInfo');
  589. if (!userInfo || !userInfo.userId) {
  590. uni.showToast({
  591. title: '请先登录',
  592. icon: 'none'
  593. });
  594. return;
  595. }
  596. uni.showLoading({
  597. title: '处理中...'
  598. });
  599. await api.dynamic.unfavorite(item.dynamicId, userInfo.userId);
  600. uni.hideLoading();
  601. uni.showToast({
  602. title: '已取消收藏',
  603. icon: 'success'
  604. });
  605. // 通知其他页面更新状态(如首页、详情页)
  606. uni.$emit('dynamic-updated', {
  607. dynamicId: item.dynamicId,
  608. isFavorited: false,
  609. favoriteCount: Math.max(0, (item.favoriteCount || 0) - 1)
  610. });
  611. // 从列表中移除该项
  612. const index = this.interactionList.findIndex(i => i.dynamicId === item.dynamicId);
  613. if (index !== -1) {
  614. this.interactionList.splice(index, 1);
  615. }
  616. } catch (error) {
  617. uni.hideLoading();
  618. console.error('取消收藏失败:', error);
  619. uni.showToast({
  620. title: error.message || '操作失败',
  621. icon: 'none'
  622. });
  623. }
  624. }
  625. }
  626. }
  627. </script>
  628. <style lang="scss" scoped>
  629. .my-dynamics-page {
  630. min-height: 100vh;
  631. background: #F5F5F5;
  632. padding-bottom: 100rpx;
  633. }
  634. /* 顶部导航栏 */
  635. .top-nav {
  636. display: flex;
  637. align-items: center;
  638. justify-content: space-between;
  639. padding: 40rpx 30rpx 20rpx;
  640. background: #FFFFFF;
  641. position: relative;
  642. z-index: 10;
  643. .back-btn {
  644. width: 60rpx;
  645. height: 60rpx;
  646. display: flex;
  647. align-items: center;
  648. justify-content: center;
  649. .back-icon {
  650. font-size: 48rpx;
  651. color: #333333;
  652. font-weight: bold;
  653. }
  654. }
  655. .nav-title {
  656. font-size: 36rpx;
  657. font-weight: bold;
  658. color: #333333;
  659. }
  660. .nav-right {
  661. width: 60rpx;
  662. }
  663. }
  664. /* 用户信息区域 */
  665. .user-info-section {
  666. display: flex;
  667. align-items: center;
  668. padding: 30rpx;
  669. background: #FFFFFF;
  670. margin-bottom: 20rpx;
  671. .avatar {
  672. width: 120rpx;
  673. height: 120rpx;
  674. border-radius: 60rpx;
  675. margin-right: 20rpx;
  676. background-color: #E0E0E0;
  677. }
  678. .user-details {
  679. display: flex;
  680. flex-direction: column;
  681. justify-content: center;
  682. .nickname {
  683. font-size: 32rpx;
  684. font-weight: bold;
  685. color: #333333;
  686. margin-bottom: 8rpx;
  687. }
  688. .dynamic-count {
  689. font-size: 24rpx;
  690. color: #999999;
  691. }
  692. }
  693. }
  694. /* 标签页导航 */
  695. .tab-nav {
  696. display: flex;
  697. background: #FFFFFF;
  698. border-bottom: 2rpx solid #F0F0F0;
  699. margin-bottom: 20rpx;
  700. .tab-item {
  701. flex: 1;
  702. text-align: center;
  703. padding: 30rpx 0;
  704. position: relative;
  705. text {
  706. font-size: 30rpx;
  707. color: #666666;
  708. }
  709. &.active {
  710. text {
  711. color: #E91E63;
  712. font-weight: bold;
  713. }
  714. &::after {
  715. content: '';
  716. position: absolute;
  717. bottom: 0;
  718. left: 50%;
  719. transform: translateX(-50%);
  720. width: 60rpx;
  721. height: 6rpx;
  722. background: #E91E63;
  723. border-radius: 3rpx;
  724. }
  725. }
  726. }
  727. }
  728. /* 互动子标签导航 */
  729. .sub-tab-nav {
  730. display: flex;
  731. background: #FFFFFF;
  732. padding: 0 30rpx;
  733. margin-bottom: 20rpx;
  734. .sub-tab-item {
  735. padding: 20rpx 30rpx;
  736. margin-right: 40rpx;
  737. text {
  738. font-size: 26rpx;
  739. color: #999999;
  740. }
  741. &.active {
  742. text {
  743. color: #E91E63;
  744. font-weight: bold;
  745. }
  746. }
  747. }
  748. }
  749. /* 内容区域 */
  750. .content-area {
  751. padding: 0 30rpx;
  752. }
  753. /* 列表通用样式 */
  754. .dynamic-list,
  755. .interaction-list,
  756. .browse-list {
  757. background: #FFFFFF;
  758. border-radius: 12rpx;
  759. overflow: hidden;
  760. }
  761. .dynamic-item,
  762. .interaction-item,
  763. .browse-item {
  764. padding: 30rpx;
  765. border-bottom: 2rpx solid #F5F5F5;
  766. position: relative;
  767. &:last-child {
  768. border-bottom: none;
  769. }
  770. }
  771. /* 动态操作菜单 */
  772. .dynamic-menu {
  773. position: absolute;
  774. top: 30rpx;
  775. right: 30rpx;
  776. width: 60rpx;
  777. height: 60rpx;
  778. display: flex;
  779. align-items: center;
  780. justify-content: center;
  781. z-index: 10;
  782. .menu-icon {
  783. font-size: 40rpx;
  784. color: #999999;
  785. font-weight: bold;
  786. line-height: 1;
  787. }
  788. }
  789. /* 互动操作菜单 */
  790. .interaction-menu {
  791. position: absolute;
  792. top: 30rpx;
  793. right: 30rpx;
  794. width: 60rpx;
  795. height: 60rpx;
  796. display: flex;
  797. align-items: center;
  798. justify-content: center;
  799. z-index: 10;
  800. .menu-icon {
  801. font-size: 40rpx;
  802. color: #999999;
  803. font-weight: bold;
  804. line-height: 1;
  805. }
  806. }
  807. /* 用户信息 */
  808. .user-info {
  809. display: flex;
  810. align-items: center;
  811. margin-bottom: 20rpx;
  812. .avatar {
  813. width: 80rpx;
  814. height: 80rpx;
  815. border-radius: 40rpx;
  816. margin-right: 20rpx;
  817. background-color: #E0E0E0;
  818. }
  819. .user-details {
  820. display: flex;
  821. flex-direction: column;
  822. .nickname {
  823. font-size: 28rpx;
  824. font-weight: bold;
  825. color: #333333;
  826. margin-bottom: 4rpx;
  827. }
  828. }
  829. }
  830. /* 内容文本 */
  831. .dynamic-content,
  832. .interaction-content,
  833. .browse-content {
  834. margin-bottom: 20rpx;
  835. .dynamic-text,
  836. .interaction-text,
  837. .browse-text {
  838. font-size: 28rpx;
  839. color: #333333;
  840. line-height: 44rpx;
  841. }
  842. }
  843. /* 图片列表 */
  844. .dynamic-images,
  845. .interaction-images,
  846. .browse-images {
  847. display: flex;
  848. flex-wrap: wrap;
  849. gap: 15rpx;
  850. margin-bottom: 20rpx;
  851. .dynamic-image,
  852. .interaction-image,
  853. .browse-image {
  854. width: calc((100% - 30rpx) / 3);
  855. height: 200rpx;
  856. border-radius: 8rpx;
  857. background-color: #E0E0E0;
  858. }
  859. }
  860. /* 信息栏 */
  861. .dynamic-info,
  862. .interaction-info,
  863. .browse-info {
  864. display: flex;
  865. align-items: center;
  866. justify-content: space-between;
  867. .dynamic-time,
  868. .interaction-time,
  869. .browse-time {
  870. font-size: 22rpx;
  871. color: #999999;
  872. }
  873. .dynamic-stats,
  874. .interaction-stats,
  875. .browse-stats {
  876. display: flex;
  877. align-items: center;
  878. .stat-item {
  879. font-size: 22rpx;
  880. color: #999999;
  881. margin-left: 30rpx;
  882. }
  883. }
  884. }
  885. /* 空状态提示 */
  886. .empty-tip {
  887. padding: 100rpx 0;
  888. text-align: center;
  889. text {
  890. font-size: 28rpx;
  891. color: #999999;
  892. }
  893. }
  894. /* 发布动态按钮 */
  895. .publish-btn {
  896. position: fixed;
  897. bottom: 80rpx;
  898. right: 40rpx;
  899. width: 100rpx;
  900. height: 100rpx;
  901. background: #E91E63;
  902. border-radius: 50rpx;
  903. display: flex;
  904. align-items: center;
  905. justify-content: center;
  906. box-shadow: 0 8rpx 20rpx rgba(233, 30, 99, 0.3);
  907. z-index: 999;
  908. .publish-icon {
  909. font-size: 60rpx;
  910. color: #FFFFFF;
  911. font-weight: bold;
  912. line-height: 1;
  913. }
  914. }
  915. /* 页面背景色 */
  916. .my-dynamics-page {
  917. background-color: #F5F5F5;
  918. }
  919. </style>