BlogList.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <template>
  2. <content-with-sidebar v-if="Object.keys(blogList).length" class="blog-wrapper">
  3. <!-- blogs -->
  4. <b-row class="blog-list-wrapper">
  5. <b-col md="6">
  6. <b-col v-for="(blog, index) in blogList.slice(0, Math.ceil(blogList.length / 2))" :key="index" md="12">
  7. <b-card tag="article" no-body>
  8. <b-link :to="{ name: 'blog-detail', params: { id: blog.id } }">
  9. <b-img :src="getImg(blog.path)" :alt="getImg(blog.path).slice(5)" class="card-img-top" />
  10. </b-link>
  11. <b-card-body>
  12. <b-card-title>
  13. <b-link :to="{ name: 'blog-detail', params: { id: blog.id } }"
  14. class="blog-title-truncate text-body-heading">
  15. {{ blog.title }}
  16. </b-link>
  17. </b-card-title>
  18. <b-media no-body>
  19. <b-media-aside vertical-align="center" class="mr-50">
  20. <b-avatar href="javascript:void(0)" size="24" :src="getUserAvatar(blog.userId)" />
  21. </b-media-aside>
  22. <b-media-body>
  23. <small class="text-muted mr-50">by</small>
  24. <small>
  25. <b-link class="text-body">{{ blog.userFullName }}</b-link>
  26. </small>
  27. <span class="text-muted ml-75 mr-50">|</span>
  28. <small class="text-muted">{{ dateFormat(blog.blogPosted) }}</small>
  29. </b-media-body>
  30. </b-media>
  31. <div class="my-1 py-25">
  32. <b-link v-for="(tag, index) in blog.tags" :key="index">
  33. <b-badge pill class="mr-75" :variant="tagsColor(tag)">
  34. {{ tag }}
  35. </b-badge>
  36. </b-link>
  37. </div>
  38. <b-card-text class="blog-content-truncate">
  39. {{ blog.excerpt }}
  40. </b-card-text>
  41. <hr>
  42. <div class="d-flex justify-content-between align-items-center">
  43. <b-link :to="{ path: `/pages/blog/${blog.id}#blogComment` }">
  44. <div class="d-flex align-items-center text-body">
  45. <feather-icon icon="MessageSquareIcon" class="mr-50" />
  46. <span class="font-weight-bold">{{ blog.comment ? kFormatter(blog.comment) : 0 }} Comments</span>
  47. </div>
  48. </b-link>
  49. <b-link :to="{ name: 'blog-detail', params: { id: blog.id } }" class="font-weight-bold">
  50. Read More
  51. </b-link>
  52. </div>
  53. </b-card-body>
  54. </b-card>
  55. </b-col>
  56. </b-col>
  57. <b-col md="6">
  58. <b-col v-for="(blog, index) in blogList.slice(Math.ceil(blogList.length / 2))" :key="index" md="12">
  59. <b-card tag="article" no-body>
  60. <b-link :to="{ name: 'blog-detail', params: { id: blog.id } }">
  61. <b-img :src="getImg(blog.path)" :alt="getImg(blog.path).slice(5)" class="card-img-top" />
  62. </b-link>
  63. <b-card-body>
  64. <b-card-title>
  65. <b-link :to="{ name: 'blog-detail', params: { id: blog.id } }"
  66. class="blog-title-truncate text-body-heading">
  67. {{ blog.title }}
  68. </b-link>
  69. </b-card-title>
  70. <b-media no-body>
  71. <b-media-aside vertical-align="center" class="mr-50">
  72. <b-avatar href="javascript:void(0)" size="24" :src="getUserAvatar(blog.userId)" />
  73. </b-media-aside>
  74. <b-media-body>
  75. <small class="text-muted mr-50">by</small>
  76. <small>
  77. <b-link class="text-body">{{ blog.userFullName }}</b-link>
  78. </small>
  79. <span class="text-muted ml-75 mr-50">|</span>
  80. <small class="text-muted">{{ dateFormat(blog.blogPosted) }}</small>
  81. </b-media-body>
  82. </b-media>
  83. <div class="my-1 py-25">
  84. <b-link v-for="(tag, index) in blog.tags" :key="index">
  85. <b-badge pill class="mr-75" :variant="tagsColor(tag)">
  86. {{ tag }}
  87. </b-badge>
  88. </b-link>
  89. </div>
  90. <b-card-text class="blog-content-truncate">
  91. {{ blog.excerpt }}
  92. </b-card-text>
  93. <hr>
  94. <div class="d-flex justify-content-between align-items-center">
  95. <b-link :to="{ path: `/pages/blog/${blog.id}#blogComment` }">
  96. <div class="d-flex align-items-center text-body">
  97. <feather-icon icon="MessageSquareIcon" class="mr-50" />
  98. <span class="font-weight-bold">{{ blog.comment ? kFormatter(blog.comment) : 0 }} Comments</span>
  99. </div>
  100. </b-link>
  101. <b-link :to="{ name: 'blog-detail', params: { id: blog.id } }" class="font-weight-bold">
  102. Read More
  103. </b-link>
  104. </div>
  105. </b-card-body>
  106. </b-card>
  107. </b-col>
  108. </b-col>
  109. <b-col cols="12">
  110. <!-- pagination -->
  111. <div class="my-2" @click="getBlogListPerpage">
  112. <b-pagination v-model="currentPage" align="center" :per-page="perPage" :total-rows="rows" first-number
  113. last-number prev-class="prev-item" next-class="next-item">
  114. <template #prev-text>
  115. <feather-icon icon="ChevronLeftIcon" size="18" />
  116. </template>
  117. <template #next-text>
  118. <feather-icon icon="ChevronRightIcon" size="18" />
  119. </template>
  120. </b-pagination>
  121. </div>
  122. </b-col>
  123. </b-row>
  124. <!--/ blogs -->
  125. <!-- sidebar -->
  126. <div slot="sidebar" class="blog-sidebar py-2 py-lg-0">
  127. <!-- input search -->
  128. <b-form-group class="blog-search">
  129. <b-input-group class="input-group-merge">
  130. <b-form-input id="search-input" v-model="search_query" placeholder="Search here" />
  131. <b-input-group-append class="cursor-pointer" is-text>
  132. <feather-icon icon="SearchIcon" />
  133. </b-input-group-append>
  134. </b-input-group>
  135. </b-form-group>
  136. <!--/ input search -->
  137. <!-- recent posts -->
  138. <div class="blog-recent-posts mt-3">
  139. <h6 class="section-label mb-75">
  140. Recent Posts
  141. </h6>
  142. <b-media v-for="(recentpost, index) in blogSidebar.recentPosts" :key="index" no-body
  143. :class="index ? 'mt-2' : ''">
  144. <b-media-aside class="mr-2">
  145. <b-link :to="{ name: 'blog-detail', params: { id: recentpost.id } }">
  146. <b-img :src="getImg(recentpost.path)" :alt="getImg(recentpost.path).slice(6)" width="100" rounded
  147. height="70" />
  148. </b-link>
  149. </b-media-aside>
  150. <b-media-body>
  151. <h6 class="blog-recent-post-title">
  152. <b-link :to="{ name: 'blog-detail', params: { id: recentpost.id } }" class="text-body-heading">
  153. {{ recentpost.title }}
  154. </b-link>
  155. </h6>
  156. <span class="text-muted mb-0">
  157. {{ dateFormat(recentpost.createdTime) }}
  158. </span>
  159. </b-media-body>
  160. </b-media>
  161. </div>
  162. <!--/ recent posts -->
  163. <!-- categories -->
  164. <div class="blog-categories mt-3">
  165. <h6 class="section-label mb-1">
  166. Categories
  167. </h6>
  168. <div v-for="category in blogSidebar.categories" :key="category.icon"
  169. class="d-flex justify-content-start align-items-center mb-75">
  170. <b-link>
  171. <b-avatar rounded size="32" :variant="tagsColor(category.category)" class="mr-75">
  172. <feather-icon :icon="category.icon" size="16" />
  173. </b-avatar>
  174. </b-link>
  175. <b-link>
  176. <div class="blog-category-title text-body">
  177. {{ category.category }}
  178. </div>
  179. </b-link>
  180. </div>
  181. </div>
  182. <!--/ categories -->
  183. </div>
  184. <!--/ sidebar -->
  185. </content-with-sidebar>
  186. </template>
  187. <script>
  188. import {
  189. BRow,
  190. BCol,
  191. BCard,
  192. BFormInput,
  193. BCardText,
  194. BCardTitle,
  195. BMedia,
  196. BAvatar,
  197. BMediaAside,
  198. BMediaBody,
  199. BImg,
  200. BCardBody,
  201. BLink,
  202. BBadge,
  203. BFormGroup,
  204. BInputGroup,
  205. BInputGroupAppend,
  206. BPagination,
  207. } from 'bootstrap-vue'
  208. import { kFormatter } from '@core/utils/filter'
  209. import ContentWithSidebar from '@core/layouts/components/content-with-sidebar/ContentWithSidebar.vue'
  210. import useJwt from '@/auth/jwt/useJwt'
  211. import { format } from 'date-fns'
  212. import { zhTW } from 'date-fns/locale'
  213. export default {
  214. components: {
  215. BRow,
  216. BCol,
  217. BCard,
  218. BFormInput,
  219. BCardText,
  220. BCardBody,
  221. BCardTitle,
  222. BMedia,
  223. BAvatar,
  224. BMediaAside,
  225. BMediaBody,
  226. BLink,
  227. BBadge,
  228. BFormGroup,
  229. BInputGroup,
  230. BInputGroupAppend,
  231. BImg,
  232. BPagination,
  233. ContentWithSidebar,
  234. },
  235. data() {
  236. return {
  237. search_query: '',
  238. blogList: [],
  239. blogSidebar: {},
  240. currentPage: 1,
  241. perPage: 4,
  242. rows: 4,
  243. }
  244. },
  245. created() {
  246. useJwt.getPost('/api/get_blog/list/total').then(res => {
  247. this.rows = res.data
  248. })
  249. useJwt.getPost('/api/get_blog/list/perpage', { offset: 0, rows: this.perPage }).then(res => {
  250. this.blogList = res.data
  251. })
  252. useJwt.getPost('/api/get_blog/sidebar').then(res => {
  253. this.blogSidebar = res.data
  254. })
  255. },
  256. methods: {
  257. kFormatter,
  258. tagsColor(tag) {
  259. if (tag === 'Quote') return 'light-info'
  260. if (tag === 'Gaming') return 'light-danger'
  261. if (tag === 'Fashion') return 'light-primary'
  262. if (tag === 'Video') return 'light-warning'
  263. if (tag === 'Food') return 'light-success'
  264. return 'light-primary'
  265. },
  266. dateFormat(date) {
  267. return format(new Date(date), 'yyyy-MM-dd hh:mm', { locale: zhTW })
  268. },
  269. getUserAvatar(userId) {
  270. return require('@/assets/images/avatars/' + userId + '-small.png')
  271. },
  272. getImg(imagePath) {
  273. return require('@/assets/images/' + imagePath)
  274. },
  275. getBlogListPerpage() {
  276. useJwt.getPost('/api/get_blog/list/perpage', { offset: ((this.currentPage - 1) * this.perPage), rows: this.perPage }).then(res => {
  277. this.blogList = res.data
  278. })
  279. },
  280. },
  281. }
  282. </script>
  283. <style lang="scss">
  284. @import '~@resources/scss/vue/pages/page-blog.scss';
  285. </style>