votes.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. <template>
  2. <view class="app-page">
  3. <PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId"
  4. :error-text="uniHaloPluginAvailableError" />
  5. <template v-else>
  6. <!-- 顶部切换 -->
  7. <view class="e-fixed">
  8. <tm-search v-model="queryParams.keyword" :round="24" :shadow="0" color="light-blue"
  9. insert-color="light-blue" :clear="true" @input="fnOnSearch" @confirm="fnOnSearch"></tm-search>
  10. <tm-dropDownMenu :shadow="1" color="light-blue" active-color="light-blue"
  11. :default-selected="filterOption.selected" :list="filterOption.list"
  12. @confirm="fnOnFilterConfirm"></tm-dropDownMenu>
  13. </view>
  14. <!-- 占位区域 -->
  15. <view style="width: 100vw;height: 210rpx;"></view>
  16. <!-- 加载区域 -->
  17. <view v-if="loading == 'loading'" class="loading-wrap pa-24">
  18. <tm-skeleton model="listAvatr"></tm-skeleton>
  19. <tm-skeleton model="listAvatr"></tm-skeleton>
  20. <tm-skeleton model="listAvatr"></tm-skeleton>
  21. <tm-skeleton model="listAvatr"></tm-skeleton>
  22. </view>
  23. <view v-else-if="loading == 'error'" class="content-empty flex flex-center">
  24. <tm-empty icon="icon-wind-cry" label="加载异常"></tm-empty>
  25. </view>
  26. <!-- 内容区域 -->
  27. <view v-else class="content">
  28. <view v-if="dataList.length == 0" class="content-empty flex flex-center">
  29. <tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
  30. </view>
  31. <block v-else>
  32. <tm-translate v-for="(item, index) in dataList" :key="item.metadata.name" animation-name="fadeUp"
  33. :wait="calcAniWait(index)">
  34. <VoteCard :vote="item" :index="index" @on-click="fnToDetail"></VoteCard>
  35. </tm-translate>
  36. <view class="load-text">{{ loadMoreText }}</view>
  37. <tm-flotbutton @click="fnToTopPage" size="m" color="light-blue"
  38. icon="icon-angle-up"></tm-flotbutton>
  39. </block>
  40. </view>
  41. </template>
  42. </view>
  43. </template>
  44. <script>
  45. import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
  46. import tmSearch from '@/tm-vuetify/components/tm-search/tm-search.vue';
  47. import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
  48. import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
  49. import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
  50. import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
  51. import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
  52. import tmDropDownMenu from '@/tm-vuetify/components/tm-dropDownMenu/tm-dropDownMenu.vue';
  53. import VoteCard from '@/components/vote-card/vote-card.vue'
  54. import {
  55. voteCacheUtil
  56. } from '@/utils/vote.js'
  57. import pluginAvailable from "@/common/mixins/pluginAvailable.js"
  58. export default {
  59. options: {
  60. styleIsolation: 'shared'
  61. },
  62. mixins: [pluginAvailable],
  63. components: {
  64. tmSkeleton,
  65. tmSearch,
  66. tmTranslate,
  67. tmTabs,
  68. tmFlotbutton,
  69. tmEmpty,
  70. tmTags,
  71. tmDropDownMenu,
  72. VoteCard
  73. },
  74. data() {
  75. return {
  76. loading: 'loading',
  77. hasNext: false,
  78. isLoadMore: false,
  79. loadMoreText: '加载中...',
  80. filterOption: {
  81. selected: [],
  82. list: [{
  83. title: '类型',
  84. children: [{
  85. title: "",
  86. model: "list",
  87. name: "type",
  88. children: [{
  89. title: "全部",
  90. id: undefined
  91. },
  92. {
  93. title: "单选",
  94. id: 'single'
  95. },
  96. {
  97. title: "多选",
  98. id: 'multiple'
  99. },
  100. {
  101. title: "双选PK",
  102. id: 'pk'
  103. }
  104. ]
  105. }]
  106. }, {
  107. title: '状态',
  108. children: [{
  109. title: "",
  110. model: "list",
  111. name: "hasEnded",
  112. children: [{
  113. title: "全部",
  114. id: undefined
  115. },
  116. {
  117. title: "进行中",
  118. id: false
  119. },
  120. {
  121. title: "已结束",
  122. id: true
  123. }
  124. ]
  125. }]
  126. }, {
  127. title: '排序',
  128. children: [{
  129. title: "",
  130. model: "list",
  131. name: "sort",
  132. children: [{
  133. title: "默认排序",
  134. id: undefined
  135. },
  136. {
  137. title: "较近创建",
  138. id: 'metadata.creationTimestamp,desc'
  139. },
  140. {
  141. title: "较早创建",
  142. id: 'metadata.creationTimestamp,asc'
  143. }
  144. ]
  145. }]
  146. }, {
  147. title: '是否已投',
  148. children: [{
  149. title: "",
  150. model: "list",
  151. name: "isVoted",
  152. children: [{
  153. title: "全部",
  154. id: undefined
  155. },
  156. {
  157. title: "未投票",
  158. id: false
  159. },
  160. {
  161. title: "已投票",
  162. id: true
  163. }
  164. ]
  165. }]
  166. }]
  167. },
  168. filterIsVoted: undefined,
  169. queryParams: {
  170. keyword: "",
  171. page: 1,
  172. size: 10,
  173. sort: undefined,
  174. type: undefined,
  175. hasEnded: undefined
  176. },
  177. dataList: []
  178. };
  179. },
  180. computed: {
  181. haloConfigs() {
  182. return this.$tm.vx.getters().getConfigs;
  183. },
  184. calcAuditModeEnabled() {
  185. return this.haloConfigs.auditConfig.auditModeEnabled
  186. },
  187. },
  188. async onLoad() {
  189. this.fnSetPageTitle('投票列表');
  190. // 检查插件
  191. this.setPluginId(this.NeedPluginIds.PluginVote)
  192. this.setPluginError("阿偶,检测到当前插件没有安装或者启用,无法使用投票功能哦,请联系管理员")
  193. if (!await this.checkPluginAvailable()) return
  194. this.fnGetData();
  195. },
  196. onPullDownRefresh() {
  197. if (!this.uniHaloPluginAvailable) return;
  198. this.fnResetSetAniWaitIndex();
  199. this.isLoadMore = false;
  200. this.queryParams.page = 0;
  201. this.fnGetData();
  202. },
  203. onReachBottom(e) {
  204. if (!this.uniHaloPluginAvailable) return;
  205. if (this.calcAuditModeEnabled) {
  206. uni.showToast({
  207. icon: 'none',
  208. title: '没有更多数据了'
  209. });
  210. return
  211. }
  212. if (this.hasNext) {
  213. this.queryParams.page += 1;
  214. this.isLoadMore = true;
  215. this.fnGetData();
  216. } else {
  217. uni.showToast({
  218. icon: 'none',
  219. title: '没有更多数据了'
  220. });
  221. }
  222. },
  223. methods: {
  224. fnOnSearch() {
  225. this.fnResetSetAniWaitIndex();
  226. this.fnToTopPage();
  227. this.fnGetData();
  228. },
  229. fnGetData() {
  230. if (this.calcAuditModeEnabled) {
  231. return;
  232. }
  233. uni.showLoading({
  234. mask: true,
  235. title: '加载中...'
  236. });
  237. // 设置状态为加载中
  238. if (!this.isLoadMore) {
  239. this.loading = 'loading';
  240. }
  241. this.loadMoreText = '加载中...';
  242. this.$httpApi.v2
  243. .getVoteList(this.queryParams)
  244. .then(res => {
  245. this.loading = 'success';
  246. this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
  247. this.hasNext = res.hasNext;
  248. const tempItems = res.items.map(item => {
  249. item.spec.disabled = true
  250. item.spec.isVoted = this.fnCalcIsVoted(item.metadata.name)
  251. item.spec.options.map((option, index) => {
  252. option.checked = this.fnCalcIsChecked(item.metadata.name, option)
  253. option.value = option.id
  254. option.label = option.title
  255. // todo:计算当前的选择占比
  256. if (item.spec.type === 'single') {
  257. option.percent = this.fnCalcPercent(option, item.stats);
  258. } else if (item.spec.type === 'multiple') {
  259. option.percent = this.fnCalcPercent(option, item.stats);
  260. } else if (item.spec.type === 'pk') {
  261. option.percent = this.fnCalcPercent(option, item.stats);
  262. }
  263. return option
  264. })
  265. return item;
  266. })
  267. if (this.isLoadMore) {
  268. this.dataList = this.dataList.concat(tempItems);
  269. } else {
  270. this.dataList = tempItems;
  271. }
  272. this.dataList = this.dataList.sort((a, b) => {
  273. return a.spec.isVoted - b.spec.isVoted
  274. })
  275. if (this.filterIsVoted != undefined) {
  276. this.dataList = this.dataList.filter(x => x.spec.isVoted == this.filterIsVoted)
  277. }
  278. })
  279. .catch(err => {
  280. console.error(err);
  281. this.loading = 'error';
  282. this.loadMoreText = '加载失败,请下拉刷新!';
  283. })
  284. .finally(() => {
  285. setTimeout(() => {
  286. uni.hideLoading();
  287. uni.stopPullDownRefresh();
  288. }, 100);
  289. });
  290. },
  291. fnCalcPercent(voteOption, stats) {
  292. if (!stats?.voteDataList) return 0;
  293. const option = stats.voteDataList.find(x => x.id == voteOption.id)
  294. if (!option) return 0;
  295. const percent = (option.voteCount / stats.voteCount) * 100
  296. return Math.round(percent)
  297. },
  298. fnCalcIsVoted(name) {
  299. return voteCacheUtil.has(name)
  300. },
  301. fnCalcIsChecked(name, option) {
  302. const data = voteCacheUtil.get(name)
  303. if (!data) return false;
  304. const checked = data.selected.includes(option.id)
  305. return checked
  306. },
  307. //跳转详情
  308. fnToDetail(item) {
  309. if (this.calcAuditModeEnabled) return;
  310. uni.navigateTo({
  311. url: '/pagesA/vote-detail/vote-detail?name=' + item.metadata.name,
  312. animationType: 'slide-in-right'
  313. });
  314. },
  315. fnOnFilterConfirm(e) {
  316. // 类型
  317. const type = e.find(x => x.name == 'type')
  318. if (type.children.length == 0) {
  319. this.queryParams.type = undefined
  320. } else {
  321. this.queryParams.type = type.children[0]?.id
  322. }
  323. // 状态
  324. const hasEnded = e.find(x => x.name == 'hasEnded')
  325. if (hasEnded.children.length == 0) {
  326. this.queryParams.hasEnded = undefined
  327. } else {
  328. this.queryParams.hasEnded = hasEnded.children[0]?.id
  329. }
  330. // 排序
  331. const sort = e.find(x => x.name == 'sort')
  332. if (sort.children.length == 0) {
  333. this.queryParams.sort = undefined
  334. } else {
  335. this.queryParams.sort = sort.children[0]?.id
  336. }
  337. // 是否已经投
  338. const isVoted = e.find(x => x.name == 'isVoted')
  339. if (isVoted.children.length == 0) {
  340. this.filterIsVoted = undefined
  341. } else {
  342. this.filterIsVoted = isVoted.children[0]?.id
  343. }
  344. this.queryParams.page = 0;
  345. this.isLoadMore = false;
  346. this.fnResetSetAniWaitIndex();
  347. this.fnToTopPage();
  348. this.fnGetData();
  349. }
  350. }
  351. };
  352. </script>
  353. <style lang="scss" scoped>
  354. .app-page {
  355. width: 100vw;
  356. min-height: 100vh;
  357. display: flex;
  358. flex-direction: column;
  359. padding-bottom: 24rpx;
  360. background-color: #fafafd;
  361. &.is-balck {
  362. background-color: #212121;
  363. }
  364. }
  365. .content {
  366. padding-top: 24rpx;
  367. }
  368. .content-empty {
  369. height: 60vh;
  370. }
  371. </style>