浏览代码

计时器

黄琪勇 1 年之前
父节点
当前提交
813da55cc3
共有 27 个文件被更改,包括 561 次插入21 次删除
  1. 二进制
      src/components/layout/images/close.png
  2. 20 2
      src/components/layout/index.module.less
  3. 29 4
      src/components/layout/index.tsx
  4. 474 0
      src/components/timerMeter/TimerMeter.vue
  5. 二进制
      src/components/timerMeter/img/bg.png
  6. 二进制
      src/components/timerMeter/img/downBtn.png
  7. 二进制
      src/components/timerMeter/img/midBg.png
  8. 二进制
      src/components/timerMeter/img/pause.png
  9. 二进制
      src/components/timerMeter/img/play.png
  10. 二进制
      src/components/timerMeter/img/resetting.png
  11. 二进制
      src/components/timerMeter/img/timeNum.png
  12. 二进制
      src/components/timerMeter/img/timeUpBom.png
  13. 二进制
      src/components/timerMeter/img/upBtn.png
  14. 2 0
      src/components/timerMeter/index.ts
  15. 0 0
      src/components/timerMeterOld/components/countdown.tsx
  16. 0 0
      src/components/timerMeterOld/components/positive.tsx
  17. 0 0
      src/components/timerMeterOld/images/add.png
  18. 0 0
      src/components/timerMeterOld/images/minus.png
  19. 0 0
      src/components/timerMeterOld/images/playing.png
  20. 0 0
      src/components/timerMeterOld/images/suspend.png
  21. 0 0
      src/components/timerMeterOld/index.module.less
  22. 0 0
      src/components/timerMeterOld/index.tsx
  23. 2 2
      src/components/timerMeterOld/modals/flipper.vue
  24. 二进制
      src/components/timerMeterOld/timer.wav
  25. 二进制
      src/views/attend-class/image/close.png
  26. 21 1
      src/views/attend-class/index.module.less
  27. 13 12
      src/views/attend-class/index.tsx

二进制
src/components/layout/images/close.png


+ 20 - 2
src/components/layout/index.module.less

@@ -278,8 +278,26 @@
   }
 }
 
-
-
+:global{
+  .timerMeterConBoxClass_drag{
+    box-shadow:initial;
+    .topDragDom{
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 100%;
+      height: 40px;
+    }
+  }
+}
+.timerMeterClose{
+  position: absolute;
+  width: 31px;
+  height: 32px;
+  top: 16px;
+  right: -31px;
+  cursor: pointer;
+}
 .propWrap {
   background-color: var(--n-color);
   border-radius: 16px;

+ 29 - 4
src/components/layout/index.tsx

@@ -14,6 +14,7 @@ import styles from './index.module.less';
 import { NButton, NImage, NModal, NPopover, NSpace, useDialog } from 'naive-ui';
 import Moveable from 'moveable';
 import toolStartClass from './images/toolStartClass.png';
+import timerMeterClose from './images/close.png';
 import toolbox from './images/toolbox.png';
 import setTimeIcon from './images/setTimeIcon.png';
 import beatIcon from './images/beatIcon.png';
@@ -43,6 +44,9 @@ import AttendClass from '/src/views/prepare-lessons/model/attend-class';
 import Pen from '/src/views/attend-class/component/tools/pen';
 import study from '/src/views/home/components/study';
 import TheAuth from '../TheAuth';
+import useDrag from '@/hooks/useDrag';
+import { getGuidanceShow } from '@/hooks/useDrag/useDragGuidance';
+import { useUserStore } from '@/store/modules/users';
 export default defineComponent({
   name: 'layoutView',
   setup() {
@@ -606,6 +610,21 @@ export default defineComponent({
       penShow: false,
       whiteboardShow: false
     });
+    /* 弹窗加拖动 */
+    // 引导页
+    getGuidanceShow();
+    // 选择课件弹窗
+    const users = useUserStore();
+    const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
+    const timerMeterConDragData = useDrag(
+      [
+        `${timerMeterConBoxClass} .timeBomCon .bom_drag`,
+        `${timerMeterConBoxClass} .topDragDom`
+      ],
+      timerMeterConBoxClass,
+      showModalTime,
+      users.info.id
+    );
     return () => (
       <div class={[styles.wrap, 'wrap']}>
         <div>
@@ -796,11 +815,17 @@ export default defineComponent({
         </NModal>
         <NModal
           v-model:show={showModalTime.value}
-          class={['modalTitle background']}
-          title={'计时器'}
-          preset="card"
-          style={{ width: px2vw(772) }}>
+          class={timerMeterConBoxClass}
+          style={timerMeterConDragData.styleDrag.value}>
           <div>
+            <img
+              class={styles.timerMeterClose}
+              src={timerMeterClose}
+              onClick={() => {
+                showModalTime.value = false;
+              }}
+            />
+            <div class="topDragDom"></div>
             <TimerMeter></TimerMeter>
           </div>
         </NModal>

+ 474 - 0
src/components/timerMeter/TimerMeter.vue

@@ -0,0 +1,474 @@
+<template>
+  <div class="timerMeter">
+    <div class="timeCon">
+      <div class="timeBox">
+        <div class="timeInput mmTimeIpt" :class="{ timeFocus: mmFocus }">
+          <div
+            class="timeInputBtn"
+            v-if="timeType === 'countdown'"
+            :class="{ palyDisabled: playState === 'play' }"
+          >
+            <img src="./img/upBtn.png" @click="handleMMTime(10)" />
+            <img src="./img/upBtn.png" @click="handleMMTime(1)" />
+          </div>
+          <div class="timeInputBox">
+            <n-input-number
+              :disabled="timeType === 'countup' || playState === 'play'"
+              @blur="handlemmblur"
+              @focus="handlemmfocus"
+              placeholder=""
+              v-model:value="mmValue"
+              :format="(value: number | null)=>{
+                return value?(value<10?`0${value}`:value+''):'00'
+              }"
+              :min="0"
+              :max="59"
+              :show-button="false"
+            />
+          </div>
+          <div
+            class="timeInputBtn"
+            v-if="timeType === 'countdown'"
+            :class="{ palyDisabled: playState === 'play' }"
+          >
+            <img src="./img/downBtn.png" @click="handleMMTime(-10)" />
+            <img src="./img/downBtn.png" @click="handleMMTime(-1)" />
+          </div>
+        </div>
+        <img class="midBg" src="./img/midBg.png" />
+        <div class="timeInput ssTimeIpt" :class="{ timeFocus: ssFocus }">
+          <div
+            class="timeInputBtn"
+            v-if="timeType === 'countdown'"
+            :class="{ palyDisabled: playState === 'play' }"
+          >
+            <img src="./img/upBtn.png" @click="handleSSTime(10)" />
+            <img src="./img/upBtn.png" @click="handleSSTime(1)" />
+          </div>
+          <div class="timeInputBox">
+            <n-input-number
+              :disabled="timeType === 'countup' || playState === 'play'"
+              @blur="handlessblur"
+              @focus="handlessfocus"
+              placeholder=""
+              v-model:value="ssValue"
+              :format="(value: number | null)=>{
+                return value?(value<10?`0${value}`:value+''):'00'
+              }"
+              :min="0"
+              :max="59"
+              :show-button="false"
+            />
+          </div>
+          <div
+            class="timeInputBtn"
+            v-if="timeType === 'countdown'"
+            :class="{ palyDisabled: playState === 'play' }"
+          >
+            <img src="./img/downBtn.png" @click="handleSSTime(-10)" />
+            <img src="./img/downBtn.png" @click="handleSSTime(-1)" />
+          </div>
+        </div>
+      </div>
+      <div class="timeTools">
+        <div class="timeType">
+          <div class="timeTypeBox">
+            <div
+              class="timeTypeName"
+              :class="{ timeTypeActive: timeType === 'countdown' }"
+              @click="handleChangeTimeType('countdown')"
+            >
+              倒计时
+            </div>
+            <div
+              class="timeTypeName"
+              :class="{ timeTypeActive: timeType === 'countup' }"
+              @click="handleChangeTimeType('countup')"
+            >
+              正计时
+            </div>
+          </div>
+        </div>
+        <div class="playState">
+          <img
+            v-if="playState === 'play'"
+            @click="handlePause"
+            src="./img/play.png"
+          />
+          <img v-else @click="handlePlay" src="./img/pause.png" />
+        </div>
+        <div class="resetting">
+          <img @click="handleInitTime" src="./img/resetting.png" />
+        </div>
+      </div>
+    </div>
+    <div class="timeBomCon">
+      <div
+        v-if="timeType === 'countdown'"
+        class="timeDownBom"
+        :class="{ palyDisabled: playState === 'play' }"
+      >
+        <div @click="handleTimeNum(300)">5分</div>
+        <div @click="handleTimeNum(60)">1分</div>
+        <div @click="handleTimeNum(30)">30秒</div>
+        <div @click="handleTimeNum(5)">5秒</div>
+      </div>
+      <img v-else class="timeUpBom" src="./img/timeUpBom.png" />
+      <Dragbom />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch, onUnmounted } from 'vue';
+import { NInputNumber } from 'naive-ui';
+import soundWav from './timer.wav';
+import Dragbom from '@/hooks/useDrag/dragbom';
+
+const soundVIdeo = new Audio(soundWav);
+onUnmounted(() => {
+  clearInterval(_time);
+  soundVIdeo.pause();
+});
+
+// input 状态
+const mmFocus = ref(false);
+const ssFocus = ref(false);
+function handlemmblur() {
+  mmFocus.value = false;
+}
+function handlessblur() {
+  ssFocus.value = false;
+}
+function handlemmfocus() {
+  mmFocus.value = true;
+}
+function handlessfocus() {
+  ssFocus.value = true;
+}
+
+// 计时类型
+const timeType = ref<'countdown' | 'countup'>('countdown');
+function handleChangeTimeType(type: 'countdown' | 'countup') {
+  if (timeType.value === type) {
+    return;
+  }
+  timeType.value = type;
+  handleInitTime();
+}
+// 清空状态
+function handleInitTime() {
+  handlePause();
+  timeNum.value = 0;
+  mmValue.value = 0;
+  ssValue.value = 0;
+}
+// 播放状态
+const playState = ref<'pause' | 'play'>('pause');
+//统计时间
+const timeNum = ref(0);
+const mmValue = ref(0);
+const ssValue = ref(0);
+watch(timeNum, () => {
+  mmValue.value = Math.floor(timeNum.value / 60);
+  ssValue.value = timeNum.value % 60;
+});
+function handleMMTime(num: number) {
+  if (playState.value === 'play') return;
+  let newMmValue = mmValue.value + num;
+  if (newMmValue > 59) {
+    newMmValue = 59;
+  }
+  if (newMmValue < 0) {
+    newMmValue = 0;
+  }
+  mmValue.value = newMmValue;
+}
+function handleSSTime(num: number) {
+  if (playState.value === 'play') return;
+  let newSsValue = ssValue.value + num;
+  if (newSsValue > 59) {
+    newSsValue = 59;
+  }
+  if (newSsValue < 0) {
+    newSsValue = 0;
+  }
+  ssValue.value = newSsValue;
+}
+function handleTimeNum(num: number) {
+  if (playState.value === 'play') return;
+  timeNum.value = num;
+}
+// 开始
+function handlePlay() {
+  if (timeType.value === 'countdown') {
+    const timeNumNow = mmValue.value * 60 + ssValue.value;
+    if (timeNumNow <= 0) {
+      return;
+    }
+    timeNum.value = timeNumNow;
+    playState.value = 'play';
+    handleCountdownStart();
+  } else {
+    if (timeNum.value >= 3599) {
+      return;
+    }
+    playState.value = 'play';
+    handleCountUpStart();
+  }
+}
+// 暂停
+function handlePause() {
+  clearInterval(_time);
+  playState.value = 'pause';
+  soundVIdeo.pause();
+}
+
+let _time: NodeJS.Timer;
+// 倒计时
+function handleCountdownStart() {
+  soundVIdeo.currentTime = 0;
+  if (timeNum.value <= 4) {
+    soundVIdeo.currentTime = 4 - timeNum.value;
+    soundVIdeo.play();
+  }
+  _time = setInterval(() => {
+    timeNum.value--;
+    if (timeNum.value === 4) {
+      soundVIdeo.play();
+    }
+    if (timeNum.value <= 0) {
+      handlePause();
+    }
+  }, 1000);
+}
+// 正计时
+function handleCountUpStart() {
+  _time = setInterval(() => {
+    timeNum.value++;
+    if (timeNum.value >= 3599) {
+      handlePause();
+    }
+  }, 1000);
+}
+</script>
+
+<style lang="less" scoped>
+.timerMeter {
+  width: 620px;
+  height: 316px;
+  background: url('./img/bg.png') no-repeat;
+  background-size: 100% 100%;
+  padding: 46px 25px 0 24px;
+  .timeCon {
+    display: flex;
+    justify-content: space-between;
+    .timeBox {
+      width: 432px;
+      height: 190px;
+      background: #3a3939;
+      box-shadow: 0px 3px 0px 0px rgba(255, 255, 255, 0.46),
+        0px -1px 3px 0px rgba(255, 255, 255, 0.65),
+        inset 0px 3px 8px 0px rgba(0, 0, 0, 0.5);
+      border-radius: 53px;
+      padding: 20px 46px 20px 42px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      .midBg {
+        width: 10px;
+        height: 36px;
+      }
+      .timeInput {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        width: 132px;
+        height: 100%;
+        position: relative;
+        padding: 6px 0 2px;
+        &.timeFocus::after {
+          content: '';
+          width: 1px;
+          height: 94px;
+          position: absolute;
+          background-color: #fff;
+          left: -10px;
+          top: 50%;
+          transform: translateY(-50%);
+          animation: blink 1.1s infinite;
+          @keyframes blink {
+            0% {
+              opacity: 1;
+            }
+            50% {
+              opacity: 0;
+            }
+            100% {
+              opacity: 1;
+            }
+          }
+        }
+        &.mmTimeIpt,
+        &.ssTimeIpt {
+          &::before {
+            content: 'M';
+            position: absolute;
+            font-weight: bold;
+            font-size: 16px;
+            color: #ffffff;
+            top: 9px;
+            right: -16px;
+          }
+          &.ssTimeIpt::before {
+            right: -10px;
+            content: 'S' !important;
+          }
+        }
+        .timeInputBox {
+          flex-grow: 1;
+          overflow: hidden;
+          :deep(.n-input-number) {
+            height: 100%;
+            .n-input {
+              height: 100%;
+              --n-height: 100% !important;
+              --n-color-focus: initial !important;
+              --n-color: initial !important;
+              --n-padding-left: initial !important;
+              --n-padding-right: initial !important;
+              --n-color-disabled: initial !important;
+              --n-caret-color: #3a3939 !important;
+              .n-input__border,
+              .n-input__state-border {
+                display: none;
+              }
+              .n-input__input-el {
+                text-align: center;
+                font-family: DINAlternate, DINAlternate !important;
+                font-weight: bold !important;
+                font-size: 126px !important;
+                color: #ffffff !important;
+              }
+              &.n-input--disabled {
+                cursor: initial;
+                .n-input__input-el {
+                  cursor: initial;
+                }
+              }
+            }
+          }
+        }
+        .timeInputBtn {
+          width: 78px;
+          flex-shrink: 0;
+          display: flex;
+          justify-content: space-between;
+          & > img {
+            cursor: pointer;
+            width: 12px;
+            height: 8px;
+          }
+          &.palyDisabled > img {
+            opacity: 0.4;
+            cursor: initial;
+          }
+        }
+      }
+    }
+    .timeTools {
+      flex-shrink: 0;
+      width: 138px;
+      .timeType {
+        width: 100%;
+        height: 40px;
+        background: rgba(229, 229, 299, 0.42);
+        border-radius: 40px;
+        padding: 6px;
+        .timeTypeBox {
+          width: 100%;
+          height: 100%;
+          background: #e3e3e3;
+          border-radius: 14px;
+          display: flex;
+          .timeTypeName {
+            width: 50%;
+            height: 100%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            font-weight: 600;
+            font-size: 14px;
+            color: #7c7c7c;
+            border-radius: 14px;
+            cursor: pointer;
+            &.timeTypeActive {
+              color: #fff;
+              background: linear-gradient(90deg, #999999 0%, #7c7c7c 100%);
+            }
+          }
+        }
+      }
+      .playState,
+      .resetting {
+        margin-top: 12px;
+        width: 100%;
+        display: flex;
+        justify-content: center;
+        & > img {
+          cursor: pointer;
+          width: 78px;
+          height: 59px;
+        }
+      }
+    }
+  }
+  .timeBomCon {
+    width: 432px;
+    height: calc(100% - 190px);
+    display: flex;
+    justify-content: center;
+    position: relative;
+    .timeDownBom {
+      width: 100%;
+      height: 56px;
+      display: flex;
+      justify-content: space-between;
+      padding: 18px 30px 0;
+      & > div {
+        width: 85px;
+        height: 38px;
+        background: url('./img/timeNum.png') no-repeat;
+        background-size: 100% 100%;
+        font-weight: 600;
+        font-size: 16px;
+        color: #7c7c7c;
+        line-height: 38px;
+        text-align: center;
+        cursor: pointer;
+        z-index: 2;
+      }
+      &.palyDisabled > div {
+        opacity: 0.4;
+        cursor: initial;
+      }
+    }
+    .timeUpBom {
+      margin-top: 25px;
+      width: 332px;
+      height: 21px;
+    }
+    :global(.timerMeter .timeBomCon .bom_drag) {
+      position: absolute;
+      left: -24px;
+      bottom: 0;
+      width: 620px;
+      height: 40px;
+      z-index: 1;
+    }
+    :global(.timerMeter .timeBomCon .bom_drag > div) {
+      display: none;
+    }
+  }
+}
+</style>

二进制
src/components/timerMeter/img/bg.png


二进制
src/components/timerMeter/img/downBtn.png


二进制
src/components/timerMeter/img/midBg.png


二进制
src/components/timerMeter/img/pause.png


二进制
src/components/timerMeter/img/play.png


二进制
src/components/timerMeter/img/resetting.png


二进制
src/components/timerMeter/img/timeNum.png


二进制
src/components/timerMeter/img/timeUpBom.png


二进制
src/components/timerMeter/img/upBtn.png


+ 2 - 0
src/components/timerMeter/index.ts

@@ -0,0 +1,2 @@
+import TimerMeter from './TimerMeter.vue';
+export default TimerMeter;

+ 0 - 0
src/components/timerMeter/components/countdown.tsx → src/components/timerMeterOld/components/countdown.tsx


+ 0 - 0
src/components/timerMeter/components/positive.tsx → src/components/timerMeterOld/components/positive.tsx


+ 0 - 0
src/components/timerMeter/images/add.png → src/components/timerMeterOld/images/add.png


+ 0 - 0
src/components/timerMeter/images/minus.png → src/components/timerMeterOld/images/minus.png


+ 0 - 0
src/components/timerMeter/images/playing.png → src/components/timerMeterOld/images/playing.png


+ 0 - 0
src/components/timerMeter/images/suspend.png → src/components/timerMeterOld/images/suspend.png


+ 0 - 0
src/components/timerMeter/index.module.less → src/components/timerMeterOld/index.module.less


+ 0 - 0
src/components/timerMeter/index.tsx → src/components/timerMeterOld/index.tsx


+ 2 - 2
src/components/timerMeter/modals/flipper.vue → src/components/timerMeterOld/modals/flipper.vue

@@ -142,7 +142,7 @@ export default {
   top: 0;
   bottom: 50%;
   border-radius: 10px 10px 0 0;
-  border-bottom: solid 2px #fff;
+  /* border-bottom: solid 2px #fff; */
 }
 
 .M-Flipper .digital:after {
@@ -171,7 +171,7 @@ export default {
 .M-Flipper.down.go .front:before {
   transform-origin: 50% 100%;
   animation: frontFlipDown 0.6s ease-in-out both;
-  box-shadow: 0 -2px 6px rgba(255, 255, 255, 0.3);
+  /* box-shadow: 0 0px 6px rgba(255, 255, 255, 0.3); */
   backface-visibility: hidden;
 }
 

二进制
src/components/timerMeterOld/timer.wav


二进制
src/views/attend-class/image/close.png


+ 21 - 1
src/views/attend-class/index.module.less

@@ -796,7 +796,7 @@
   }
 }
 :global{
-  .metronomeConBoxClass_drag,.timerMeterConBoxClass_drag{
+  .metronomeConBoxClass_drag{
     .bom_drag{
       position: absolute;
       bottom: 0;
@@ -805,6 +805,26 @@
     }
   }
 }
+:global{
+  .timerMeterConBoxClass_drag{
+    box-shadow:initial;
+    .topDragDom{
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 100%;
+      height: 40px;
+    }
+  }
+}
+.timerMeterClose{
+  position: absolute;
+  width: 31px;
+  height: 32px;
+  top: 16px;
+  right: -31px;
+  cursor: pointer;
+}
 .workContainer {
   display: flex;
   align-items: center;

+ 13 - 12
src/views/attend-class/index.tsx

@@ -59,7 +59,7 @@ import { useRouter } from 'vue-router';
 import { useUserStore } from '@/store/modules/users';
 import iconNote from './new-image/icon-note.png';
 import iconWhite from './new-image/icon-white.png';
-
+import timerMeterClose from './image/close.png';
 import rightIconEnd from './image/right_icon1.png';
 import rightIconArrange from './image/right_icon2.png';
 import rightIconPostil from './image/right_icon3.png';
@@ -1526,8 +1526,8 @@ export default defineComponent({
     const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
     const timerMeterConDragData = useDrag(
       [
-        `${timerMeterConBoxClass}>.n-card-header`,
-        `${timerMeterConBoxClass} .bom_drag`
+        `${timerMeterConBoxClass} .timeBomCon .bom_drag`,
+        `${timerMeterConBoxClass} .topDragDom`
       ],
       timerMeterConBoxClass,
       showModalTime,
@@ -2280,18 +2280,19 @@ export default defineComponent({
           </div>
         </NModal>
         <NModal
-          transformOrigin="center"
           v-model:show={showModalTime.value}
-          class={['modalTitle background', timerMeterConBoxClass]}
-          title={'计时器'}
-          preset="card"
-          style={{
-            width: px2vw(772),
-            ...timerMeterConDragData.styleDrag.value
-          }}>
+          class={timerMeterConBoxClass}
+          style={timerMeterConDragData.styleDrag.value}>
           <div>
+            <img
+              class={styles.timerMeterClose}
+              src={timerMeterClose}
+              onClick={() => {
+                showModalTime.value = false;
+              }}
+            />
+            <div class="topDragDom"></div>
             <TimerMeter></TimerMeter>
-            <Dragbom></Dragbom>
           </div>
         </NModal>