index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <template>
  2. <div class="finance">
  3. <img src="../../assets/img/financeBanner.jpg" style="width: 100%" />
  4. <div class="guize" @click="showGuize">投票规则</div>
  5. <div class="guize" style="top: 10em" @click="paiming">查看排名</div>
  6. <van-search
  7. :background="background"
  8. clearable
  9. v-model="search"
  10. shape="round"
  11. placeholder="请输入搜索关键词"
  12. />
  13. <van-tabs :color="theme" :background="background">
  14. <van-tab :title="_sort[i]" v-for="(v, i) in orilist" :key="i">
  15. <van-row gutter="8">
  16. <van-col
  17. span="12"
  18. :style="{
  19. position: 'relative',
  20. display: item.noShow ? 'none' : 'inline-block',
  21. }"
  22. v-for="item in v"
  23. :key="item.id"
  24. >
  25. <van-image
  26. :width="width"
  27. :height="height"
  28. fit="cover"
  29. @click="() => clickShowVideo(item)"
  30. position="left"
  31. :src="create_url_image(item.workTitle)"
  32. />
  33. <van-row>
  34. <van-col
  35. span="24"
  36. style="text-align: center"
  37. class="van-ellipsis unit"
  38. >{{ item.university }}</van-col
  39. >
  40. <van-col span="24" style="text-align: center">
  41. <div class="van-multi-ellipsis--l2">
  42. {{ item.name }}
  43. </div>
  44. </van-col>
  45. </van-row>
  46. <br />
  47. <van-checkbox
  48. class="act"
  49. v-model="ids[item.phone].select"
  50. :checked-color="theme"
  51. />
  52. </van-col>
  53. </van-row>
  54. </van-tab>
  55. </van-tabs>
  56. <div
  57. class="btn"
  58. :style="{ bottom: config.isShanShiPin ? '5px' : '75px' }"
  59. @click="toupiao"
  60. >
  61. 投票
  62. </div>
  63. <Shanshipin v-if="!config.isShanShiPin" :color="background" />
  64. <!-- 投票框 -->
  65. <van-dialog v-model:show="showPaihang" confirm-button-text="关闭">
  66. <div style="height: 50vh">
  67. <van-tabs :color="theme">
  68. <van-tab :title="key" v-for="(v, key) in rankList" :key="key">
  69. <div class="SilkList">
  70. <van-cell-group inset>
  71. <van-cell title="名称" value="投票量"></van-cell>
  72. <van-row v-for="item in v" :key="item.id + 'team'">
  73. <van-col span="16"
  74. ><p class="text van-ellipsis--l2" v-text="item.name"></p
  75. ></van-col>
  76. <van-col span="8"
  77. ><p
  78. class="text"
  79. style="text-align: right"
  80. v-text="formmate(item.votes)"
  81. ></p
  82. ></van-col>
  83. </van-row>
  84. </van-cell-group>
  85. </div>
  86. </van-tab>
  87. </van-tabs>
  88. </div>
  89. </van-dialog>
  90. <!-- 登录框 -->
  91. <van-dialog
  92. theme="round-button"
  93. v-model:show="showLogin"
  94. confirm-button-text="登录"
  95. :before-close="beforeClose"
  96. >
  97. <van-cell-group inset>
  98. <van-field
  99. v-model="login.phone"
  100. required
  101. type="tel"
  102. label="手机号"
  103. placeholder="请输入手机号"
  104. >
  105. <template #button>
  106. <van-button
  107. v-if="codePhoen === 0"
  108. @click="getCode"
  109. size="small"
  110. type="primary"
  111. :color="theme"
  112. >
  113. 发送验证码
  114. </van-button>
  115. <van-count-down
  116. format="ss 秒"
  117. v-else
  118. :time="codePhoen"
  119. @finish="() => (codePhoen = 0)"
  120. />
  121. </template>
  122. </van-field>
  123. <van-field required v-model="login.code" type="digit" label="验证码" />
  124. </van-cell-group>
  125. </van-dialog>
  126. <!-- 候选人详情框 -->
  127. <van-dialog
  128. v-model:show="hxrDialog"
  129. confirm-button-text="投票"
  130. cancel-button-text="为他拉票"
  131. :show-cancel-button="true"
  132. @close="close"
  133. @cancel="cancel"
  134. @confirm="shear_page"
  135. style="padding: 5px"
  136. >
  137. <div
  138. class="van-ellipsis"
  139. style="margin: 0.5em 0"
  140. v-text="selectItem.name"
  141. ></div>
  142. <div
  143. class="van-ellipsis unit"
  144. style="margin: 0.5em 0"
  145. v-text="selectItem.university"
  146. ></div>
  147. <div style="position: relative" @click="toDetal(selectItem.file)">
  148. <van-icon
  149. size="50"
  150. color="#fff"
  151. name="play-circle-o"
  152. style="
  153. position: absolute;
  154. top: 50%;
  155. left: 50%;
  156. transform: translate(-50%, -50%);
  157. "
  158. />
  159. <img
  160. :src="create_url_image(selectItem.workTitle)"
  161. style="width: 100%"
  162. />
  163. </div>
  164. </van-dialog>
  165. <div class="zhezhao" v-show="imgObj.image">
  166. <div class="zhezhao_img">
  167. <img :src="imgObj.image" style="width: 100%" />
  168. <p style="text-align: center; color: #fff; margin: 1em 0">
  169. 长按保存图片,发给好友一起参与
  170. </p>
  171. <van-button
  172. @click="
  173. () => {
  174. imgObj = {};
  175. }
  176. "
  177. color="#fff"
  178. style="
  179. margin: 0 auto;
  180. display: block;
  181. border: none;
  182. background-color: #ffffff00;
  183. "
  184. icon="cross"
  185. type="default"
  186. round
  187. />
  188. </div>
  189. </div>
  190. </div>
  191. </template>
  192. <script setup>
  193. import { ref, reactive, watch, onMounted } from 'vue';
  194. import QRCode from 'qrcode';
  195. import { showDialog, showConfirmDialog, showToast } from 'vant';
  196. import {
  197. getEnrollList,
  198. getEnrollRank,
  199. getVote,
  200. getTimes,
  201. getSendVerifyCode,
  202. verifyCode,
  203. } from '@/api/SilkRoadSpringGala.js';
  204. import { canvasToImage } from '@/utils/tool.js';
  205. import Shanshipin from '@/components/shanshipin.vue';
  206. const theme = '#38379f';
  207. const background = '#e0f2fa';
  208. const w = window.$originData.orginParames.availWidth || 0;
  209. const operateId = window.$originData.orginParames.operateId;
  210. const width = (w - 16) / 2;
  211. const height = width * 0.57;
  212. const hxrDialog = ref(false);
  213. const showPaihang = ref(false);
  214. const showLogin = ref(false);
  215. const selectItem = ref({});
  216. const imgObj = ref({});
  217. const rankList = ref({});
  218. const orilist = ref([]);
  219. const _sort = ['党建引领', '金融知识', '风险防范', '金融服务', '优秀宣讲员'];
  220. const ids = ref({});
  221. const codePhoen = ref(0);
  222. const config = reactive({
  223. times: -1,
  224. isShanShiPin: window.$shanshipin.UserId,
  225. });
  226. const search = ref('');
  227. const t = config.isShanShiPin ? 5 : 1;
  228. let time_old = undefined;
  229. const phone =
  230. window.$shanshipin.Phone && window.$shanshipin.Phone != 0
  231. ? window.$shanshipin.Phone
  232. : localStorage.getItem('financePhone') || '18439106376';
  233. const login = reactive({
  234. phone,
  235. code: '',
  236. });
  237. const getList = call => {
  238. getEnrollList({
  239. operateId,
  240. }).then(r => {
  241. const array = r || [];
  242. const obj = [];
  243. const ids_list = [];
  244. let item = undefined;
  245. for (let i = 0; i < array.length; i++) {
  246. const v = array[i];
  247. v.noShow = false;
  248. const num = getIndex(v.type);
  249. if (obj[num]) obj[num].push(v);
  250. else obj[num] = [v];
  251. ids_list[v.phone] = {
  252. select: false,
  253. name: v.name,
  254. phone: v.phone,
  255. };
  256. if (
  257. window.$originData.urlParames.id &&
  258. v.phone === window.$originData.urlParames.id
  259. ) {
  260. // 默认打开卡片
  261. item = v;
  262. }
  263. }
  264. if (item) {
  265. time_old = setTimeout(() => {
  266. clickShowVideo(item);
  267. clearTimeout(time_old);
  268. }, 500);
  269. }
  270. ids.value = ids_list;
  271. orilist.value = obj;
  272. call && call();
  273. });
  274. };
  275. onMounted(() => {
  276. getList();
  277. });
  278. const getIndex = key => {
  279. const sort_list = {
  280. 党建引领: 0,
  281. 金融知识: 1,
  282. 风险防范: 2,
  283. 金融服务: 3,
  284. 优秀宣讲员: 4,
  285. };
  286. return sort_list[key] || 0;
  287. };
  288. const getRankList = Call => {
  289. getEnrollRank({
  290. operateId,
  291. }).then(r => {
  292. const l = r || [];
  293. const obj = {};
  294. for (let i = 0; i < l.length; i++) {
  295. const v = l[i];
  296. if (obj[v.type]) obj[v.type].push(v);
  297. else obj[v.type] = [v];
  298. }
  299. rankList.value = obj;
  300. Call && Call();
  301. });
  302. };
  303. const shear_page = () => {
  304. // 投票
  305. if (!login.phone || login.phone.length < 11) return (showLogin.value = true); //如果没有手机号提示登录
  306. showConfirmDialog({
  307. message: '确定投票给' + selectItem.value.name + '么?',
  308. beforeClose: type => {
  309. if (type !== 'confirm') return true;
  310. return new Promise(resolve => {
  311. getVote({
  312. target: [selectItem.value.phone],
  313. source: login.phone,
  314. client: config.isShanShiPin ? '闪视频' : '其他',
  315. operateId,
  316. })
  317. .then(() => {
  318. showToast('投票成功!');
  319. resolve(true);
  320. })
  321. .catch(err => {
  322. showToast(err.message || '投票失败!');
  323. resolve(true);
  324. });
  325. });
  326. },
  327. });
  328. };
  329. const toDetal = file => {
  330. location.href = file;
  331. };
  332. const cancel = async () => {
  333. const w = 900 * window.devicePixelRatio;
  334. const w1 = w * 0.8;
  335. const fs = w / 20;
  336. const bs = w / 15;
  337. const title_text = selectItem.value.name.replace(/《|》/g, '');
  338. const title =
  339. title_text.length >= 16
  340. ? title_text.split('').splice(0, 13).join('') + '...'
  341. : title_text;
  342. const qt = await QRCode.toDataURL(
  343. [location.protocol, '//', location.host, '/finance.html?id=', selectItem.value.id].join('')
  344. );
  345. const layers = {
  346. list: [
  347. {
  348. type: 'image',
  349. src: 'https://cxzx.smcic.net/topic/tool/img/0.jpg',
  350. width: w,
  351. height: w,
  352. x: 0,
  353. y: 0,
  354. },
  355. {
  356. type: 'image',
  357. src: create_url_image(selectItem.value.workTitle),
  358. width: w1,
  359. height: (w1 / 16) * 9,
  360. x: w / 10,
  361. y: bs,
  362. },
  363. {
  364. type: 'text',
  365. color: '#ffffff',
  366. font: fs + 'px Arial',
  367. text: title,
  368. x: w / 10,
  369. y: (w1 / 16) * 9 + bs * 2,
  370. },
  371. {
  372. type: 'text',
  373. color: '#ffffff',
  374. font: fs + 'px Arial',
  375. text: '快来为我投上一票把!',
  376. x: w / 10,
  377. y: (w1 / 16) * 9 + bs * 3,
  378. },
  379. {
  380. type: 'image',
  381. src: qt,
  382. width: w / 5,
  383. height: w / 5,
  384. x: w1 + bs - w / 5,
  385. y: (w1 / 16) * 9 + bs * 4,
  386. },
  387. ],
  388. width: w,
  389. height: w,
  390. };
  391. imgObj.value = await canvasToImage(layers);
  392. };
  393. let time = null;
  394. watch(search, res => {
  395. if (time) clearTimeout(time);
  396. time = setTimeout(() => {
  397. const rex = res ? new RegExp(res) : /[.\n]*/;
  398. for (let o = 0; o < orilist.value.length; o++) {
  399. const v = orilist.value[o];
  400. for (let i = 0; i < v.length; i++) {
  401. const vv = v[i];
  402. vv.noShow = !rex.test(vv.name) && !rex.test(vv.university);
  403. }
  404. }
  405. clearTimeout(time);
  406. time = null;
  407. }, 200);
  408. });
  409. const getCode = () => {
  410. // 获取验证码
  411. if (!/1[0-9]{10}/.test(login.phone)) {
  412. showToast('请输入正确的手机号');
  413. return;
  414. }
  415. getSendVerifyCode({
  416. phone: login.phone,
  417. }).then(() => {
  418. codePhoen.value = 60000;
  419. });
  420. };
  421. const toupiao = function () {
  422. let names = [];
  423. let phones = [];
  424. for (let i = 0; i < ids.value.length; i++) {
  425. const v = ids.value[i] || {};
  426. if (!v.select) continue;
  427. names.push(v.name);
  428. phones.push(v.phone);
  429. }
  430. if (!phones.length) return showToast('请选择您中意的候选人');
  431. if (phones.length > 10) return showToast('最多只能投10人');
  432. if (!login.phone || login.phone.length < 11) return (showLogin.value = true); //如果没有手机号提示登录
  433. showConfirmDialog({
  434. message: '确定投票给' + names.join(',') + '么?',
  435. beforeClose: type => {
  436. if (type !== 'confirm') return true;
  437. return new Promise(resolve => {
  438. getVote({
  439. target: phones.join(','),
  440. source: login.phone,
  441. client: config.isShanShiPin ? '闪视频' : '其他',
  442. operateId,
  443. })
  444. .then(() => {
  445. showToast('投票成功!');
  446. resolve(true);
  447. })
  448. .catch(err => {
  449. showToast(err.message || '投票失败!');
  450. resolve(true);
  451. });
  452. });
  453. },
  454. });
  455. };
  456. const create_url_image = url => {
  457. if (/\.mp4$/.test(url))
  458. url = url + '?x-oss-process=video/snapshot,t_4000,f_jpg';
  459. return url;
  460. };
  461. const beforeClose = () => {
  462. if (!login.phone || !login.code) return false;
  463. getTimes({
  464. phone: login.phone,
  465. }).then(r => {
  466. config.times = r.times - t >= 0;
  467. verifyCode({
  468. phone: login.phone,
  469. code: login.code,
  470. })
  471. .then(r => {
  472. localStorage.setItem('financePhone', login.phone);
  473. localStorage.setItem('financeToken', r.token);
  474. return true;
  475. })
  476. .catch(() => false);
  477. });
  478. return true;
  479. };
  480. const showGuize = () => {
  481. showDialog({
  482. title: '投票规则',
  483. messageAlign: 'left',
  484. confirmButtonText: '关闭',
  485. message: () => (
  486. <div class='art'>
  487. <p>
  488. 1、投票主题:“传播金融正能量 争做金融好网民”2023陕西金融好网民网络票选
  489. </p>
  490. <p>2、投票时间:2023年12月15日9:00至12月21日9:00(共7天)。</p>
  491. <p>
  492. 3、投票内容:网络短视频作品137件。(分为党建引领、金融知识类、风险防范、金融服务四大类),优秀宣讲员作品27件。
  493. </p>
  494. <p>
  495. 4、投票形式:通过微信H5或者闪视频APP进行线上投票,可分享拉票,助力你支持的作品、宣讲员登上排名。
  496. </p>
  497. <p>
  498. 5、投票须知:每个用户每日的票数固定,即可分散投票,也可集中投票,每人每天10票。
  499. </p>
  500. <p>
  501. 本次投票活动公开透明,排行榜实时更新,如有违法一经查实将取消参赛资格,最终解释权归主办方所有。
  502. </p>
  503. </div>
  504. ),
  505. });
  506. };
  507. const paiming = () => {
  508. if (!config.isShanShiPin) return showToast('下载闪视频查看投票结果。');
  509. getRankList(() => {
  510. showPaihang.value = true;
  511. });
  512. };
  513. const formmate = num => {
  514. if (num === 0) return num;
  515. let N = num || 0;
  516. switch (true) {
  517. case N >= 10000:
  518. N = (N / 10000).toFixed(2) - 0 + '万';
  519. break;
  520. case N >= 100000000:
  521. N = (N / 100000000).toFixed(2) - 0 + '亿';
  522. break;
  523. }
  524. return N;
  525. };
  526. const clickShowVideo = item => {
  527. selectItem.value = item || {};
  528. hxrDialog.value = true;
  529. };
  530. const close = () => {};
  531. </script>
  532. <style lang="scss">
  533. .finance {
  534. $color: #38379f;
  535. width: 100vw;
  536. padding-bottom: 120px;
  537. background-color: #e0f2fa;
  538. min-height: 100vh;
  539. .van-cell__value {
  540. color: #000;
  541. }
  542. .act {
  543. position: absolute;
  544. top: 2px;
  545. left: 2px;
  546. z-index: 1;
  547. }
  548. .text {
  549. padding: 10px 16px;
  550. }
  551. .guize {
  552. background-color: $color;
  553. position: fixed;
  554. right: 0;
  555. top: 8em;
  556. color: #fff;
  557. font-weight: 400;
  558. font-size: 16px;
  559. padding: 3px 5px 3px 15px;
  560. border-top-left-radius: 1em;
  561. border-bottom-left-radius: 1em;
  562. z-index: 2;
  563. }
  564. .SilkList {
  565. height: calc(50vh - 60px);
  566. overflow-y: auto;
  567. }
  568. .btn {
  569. text-align: center;
  570. color: #fff;
  571. position: fixed;
  572. line-height: 2.5em;
  573. height: 2.5em;
  574. left: 5%;
  575. width: 90%;
  576. background-color: $color;
  577. z-index: 2;
  578. padding: 3px 0;
  579. border-radius: 5px;
  580. }
  581. .videoEle {
  582. width: 100vw;
  583. position: fixed;
  584. background-color: #000;
  585. top: 50%;
  586. z-index: 21;
  587. transform: translateY(-50%);
  588. }
  589. .van-overlay {
  590. .video {
  591. width: 100vw;
  592. height: 100vh;
  593. top: 0;
  594. position: fixed;
  595. .videoText {
  596. color: #fff;
  597. position: absolute;
  598. bottom: 0;
  599. padding: 0.5em;
  600. text-indent: 2em;
  601. }
  602. }
  603. .close {
  604. position: absolute;
  605. top: 10px;
  606. right: 10px;
  607. z-index: 22;
  608. color: #fff;
  609. font-size: 20px;
  610. }
  611. }
  612. .unit {
  613. font-size: 14px;
  614. color: #646566;
  615. }
  616. .zhezhao {
  617. position: fixed;
  618. top: 0;
  619. left: 0;
  620. width: 100vw;
  621. height: 100vh;
  622. background-color: rgba(0, 0, 0, 0.8);
  623. z-index: 10;
  624. .zhezhao_img {
  625. position: absolute;
  626. top: 50%;
  627. left: 50%;
  628. transform: translate(-50%, -50%);
  629. width: 80vw;
  630. }
  631. }
  632. }
  633. </style>