mo 1 year ago
parent
commit
dc98138a05

+ 1 - 3
components.d.ts

@@ -9,10 +9,8 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
-    Application: typeof import('./src/components/Application/Application.vue')['default']
-    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    Flipper: typeof import('./src/components/timerMeter/modals/flipper.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
-    VanButton: typeof import('vant/es')['Button']
   }
 }

+ 5 - 0
src/components/layout/index.module.less

@@ -407,4 +407,9 @@
 
 .changePwdModal {
   border-radius: 16px;
+}
+
+.modeWrap {
+  overflow: hidden;
+  border-radius: 16px;
 }

+ 16 - 15
src/components/layout/index.tsx

@@ -12,6 +12,7 @@ import beatImage from './images/beatImage.png';
 import toneImage from './images/toneImage.png';
 import setTimeImage from './images/setTimeImage.png';
 import dragingBoxIcon from './images/dragingBoxIcon.png';
+import TimerMeter from '../timerMeter';
 import { useRoute } from 'vue-router';
 export default defineComponent({
   name: 'layoutView',
@@ -19,7 +20,7 @@ export default defineComponent({
     const directionType = ref('left');
     const showModalBeat = ref(false);
     const showModalTone = ref(false);
-    const showModalTime = ref(false);
+    const showModalTime = ref(true);
     const route = useRoute();
     const isDragIng = ref(false);
     const initMoveable = async () => {
@@ -214,15 +215,19 @@ export default defineComponent({
           </div>
         </NPopover>
 
-        <NModal v-model:show={showModalBeat.value}>
+        <NModal
+         class={['modalTitle background']}
+          title={'节拍器'}
+          preset="card"
+          v-model:show={showModalBeat.value}  style={{ width: '687px' }}>
           <div
-            onClick={() => {
-              showModalBeat.value = false;
-            }}>
-            <NImage
+            class={styles.modeWrap}
+           >
+            {/* <NImage
               src={beatImage}
               previewDisabled
-              class={styles.beatImage}></NImage>
+              class={styles.beatImage}></NImage> */}
+              <iframe src="https://test.lexiaoya.cn/metronome/"  scrolling='no'  frameborder="0" width='100%'  height={'650px'} ></iframe>
           </div>
         </NModal>
         <NModal v-model:show={showModalTone.value}>
@@ -236,15 +241,11 @@ export default defineComponent({
               class={styles.beatImage}></NImage>
           </div>
         </NModal>
-        <NModal v-model:show={showModalTime.value}>
+        <NModal v-model:show={showModalTime.value}   class={['modalTitle background']}
+          title={'计时器'}  preset="card" style={{ width: '772px' }}>
           <div
-            onClick={() => {
-              showModalTime.value = false;
-            }}>
-            <NImage
-              src={setTimeImage}
-              previewDisabled
-              class={styles.setTimeImage}></NImage>
+           >
+          <TimerMeter></TimerMeter>
           </div>
         </NModal>
       </div>

+ 206 - 0
src/components/timerMeter/components/countdown.tsx

@@ -0,0 +1,206 @@
+import { defineComponent, ref, watch, nextTick, onMounted } from 'vue';
+import styles from '../index.module.less';
+import { NTabs, NTabPane, NSpace, NButton, NImage, NInput, NInputNumber } from 'naive-ui';
+import { useRoute } from 'vue-router';
+import Flipper from '../modals/flipper.vue'
+import { stringify } from 'crypto-js/enc-utf8';
+import dayjs from 'dayjs';
+import playIcon from '../images/playing.png'
+import suspend from '../images/suspend.png'
+import add from '../images/add.png'
+import minus from '../images/minus.png'
+import { getSecond } from '@/utils/index'
+export default defineComponent({
+  name: 'timer-countdown',
+  setup() {
+    const activeTab = ref('positive');  //countdown
+    const route = useRoute();
+    // const flipperHour1 = ref()
+    // const flipperHour2 = ref()
+    const flipperMinute1 = ref()
+    const flipperMinute2 = ref()
+    const flipperSecond1 = ref()
+    const flipperSecond2 = ref()
+    const timer = ref(null as any)
+    const nowTimer = ref(null) as any
+    const nowDate = ref(new Date) as any
+    nowTimer.value = setInterval(() => {
+      nowDate.value = new Date()
+    }, 1000)
+    const count = ref(0)
+    const mine = ref(0)
+    const second = ref(0)
+    const isPlaying = ref(false)
+    // flipperHour1, flipperHour2,
+    const flipObjs = ref([flipperMinute1, flipperMinute2, flipperSecond1, flipperSecond2]) as any
+    const init = () => {
+      const now = new Date()
+      const nowTimeStr = '0000'
+
+      for (let i = 0; i < flipObjs.value.length; i++) {
+        flipObjs.value[i].value.setFront(nowTimeStr[i])
+      }
+    }
+
+    const formatDate = (date: Date, dateFormat: string) => {
+      /* 单独格式化年份,根据y的字符数量输出年份
+     * 例如:yyyy => 2019
+            yy => 19
+            y => 9
+     */
+      if (/(y+)/.test(dateFormat)) {
+        dateFormat = dateFormat.replace(
+          RegExp.$1,
+          (date.getFullYear() + '').substr(4 - RegExp.$1.length)
+        )
+      }
+      // 格式化月、日、时、分、秒
+      const o = {
+        'm+': date.getMonth() + 1,
+        'd+': date.getDate(),
+        'h+': date.getHours(),
+        'i+': date.getMinutes(),
+        's+': date.getSeconds()
+      } as any
+      for (const k in o) {
+        if (new RegExp(`(${k})`).test(dateFormat)) {
+          // 取出对应的值
+          const str = o[k] + ''
+          /* 根据设置的格式,输出对应的字符
+           * 例如: 早上8时,hh => 08,h => 8
+           * 但是,当数字>=10时,无论格式为一位还是多位,不做截取,这是与年份格式化不一致的地方
+           * 例如: 下午15时,hh => 15, h => 15
+           */
+          dateFormat = dateFormat.replace(
+            RegExp.$1,
+            RegExp.$1.length === 1 ? str : padLeftZero(str)
+          )
+        }
+      }
+      return dateFormat
+    }
+    const padLeftZero = (str: string) => {
+      return ('00' + str).substr(str.length)
+    }
+
+    const run = () => {
+      timer.value = setInterval(() => {
+        // 获取当前时间
+        const now = new Date()
+        const nowTimeStr = formatDate(new Date(now.getTime() - 1000), 'iiss')
+        const nextTimeStr = formatDate(now, 'iiss')
+        console.log(nowTimeStr, nextTimeStr)
+        for (let i = 0; i < flipObjs.value.length; i++) {
+          if (nowTimeStr[i] === nextTimeStr[i]) {
+            continue
+          }
+          flipObjs.value[i].value.flipDown(
+            nowTimeStr[i],
+            nextTimeStr[i]
+          )
+        }
+      }, 1000)
+
+    }
+    const startTimer = () => {
+      isPlaying.value = true;
+      timer.value = setInterval(() => {
+        // 获取当前时间
+        const lastStr = getSecond(count.value)
+        count.value++
+
+        const str = getSecond(count.value)
+        for (let i = 0; i < flipObjs.value.length; i++) {
+          if (lastStr[i] === str[i]) {
+            continue
+          }
+          flipObjs.value[i].value.flipDown(lastStr[i], str[i])
+        }
+      }, 1000)
+    }
+
+    const suspendNum = () => {
+      isPlaying.value = false;
+      if (timer.value) {
+        clearInterval(timer.value)
+        timer.value = null
+      }
+    }
+    const onReset = () => {
+
+      suspendNum()
+      nextTick(() => {
+        count.value = 0
+        init()
+      })
+
+    }
+    onMounted(() => {
+      nextTick(() => {
+        init()
+        // run()
+      })
+
+    })
+
+    return () => (
+      <div class={styles.timerItemWrap}>
+        dao
+        <div class={styles.timerItemInfo}>
+          <div class={styles.timerItemInset}>
+            <div class={styles.timerItemInfoTop}>
+              <div class={styles.timerItemTopCore}>
+                <h4> 分</h4>
+
+                <div class={styles.FlipClock}>
+                  <Flipper ref={flipperMinute1} />
+                  <Flipper ref={flipperMinute2} />
+                  <div  class={styles.chioseWrap}>
+                    <img src={add} class={styles.add} alt="" />
+                    <NInputNumber class={styles.countInput} min={0} max={59} show-button={false} v-model:value={mine.value}></NInputNumber>
+                    <img src={minus} class={styles.minus} alt="" />
+                  </div>
+                </div>
+              </div>
+              <div class={styles.timerItemTopCore}>
+                <h4 class={styles.dotTop}></h4>
+                <div class={styles.dot}></div>
+                <div class={styles.dot}></div>
+              </div>
+              <div class={styles.timerItemTopCore}>
+                <h4> 秒 </h4>
+                <div class={styles.FlipClock}>
+                  <Flipper ref={flipperSecond1} />
+                  <Flipper ref={flipperSecond2} />
+                  <div  class={styles.chioseWrap}>
+                    <img src={add} class={styles.add} alt="" />
+                    <NInputNumber class={styles.countInput} min={0} max={59} show-button={false} v-model:value={second.value}></NInputNumber>
+                    <img src={minus} class={styles.minus} alt="" />
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+        </div>
+
+        <NSpace class={styles.btnGroupModal} justify="center">
+          <NButton round onClick={() => onReset()}>
+            重置
+          </NButton>
+          {isPlaying.value ? <NButton round type="primary" icon-placement="right" onClick={() => suspendNum()} v-slots={{
+            default: () => <>暂停</>,
+            icon: () => <NImage previewDisabled class={styles.palyIcon} src={suspend}></NImage>
+          }}>
+
+          </NButton> : <NButton round type="primary" icon-placement="right" onClick={() => startTimer()} v-slots={{
+            default: () => <>开始</>,
+            icon: () => <NImage previewDisabled class={styles.palyIcon} src={playIcon}></NImage>
+          }}>
+          </NButton>}
+
+        </NSpace>
+      </div>
+    );
+  }
+});

+ 195 - 0
src/components/timerMeter/components/positive.tsx

@@ -0,0 +1,195 @@
+import { defineComponent, ref, watch, nextTick, onMounted } from 'vue';
+import styles from '../index.module.less';
+import { NTabs, NTabPane, NSpace, NButton, NImage } from 'naive-ui';
+import { useRoute } from 'vue-router';
+import Flipper from '../modals/flipper.vue'
+import { stringify } from 'crypto-js/enc-utf8';
+import dayjs from 'dayjs';
+import playIcon from '../images/playing.png'
+import suspend from '../images/suspend.png'
+import { getSecond } from '@/utils/index'
+export default defineComponent({
+  name: 'timer-positive',
+  setup() {
+    const activeTab = ref('positive');  //countdown
+    const route = useRoute();
+    // const flipperHour1 = ref()
+    // const flipperHour2 = ref()
+    const flipperMinute1 = ref()
+    const flipperMinute2 = ref()
+    const flipperSecond1 = ref()
+    const flipperSecond2 = ref()
+    const timer = ref(null as any)
+    const nowTimer = ref(null) as any
+    const nowDate = ref(new Date) as any
+    nowTimer.value = setInterval(() => {
+      nowDate.value = new Date()
+    }, 1000)
+    const count = ref(0)
+    const isPlaying = ref(false)
+    // flipperHour1, flipperHour2,
+    const flipObjs = ref([flipperMinute1, flipperMinute2, flipperSecond1, flipperSecond2]) as any
+    const init = () => {
+      const now = new Date()
+      const nowTimeStr = '0000'
+
+      for (let i = 0; i < flipObjs.value.length; i++) {
+        flipObjs.value[i].value.setFront(nowTimeStr[i])
+      }
+    }
+
+    const formatDate = (date: Date, dateFormat: string) => {
+      /* 单独格式化年份,根据y的字符数量输出年份
+     * 例如:yyyy => 2019
+            yy => 19
+            y => 9
+     */
+      if (/(y+)/.test(dateFormat)) {
+        dateFormat = dateFormat.replace(
+          RegExp.$1,
+          (date.getFullYear() + '').substr(4 - RegExp.$1.length)
+        )
+      }
+      // 格式化月、日、时、分、秒
+      const o = {
+        'm+': date.getMonth() + 1,
+        'd+': date.getDate(),
+        'h+': date.getHours(),
+        'i+': date.getMinutes(),
+        's+': date.getSeconds()
+      } as any
+      for (const k in o) {
+        if (new RegExp(`(${k})`).test(dateFormat)) {
+          // 取出对应的值
+          const str = o[k] + ''
+          /* 根据设置的格式,输出对应的字符
+           * 例如: 早上8时,hh => 08,h => 8
+           * 但是,当数字>=10时,无论格式为一位还是多位,不做截取,这是与年份格式化不一致的地方
+           * 例如: 下午15时,hh => 15, h => 15
+           */
+          dateFormat = dateFormat.replace(
+            RegExp.$1,
+            RegExp.$1.length === 1 ? str : padLeftZero(str)
+          )
+        }
+      }
+      return dateFormat
+    }
+    const padLeftZero = (str: string) => {
+      return ('00' + str).substr(str.length)
+    }
+
+    const run = () => {
+      timer.value = setInterval(() => {
+        // 获取当前时间
+        const now = new Date()
+        const nowTimeStr = formatDate(new Date(now.getTime() - 1000), 'iiss')
+        const nextTimeStr = formatDate(now, 'iiss')
+        console.log(nowTimeStr, nextTimeStr)
+        for (let i = 0; i < flipObjs.value.length; i++) {
+          if (nowTimeStr[i] === nextTimeStr[i]) {
+            continue
+          }
+          flipObjs.value[i].value.flipDown(
+            nowTimeStr[i],
+            nextTimeStr[i]
+          )
+        }
+      }, 1000)
+
+    }
+    const startTimer = () => {
+      isPlaying.value = true;
+      timer.value = setInterval(() => {
+        // 获取当前时间
+        const lastStr = getSecond(count.value)
+        count.value++
+
+        const str = getSecond(count.value)
+        for (let i = 0; i < flipObjs.value.length; i++) {
+          if (lastStr[i] === str[i]) {
+            continue
+          }
+          flipObjs.value[i].value.flipDown(lastStr[i], str[i])
+        }
+      }, 1000)
+    }
+
+    const suspendNum = () => {
+      isPlaying.value = false;
+      if (timer.value) {
+        clearInterval(timer.value)
+        timer.value = null
+      }
+    }
+    const onReset = () => {
+
+      suspendNum()
+      nextTick(() => {
+        count.value = 0
+        init()
+      })
+
+    }
+    onMounted(() => {
+      nextTick(() => {
+        init()
+        // run()
+      })
+
+    })
+
+    return () => (
+      <div class={styles.timerItemWrap}>
+        <div class={styles.timerItemInfo}>
+          <div class={styles.timerItemInset}>
+            <div class={styles.timerItemInfoTop}>
+              <div class={styles.timerItemTopCore}>
+                <h4> 分</h4>
+
+                <div class={styles.FlipClock}>
+                  <Flipper ref={flipperMinute1} />
+                  <Flipper ref={flipperMinute2} />
+                </div>
+              </div>
+              <div class={styles.timerItemTopCore}>
+                <h4 class={styles.dotTop}></h4>
+                <div class={styles.dot}></div>
+                <div class={styles.dot}></div>
+              </div>
+              <div class={styles.timerItemTopCore}>
+                <h4> 秒</h4>
+
+                <div class={styles.FlipClock}>
+                  <Flipper ref={flipperSecond1} />
+                  <Flipper ref={flipperSecond2} />
+                </div>
+              </div>
+            </div>
+            <div class={styles.nowTimerWrap}>
+              {dayjs(nowDate.value).format('YYYY年MM月DD日 HH:mm:ss')}
+            </div>
+          </div>
+
+        </div>
+
+        <NSpace class={styles.btnGroupModal} justify="center">
+          <NButton round onClick={() => onReset()}>
+            重置
+          </NButton>
+          {isPlaying.value ? <NButton round type="primary" icon-placement="right" onClick={() => suspendNum()} v-slots={{
+            default: () => <>暂停</>,
+            icon: () => <NImage previewDisabled class={styles.palyIcon} src={suspend}></NImage>
+          }}>
+
+          </NButton> : <NButton round type="primary" icon-placement="right" onClick={() => startTimer()} v-slots={{
+            default: () => <>开始</>,
+            icon: () => <NImage previewDisabled class={styles.palyIcon} src={playIcon}></NImage>
+          }}>
+          </NButton>}
+
+        </NSpace>
+      </div>
+    );
+  }
+});

BIN
src/components/timerMeter/images/add.png


BIN
src/components/timerMeter/images/minus.png


BIN
src/components/timerMeter/images/playing.png


BIN
src/components/timerMeter/images/suspend.png


+ 172 - 0
src/components/timerMeter/index.module.less

@@ -0,0 +1,172 @@
+.timerWrap {
+  background-color: #fff;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  .timerTop {
+    width: 276px;
+    height: 41px;
+    background: #F5F6FA;
+    border-radius: 8px;
+    margin-top: 24px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    margin-bottom: 24px;
+
+    .timerTopPane {
+      width: 50%;
+      text-align: center;
+      line-height: 41px;
+      cursor: pointer;
+
+    }
+
+    .timerTopPaneActive {
+      background: #198CFE;
+      border-radius: 8px;
+      color: #fff;
+    }
+  }
+}
+
+
+// 计时器
+.timerItemInfo {
+  width: 692px;
+
+  background: #A6D1FF;
+  box-shadow: 0px 9px 0px 0px #CBD6DF;
+  border-radius: 46px;
+  border: 13px solid #EEF7FF;
+  // margin-bottom: 40px;
+  padding: 10px;
+
+  .timerItemInset {
+    border-radius: 28px 28px 38px 38px;
+    background: #D8ECFE;
+  }
+
+  .timerItemInfoTop {
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+
+    .dot {
+      width: 10px;
+      height: 10px;
+      background: #131415;
+      border-radius: 28px;
+      margin-bottom: 15px;
+      margin: 7px 70px;
+    }
+
+    .dotTop {
+
+      width: 10px;
+      height: 70px;
+    }
+
+    .timerItemTopCore {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+
+      h4 {
+        font-size: 22px;
+        font-weight: 600;
+        color: #7CAEE1;
+        line-height: 70px;
+      }
+    }
+
+
+  }
+}
+
+.nowTimerWrap {
+  margin-top: 20px;
+  width: 100%;
+  text-align: center;
+  font-size: 24px;
+
+  font-weight: 400;
+  color: rgba(19, 20, 21, .5);
+  line-height: 33px;
+  padding-bottom: 20px;
+}
+
+.btnGroupModal {
+  padding: 40px 0 32px;
+
+  :global {
+    .n-button {
+      height: 47px;
+      min-width: 156px;
+    }
+  }
+}
+
+.palyIcon {
+  width: 11px;
+  height: 14px;
+}
+
+.countInput {
+  // width: 66px;
+  // height: 33px;
+  // background: #F5F6FA;
+  // border-radius: 17px;
+  margin: 0 16px;
+
+  :global {
+    .n-input__border {
+      display: none;
+    }
+
+    .n-input__state-border {
+      display: none;
+    }
+
+    .n-input {
+      width: 66px;
+      height: 33px;
+      border-radius: 17px;
+      overflow: hidden;
+
+      .n-input-wrapper {
+        text-align: center;
+        background: #F5F6FA;
+        padding: 0;
+      }
+    }
+  }
+}
+
+.chioseWrap {
+  margin: 14px 0 26px;
+  width: 174px;
+  line-height: 45px;
+  height: 45px;
+  background: linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 100%);
+  box-shadow: 2px 2px 0px 0px #A2CAEE;
+  border-radius: 23px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+
+  .add {
+    width: 16px;
+    height: 16px;
+  }
+
+  .minus {
+    width: 16px;
+    height: 4px;
+  }
+}

+ 29 - 0
src/components/timerMeter/index.tsx

@@ -0,0 +1,29 @@
+import { defineComponent, ref, watch, nextTick, onMounted } from 'vue';
+import styles from './index.module.less';
+import { NTabs, NTabPane, NSpace, NButton } from 'naive-ui';
+import { useRoute } from 'vue-router';
+import Countdown from './components/countdown';
+import Positive from './components/positive';
+export default defineComponent({
+  name: 'data-module',
+  setup() {
+    const activeTab = ref('positive');  //countdown
+    const route = useRoute();
+    // onMounted(() => {
+    // })
+    const setActivePinia = (name: string) => {
+      activeTab.value = name
+    }
+    return () => (
+      <div>
+        <div class={styles.timerWrap}>
+          <div class={styles.timerTop}>
+            <div class={[styles.timerTopPane, activeTab.value == 'positive' ? styles.timerTopPaneActive : '']} onClick={() => { setActivePinia('positive') }}>正计时</div>
+            <div class={[styles.timerTopPane, activeTab.value == 'countdown' ? styles.timerTopPaneActive : '']} onClick={() => { setActivePinia('countdown') }}>倒计时</div>
+          </div>
+          {activeTab.value == 'positive' ? <Positive></Positive> : <Countdown></Countdown>}
+        </div>
+      </div>
+    );
+  }
+});

+ 291 - 0
src/components/timerMeter/modals/flipper.vue

@@ -0,0 +1,291 @@
+
+<template>
+  <div class="M-Flipper" :class="[flipType, {'go': isFlipping}]">
+    <div class="digital front" :class="_textClass(frontTextFromData)"></div>
+    <div class="digital back" :class="_textClass(backTextFromData)"></div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'FlipClock',
+  data() {
+    return {
+      isFlipping: false,
+      flipType: 'down',
+      frontTextFromData: 0,
+      backTextFromData: 1
+    }
+  },
+  props: {
+    // front paper text
+    // 前牌文字
+    frontText: {
+      type: [Number, String],
+      default: 0
+    },
+    // back paper text
+    // 后牌文字
+    backText: {
+      type: [Number, String],
+      default: 1
+    },
+    // flipping duration, please be consistent with the CSS animation-duration value.
+    // 翻牌动画时间,与CSS中设置的animation-duration保持一致
+    duration: {
+      type: Number,
+      default: 600
+    }
+  },
+  methods: {
+    _textClass(number) {
+      return 'number' + number
+    },
+    _flip(type, front, back) {
+      // 如果处于翻转中,则不执行
+      if (this.isFlipping) {
+        return false
+      }
+      this.frontTextFromData = front
+      this.backTextFromData = back
+      // 根据传递过来的type设置翻转方向
+      this.flipType = type
+      // 设置翻转状态为true
+      this.isFlipping = true
+      setTimeout(() => {
+        // 设置翻转状态为false
+        this.isFlipping = false
+        this.frontTextFromData = back
+      }, this.duration)
+    },
+    // 下翻牌
+    flipDown(front, back) {
+      this._flip('down', front, back)
+    },
+    // 上翻牌
+    flipUp(front, back) {
+      this._flip('up', front, back)
+    },
+    // 设置前牌文字
+    setFront(text) {
+        this.frontTextFromData = text
+    },
+    // 设置后牌文字
+    setBack(text) {
+        this.backTextFromData = text
+    }
+  },
+  created() {
+      this.frontTextFromData = this.frontText
+      this.backTextFromData = this.backText
+  }
+}
+</script>
+
+<style>
+.M-Flipper {
+  display: inline-block;
+  position: relative;
+  width: 90px;
+  height: 178px;
+  line-height: 178px;
+  /* border: solid 1px #000; */
+  border-radius: 10px;
+  background: #fff;
+  font-size: 128px;
+  color: #131415;
+  box-shadow: 4px 4px 0px 0px #A2CAEE;
+  text-align: center;
+  font-family: 'DINA';
+}
+
+/* @media screen and (min-width: 375px) and (max-width: 768px){
+   .M-Flipper {
+       width: 35px;
+      font-size: 40px;
+   }
+}
+
+@media screen and (max-width: 320px){
+   .M-Flipper {
+       width: 25px;
+      font-size: 40px;
+   }
+}
+
+@media screen and (min-width: 320px) and (max-width: 375px){
+   .M-Flipper {
+      width: 27px;
+      font-size: 40px;
+   }
+} */
+
+.M-Flipper .digital:before,
+.M-Flipper .digital:after {
+  content: '';
+  position: absolute;
+  left: 0;
+  right: 0;
+  background: #fff;
+  overflow: hidden;
+  box-sizing: border-box;
+}
+
+.M-Flipper .digital:before {
+  top: 0;
+  bottom: 50%;
+  border-radius: 10px 10px 0 0;
+  border-bottom: solid 2px #fff;
+}
+
+.M-Flipper .digital:after {
+  top: 50%;
+  bottom: 0;
+  border-radius: 0 0 10px 10px;
+  line-height: 0;
+}
+
+/*向下翻*/
+.M-Flipper.down .front:before {
+  z-index: 3;
+}
+
+.M-Flipper.down .back:after {
+  z-index: 2;
+  transform-origin: 50% 0%;
+  transform: perspective(160px) rotateX(180deg);
+}
+
+.M-Flipper.down .front:after,
+.M-Flipper.down .back:before {
+  z-index: 1;
+}
+
+.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);
+  backface-visibility: hidden;
+}
+
+.M-Flipper.down.go .back:after {
+  animation: backFlipDown 0.6s ease-in-out both;
+}
+
+/*向上翻*/
+.M-Flipper.up .front:after {
+  z-index: 3;
+}
+
+.M-Flipper.up .back:before {
+  z-index: 2;
+  transform-origin: 50% 100%;
+  transform: perspective(160px) rotateX(-180deg);
+}
+
+.M-Flipper.up .front:before,
+.M-Flipper.up .back:after {
+  z-index: 1;
+}
+
+.M-Flipper.up.go .front:after {
+  transform-origin: 50% 0;
+  animation: frontFlipUp 0.6s ease-in-out both;
+  box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3);
+  backface-visibility: hidden;
+}
+
+.M-Flipper.up.go .back:before {
+  animation: backFlipUp 0.6s ease-in-out both;
+}
+
+@keyframes frontFlipDown {
+  0% {
+    transform: perspective(160px) rotateX(0deg);
+  }
+
+  100% {
+    transform: perspective(160px) rotateX(-180deg);
+  }
+}
+
+@keyframes backFlipDown {
+  0% {
+    transform: perspective(160px) rotateX(180deg);
+  }
+
+  100% {
+    transform: perspective(160px) rotateX(0deg);
+  }
+}
+
+@keyframes frontFlipUp {
+  0% {
+    transform: perspective(160px) rotateX(0deg);
+  }
+
+  100% {
+    transform: perspective(160px) rotateX(180deg);
+  }
+}
+
+@keyframes backFlipUp {
+  0% {
+    transform: perspective(160px) rotateX(-180deg);
+  }
+
+  100% {
+    transform: perspective(160px) rotateX(0deg);
+  }
+}
+
+.M-Flipper .number0:before,
+.M-Flipper .number0:after {
+  content: '0';
+}
+
+.M-Flipper .number1:before,
+.M-Flipper .number1:after {
+  content: '1';
+}
+
+.M-Flipper .number2:before,
+.M-Flipper .number2:after {
+  content: '2';
+}
+
+.M-Flipper .number3:before,
+.M-Flipper .number3:after {
+  content: '3';
+}
+
+.M-Flipper .number4:before,
+.M-Flipper .number4:after {
+  content: '4';
+}
+
+.M-Flipper .number5:before,
+.M-Flipper .number5:after {
+  content: '5';
+}
+
+.M-Flipper .number6:before,
+.M-Flipper .number6:after {
+  content: '6';
+}
+
+.M-Flipper .number7:before,
+.M-Flipper .number7:after {
+  content: '7';
+}
+
+.M-Flipper .number8:before,
+.M-Flipper .number8:after {
+  content: '8';
+}
+
+.M-Flipper .number9:before,
+.M-Flipper .number9:after {
+  content: '9';
+}
+</style>

+ 1 - 1
src/styles/index.less

@@ -304,4 +304,4 @@ body {
 
 .no-move {
   transition: transform 0s;
-}
+}

+ 12 - 0
src/utils/index.ts

@@ -283,6 +283,18 @@ export const getSecondRPM = (second: number, type?: string) => {
   }
 };
 
+// 秒转分
+export const getSecond = (second: number, type?: string) => {
+  if (isNaN(second)) return '0000';
+  const mm = Math.floor(second / 60)
+    .toString()
+    .padStart(2, '0');
+  const dd = Math.floor(second % 60)
+    .toString()
+    .padStart(2, '0');
+  return `${mm}${dd}`
+};
+
 /** 滚动到表单填写错误的地方 */
 export function scrollToErrorForm() {
   const isError =

+ 3 - 0
src/views/setting/index.module.less

@@ -164,6 +164,7 @@
         &.n-input--disabled {
           background-color: #f5f6fa;
           color: rgba(149, 149, 152, 1);
+
           .n-input__input-el {
             background-color: #F5F6FA;
             color: rgba(0, 0, 0, 0.4);
@@ -243,6 +244,7 @@
       }
     }
   }
+
   .sendMsg {
     min-width: 108Px;
     height: 50Px;
@@ -253,6 +255,7 @@
     height: 24Px;
     cursor: pointer;
   }
+
   .submitBtm {
     width: 45%;
     height: 46Px;