workDetail.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. <template>
  2. <view class="page">
  3. <!-- 引入FontAwesome -->
  4. <view>
  5. <link rel="stylesheet"
  6. href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
  7. </view>
  8. <!-- 顶部导航栏 -->
  9. <view class="custom-navbar">
  10. <view class="navbar-left" @click="goBack">
  11. <text class="fa fa-angle-left"></text>
  12. </view>
  13. <view class="navbar-center">
  14. <view class="navbar-title" @click="goToUserHomepage(author.id)">
  15. <image class="navbar-avatar" :src="author.avator" mode="aspectFill"></image>
  16. <text class="navbar-text">{{ author.nickname }}</text>
  17. <!-- <text class="navbar-badge" v-if="author.is_vip > 0">VIP</text> -->
  18. </view>
  19. </view>
  20. <view class="navbar-right">
  21. <text class="followTheAuthor followTheAuthor1" v-if="author.is_attention == 0"
  22. @click="() => followTheAuthor(1)">+关注</text>
  23. <text class="followTheAuthor followTheAuthor0" v-if="author.is_attention == 1"
  24. @click="followTheAuthor(0)">已关注</text>
  25. <image v-if="true" @click="showActionSheet(0)" src="../../static/icon/sy_icon_fenxiang.png" mode="widthFix">
  26. </image>
  27. <view v-else class="navbar-right" @click="showActionSheet(1)">
  28. <text class="fa fa-ellipsis-h"></text>
  29. </view>
  30. </view>
  31. </view>
  32. <!-- 灵感 -->
  33. <template v-if="articleInfo.task_type == 1">
  34. <view class="inspiration-content" v-if="home_image">
  35. <image v-if="home_image" :src="home_image" class="inspirationPictures" mode="widthFix"></image>
  36. </view>
  37. </template>
  38. <!-- 音乐 -->
  39. <template v-else-if="articleInfo.task_type == 2">
  40. <view class="musicContentBox">
  41. <view class="headCard">
  42. <image :src="home_image" class="songCover"></image>
  43. <view class="songInfo">
  44. <view class="songTitle">{{ addBrackets(articleInfo.title) }}</view>
  45. <view class="songTag">
  46. <view class="tag" v-for="(item, index) in commaToArray(articleInfo.style)" :key="index + item">{{ item }}
  47. </view>
  48. </view>
  49. </view>
  50. <template v-if="articleInfo.task_type == 2">
  51. <image @click="toggleAudio" v-if="isPlaying" src="@/static/makedetail/cz_icon_zanting.png"
  52. class="playerButton"></image>
  53. <image @click="toggleAudio" v-else src="@/static/makedetail/cz_icon_bofang.png" class="playerButton">
  54. </image>
  55. </template>
  56. </view>
  57. <view class="contentHeader">
  58. <view class="musicContent">
  59. <text> {{ articleInfo.lyrics }}</text>
  60. <image class="roll" src="@/static/icon/roll.png"></image>
  61. </view>
  62. <view class="maskLayer"></view>
  63. </view>
  64. </view>
  65. </template>
  66. <!-- 作品描述 -->
  67. <view class="workDescription">
  68. <view class="workDescription-title">
  69. <view>创作说明 </view>
  70. <!-- <image class="pen" src="@/static/icon/wd_icon_bianji.png"></image> -->
  71. </view>
  72. <view class="workDescription-content">
  73. {{ content || "暂无内容" }}
  74. </view>
  75. </view>
  76. <view class="goCreate" @click="goCreate()">去创作</view>
  77. <DialogBox ref="customConfirm"></DialogBox>
  78. <!-- 文章头图区域 -->
  79. <view class="topUser" v-if="false">
  80. <image :src="home_image" class="home_image" mode="aspectFill"></image>
  81. <!-- 图片指示器 -->
  82. <view class="image-indicator" v-if="image_list.length > 1">
  83. <text>{{ selImg + 1 }}/{{ image_list.length }}</text>
  84. </view>
  85. <!-- 音乐类型时显示歌词 -->
  86. <view class="lyrics-overlay" v-if="articleInfo.task_type == 2">
  87. <text class="lyrics-text">{{ articleInfo.lyrics }}</text>
  88. </view>
  89. <!-- 音乐类型时显示播放按钮 -->
  90. <view class="play-button" v-if="articleInfo.task_type == 2 && articleInfo.result_audio" @click="toggleAudio">
  91. <text class="fa" :class="isPlaying ? 'fa-pause' : 'fa-play'"></text>
  92. </view>
  93. <!-- 缩略图列表 -->
  94. <view class="list">
  95. <view class="img" :class="selImg == index ? 'active' : ''" v-for="(item, index) in image_list" :key="index"
  96. @click="selPhoto(item, index)">
  97. <image :src="item" mode="aspectFill"></image>
  98. </view>
  99. </view>
  100. </view>
  101. <!-- 音频元素 -->
  102. <audio id="audioPlayer" :src="articleInfo.result_audio" style="display: none" v-if="false"></audio>
  103. <!-- 文章内容区域 -->
  104. <view class="body" v-if="false">
  105. <!-- 文章标题与元信息 -->
  106. <view class="article-header">
  107. <view class="title">
  108. {{ articleInfo.title || "暂无标题" }}
  109. </view>
  110. <view class="meta-info">
  111. <view class="meta-item">
  112. <text class="fa fa-calendar"></text>
  113. <text class="meta-text">{{
  114. articleInfo.create_time || "暂无时间"
  115. }}</text>
  116. </view>
  117. <view class="meta-item">
  118. <text class="fa fa-eye"></text>
  119. <text class="meta-text">{{ articleInfo.num_view || 0 }}次阅读</text>
  120. </view>
  121. <view class="meta-item" v-if="articleInfo.author">
  122. <text class="fa fa-user"></text>
  123. <text class="meta-text">{{ articleInfo.author }}</text>
  124. </view>
  125. </view>
  126. </view>
  127. <!-- 内容分隔线 -->
  128. <view class="divider"></view>
  129. <!-- 文章内容 -->
  130. <!-- <view class="article-content" v-if="articleInfo.type == 'user'">
  131. <view class="content">{{ articleInfo.content || '暂无内容' }}</view>
  132. </view> -->
  133. <view class="article-content">
  134. <view class="content">
  135. <!-- <rich-text :nodes="content" style="font-size: 14px"></rich-text> -->
  136. <uv-parse :content="articleInfo.content"></uv-parse>
  137. </view>
  138. </view>
  139. <!-- 文章底部区域 -->
  140. <view class="article-footer">
  141. <view class="action-bar">
  142. <view class="action-item" @tap="likeArticle">
  143. <text class="fa" :class="articleInfo.is_like ? 'fa-thumbs-up liked' : 'fa-thumbs-o-up'
  144. "></text>
  145. <text class="action-text">{{ articleInfo.like_count || 0 }}</text>
  146. </view>
  147. <view class="action-item" @tap="openComment">
  148. <text class="fa fa-comment-o"></text>
  149. <text class="action-text">{{ tableTotal || 0 }}</text>
  150. </view>
  151. <view class="action-item" @tap="shareArticle">
  152. <text class="fa fa-share-alt"></text>
  153. <text class="action-text">分享</text>
  154. </view>
  155. </view>
  156. </view>
  157. <!-- 空白占位 -->
  158. <view class="list_info">
  159. <view class="blankHeight"></view>
  160. </view>
  161. </view>
  162. <!-- <view class="btn_submit" @click="chatTA()">
  163. <image class="icon" src="../../static/icon/icon_chat_white.png" mode="widthFix"></image>
  164. 私聊
  165. </view> -->
  166. <view class="thread2"></view>
  167. <view class="thread2"></view>
  168. <previewImage ref="previewImage" :opacity="0.8" :circular="true" :imgs="imgs" :descs="descs"></previewImage>
  169. <!-- 评论区域 -->
  170. <CommentSection v-if="userInfo.id != 0 && articleInfo.title" ref="commentSection" :myInfo="myInfo"
  171. :userInfo="userInfo" :articleId="arcID" @totalNumberOfComments="totalNumberOfComments" articleInfo="articleInfo">
  172. </CommentSection>
  173. <!-- 自定义 ActionSheet -->
  174. <ActionSheet ref="actionSheet" :items="items" @select="handleActionSelect" @cancel="handleActionCancel" />
  175. <view class="thread2"></view>
  176. </view>
  177. </template>
  178. <script>
  179. import htmlParser from "../../common/html-parser";
  180. import previewImage from "@/components/kxj-previewImage/kxj-previewImage.vue"; //引用插件
  181. import CommentSection from "@/components/CommentSection/CommentSection.vue";
  182. import ActionSheet from "@/components/ActionSheet/ActionSheet.vue";
  183. function parseImgs(nodes) {
  184. nodes.forEach((node) => {
  185. if (node.name === "img" && node.attrs && node.attrs["data-img-size-val"]) {
  186. const sizes = node.attrs["data-img-size-val"].split(",");
  187. const width = uni.upx2px(720 * 0.9);
  188. const height = parseInt(width * (sizes[1] / sizes[0]));
  189. node.attrs.style = `width:${width};height:${height};`;
  190. }
  191. if (Array.isArray(node.children)) {
  192. parseImgs(node.children);
  193. }
  194. });
  195. return nodes;
  196. }
  197. export default {
  198. components: {
  199. previewImage,
  200. CommentSection,
  201. ActionSheet,
  202. },
  203. data() {
  204. return {
  205. title: "",
  206. arcID: 0,
  207. selImg: 0,
  208. home_image: "",
  209. articleInfo: {},
  210. tag_list: [],
  211. image_list: [],
  212. imgs: [],
  213. descs: [],
  214. list_wish: [],
  215. content: "",
  216. author: {},
  217. // 添加文章信息字段
  218. articleInfo: {
  219. title: "",
  220. content: "",
  221. create_time: "",
  222. images: "",
  223. view_count: 0,
  224. author: "",
  225. like_count: 0,
  226. is_like: false,
  227. },
  228. items: [],
  229. myInfo: {
  230. user_id: getApp().globalData.user_id, // 用户id
  231. user_name: getApp().globalData.nickname, // 用户名
  232. user_avatar: getApp().globalData.avator, // 用户头像地址
  233. },
  234. // 文章作者信息(提示: 一般来自localstorage, 如果是实时获取的话, 那么获取到数据后再v-if显示评论组件)
  235. userInfo: {
  236. user_id: 0, // 用户id
  237. user_name: "", // 用户名
  238. user_avatar: "", // 用户头像地址
  239. },
  240. deleteMode: "all", //删除模式
  241. // 评论总数
  242. tableTotal: 4,
  243. // 评论表
  244. tableData: [],
  245. isPlaying: false, // 添加播放状态
  246. audioPlayer: null, // 添加音频播放器实例
  247. sms_id: 0,
  248. isMessage: true,
  249. };
  250. },
  251. onLoad(parms) {
  252. let self = this;
  253. this.arcID = parms.id
  254. if (parms.type == "sms") {
  255. this.isMessage = false;
  256. }
  257. if (parms.sms_id) {
  258. this.sms_id = parms.sms_id;
  259. }
  260. },
  261. onShow() {
  262. uni.$emit("check_update");
  263. this.loadData();
  264. this.$nextTick(() => {
  265. if (this.$refs.commentSection) {
  266. this.$refs.commentSection.loadCommentData();
  267. }
  268. });
  269. },
  270. onReady() {
  271. // 初始化音频播放器
  272. this.audioPlayer = uni.createInnerAudioContext();
  273. this.audioPlayer.onEnded(() => {
  274. this.isPlaying = false;
  275. });
  276. },
  277. onUnload() {
  278. // 页面卸载时停止音频播放
  279. if (this.audioPlayer) {
  280. this.audioPlayer.stop();
  281. this.audioPlayer.destroy();
  282. }
  283. },
  284. methods: {
  285. // 返回上一页
  286. goBack() {
  287. uni.navigateBack({
  288. delta: 1,
  289. });
  290. },
  291. totalNumberOfComments(tableTotal) {
  292. this.tableTotal = tableTotal;
  293. },
  294. onLinqu(item) {
  295. uni.navigateTo({
  296. url: "/pages/my/wishHelp?id=" + item.myid,
  297. });
  298. },
  299. selPhoto(item, sel) {
  300. this.selImg = sel;
  301. this.home_image = this.image_list[sel];
  302. },
  303. toArr(imgs) {
  304. let arr = imgs.split("|");
  305. return arr;
  306. },
  307. previewOpen(imgs1, index) {
  308. this.imgs = imgs1.split("|");
  309. setTimeout(() => this.$refs.previewImage.open(index), 0);
  310. // 传入当前选中的图片地址或序号
  311. return; //如需测试和uni原生预览差别可注释这两行
  312. },
  313. loadData() {
  314. uni.request({
  315. url: this.$apiHost + "/Work/getinfo",
  316. data: {
  317. uuid: getApp().globalData.uuid,
  318. id: this.arcID,
  319. },
  320. header: {
  321. "content-type": "application/json",
  322. sign: getApp().globalData.headerSign,
  323. },
  324. success: (res) => {
  325. console.log("文章信息:", res.data);
  326. if (res.data.success === "yes") {
  327. console.log("文章信息:", res.data.data);
  328. // 更新文章信息
  329. if (res.data.article) {
  330. this.articleInfo = res.data.article;
  331. }
  332. this.articleInfo = res.data.data;
  333. this.articleInfo.sms_id = this.sms_id;
  334. this.content = res.data.data.content;
  335. this.home_image = res.data.data.images;
  336. this.author = res.data.author;
  337. this.userInfo.user_id = res.data.id; // 用户id
  338. this.userInfo.user_name = res.data.nickname; // 用户名
  339. this.userInfo.user_avatar = res.data.avator; // 用户头像地址
  340. } else {
  341. uni.showToast({
  342. title: "获取信息失败",
  343. icon: "none",
  344. });
  345. }
  346. },
  347. complete: (com) => {
  348. // uni.hideLoading();
  349. },
  350. fail: (e) => {
  351. console.log("请求失败:", e);
  352. uni.showToast({
  353. title: "网络请求失败",
  354. icon: "none",
  355. });
  356. },
  357. });
  358. },
  359. // 唤起新评论弹框
  360. openComment() {
  361. if (this.$refs.commentSection) {
  362. this.$refs.commentSection.openComment();
  363. }
  364. },
  365. // 文章点赞
  366. likeArticle() {
  367. uni.request({
  368. url: this.$apiHost + "/Work/zanTA",
  369. data: {
  370. uuid: getApp().globalData.uuid,
  371. id: this.arcID,
  372. },
  373. header: {
  374. "content-type": "application/json",
  375. sign: getApp().globalData.headerSign,
  376. },
  377. success: (res) => {
  378. console.log("点赞结果:", res.data);
  379. if (res.data.success === "yes") {
  380. // 更新点赞状态
  381. if (!this.articleInfo.is_like) {
  382. this.articleInfo.like_count =
  383. (this.articleInfo.like_count || 0) + 1;
  384. this.articleInfo.is_like = true;
  385. uni.showToast({
  386. title: "点赞成功",
  387. icon: "none",
  388. });
  389. } else {
  390. this.articleInfo.like_count =
  391. (this.articleInfo.like_count || 0) - 1;
  392. this.articleInfo.is_like = false;
  393. uni.showToast({
  394. title: res.data.str,
  395. icon: "none",
  396. });
  397. }
  398. } else {
  399. uni.showToast({
  400. title: res.data.str,
  401. icon: "none",
  402. });
  403. }
  404. },
  405. fail: (e) => {
  406. console.log("点赞失败:", e);
  407. uni.showToast({
  408. title: "网络请求失败",
  409. icon: "none",
  410. });
  411. },
  412. });
  413. },
  414. // 关注作者
  415. followTheAuthor(n) {
  416. uni.$emit('check_login', () => {
  417. uni.request({
  418. url: this.$apiHost + "/Member/attention",
  419. data: {
  420. uuid: getApp().globalData.uuid,
  421. id: this.author.id,
  422. },
  423. header: {
  424. "content-type": "application/json",
  425. sign: getApp().globalData.headerSign,
  426. },
  427. success: (res) => {
  428. console.log("点赞结果:", res.data);
  429. uni.showToast({
  430. title: res.data.str,
  431. icon: "none",
  432. });
  433. if (res.data.success === "yes") {
  434. console.log("关注结果:", res.data, n);
  435. this.author.is_attention = n;
  436. }
  437. },
  438. fail: (e) => {
  439. console.log("关注失败:", e);
  440. uni.showToast({
  441. title: "网络请求失败",
  442. icon: "none",
  443. });
  444. },
  445. });
  446. })
  447. },
  448. // 分享文章
  449. shareArticle() {
  450. // 如果在微信小程序环境
  451. if (uni.getSystemInfoSync().platform === "mp-weixin") {
  452. uni.showShareMenu({
  453. withShareTicket: true,
  454. menus: ["shareAppMessage", "shareTimeline"],
  455. });
  456. } else {
  457. // 其他环境,如APP
  458. uni.share({
  459. provider: "weixin",
  460. scene: "WXSceneSession",
  461. type: 0,
  462. title: this.articleInfo.title,
  463. summary: this.articleInfo.content.substring(0, 40) + "...",
  464. imageUrl: this.home_image,
  465. success: function (res) {
  466. console.log("分享成功:" + JSON.stringify(res));
  467. },
  468. fail: function (err) {
  469. console.log("分享失败:" + JSON.stringify(err));
  470. },
  471. });
  472. }
  473. },
  474. // 切换音频播放状态
  475. toggleAudio() {
  476. if (!this.articleInfo.result_audio) return;
  477. if (this.isPlaying) {
  478. this.audioPlayer.pause();
  479. this.isPlaying = false;
  480. } else {
  481. this.audioPlayer.src = this.articleInfo.result_audio;
  482. this.audioPlayer.play();
  483. this.isPlaying = true;
  484. }
  485. },
  486. // 新增过滤器方法
  487. commaToArray(str) {
  488. if (!str) return "";
  489. return str.split(",");
  490. },
  491. // 新增过滤器方法
  492. addBrackets(str) {
  493. if (!str) return "";
  494. if (!str.startsWith("《")) {
  495. str = "《" + str;
  496. }
  497. if (!str.endsWith("》")) {
  498. str = str + "》";
  499. }
  500. return str;
  501. },
  502. getInfoData() {
  503. uni.request({
  504. url: this.$apiHost + "/Member/getinfoData",
  505. data: {
  506. uuid: getApp().globalData.uuid,
  507. },
  508. header: {
  509. "content-type": "application/json",
  510. },
  511. success: (res) => {
  512. console.log("用户信息", res.data);
  513. },
  514. });
  515. },
  516. showActionSheet(n) {
  517. uni.$emit('check_login', () => {
  518. if (n == 0) {
  519. this.items = [
  520. {
  521. text: "分享作品",
  522. icon: "../../static/icon/cz_icon_fenxiangzuopin.png",
  523. },
  524. ];
  525. }
  526. if (n == 1) {
  527. this.items = [
  528. {
  529. text: "分享作品",
  530. icon: "../../static/icon/cz_icon_fenxiangzuopin.png",
  531. },
  532. {
  533. text: "修改封面",
  534. icon: "../../static/icon/cz_icon_xiugaifengmian.png",
  535. },
  536. {
  537. text: "删除作品",
  538. icon: "../../static/icon/sy_icon_shanchu.png",
  539. danger: true,
  540. },
  541. ];
  542. }
  543. this.$refs.actionSheet.show();
  544. })
  545. },
  546. handleActionCancel() {
  547. console.log("ActionSheet cancelled");
  548. },
  549. handleActionSelect(index, item) {
  550. console.log("ActionSheet selected index:", item.text);
  551. },
  552. goCreate() {
  553. // 切换到标签页
  554. uni.switchTab({
  555. // 指定要切换到的页面路径
  556. url: "/pages/make/index",
  557. });
  558. },
  559. goToUserHomepage(id) {
  560. uni.$emit('check_login', () => {
  561. if (!id) {
  562. return;
  563. }
  564. uni.navigateTo({
  565. url: "/pages/my/userHomepage?id=" + id,
  566. });
  567. })
  568. },
  569. },
  570. };
  571. </script>
  572. <style scoped lang="scss">
  573. @import "workDetail.scss";
  574. </style>