preview.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <template>
  2. <unicloud-db v-slot:default="{loading, error, options}" :collection="collection" :options="formData"
  3. :getone="true" :where="where" :manual="true" ref="detail" foreignKey="uni-cms-articles.user_id"
  4. @load="loadData"
  5. class="article">
  6. <template v-if="!loading && articleData">
  7. <view class="preview-tip">此页面仅用于临时预览文章,链接将会在短期内失效。</view>
  8. <view class="meta">
  9. <view class="title">
  10. <text class="text">{{ articleData.title }}</text>
  11. </view>
  12. <view class="excerpt">
  13. <text class="text">{{ articleData.excerpt }}</text>
  14. </view>
  15. <view class="author">
  16. <template v-if="articleData.user_id && articleData.user_id[0]">
  17. <text class="at">{{ articleData.user_id[0].nickname || '' }}</text>
  18. <text class="split">·</text>
  19. </template>
  20. <text class="date">{{ publishTime(articleData.publish_date) }}</text>
  21. </view>
  22. </view>
  23. <render-article-detail
  24. :content="articleData.content"
  25. :content-images="articleData.content_images"
  26. :ad-config="{ adpId, watchAdUniqueType }"
  27. ></render-article-detail>
  28. </template>
  29. <view class="detail-loading" v-else>
  30. <uni-icons type="spinner-cycle" size="35px"/>
  31. </view>
  32. </unicloud-db>
  33. </template>
  34. <script>
  35. import uniNavBar from '@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue';
  36. import renderArticleDetail from "@/uni_modules/uni-cms-article/components/render-article-detail/index.vue";
  37. import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
  38. const db = uniCloud.database()
  39. const articleDBName = 'uni-cms-articles'
  40. const userDBName = 'uni-id-users'
  41. export default {
  42. components: {
  43. uniNavBar,
  44. renderArticleDetail
  45. },
  46. data() {
  47. return {
  48. id: "", // 文章ID
  49. title: "", // 文章标题
  50. secret: "", // 文章预览密钥
  51. formData: {}, // 表单数据
  52. articleData: null, // 文章数据
  53. // 广告相关配置
  54. adpId: "", // TODO: 请填写广告位ID
  55. watchAdUniqueType: "device" // TODO: 观看广告的唯一标识类型,可选值为 user 或者 device,user 表示用户唯一,device 表示设备唯一
  56. }
  57. },
  58. computed: {
  59. where() {
  60. //拼接where条件 查询条件 ,更多详见 :https://uniapp.dcloud.net.cn/uniCloud/unicloud-db?id=jsquery
  61. return `_id =="${this.id}" && preview_secret =="${this.secret}"`
  62. },
  63. collection() {
  64. return [
  65. db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content,preview_secret,preview_expired,article_status').getTemp(),
  66. db.collection(userDBName).field('_id, nickname').getTemp()
  67. ]
  68. }
  69. },
  70. onReady() {
  71. // 开始加载数据,修改 where 条件后才开始去加载 clinetDB 的数据 ,需要等组件渲染完毕后才开始执行 loadData,所以不能再 onLoad 中执行
  72. if (this.id) { // ID 不为空,则发起查询
  73. this.$refs.detail.loadData()
  74. } else {
  75. uni.showToast({
  76. icon: 'none',
  77. title: 'id 不能为空'
  78. })
  79. }
  80. },
  81. onLoad(event) {
  82. //获取文章id,通常 id 来自上一个页面
  83. if (event.id) {
  84. this.id = event.id
  85. this.secret = event.secret
  86. }
  87. // 监听解锁内容事件
  88. uni.$on('onUnlockContent', this.onUnlockContent)
  89. },
  90. onUnload() {
  91. // 页面卸载时,移除监听事件
  92. uni.$off('onUnlockContent', this.onUnlockContent)
  93. },
  94. onPageScroll(e) {
  95. // 根据滚动位置判断是否显示导航栏
  96. if (e.scrollTop > 100) {
  97. uni.setNavigationBarTitle({
  98. title: this.title
  99. })
  100. } else {
  101. uni.setNavigationBarTitle({
  102. title: ''
  103. })
  104. }
  105. },
  106. methods: {
  107. // 将时间戳转换为可读的时间格式
  108. publishTime(timestamp) {
  109. return translatePublishTime(timestamp)
  110. },
  111. // 加载数据
  112. loadData(data) {
  113. if (!data) {
  114. return uni.showModal({
  115. content: "文章不存在/预览密钥不存在",
  116. showCancel: false,
  117. success: () => {
  118. // #ifdef H5
  119. window.close()
  120. // #endif
  121. // #ifndef H5
  122. uni.navigateBack()
  123. // #endif
  124. }
  125. })
  126. }
  127. // 文章已发布,跳转到文章详情页
  128. if (data.article_status === 1) {
  129. uni.showToast({
  130. icon: 'none',
  131. title: '文章已发布'
  132. })
  133. uni.redirectTo({
  134. url: `/uni_modules/uni-cms-article/pages/detail/detail?id=${this.id}`
  135. })
  136. return
  137. }
  138. // 预览已过期,提示用户
  139. if (data.preview_expired < Date.now()) {
  140. return uni.showModal({
  141. content: "预览已失效",
  142. showCancel: false,
  143. success: () => {
  144. // #ifdef H5
  145. window.close()
  146. // #endif
  147. // #ifndef H5
  148. uni.navigateBack()
  149. // #endif
  150. }
  151. })
  152. }
  153. // 设置文章标题
  154. this.title = data.title
  155. this.articleData = data
  156. },
  157. // 监听解锁内容事件,解锁内容后重新加载数据
  158. async onUnlockContent() {
  159. this.$refs.detail.loadData()
  160. }
  161. }
  162. }
  163. </script>
  164. <style scoped lang="scss">
  165. /* #ifdef APP-NVUE */
  166. .article {
  167. background-color: #fff;
  168. }
  169. /* #endif */
  170. @mixin cp {
  171. padding: 0 30rpx;
  172. }
  173. .detail-loading {
  174. margin: 100rpx auto 0;
  175. width: 35px;
  176. height: 35px;
  177. animation: rotate360 2s linear infinite;
  178. }
  179. @keyframes rotate360 {
  180. 0% {
  181. transform: rotate(0deg);
  182. transform-origin: center center;
  183. }
  184. 100% {
  185. transform: rotate(360deg);
  186. transform-origin: center center;
  187. }
  188. }
  189. .meta {
  190. @include cp;
  191. position: relative;
  192. z-index: 1;
  193. padding-top: 20rpx;
  194. .title {
  195. .text {
  196. font-size: 40rpx;
  197. line-height: 66rpx;
  198. font-weight: bold;
  199. color: #333;
  200. }
  201. }
  202. .excerpt {
  203. margin-top: 10rpx;
  204. .text {
  205. font-size: 26rpx;
  206. line-height: 40rpx;
  207. color: #999;
  208. }
  209. }
  210. .author {
  211. display: flex;
  212. align-items: center;
  213. justify-content: flex-start;
  214. flex-direction: row;
  215. margin-top: 20rpx;
  216. .at,
  217. .split,
  218. .date {
  219. font-size: 26rpx;
  220. color: #ccc;
  221. }
  222. .split {
  223. margin: 0 10rpx;
  224. }
  225. }
  226. }
  227. .preview-tip {
  228. font-size: 13px;
  229. color: #333;
  230. background: #fcd791;
  231. padding: 10px;
  232. }
  233. </style>