Эх сурвалжийг харах

feat: Add count-to demo

feat: Add useWatermark hook and add useWatermark demo
陈凯龙 3 жил өмнө
parent
commit
d3fbd3a06c

+ 1 - 3
src/hooks/web/usePageLoading.ts

@@ -8,9 +8,7 @@ export const usePageLoading = () => {
   }
 
   const loadDone = () => {
-    setTimeout(() => {
-      appStore.setPageLoading(false)
-    }, 1000)
+    appStore.setPageLoading(false)
   }
 
   return {

+ 55 - 0
src/hooks/web/useWatermark.ts

@@ -0,0 +1,55 @@
+const domSymbol = Symbol('watermark-dom')
+
+export function useWatermark(appendEl: HTMLElement | null = document.body) {
+  let func: Fn = () => {}
+  const id = domSymbol.toString()
+  const clear = () => {
+    const domId = document.getElementById(id)
+    if (domId) {
+      const el = appendEl
+      el && el.removeChild(domId)
+    }
+    window.removeEventListener('resize', func)
+  }
+  const createWatermark = (str: string) => {
+    clear()
+
+    const can = document.createElement('canvas')
+    can.width = 300
+    can.height = 240
+
+    const cans = can.getContext('2d')
+    if (cans) {
+      cans.rotate((-20 * Math.PI) / 120)
+      cans.font = '15px Vedana'
+      cans.fillStyle = 'rgba(0, 0, 0, 0.15)'
+      cans.textAlign = 'left'
+      cans.textBaseline = 'middle'
+      cans.fillText(str, can.width / 20, can.height)
+    }
+
+    const div = document.createElement('div')
+    div.id = id
+    div.style.pointerEvents = 'none'
+    div.style.top = '0px'
+    div.style.left = '0px'
+    div.style.position = 'absolute'
+    div.style.zIndex = '100000000'
+    div.style.width = document.documentElement.clientWidth + 'px'
+    div.style.height = document.documentElement.clientHeight + 'px'
+    div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
+    const el = appendEl
+    el && el.appendChild(div)
+    return id
+  }
+
+  function setWatermark(str: string) {
+    createWatermark(str)
+    func = () => {
+      createWatermark(str)
+    }
+    window.addEventListener('resize', func)
+  }
+
+  return { setWatermark, clear }
+}

+ 29 - 1
src/locales/en.ts

@@ -87,7 +87,10 @@ export default {
     workplace: 'Workplace',
     guide: 'Guide',
     component: 'Component',
-    icon: 'Icon'
+    icon: 'Icon',
+    echart: 'Echart',
+    countTo: 'Count to',
+    watermark: 'Watermark'
   },
   analysis: {
     newUser: 'New user',
@@ -203,5 +206,30 @@ export default {
     recommendeDes:
       'Iconify component basically contains all icons. You can query any icon you want. And packaging will only package the icons used.',
     accessAddress: 'Access address'
+  },
+  echartDemo: {
+    echart: 'Echart',
+    echartDes:
+      'Based on the secondary packaging components of eckarts, the width is adaptive. The corresponding chart can be displayed by passing in the options and height attributes.'
+  },
+  countToDemo: {
+    countTo: 'CountTo',
+    countToDes:
+      'The transformation is based on vue-count-to and supports all vue-count-to parameters.',
+    suffix: 'Suffix',
+    prefix: 'Prefix',
+    separator: 'Separator',
+    duration: 'Duration',
+    endVal: 'End val',
+    startVal: 'Start val',
+    start: 'Start',
+    pause: 'Pause',
+    resume: 'Resume'
+  },
+  watermarkDemo: {
+    watermark: 'Watermark',
+    createdWatermark: 'Created watermark',
+    clearWatermark: 'Clear watermark',
+    resetWatermark: 'Reset watermark'
   }
 }

+ 28 - 1
src/locales/zh-CN.ts

@@ -87,7 +87,10 @@ export default {
     workplace: '工作台',
     guide: '引导',
     component: '组件',
-    icon: '图标'
+    icon: '图标',
+    echart: '图表',
+    countTo: '数字动画',
+    watermark: '水印'
   },
   analysis: {
     newUser: '新增用户',
@@ -203,5 +206,29 @@ export default {
     recommendeDes:
       'Iconify组件基本包含所有的图标,你可以查询到你想要的任何图标。并且打包只会打包所用到的图标。',
     accessAddress: '访问地址'
+  },
+  echartDemo: {
+    echart: '图表',
+    echartDes:
+      '基于 echarts 二次封装组件,自适应宽度,只需传入 options 与 height 属性即可展示对应的图表。'
+  },
+  countToDemo: {
+    countTo: '数字动画',
+    countToDes: '基于 vue-count-to 进行改造,支持所有 vue-count-to 参数。',
+    suffix: '后缀',
+    prefix: '前缀',
+    separator: '分割符号',
+    duration: '持续时间',
+    endVal: '结束值',
+    startVal: '开始值',
+    start: '开始',
+    pause: '暂停',
+    resume: '继续'
+  },
+  watermarkDemo: {
+    watermark: '水印',
+    createdWatermark: '创建水印',
+    clearWatermark: '清除水印',
+    resetWatermark: '重置水印'
   }
 }

+ 24 - 0
src/router/index.ts

@@ -103,6 +103,30 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
         meta: {
           title: t('router.icon')
         }
+      },
+      {
+        path: 'echart',
+        component: () => import('@/views/Components/Echart.vue'),
+        name: 'Echart',
+        meta: {
+          title: t('router.echart')
+        }
+      },
+      {
+        path: 'count-to',
+        component: () => import('@/views/Components/CountTo.vue'),
+        name: 'CountTo',
+        meta: {
+          title: t('router.countTo')
+        }
+      },
+      {
+        path: 'watermark',
+        component: () => import('@/views/Components/Watermark.vue'),
+        name: 'Watermark',
+        meta: {
+          title: t('router.watermark')
+        }
       }
     ]
   },

+ 100 - 0
src/views/Components/CountTo.vue

@@ -0,0 +1,100 @@
+<script setup lang="ts">
+import { ContentWrap } from '@/components/ContentWrap'
+import { useI18n } from '@/hooks/web/useI18n'
+import { CountTo } from '@/components/CountTo'
+import { ElRow, ElCol, ElInputNumber, ElInput, ElButton } from 'element-plus'
+import { ref, unref } from 'vue'
+
+const { t } = useI18n()
+
+const countRef = ref<ComponentRef<typeof CountTo>>()
+
+const startVal = ref(0)
+
+const endVal = ref(1314512)
+
+const duration = ref(3000)
+
+const decimals = ref(0)
+
+const separator = ref(',')
+
+const prefix = ref('¥ ')
+
+const suffix = ref(' rmb')
+
+const autoplay = ref(false)
+
+const start = () => {
+  unref(countRef)?.start()
+}
+
+const pauseResume = () => {
+  unref(countRef)?.pauseResume()
+}
+</script>
+
+<template>
+  <ContentWrap :title="t('countToDemo.countTo')" :message="t('countToDemo.countToDes')">
+    <div class="text-center mb-40px">
+      <CountTo
+        ref="countRef"
+        :start-val="startVal"
+        :end-val="endVal"
+        :duration="duration"
+        :decimals="decimals"
+        :separator="separator"
+        :prefix="prefix"
+        :suffix="suffix"
+        :autoplay="autoplay"
+        class="text-30px font-bold text-[var(--el-color-primary)]"
+      />
+    </div>
+    <ElRow :gutter="20" justify="space-between">
+      <ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
+        <div class="flex mb-20px items-center">
+          <span class="min-w-90px text-right">{{ t('countToDemo.startVal') }}:</span>
+          <ElInputNumber v-model="startVal" :min="0" />
+        </div>
+      </ElCol>
+      <ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
+        <div class="flex mb-20px items-center">
+          <span class="min-w-90px text-right">{{ t('countToDemo.endVal') }}:</span>
+          <ElInputNumber v-model="endVal" :min="1" />
+        </div>
+      </ElCol>
+      <ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
+        <div class="flex mb-20px items-center">
+          <span class="min-w-90px text-right">{{ t('countToDemo.duration') }}:</span>
+          <ElInputNumber v-model="duration" :min="1000" />
+        </div>
+      </ElCol>
+      <ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
+        <div class="flex mb-20px items-center">
+          <span class="min-w-90px text-right">{{ t('countToDemo.separator') }}:</span>
+          <ElInput v-model="separator" />
+        </div>
+      </ElCol>
+      <ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
+        <div class="flex mb-20px items-center">
+          <span class="min-w-90px text-right">{{ t('countToDemo.prefix') }}:</span>
+          <ElInput v-model="prefix" />
+        </div>
+      </ElCol>
+      <ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
+        <div class="flex mb-20px items-center">
+          <span class="min-w-90px text-right">{{ t('countToDemo.suffix') }}:</span>
+          <ElInput v-model="suffix" />
+        </div>
+      </ElCol>
+      <ElCol :span="24">
+        <div class="text-center">
+          <ElButton type="primary" @click="start">{{ t('countToDemo.start') }}</ElButton>
+          <ElButton @click="pauseResume">
+            {{ t('countToDemo.pause') }}/{{ t('countToDemo.resume') }}
+          </ElButton>
+        </div>
+      </ElCol>
+    </ElRow>
+  </ContentWrap>
+</template>

+ 36 - 0
src/views/Components/Echart.vue

@@ -0,0 +1,36 @@
+<script setup lang="ts">
+import { ContentWrap } from '@/components/ContentWrap'
+import { useI18n } from '@/hooks/web/useI18n'
+import { pieOptions, barOptions, lineOptions, wordOptions } from '@/views/Dashboard/echarts-data'
+import { Echart } from '@/components/Echart'
+import { ElRow, ElCol, ElCard } from 'element-plus'
+
+const { t } = useI18n()
+</script>
+
+<template>
+  <ContentWrap :title="t('echartDemo.echart')" :message="t('echartDemo.echartDes')">
+    <ElRow :gutter="20" justify="space-between">
+      <ElCol :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
+        <ElCard shadow="hover" class="mb-20px">
+          <Echart :options="pieOptions" :height="300" />
+        </ElCard>
+      </ElCol>
+      <ElCol :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
+        <ElCard shadow="hover" class="mb-20px">
+          <Echart :options="barOptions" :height="300" />
+        </ElCard>
+      </ElCol>
+      <ElCol :span="24">
+        <ElCard shadow="hover" class="mb-20px">
+          <Echart :options="lineOptions" :height="350" />
+        </ElCard>
+      </ElCol>
+      <ElCol :span="24">
+        <ElCard shadow="hover" class="mb-20px">
+          <Echart :options="wordOptions" :height="300" />
+        </ElCard>
+      </ElCol>
+    </ElRow>
+  </ContentWrap>
+</template>

+ 32 - 0
src/views/Components/Watermark.vue

@@ -0,0 +1,32 @@
+<script setup lang="ts">
+import { ContentWrap } from '@/components/ContentWrap'
+import { useI18n } from '@/hooks/web/useI18n'
+import { ElButton } from 'element-plus'
+import { useWatermark } from '@/hooks/web/useWatermark'
+import { computed, onBeforeUnmount } from 'vue'
+import { useAppStore } from '@/store/modules/app'
+
+const appStore = useAppStore()
+
+const title = computed(() => appStore.getTitle)
+
+const { setWatermark, clear } = useWatermark()
+
+const { t } = useI18n()
+
+onBeforeUnmount(() => {
+  clear()
+})
+</script>
+
+<template>
+  <ContentWrap :title="t('watermarkDemo.watermark')">
+    <ElButton type="primary" @click="setWatermark(title)">
+      {{ t('watermarkDemo.createdWatermark') }}
+    </ElButton>
+    <ElButton type="danger" @click="clear">{{ t('watermarkDemo.clearWatermark') }}</ElButton>
+    <ElButton type="warning" @click="setWatermark(`${title}-new`)">
+      {{ t('watermarkDemo.resetWatermark') }}
+    </ElButton>
+  </ContentWrap>
+</template>

+ 2 - 2
src/views/Dashboard/Analysis.vue

@@ -17,7 +17,7 @@ const { t } = useI18n()
 
 const loading = ref(true)
 
-const pieOptionsData = reactive<EChartsOption>(pieOptions)
+const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
 
 // 用户来源
 const getUserAccessSource = async () => {
@@ -100,7 +100,7 @@ getAllApi()
     <ElCol :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
       <ElCard shadow="hover" class="mb-20px">
         <ElSkeleton :loading="loading" animated>
-          <Echart :options="pieOptions" :height="300" />
+          <Echart :options="pieOptionsData" :height="300" />
         </ElSkeleton>
       </ElCard>
     </ElCol>

+ 125 - 0
src/views/Dashboard/echarts-data.ts

@@ -1,4 +1,5 @@
 import { EChartsOption } from 'echarts'
+import { EChartsOption as EChartsWordOption } from 'echarts-wordcloud'
 import { useI18n } from '@/hooks/web/useI18n'
 
 const { t } = useI18n()
@@ -183,3 +184,127 @@ export const radarOption: EChartsOption = {
     }
   ]
 }
+
+export const wordOptions: EChartsWordOption = {
+  series: [
+    {
+      type: 'wordCloud',
+      gridSize: 2,
+      sizeRange: [12, 50],
+      rotationRange: [-90, 90],
+      shape: 'pentagon',
+      width: 600,
+      height: 400,
+      drawOutOfBound: true,
+      textStyle: {
+        color: function () {
+          return (
+            'rgb(' +
+            [
+              Math.round(Math.random() * 160),
+              Math.round(Math.random() * 160),
+              Math.round(Math.random() * 160)
+            ].join(',') +
+            ')'
+          )
+        }
+      },
+      emphasis: {
+        textStyle: {
+          shadowBlur: 10,
+          shadowColor: '#333'
+        }
+      },
+      data: [
+        {
+          name: 'Sam S Club',
+          value: 10000,
+          textStyle: {
+            color: 'black'
+          },
+          emphasis: {
+            textStyle: {
+              color: 'red'
+            }
+          }
+        },
+        {
+          name: 'Macys',
+          value: 6181
+        },
+        {
+          name: 'Amy Schumer',
+          value: 4386
+        },
+        {
+          name: 'Jurassic World',
+          value: 4055
+        },
+        {
+          name: 'Charter Communications',
+          value: 2467
+        },
+        {
+          name: 'Chick Fil A',
+          value: 2244
+        },
+        {
+          name: 'Planet Fitness',
+          value: 1898
+        },
+        {
+          name: 'Pitch Perfect',
+          value: 1484
+        },
+        {
+          name: 'Express',
+          value: 1112
+        },
+        {
+          name: 'Home',
+          value: 965
+        },
+        {
+          name: 'Johnny Depp',
+          value: 847
+        },
+        {
+          name: 'Lena Dunham',
+          value: 582
+        },
+        {
+          name: 'Lewis Hamilton',
+          value: 555
+        },
+        {
+          name: 'KXAN',
+          value: 550
+        },
+        {
+          name: 'Mary Ellen Mark',
+          value: 462
+        },
+        {
+          name: 'Farrah Abraham',
+          value: 366
+        },
+        {
+          name: 'Rita Ora',
+          value: 360
+        },
+        {
+          name: 'Serena Williams',
+          value: 282
+        },
+        {
+          name: 'NCAA baseball tournament',
+          value: 273
+        },
+        {
+          name: 'Point Break',
+          value: 265
+        }
+      ]
+    }
+  ]
+}