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

feat(hooks): Add useIntro hook

feat: Add guide demo
陈凯龙 3 жил өмнө
parent
commit
0832194e61

+ 2 - 0
package.json

@@ -33,6 +33,7 @@
     "echarts": "^5.2.2",
     "echarts-wordcloud": "^2.0.0",
     "element-plus": "1.3.0-beta.7",
+    "intro.js": "^4.3.0",
     "lodash-es": "^4.17.21",
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
@@ -50,6 +51,7 @@
     "@iconify/json": "^1.1.459",
     "@intlify/vite-plugin-vue-i18n": "^3.2.1",
     "@purge-icons/generated": "^0.7.0",
+    "@types/intro.js": "^3.0.2",
     "@types/lodash-es": "^4.17.5",
     "@types/node": "^17.0.10",
     "@types/nprogress": "^0.2.0",

+ 58 - 32
pnpm-lock.yaml

@@ -7,6 +7,7 @@ specifiers:
   '@iconify/json': ^1.1.459
   '@intlify/vite-plugin-vue-i18n': ^3.2.1
   '@purge-icons/generated': ^0.7.0
+  '@types/intro.js': ^3.0.2
   '@types/lodash-es': ^4.17.5
   '@types/node': ^17.0.10
   '@types/nprogress': ^0.2.0
@@ -30,6 +31,7 @@ specifiers:
   eslint-plugin-prettier: ^4.0.0
   eslint-plugin-vue: ^8.3.0
   husky: ^7.0.4
+  intro.js: ^4.3.0
   less: ^4.1.2
   lint-staged: ^12.2.2
   lodash-es: ^4.17.21
@@ -75,6 +77,7 @@ dependencies:
   echarts: registry.npmmirror.com/echarts/5.2.2
   echarts-wordcloud: registry.npmmirror.com/echarts-wordcloud/2.0.0_echarts@5.2.2
   element-plus: registry.npmmirror.com/element-plus/1.3.0-beta.7_vue@3.2.26
+  intro.js: registry.npmmirror.com/intro.js/4.3.0
   lodash-es: registry.nlark.com/lodash-es/4.17.21
   mockjs: registry.npmmirror.com/mockjs/1.1.0
   nprogress: registry.npmmirror.com/nprogress/0.2.0
@@ -92,6 +95,7 @@ devDependencies:
   '@iconify/json': registry.npmmirror.com/@iconify/json/1.1.459
   '@intlify/vite-plugin-vue-i18n': registry.npmmirror.com/@intlify/vite-plugin-vue-i18n/3.2.1_vite@2.7.13+vue-i18n@9.1.9
   '@purge-icons/generated': registry.nlark.com/@purge-icons/generated/0.7.0
+  '@types/intro.js': registry.npmmirror.com/@types/intro.js/3.0.2
   '@types/lodash-es': registry.npmmirror.com/@types/lodash-es/4.17.5
   '@types/node': registry.npmmirror.com/@types/node/17.0.10
   '@types/nprogress': registry.npmmirror.com/@types/nprogress/0.2.0
@@ -3910,25 +3914,6 @@ packages:
     version: 1.4.0
     dev: true
 
-  registry.nlark.com/needle/2.9.1:
-    resolution:
-      {
-        integrity: sha1-ItHf++NJDCuD4wH3cJtnNs2PJoQ=,
-        registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.nlark.com/needle/download/needle-2.9.1.tgz
-      }
-    name: needle
-    version: 2.9.1
-    engines: { node: '>= 4.4.x' }
-    hasBin: true
-    requiresBuild: true
-    dependencies:
-      debug: registry.npmmirror.com/debug/3.2.7
-      iconv-lite: registry.nlark.com/iconv-lite/0.4.24
-      sax: registry.nlark.com/sax/1.2.4
-    dev: true
-    optional: true
-
   registry.nlark.com/no-case/3.0.4:
     resolution:
       {
@@ -7288,8 +7273,8 @@ packages:
       vue-i18n:
         optional: true
     dependencies:
-      '@intlify/message-compiler': registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.28
-      '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.28
+      '@intlify/message-compiler': registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.29
+      '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.29
       jsonc-eslint-parser: registry.npmmirror.com/jsonc-eslint-parser/1.4.1
       source-map: registry.nlark.com/source-map/0.6.1
       vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26
@@ -7345,18 +7330,18 @@ packages:
       source-map: registry.nlark.com/source-map/0.6.1
     dev: false
 
-  registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.28:
+  registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.29:
     resolution:
       {
-        integrity: sha512-NBH9fZyitN2cijGt8bmU1W7ZPdhKbgW01L1RxJKFJW0cRaCmknJq63Aif1Q6xcxKt9ZhPbvIKHgPGzg1nWMfeA==,
+        integrity: sha512-FvMDwe57VvupujvNYUY90J8wv26wKu6j7I93dLwBOo/PTg7nQqFrmYQAF23UfDAdXO4FTdgHfFyb5ecYrN+n3g==,
         registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.npmmirror.com/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.28.tgz
+        tarball: https://registry.npmmirror.com/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.29.tgz
       }
     name: '@intlify/message-compiler'
-    version: 9.2.0-beta.28
+    version: 9.2.0-beta.29
     engines: { node: '>= 12' }
     dependencies:
-      '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.28
+      '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.29
       source-map: registry.nlark.com/source-map/0.6.1
     dev: true
 
@@ -7400,15 +7385,15 @@ packages:
     engines: { node: '>= 10' }
     dev: false
 
-  registry.npmmirror.com/@intlify/shared/9.2.0-beta.28:
+  registry.npmmirror.com/@intlify/shared/9.2.0-beta.29:
     resolution:
       {
-        integrity: sha512-JBMcoj1D4kSAma7Vb0+d8z6lPLIn7hIdZJPxbU8bgeMMniwKLoIS/jGlEfrZihsB5+otckPeQp203z8skwVS0w==,
+        integrity: sha512-blMW14WBr3fiCEk/XO4IbSxM8WMAhQOzEgWzP1aqbkeXbIMiHeyFI0ZexwyTKsvDZz0wEWlhupQi+9udrJsozA==,
         registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.npmmirror.com/@intlify/shared/download/@intlify/shared-9.2.0-beta.28.tgz
+        tarball: https://registry.npmmirror.com/@intlify/shared/download/@intlify/shared-9.2.0-beta.29.tgz
       }
     name: '@intlify/shared'
-    version: 9.2.0-beta.28
+    version: 9.2.0-beta.29
     engines: { node: '>= 12' }
     dev: true
 
@@ -7434,7 +7419,7 @@ packages:
         optional: true
     dependencies:
       '@intlify/bundle-utils': registry.npmmirror.com/@intlify/bundle-utils/2.2.0_vue-i18n@9.1.9
-      '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.28
+      '@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.29
       '@rollup/pluginutils': registry.npmmirror.com/@rollup/pluginutils/4.1.2
       debug: registry.npmmirror.com/debug/4.3.3
       fast-glob: registry.nlark.com/fast-glob/3.2.7
@@ -7572,6 +7557,17 @@ packages:
     version: 0.0.39
     dev: true
 
+  registry.npmmirror.com/@types/intro.js/3.0.2:
+    resolution:
+      {
+        integrity: sha1-y/m2nwVbd1gsws6+I26aQFsw770=,
+        registry: https://registry.npm.taobao.org/,
+        tarball: https://registry.npmmirror.com/@types/intro.js/download/@types/intro.js-3.0.2.tgz
+      }
+    name: '@types/intro.js'
+    version: 3.0.2
+    dev: true
+
   registry.npmmirror.com/@types/json-schema/7.0.9:
     resolution:
       {
@@ -10629,6 +10625,17 @@ packages:
       through: registry.nlark.com/through/2.3.8
     dev: true
 
+  registry.npmmirror.com/intro.js/4.3.0:
+    resolution:
+      {
+        integrity: sha512-F4LXM42QIXcO3/2myGBBFdbbUVUK0lBhK7pAxPLd082u+3HfvigFR+Rptqy05q3OzjG/O0vMFDedrnHdSPi2rQ==,
+        registry: https://registry.npm.taobao.org/,
+        tarball: https://registry.npmmirror.com/intro.js/download/intro.js-4.3.0.tgz
+      }
+    name: intro.js
+    version: 4.3.0
+    dev: false
+
   registry.npmmirror.com/is-buffer/1.1.6:
     resolution:
       {
@@ -10840,7 +10847,7 @@ packages:
       image-size: registry.npmmirror.com/image-size/0.5.5
       make-dir: registry.nlark.com/make-dir/2.1.0
       mime: registry.npmmirror.com/mime/1.6.0
-      needle: registry.nlark.com/needle/2.9.1
+      needle: registry.npmmirror.com/needle/2.9.1
       source-map: registry.nlark.com/source-map/0.6.1
     dev: true
 
@@ -11191,6 +11198,25 @@ packages:
     engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 }
     hasBin: true
 
+  registry.npmmirror.com/needle/2.9.1:
+    resolution:
+      {
+        integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==,
+        registry: https://registry.npm.taobao.org/,
+        tarball: https://registry.npmmirror.com/needle/download/needle-2.9.1.tgz
+      }
+    name: needle
+    version: 2.9.1
+    engines: { node: '>= 4.4.x' }
+    hasBin: true
+    requiresBuild: true
+    dependencies:
+      debug: registry.npmmirror.com/debug/3.2.7
+      iconv-lite: registry.nlark.com/iconv-lite/0.4.24
+      sax: registry.nlark.com/sax/1.2.4
+    dev: true
+    optional: true
+
   registry.npmmirror.com/node-fetch/2.6.1:
     resolution:
       {

+ 3 - 0
src/components/ContentWrap/index.ts

@@ -0,0 +1,3 @@
+import ContentWrap from './src/ContentWrap.vue'
+
+export { ContentWrap }

+ 35 - 0
src/components/ContentWrap/src/ContentWrap.vue

@@ -0,0 +1,35 @@
+<script setup lang="ts">
+import { ElCard, ElTooltip } from 'element-plus'
+import { propTypes } from '@/utils/propTypes'
+import { useDesign } from '@/hooks/web/useDesign'
+
+const { getPrefixCls } = useDesign()
+
+const prefixCls = getPrefixCls('content-wrap')
+
+defineProps({
+  title: propTypes.string.def(''),
+  message: propTypes.string.def('')
+})
+</script>
+
+<template>
+  <ElCard :class="prefixCls" shadow="never">
+    <template v-if="title" #header>
+      <div class="flex items-center">
+        {{ title }}
+        <ElTooltip v-if="message" effect="dark" placement="right">
+          <template #content>
+            <div class="max-w-200px">{{ message }}</div>
+          </template>
+          <Icon class="ml-5px" icon="bi:question-circle-fill" :size="14" />
+        </ElTooltip>
+      </div>
+    </template>
+    <div>
+      <slot></slot>
+    </div>
+  </ElCard>
+</template>
+
+<style lang="less" scoped></style>

+ 1 - 0
src/components/Menu/src/Menu.vue

@@ -70,6 +70,7 @@ export default defineComponent({
 
     return () => (
       <div
+        id={prefixCls}
         class={[
           `${prefixCls} ${prefixCls}__${unref(menuMode)}`,
           'h-[100%] overflow-hidden z-100 flex-col bg-[var(--left-menu-bg-color)]',

+ 2 - 1
src/components/TabMenu/src/TabMenu.vue

@@ -12,7 +12,7 @@ import { cloneDeep } from 'lodash-es'
 import { filterMenusPath, initTabMap, tabPathMap } from './helper'
 import { useDesign } from '@/hooks/web/useDesign'
 
-const { getPrefixCls } = useDesign()
+const { getPrefixCls, variables } = useDesign()
 
 const prefixCls = getPrefixCls('tab-menu')
 
@@ -106,6 +106,7 @@ export default defineComponent({
 
     return () => (
       <div
+        id={`${variables.namespace}-menu`}
         class={[
           prefixCls,
           'relative bg-[var(--left-menu-bg-color)] top-1px',

+ 1 - 1
src/components/TagsView/src/TagsView.vue

@@ -148,7 +148,7 @@ watch(
 </script>
 
 <template>
-  <div :class="prefixCls" class="h-[var(--tags-view-height)] flex w-full relative">
+  <div :id="prefixCls" :class="prefixCls" class="h-[var(--tags-view-height)] flex w-full relative">
     <span
       :class="`${prefixCls}__tool`"
       class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer"

+ 41 - 0
src/hooks/web/useIntro.ts

@@ -0,0 +1,41 @@
+import introJs from 'intro.js'
+import { IntroJs, Step, Options } from 'intro.js'
+import 'intro.js/introjs.css'
+import { useI18n } from '@/hooks/web/useI18n'
+import { useDesign } from '@/hooks/web/useDesign'
+
+export const useIntro = (setps?: Step[], options?: Options) => {
+  const { t } = useI18n()
+
+  const { variables } = useDesign()
+
+  const defaultSetps: Step[] = setps || [
+    {
+      element: `#${variables.namespace}-menu`,
+      title: t('common.menu'),
+      intro: t('common.menuDes'),
+      position: 'right'
+    },
+    {
+      element: `#${variables.namespace}-tags-view`,
+      title: t('common.tagsView'),
+      intro: t('common.tagsViewDes'),
+      position: 'bottom'
+    }
+  ]
+
+  const defaultOptions: Options = options || {
+    prevLabel: t('common.prevLabel'),
+    nextLabel: t('common.nextLabel'),
+    skipLabel: t('common.skipLabel'),
+    doneLabel: t('common.doneLabel')
+  }
+
+  const introRef: IntroJs = introJs()
+
+  introRef.addSteps(defaultSetps).setOptions(defaultOptions)
+
+  return {
+    introRef
+  }
+}

+ 9 - 10
src/layout/components/useRenderLayout.tsx

@@ -11,7 +11,7 @@ import { useDesign } from '@/hooks/web/useDesign'
 
 const { getPrefixCls } = useDesign()
 
-const prefixCls = getPrefixCls('layout-content')
+const prefixCls = getPrefixCls('layout')
 
 const appStore = useAppStore()
 
@@ -54,7 +54,7 @@ export const useRenderLayout = () => {
         </div>
         <div
           class={[
-            prefixCls,
+            `${prefixCls}-content`,
             'absolute top-0 h-[100%]',
             {
               'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
@@ -69,7 +69,7 @@ export const useRenderLayout = () => {
           <ElScrollbar
             v-loading={pageLoading.value}
             class={[
-              `${prefixCls}-scrollbar`,
+              `${prefixCls}-content-scrollbar`,
               {
                 '!h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))] mt-[calc(var(--top-tool-height)+var(--tags-view-height))]':
                   fixedHeader.value
@@ -115,7 +115,7 @@ export const useRenderLayout = () => {
           <Menu class="!h-full"></Menu>
           <div
             class={[
-              prefixCls,
+              `${prefixCls}-content`,
               'h-[100%]',
               {
                 'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
@@ -129,7 +129,7 @@ export const useRenderLayout = () => {
             <ElScrollbar
               v-loading={pageLoading.value}
               class={[
-                `${prefixCls}-scrollbar`,
+                `${prefixCls}-content-scrollbar`,
                 {
                   '!h-[calc(100%-var(--tags-view-height))] mt-[calc(var(--tags-view-height))]':
                     fixedHeader.value && tagsView.value
@@ -168,11 +168,11 @@ export const useRenderLayout = () => {
           <Menu class="flex-1 px-10px h-[var(--top-tool-height)]"></Menu>
           <ToolHeader></ToolHeader>
         </div>
-        <div class={[prefixCls, 'v-content h-full w-full']}>
+        <div class={[`${prefixCls}-content`, 'h-full w-full']}>
           <ElScrollbar
             v-loading={pageLoading.value}
             class={[
-              `${prefixCls}-scrollbar`,
+              `${prefixCls}-content-scrollbar`,
               {
                 'mt-[var(--tags-view-height)]': fixedHeader.value
               }
@@ -207,10 +207,9 @@ export const useRenderLayout = () => {
         </div>
         <div class="absolute top-[var(--logo-height)] left-0 w-full h-[calc(100%-var(--logo-height))] flex">
           <TabMenu></TabMenu>
-          {/* <Menu class="!h-full"></Menu> */}
           <div
             class={[
-              prefixCls,
+              `${prefixCls}-content`,
               'h-[100%]',
               {
                 'w-[calc(100%-var(--tab-menu-min-width))] left-[var(--tab-menu-min-width)]':
@@ -224,7 +223,7 @@ export const useRenderLayout = () => {
             <ElScrollbar
               v-loading={pageLoading.value}
               class={[
-                `${prefixCls}-scrollbar`,
+                `${prefixCls}-content-scrollbar`,
                 {
                   '!h-[calc(100%-var(--tags-view-height))] mt-[calc(var(--tags-view-height))]':
                     fixedHeader.value && tagsView.value

+ 19 - 2
src/locales/en.ts

@@ -17,7 +17,17 @@ export default {
     closeTheLeftTab: 'Close left',
     closeTheRightTab: 'Close right',
     closeOther: 'Close other',
-    closeAll: 'Close all'
+    closeAll: 'Close all',
+    prevLabel: 'Prev',
+    nextLabel: 'Next',
+    skipLabel: 'Jump',
+    doneLabel: 'End',
+    menu: 'Menu',
+    menuDes: 'Menu bar rendered in routed structure',
+    collapse: 'Collapse',
+    collapseDes: 'Expand and zoom the menu bar',
+    tagsView: 'Tags view',
+    tagsViewDes: 'Used to record routing history'
   },
   setting: {
     projectSetting: 'Project setting',
@@ -72,7 +82,8 @@ export default {
     menu2: 'Menu2',
     dashboard: 'Dashboard',
     analysis: 'Analysis',
-    workplace: 'Workplace'
+    workplace: 'Workplace',
+    guide: 'Guide'
   },
   analysis: {
     newUser: 'New user',
@@ -173,5 +184,11 @@ export default {
     timeSelect: 'Time Select',
     inputPassword: 'input Password',
     passwordStrength: 'Password Strength'
+  },
+  guideDemo: {
+    guide: 'Guide',
+    start: 'Start',
+    message:
+      'The guide page is very useful for some people who enter the project for the first time. You can briefly introduce the functions of the project. The boot page is based on intro js'
   }
 }

+ 19 - 2
src/locales/zh-CN.ts

@@ -17,7 +17,17 @@ export default {
     closeTheLeftTab: '关闭左侧标签页',
     closeTheRightTab: '关闭右侧标签页',
     closeOther: '关闭其他标签页',
-    closeAll: '关闭全部标签页'
+    closeAll: '关闭全部标签页',
+    prevLabel: '上一步',
+    nextLabel: '下一步',
+    skipLabel: '跳过',
+    doneLabel: '结束',
+    menu: '菜单',
+    menuDes: '以路由的结构渲染的菜单栏',
+    collapse: '展开缩收',
+    collapseDes: '展开和缩放菜单栏',
+    tagsView: '标签页',
+    tagsViewDes: '用于记录路由历史记录'
   },
   setting: {
     projectSetting: '项目配置',
@@ -72,7 +82,8 @@ export default {
     menu2: '菜单2',
     dashboard: '首页',
     analysis: '分析页',
-    workplace: '工作台'
+    workplace: '工作台',
+    guide: '引导'
   },
   analysis: {
     newUser: '新增用户',
@@ -173,5 +184,11 @@ export default {
     timeSelect: '时间选择',
     inputPassword: '密码输入框',
     passwordStrength: '密码强度'
+  },
+  guideDemo: {
+    guide: '引导页',
+    start: '开始',
+    message:
+      '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。引导页基于 intro.js'
   }
 }

+ 17 - 0
src/router/index.ts

@@ -68,6 +68,23 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
       }
     ]
   },
+  {
+    path: '/guide',
+    component: Layout,
+    name: 'Guide',
+    meta: {},
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/Guide/Guide.vue'),
+        name: 'GuideDemo',
+        meta: {
+          title: t('router.guide'),
+          icon: 'cib:telegram-plane'
+        }
+      }
+    ]
+  },
   {
     path: '/level',
     component: Layout,

+ 20 - 0
src/views/Guide/Guide.vue

@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { ContentWrap } from '@/components/ContentWrap'
+import { useI18n } from '@/hooks/web/useI18n'
+import { useIntro } from '@/hooks/web/useIntro'
+import { ElButton } from 'element-plus'
+
+const { t } = useI18n()
+
+const { introRef } = useIntro()
+
+const guideStart = () => {
+  introRef.start()
+}
+</script>
+
+<template>
+  <ContentWrap :title="t('guideDemo.guide')" :message="t('guideDemo.message')">
+    <ElButton type="primary" @click="guideStart">{{ t('guideDemo.start') }}</ElButton>
+  </ContentWrap>
+</template>

+ 6 - 1
tsconfig.json

@@ -23,7 +23,12 @@
     "paths": {
       "@/*": ["src/*"]
     },
-    "types": ["@intlify/vite-plugin-vue-i18n/client", "vite/client", "element-plus/global"],
+    "types": [
+      "@intlify/vite-plugin-vue-i18n/client",
+      "vite/client",
+      "element-plus/global",
+      "@types/intro.js"
+    ],
     "typeRoots": ["./node_modules/@types/", "./types"]
   },
   "include": ["src/**/*", "types/**/*.d.ts", "mock/**/*.ts"],

+ 2 - 1
vite.config.ts

@@ -132,7 +132,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
         'axios',
         'qs',
         'echarts',
-        'echarts-wordcloud'
+        'echarts-wordcloud',
+        'intro.js'
       ]
     }
   }