Browse Source

抽奖功能

lighter 1 year ago
parent
commit
92eae97489

+ 24 - 0
src/api/single-page/lottery.js

@@ -0,0 +1,24 @@
+import request from '../../utils/request'
+
+export function selectLotteryUsers() {
+    return request({
+        url: '/lottery/selectLotteryUsers',
+        method: 'get',
+    })
+}
+
+export function chooseWinner(data) {
+    return request({
+        url: '/lottery/chooseWinner',
+        method: 'post',
+        data
+    })
+}
+
+export function recordLotteryResult(data) {
+    return request({
+        url: '/lottery/recordLotteryResult',
+        method: 'post',
+        data
+    })
+}

BIN
src/assets/lottery/happyAside.jpg


BIN
src/assets/lottery/happyAside2.jpg


BIN
src/assets/lottery/happyNewYear.jpg


+ 5 - 0
src/router/modules/dashboard.js

@@ -25,6 +25,11 @@ const route = [
         hideMenu: true,
         meta: {title: '今日号源', hideTabs: true},
     },
+    {
+        path: '/lottery',
+        component: createNameComponent(() => import('@/views/single-page/Lottery.vue')),
+        meta: {title: '抽奖'}
+    },
     {
         path: '/myEmrEditor/:pat?/:refresh?',
         component: createNameComponent(() => import('@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/Home.vue')),

+ 360 - 0
src/views/single-page/Lottery.vue

@@ -0,0 +1,360 @@
+<template>
+  <page-layer>
+    <template #aside>
+      <div style="width: 300px;height: 100%;
+            background-size: 100% 100%;
+            background-repeat: no-repeat;
+            background-image: url('/src/assets/lottery/happyAside.jpg')">
+
+        <div style="height: 20%"></div>
+
+        <div style="width: 80%; margin-left: 10%">
+          <el-upload
+              drag
+              action="http://172.16.30.26:8706/lottery/uploadLotteryUsers"
+              :show-file-list="false"
+              :on-success="uploadSuccess"
+          >
+            <el-icon class="el-icon--upload" style="color: white">
+              <upload-filled/>
+            </el-icon>
+            <div style="color: white">
+              导入抽奖名单
+            </div>
+          </el-upload>
+        </div>
+
+        <div style="margin: 24px 0 12px 30px; font-size: 18px; color: #c4c4c4;">
+          抽奖详情设置
+          <span class="reset-pool" @click="initLotteryPool">重置为初始状态</span>
+        </div>
+        <div style="margin-left: 50px">
+          <div v-for="item in lotteryPool" :key="item.code">
+            <div style="display: flex; line-height: 32px; font-size: 16px">
+              <div style="color: white; font-weight: bold">{{ item.name }}:</div>
+              <div style="width: 60px; color: lightgreen; font-weight: bold">{{ item.amount }} 份</div>
+              <div style="width: 40px; text-align: right">
+                <el-button circle icon="Edit" @click="editRule(item)"></el-button>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div style="width: 300px; text-align: center; margin-top: 30px">
+          <el-button style="width: 200px" size="large" :disabled="duringLottery"
+                     type="danger" icon="Star" @click="startLottery">开始抽奖
+          </el-button>
+        </div>
+
+<!--        <div style="color: rgb(42,252,87)"></div>-->
+
+      </div>
+    </template>
+    <template #main>
+      <div style="position:relative;width: 100%; height: 100%;
+        overflow-y: hidden;
+        background-size: 100% 100%;
+        background-repeat: no-repeat;
+        background-image: url('/src/assets/lottery/happyNewYear.jpg')">
+
+        <div style="height: 19%;width: 100%;display: flex;align-items: end;
+              justify-content: center;color: white;font-size: 56px;font-weight: bold">
+          当前奖项:{{ currentLotteryName }}
+        </div>
+
+        <div style="height: 60%; width: 100%; display: flex;align-items: center;
+              justify-content: center;color: white;font-size: 152px;font-weight: bold">
+          <span id="winner-div">{{ currentUserName }}</span>
+        </div>
+
+        <div style="position: absolute; top:0;right:0;bottom: 0">
+          <canvas id="canvas"></canvas>
+        </div>
+      </div>
+    </template>
+  </page-layer>
+</template>
+<script setup>
+import PageLayer from "@/layout/PageLayer.vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {selectLotteryUsers, chooseWinner, recordLotteryResult} from "@/api/single-page/lottery";
+
+const lotteryPool = ref([])
+const lotteryUsers = ref([])
+const duringLottery = ref(false)
+
+const carousel = ref(null)
+
+const winner = reactive({
+  index: -1,
+  lotteryCode: '',
+  userCodeRs: '',
+  userName: ''
+})
+
+const currentLotteryName = ref('三等奖')
+const currentUserName = ref('等待抽奖')
+
+function uploadSuccess(response) {
+  ElMessage({
+    type: 'success',
+    message: '导入成功。',
+    duration: 2500
+  })
+  lotteryUsers.value = response.data
+}
+
+function initLotteryPool() {
+  lotteryPool.value = [
+    {code: 0, name: '特等奖', amount: 3, round: 0},
+    {code: 1, name: '一等奖', amount: 3, round: 0},
+    {code: 2, name: '二等奖', amount: 5, round: 0},
+    {code: 3, name: '三等奖', amount: 10, round: 0},
+  ]
+  localStorage.setItem('lotteryPool', JSON.stringify(lotteryPool.value))
+}
+
+let carouselInterval = null
+
+function moveToWinner() {
+  let timer = 0;
+  carouselInterval = setInterval(() => {
+    timer += 100
+    if (timer >= 5000) {
+      winnerExposed()
+      return
+    }
+    let index = Math.floor(Math.random() * lotteryUsers.value.length);
+    let tempUser = lotteryUsers.value[index]
+    currentUserName.value = tempUser.codeRs + ' - ' + tempUser.name
+    if (tempUser.codeRs === winner.userCodeRs && timer >= 3000) {
+      winnerExposed()
+    }
+  }, 100)
+
+}
+
+function winnerExposed() {
+  recordLotteryResult(winner).then(() => {
+
+    currentUserName.value = winner.userCodeRs + ' - ' + winner.userName
+    duringLottery.value = false;
+    clearInterval(carouselInterval)
+
+    startFirework()
+
+    const element = document.getElementById('winner-div')
+    element.style.transitionDuration = '.5s'
+    element.style.transitionTimingFunction = 'ease-in-out'
+    element.style.transformOrigin = 'center center'
+
+    element.style.transform = `scale(1.3, 1.3)`;
+
+    // 完成后重置样式
+    setTimeout(() => {
+      element.style.removeProperty('transition-duration');
+      element.style.removeProperty('transition-timing-function');
+      element.style.removeProperty('transform-origin');
+      element.style.removeProperty('transform');
+
+    }, 600);
+  })
+
+}
+
+function startLottery() {
+  duringLottery.value = true
+  getWinnableLottery().then(item => {
+    chooseWinner(item).then(res => {
+      winner.lotteryCode = item.code
+      winner.userCodeRs = res.codeRs
+      winner.userName = res.name
+      moveToWinner()
+    })
+  }).catch(() => {
+    duringLottery.value = false
+    ElMessageBox.alert('所有奖项均已抽完!', '提示', {
+      type: 'error',
+      showCancelButton: false
+    })
+  })
+}
+
+function getWinnableLottery() {
+  return new Promise((resolve, reject) => {
+    for (let i = lotteryPool.value.length - 1; i >= 0; i--) {
+      const item = lotteryPool.value[i]
+      if (item.amount > 0) {
+        item.amount -= 1
+        item.round += 1
+        currentLotteryName.value = item.name
+        localStorage.setItem('lotteryPool', JSON.stringify(lotteryPool.value))
+        resolve(item)
+        return
+      }
+    }
+    reject()
+  })
+}
+
+function editRule(item) {
+  ElMessageBox.prompt(`设置${item.name}数量:`, '提示', {
+    type: "warning",
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    inputPattern: /^100$|^(\d|[1-9]\d)$/,
+    inputErrorMessage: '请输入 0至100 的整数',
+  }).then(({value}) => {
+    item.amount = Number.parseInt(value)
+    localStorage.setItem('lotteryPool', JSON.stringify(lotteryPool.value))
+  })
+}
+
+onMounted(() => {
+  let storage = localStorage.getItem('lotteryPool')
+  if (storage) {
+    lotteryPool.value = JSON.parse(storage);
+  } else {
+    initLotteryPool()
+  }
+  selectLotteryUsers().then(res => {
+    lotteryUsers.value = res
+    initFireworkCanvas()
+  })
+})
+
+
+
+
+
+let canvas, canvasContext, w, h, particles = [], probability = 0.04,
+    xPoint, yPoint;
+const fireworkRunnable = ref(false)
+
+function initFireworkCanvas() {
+  canvas = document.getElementById("canvas");
+  canvasContext = canvas.getContext("2d");
+  resizeCanvas();
+}
+
+function resizeCanvas() {
+  if (canvas) {
+    w = canvas.width = window.innerWidth;
+    h = canvas.height = window.innerHeight;
+  }
+}
+
+function startFirework() {
+  fireworkRunnable.value = true
+  window.requestAnimationFrame(updateWorld);
+  setTimeout(() => {
+    fireworkRunnable.value = false
+    canvasContext.clearRect(0, 0, canvas.width, canvas.height);
+  }, 4000)
+}
+
+function updateWorld() {
+  if (fireworkRunnable.value) {
+    update();
+    paint();
+    window.requestAnimationFrame(updateWorld);
+  }
+}
+
+function update() {
+  if (particles.length < 500 && Math.random() < probability) {
+    createFirework();
+  }
+  let alive = [];
+  for (let i = 0; i < particles.length; i++) {
+    if (particles[i].move()) {
+      alive.push(particles[i]);
+    }
+  }
+  particles = alive;
+  canvasContext.clearRect(0, 0, canvas.width, canvas.height);
+}
+
+function paint() {
+  canvasContext.globalCompositeOperation = 'source-over';
+  canvasContext.fillStyle = "transparent";
+
+  canvasContext.fillRect(0, 0, w, h);
+  canvasContext.globalCompositeOperation = 'lighter';
+  for (let i = 0; i < particles.length; i++) {
+    particles[i].draw(canvasContext);
+  }
+}
+
+function createFirework() {
+  xPoint = Math.random() * (w - 200) + 100;
+  yPoint = Math.random() * (h - 200) + 100;
+  let nFire = Math.floor(Math.random() * 100) + 100;
+
+  let c1 = 'rgb(255,' + Math.floor((Math.random() * 255)) + ',' + Math.floor((Math.random() * 255)) + ')';
+  let c2 = 'rgb(' + Math.floor((Math.random() * 255)) +  ',255,' + Math.floor((Math.random() * 255)) + ')';
+  let c3 = 'rgb(' + Math.floor((Math.random() * 255)) + ',' + Math.floor((Math.random() * 255)) + ',255)';
+  let c=[c1,c2,c3]
+
+  for (let i = 0; i < nFire; i++) {
+    let particle = new Particle();
+    particle.color = c[Math.floor(Math.random() * 3)];
+    let vy = Math.sqrt(25 - particle.vx * particle.vx);
+    if (Math.abs(particle.vy) > vy) {
+      particle.vy = particle.vy > 0 ? vy : -vy;
+    }
+    particles.push(particle);
+  }
+}
+
+function Particle() {
+  this.w = this.h = Math.random() * 6 + 1;
+  this.x = xPoint - this.w / 2;
+  this.y = yPoint - this.h / 2;
+  this.vx = (Math.random() - 0.5) * 10;
+  this.vy = (Math.random() - 0.5) * 10;
+  this.alpha = Math.random() * .5 + .5;
+  this.color = '';
+}
+
+Particle.prototype = {
+  gravity: 0.05,
+  move: function () {
+    this.x += this.vx;
+    this.vy += this.gravity;
+    this.y += this.vy;
+    this.alpha -= 0.01;
+    return !(this.x <= -this.w || this.x >= screen.width ||
+        this.y >= screen.height ||
+        this.alpha <= 0);
+  },
+  draw: function (c) {
+    c.save();
+    c.beginPath();
+    c.translate(this.x + this.w / 2, this.y + this.h / 2);
+    c.arc(0, 0, this.w, 0, Math.PI * 2);
+    c.fillStyle = this.color;
+    c.globalAlpha = this.alpha;
+    c.closePath();
+    c.fill();
+    c.restore();
+  }
+}
+
+
+</script>
+
+<style scoped>
+:deep(.el-upload-dragger) {
+  background-color: transparent;
+}
+
+.reset-pool {
+  color: red;
+  font-size: 12px
+}
+
+.reset-pool:hover {
+  cursor: pointer;
+  text-decoration: underline;
+}
+</style>