gallery.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <template>
  2. <view class="app-page">
  3. <!-- 顶部切换 -->
  4. <view class="e-fixed" v-if="category.list.length > 0">
  5. <tm-tabs color="light-blue" v-model="category.activeIndex" range-key="displayName" :list="category.list"
  6. align="left" @change="fnOnCategoryChange"></tm-tabs>
  7. </view>
  8. <!-- 占位区域 -->
  9. <view v-if="category.list.length > 0" style="width: 100vw;height: 90rpx;"></view>
  10. <!-- 加载区域 -->
  11. <view v-if="loading !== 'success'" class="loading-wrap">
  12. <tm-skeleton model="card"></tm-skeleton>
  13. <tm-skeleton model="card"></tm-skeleton>
  14. <tm-skeleton model="card"></tm-skeleton>
  15. <tm-skeleton model="card"></tm-skeleton>
  16. </view>
  17. <!-- 内容区域 -->
  18. <view class="content" v-else>
  19. <view v-if="dataList.length === 0" class="content-empty">
  20. <!-- 空布局 -->
  21. <tm-empty icon="icon-shiliangzhinengduixiang-" label="博主还没有分享图片~"></tm-empty>
  22. </view>
  23. <block v-else>
  24. <block v-if="galleryConfig.useWaterfall">
  25. <!--瀑布流-->
  26. <tm-flowLayout-custom ref="wafll" style="width: 100%;" @click="fnOnFlowClick"></tm-flowLayout-custom>
  27. </block>
  28. <!-- 列表 -->
  29. <block v-else>
  30. <tm-translate v-for="(item, index) in dataList" :key="index"
  31. style="box-sizing: border-box;padding: 6rpx;width: 50%;height: 250rpx;"
  32. animation-name="fadeUp" :wait="calcAniWait(index)">
  33. <view style="border-radius: 12rpx;overflow: hidden;width: 100%;height: 250rpx;">
  34. <image style="width: 100%;height: 100%;" mode="aspectFill" :src="item.spec.url"
  35. @click="fnPreview(item)"/>
  36. </view>
  37. </tm-translate>
  38. </block>
  39. <tm-flotbutton @click="fnToTopPage" color="light-blue" size="m" icon="icon-angle-up"></tm-flotbutton>
  40. <view class="load-text">{{ loadMoreText }}</view>
  41. </block>
  42. </view>
  43. </view>
  44. </template>
  45. <script>
  46. import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
  47. import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
  48. import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
  49. import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
  50. import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
  51. import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
  52. import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
  53. import tmFlowLayoutCustom from '@/tm-vuetify/components/tm-flowLayout-custom/tm-flowLayout-custom.vue';
  54. import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
  55. export default {
  56. options: {
  57. multipleSlots: true
  58. },
  59. components: {
  60. tmSkeleton,
  61. tmTranslate,
  62. tmFlotbutton,
  63. tmTags,
  64. tmEmpty,
  65. tmIcons,
  66. tmImages,
  67. tmFlowLayoutCustom,
  68. tmTabs
  69. },
  70. data() {
  71. return {
  72. isBlackTheme: false,
  73. loading: 'loading',
  74. category: {
  75. activeIndex: 0,
  76. activeValue: '',
  77. list: []
  78. },
  79. queryParams: {
  80. size: 10,
  81. page: 1,
  82. group: ""
  83. },
  84. isLoadMore: false,
  85. loadMoreText: '',
  86. hasNext: false,
  87. dataList: []
  88. };
  89. },
  90. computed: {
  91. galleryConfig() {
  92. return this.$tm.vx.getters().getConfigs.pageConfig.galleryConfig;
  93. },
  94. haloConfigs() {
  95. return this.$tm.vx.getters().getConfigs;
  96. },
  97. mockJson() {
  98. return this.$tm.vx.getters().getMockJson;
  99. }
  100. },
  101. watch: {
  102. galleryConfig: {
  103. handler(newValue, oldValue) {
  104. if (!newValue) return;
  105. this.fnSetPageTitle(newValue.pageTitle);
  106. this.fnGetCategory();
  107. },
  108. deep: true,
  109. immediate: true
  110. }
  111. },
  112. onPullDownRefresh() {
  113. this.dataList = []
  114. this.isLoadMore = false;
  115. this.queryParams.page = 1;
  116. this.fnGetData(true);
  117. },
  118. onReachBottom(e) {
  119. if (this.haloConfigs.basicConfig.auditModeEnabled) {
  120. uni.showToast({
  121. icon: 'none',
  122. title: '没有更多数据了'
  123. });
  124. return;
  125. }
  126. if (this.hasNext) {
  127. this.queryParams.page += 1;
  128. this.isLoadMore = true;
  129. this.fnGetData(false);
  130. } else {
  131. uni.showToast({
  132. icon: 'none',
  133. title: '没有更多数据了'
  134. });
  135. }
  136. },
  137. methods: {
  138. fnOnCategoryChange(index) {
  139. this.fnResetSetAniWaitIndex();
  140. this.queryParams.group = this.category.list[index].name;
  141. this.queryParams.page = 1;
  142. this.fnToTopPage();
  143. this.dataList = [];
  144. this.fnGetData(true);
  145. },
  146. fnGetCategory() {
  147. if (this.haloConfigs.basicConfig.auditModeEnabled) {
  148. this.fnGetData(true);
  149. return
  150. }
  151. this.$httpApi.v2.getPhotoGroupList({
  152. page: 1,
  153. size: 0
  154. }).then(res => {
  155. this.category.list = res.items.map(item => {
  156. return {
  157. name: item.metadata.name,
  158. displayName: item.spec.displayName
  159. }
  160. });
  161. if (this.category.list.length !== 0) {
  162. this.queryParams.group = this.category.list[0].name;
  163. this.fnGetData(true);
  164. }
  165. });
  166. },
  167. fnGetData(isClearWaterfall = false) {
  168. if (this.haloConfigs.basicConfig.auditModeEnabled) {
  169. this.dataList = this.mockJson.gallery.list.map(item => {
  170. return {
  171. metadata: {
  172. name: Date.now() * Math.random(),
  173. },
  174. spec: {
  175. url: this.$utils.checkImageUrl(item)
  176. }
  177. }
  178. })
  179. this.loading = 'success';
  180. if (this.galleryConfig.useWaterfall) {
  181. this.$nextTick(() => {
  182. if (isClearWaterfall) {
  183. this.$refs.wafll.clear()
  184. }
  185. setTimeout(() => {
  186. this.$refs.wafll.pushData(this.dataList)
  187. }, 50)
  188. })
  189. }
  190. this.loadMoreText = '呜呜,没有更多数据啦~';
  191. uni.hideLoading();
  192. uni.stopPullDownRefresh();
  193. return;
  194. }
  195. // 设置状态为加载中
  196. if (!this.isLoadMore) {
  197. this.loading = 'loading';
  198. }
  199. this.loadMoreText = '';
  200. this.$httpApi.v2
  201. .getPhotoListByGroupName(this.queryParams)
  202. .then(res => {
  203. this.hasNext = res.hasNext;
  204. this.loading = 'success';
  205. if (res.items.length !== 0) {
  206. const _list = res.items.map((item, index) => {
  207. item.spec.url = this.$utils.checkImageUrl(item.spec.url || item.spec.cover);
  208. return item;
  209. });
  210. if (this.isLoadMore) {
  211. this.dataList = this.dataList.concat(_list);
  212. } else {
  213. this.dataList = _list;
  214. }
  215. if (this.galleryConfig.useWaterfall) {
  216. this.$nextTick(() => {
  217. if (isClearWaterfall) {
  218. this.$refs.wafll.clear()
  219. }
  220. this.$refs.wafll.pushData(_list)
  221. })
  222. }
  223. }
  224. this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
  225. })
  226. .catch(err => {
  227. console.error(err);
  228. this.loading = 'error';
  229. this.loadMoreText = '加载失败,请下拉刷新!';
  230. })
  231. .finally(() => {
  232. setTimeout(() => {
  233. uni.hideLoading();
  234. uni.stopPullDownRefresh();
  235. }, 500);
  236. });
  237. },
  238. fnOnFlowClick({item}) {
  239. this.fnPreview(item)
  240. },
  241. // 预览
  242. fnPreview(data) {
  243. uni.previewImage({
  244. current: this.dataList.findIndex(x => x.metadata.name === data.metadata.name),
  245. urls: this.dataList.map(x => x.spec.url),
  246. indicator: 'number',
  247. loop: true
  248. });
  249. }
  250. }
  251. };
  252. </script>
  253. <style lang="scss" scoped>
  254. .app-page {
  255. width: 100vw;
  256. min-height: 100vh;
  257. display: flex;
  258. flex-direction: column;
  259. padding-bottom: 24rpx;
  260. background-color: #fafafa;
  261. }
  262. .content {
  263. display: flex;
  264. flex-wrap: wrap;
  265. box-sizing: border-box;
  266. padding: 24rpx 24rpx 0;
  267. gap: 12rpx 0;
  268. .content-empty {
  269. width: 100%;
  270. height: 70vh;
  271. display: flex;
  272. align-items: center;
  273. justify-content: center;
  274. }
  275. }
  276. .loading-wrap {
  277. box-sizing: border-box;
  278. padding: 24rpx;
  279. }
  280. .load-text {
  281. width: 100%;
  282. text-align: center;
  283. }
  284. </style>