detail.uvue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <template>
  2. <scroll-view :scroll-y="true" v-if="!loading">
  3. <view class="meta">
  4. <view class="title">
  5. <text class="text">{{ articleDetail.title }}</text>
  6. </view>
  7. <view class="excerpt">
  8. <text class="text">{{ articleDetail.excerpt }}</text>
  9. </view>
  10. <view class="author">
  11. <template v-if="articleDetail.user_id != null">
  12. <text class="at">{{ articleDetail.user_id?.nickname ?? '' }}</text>
  13. <text class="split">·</text>
  14. </template>
  15. <text class="date">{{ publishTime(articleDetail.publish_date as number) }}</text>
  16. </view>
  17. </view>
  18. <view class="content" v-if="articleDetail.content != null">
  19. <template v-for="(block, index) in articleDetail.content" :key="index">
  20. <view v-if="block.type == 'mediaVideo'">
  21. <video
  22. style="width: 300px; height: 200px; margin: 0 auto 20px;"
  23. :src="(block.data as UTSJSONObject).getJSON('attributes')!.getString('src')!"
  24. :poster="(block.data as UTSJSONObject).getJSON('attributes')!.getString('poster')!"
  25. ></video>
  26. </view>
  27. <view v-if="block.type == 'divider'" class="divider"></view>
  28. <view v-if="block.type == 'unlockContent'" class="unlock-content">
  29. <button @click="unlockContent">请观看广告后解锁全文</button>
  30. </view>
  31. <rich-text v-if="block.type == 'rich-text'" :selectable="false" :nodes="block.data" @itemclick="richTextItemClick"></rich-text>
  32. <render-image-component v-if="block.type == 'image'" :deltaOp="block.data" @image-preview="onImagePreview"></render-image-component>
  33. </template>
  34. </view>
  35. </scroll-view>
  36. </template>
  37. <script lang="uts">
  38. import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
  39. import RenderImageComponent from '@/uni_modules/uni-cms-article/components/render-article-detail/image.uvue'
  40. type Author = {
  41. _id: string
  42. nickname: string
  43. }
  44. type Content = {
  45. type: string
  46. data: any
  47. }
  48. type Article = {
  49. _id: string | null
  50. title: string | null
  51. content: Content[] | null
  52. excerpt: string | null
  53. publish_date: number | null
  54. user_id: Author | null
  55. thumbnail: string[] | null
  56. content_images: string[] | null
  57. }
  58. const db = uniCloud.databaseForJQL()
  59. const articleDBName = 'uni-cms-articles'
  60. const userDBName = 'uni-id-users'
  61. export default {
  62. components: {
  63. RenderImageComponent
  64. },
  65. data() {
  66. return {
  67. loading: true,
  68. id: "", // 文章ID
  69. title: "", // 文章标题
  70. articleDetail: {} as Article, // 文章详情
  71. // 广告相关配置
  72. adpId: "", // TODO: 请填写广告位ID
  73. watchAdUniqueType: "device" // TODO: 观看广告的唯一标识类型,可选值为 user 或者 device,user 表示用户唯一,device 表示设备唯一
  74. }
  75. },
  76. computed: {
  77. where(): string {
  78. //拼接where条件 查询条件 ,更多详见 :https://uniapp.dcloud.net.cn/uniCloud/unicloud-db?id=jsquery
  79. return `_id =="${this.id}"`
  80. },
  81. collection(): any[] {
  82. return [
  83. db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content').getTemp(),
  84. db.collection(userDBName).field('_id, nickname').getTemp()
  85. ]
  86. }
  87. },
  88. onLoad(event: OnLoadOptions) {
  89. if (event.has('id')) {
  90. this.id = event.get('id') as string
  91. this.load()
  92. }
  93. if (event.has('title')) {
  94. uni.setNavigationBarTitle({
  95. title: event.get('title') as string
  96. })
  97. }
  98. },
  99. methods: {
  100. async load (): Promise<void> {
  101. uni.showLoading({
  102. title: "加载中..."
  103. })
  104. const articledb = db.collection(articleDBName).where(this.where).field('user_id,thumbnail,excerpt,publish_date,title,content').getTemp()
  105. const userdb = db.collection(userDBName).field('_id, nickname').getTemp()
  106. const res = await db.collection(articledb, userdb).get()
  107. this.loadData(res.data)
  108. this.loading = false
  109. uni.hideLoading()
  110. },
  111. // 格式化发布时间
  112. publishTime(timestamp: number): string {
  113. return translatePublishTime(timestamp)
  114. },
  115. loadData(data: UTSJSONObject[]) {
  116. if (data.length <= 0) return
  117. const detail = data[0]
  118. const user_id = detail.getArray<Author>('user_id')!;
  119. this.articleDetail = {
  120. title: detail.getString('title'),
  121. content: detail.getArray<Content>('content'),
  122. excerpt: detail.getString('excerpt'),
  123. publish_date: detail.getNumber('publish_date'),
  124. thumbnail: detail.getArray<string>('thumbnail'),
  125. user_id: user_id.length > 0 ? user_id[0]: null,
  126. content_images: detail.getArray<string>('content_images')
  127. } as Article
  128. this.title = detail.getString('title')!
  129. uni.setNavigationBarTitle({
  130. title: this.title
  131. })
  132. },
  133. unlockContent () {
  134. uni.showModal({
  135. content: 'uni-app-x 暂不支持观看广告解锁全文',
  136. showCancel: false
  137. })
  138. },
  139. richTextItemClick (e: RichTextItemClickEvent) {
  140. if (e.detail.href != null) {
  141. uni.navigateTo({
  142. url: `/uni_modules/uni-cms-article/pages/webview/webview?url=${encodeURIComponent(e.detail.href as string)}`
  143. })
  144. }
  145. },
  146. onImagePreview (url: string) {
  147. const contentImages = this.articleDetail.content_images != null ? this.articleDetail.content_images: [] as string[]
  148. uni.previewImage({
  149. current: url, // 当前显示图片的http链接
  150. urls: contentImages as string[] // 需要预览的图片http链接列表
  151. })
  152. }
  153. }
  154. }
  155. </script>
  156. <style scoped lang="scss">
  157. .meta {
  158. padding: 20rpx 30rpx 0;
  159. position: relative;
  160. z-index: 1;
  161. .title {
  162. .text {
  163. font-size: 40rpx;
  164. line-height: 66rpx;
  165. font-weight: bold;
  166. color: #333;
  167. }
  168. }
  169. .excerpt {
  170. margin-top: 10rpx;
  171. .text {
  172. font-size: 26rpx;
  173. line-height: 40rpx;
  174. color: #999;
  175. }
  176. }
  177. .author {
  178. display: flex;
  179. align-items: center;
  180. justify-content: flex-start;
  181. flex-direction: row;
  182. margin-top: 20rpx;
  183. .at,
  184. .split,
  185. .date {
  186. font-size: 26rpx;
  187. color: #ccc;
  188. }
  189. .split {
  190. margin: 0 10rpx;
  191. }
  192. }
  193. }
  194. .content {
  195. margin-top: 40rpx;
  196. padding: 0 30rpx 80rpx;
  197. }
  198. .divider {
  199. height: 1px;
  200. width: 100%;
  201. background: rgba(0, 0, 0, 0.1);
  202. }
  203. </style>