mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 07:43:42 +08:00 
			
		
		
		
	feat(projects): 登录页面实现
This commit is contained in:
		@@ -12,7 +12,6 @@ module.exports = {
 | 
			
		||||
  eslintIntegration: false, //不让prettier使用eslint的代码格式进行校验
 | 
			
		||||
  htmlWhitespaceSensitivity: 'ignore', // 指定HTML文件的全局空白区域敏感度 有效选项:"css"- 遵守CSS display属性的默认值。"strict" - 空格被认为是敏感的。"ignore" - 空格被认为是不敏感的。html 中空格也会占位,影响布局,prettier 格式化的时候可能会将文本换行,造成布局错乱
 | 
			
		||||
  ignorePath: '.prettierignore', // 不使用prettier格式化的文件填写在项目的.prettierignore文件中
 | 
			
		||||
  jsxBracketSameLine: false, // 在jsx中把'>' 是否单独放一行
 | 
			
		||||
  jsxSingleQuote: false, // 在jsx中使用单引号代替双引号
 | 
			
		||||
  // parser: 'babylon', // 格式化的解析器,默认是babylon
 | 
			
		||||
  requireConfig: false, // Require a 'prettierconfig' to format prettier
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
    "@vueuse/core": "^6.3.2",
 | 
			
		||||
    "axios": "^0.21.4",
 | 
			
		||||
    "chroma-js": "^2.1.2",
 | 
			
		||||
    "dayjs": "^1.10.6",
 | 
			
		||||
    "dayjs": "^1.10.7",
 | 
			
		||||
    "form-data": "^4.0.0",
 | 
			
		||||
    "naive-ui": "^2.18.1",
 | 
			
		||||
    "pinia": "^2.0.0-rc.4",
 | 
			
		||||
@@ -33,7 +33,7 @@
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@commitlint/cli": "^13.1.0",
 | 
			
		||||
    "@commitlint/config-conventional": "^13.1.0",
 | 
			
		||||
    "@iconify/json": "^1.1.399",
 | 
			
		||||
    "@iconify/json": "^1.1.400",
 | 
			
		||||
    "@iconify/vue": "^3.0.0",
 | 
			
		||||
    "@types/chroma-js": "^2.1.3",
 | 
			
		||||
    "@types/nprogress": "^0.2.0",
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
    "patch-package": "^6.4.7",
 | 
			
		||||
    "postinstall-postinstall": "^2.1.0",
 | 
			
		||||
    "prettier": "^2.4.0",
 | 
			
		||||
    "sass": "^1.39.0",
 | 
			
		||||
    "sass": "^1.39.2",
 | 
			
		||||
    "typescript": "^4.4.2",
 | 
			
		||||
    "unplugin-icons": "^0.7.6",
 | 
			
		||||
    "unplugin-vue-components": "^0.15.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										39
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -3,7 +3,7 @@ lockfileVersion: 5.3
 | 
			
		||||
specifiers:
 | 
			
		||||
  '@commitlint/cli': ^13.1.0
 | 
			
		||||
  '@commitlint/config-conventional': ^13.1.0
 | 
			
		||||
  '@iconify/json': ^1.1.399
 | 
			
		||||
  '@iconify/json': ^1.1.400
 | 
			
		||||
  '@iconify/vue': ^3.0.0
 | 
			
		||||
  '@types/chroma-js': ^2.1.3
 | 
			
		||||
  '@types/nprogress': ^0.2.0
 | 
			
		||||
@@ -21,7 +21,7 @@ specifiers:
 | 
			
		||||
  commitizen: ^4.2.4
 | 
			
		||||
  cz-conventional-changelog: ^3.3.0
 | 
			
		||||
  cz-customizable: ^6.3.0
 | 
			
		||||
  dayjs: ^1.10.6
 | 
			
		||||
  dayjs: ^1.10.7
 | 
			
		||||
  dotenv: ^10.0.0
 | 
			
		||||
  eslint: ^7.32.0
 | 
			
		||||
  eslint-config-airbnb-base: ^14.2.1
 | 
			
		||||
@@ -38,7 +38,7 @@ specifiers:
 | 
			
		||||
  postinstall-postinstall: ^2.1.0
 | 
			
		||||
  prettier: ^2.4.0
 | 
			
		||||
  qs: ^6.10.1
 | 
			
		||||
  sass: ^1.39.0
 | 
			
		||||
  sass: ^1.39.2
 | 
			
		||||
  smoothscroll-polyfill: ^0.4.4
 | 
			
		||||
  typescript: ^4.4.2
 | 
			
		||||
  unplugin-icons: ^0.7.6
 | 
			
		||||
@@ -55,7 +55,7 @@ dependencies:
 | 
			
		||||
  '@vueuse/core': registry.nlark.com/@vueuse/core/6.3.2_vue@3.2.10
 | 
			
		||||
  axios: registry.nlark.com/axios/0.21.4
 | 
			
		||||
  chroma-js: registry.nlark.com/chroma-js/2.1.2
 | 
			
		||||
  dayjs: registry.nlark.com/dayjs/1.10.6
 | 
			
		||||
  dayjs: registry.nlark.com/dayjs/1.10.7
 | 
			
		||||
  form-data: 4.0.0
 | 
			
		||||
  naive-ui: registry.nlark.com/naive-ui/2.18.1_vue@3.2.10
 | 
			
		||||
  pinia: registry.nlark.com/pinia/2.0.0-rc.4_typescript@4.4.2+vue@3.2.10
 | 
			
		||||
@@ -67,7 +67,7 @@ dependencies:
 | 
			
		||||
devDependencies:
 | 
			
		||||
  '@commitlint/cli': registry.nlark.com/@commitlint/cli/13.1.0
 | 
			
		||||
  '@commitlint/config-conventional': registry.nlark.com/@commitlint/config-conventional/13.1.0
 | 
			
		||||
  '@iconify/json': registry.nlark.com/@iconify/json/1.1.399
 | 
			
		||||
  '@iconify/json': registry.nlark.com/@iconify/json/1.1.400
 | 
			
		||||
  '@iconify/vue': registry.nlark.com/@iconify/vue/3.0.0_vue@3.2.10
 | 
			
		||||
  '@types/chroma-js': registry.nlark.com/@types/chroma-js/2.1.3
 | 
			
		||||
  '@types/nprogress': registry.nlark.com/@types/nprogress/0.2.0
 | 
			
		||||
@@ -94,9 +94,9 @@ devDependencies:
 | 
			
		||||
  patch-package: registry.nlark.com/patch-package/6.4.7
 | 
			
		||||
  postinstall-postinstall: registry.nlark.com/postinstall-postinstall/2.1.0
 | 
			
		||||
  prettier: registry.nlark.com/prettier/2.4.0
 | 
			
		||||
  sass: registry.nlark.com/sass/1.39.0
 | 
			
		||||
  sass: registry.nlark.com/sass/1.39.2
 | 
			
		||||
  typescript: registry.nlark.com/typescript/4.4.2
 | 
			
		||||
  unplugin-icons: registry.nlark.com/unplugin-icons/0.7.6_5d72f6392975d02ee45d0acbc066efa3
 | 
			
		||||
  unplugin-icons: registry.nlark.com/unplugin-icons/0.7.6_ab88f3ed6cd34af3ce86467cf77018e8
 | 
			
		||||
  unplugin-vue-components: registry.nlark.com/unplugin-vue-components/0.15.0_vite@2.5.6+vue@3.2.10
 | 
			
		||||
  vite: registry.nlark.com/vite/2.5.6
 | 
			
		||||
  vite-plugin-html: registry.nlark.com/vite-plugin-html/2.1.0_vite@2.5.6
 | 
			
		||||
@@ -2345,10 +2345,10 @@ packages:
 | 
			
		||||
    version: 1.0.10
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  registry.nlark.com/@iconify/json/1.1.399:
 | 
			
		||||
    resolution: {integrity: sha1-hdBstQTkqkRkdYzo9n//lH1VKDU=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/@iconify/json/download/@iconify/json-1.1.399.tgz}
 | 
			
		||||
  registry.nlark.com/@iconify/json/1.1.400:
 | 
			
		||||
    resolution: {integrity: sha1-ZlZgMgOxo4klDH396+BrL9+LGwE=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/@iconify/json/download/@iconify/json-1.1.400.tgz}
 | 
			
		||||
    name: '@iconify/json'
 | 
			
		||||
    version: 1.1.399
 | 
			
		||||
    version: 1.1.400
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  registry.nlark.com/@iconify/vue/3.0.0_vue@3.2.10:
 | 
			
		||||
@@ -3461,10 +3461,10 @@ packages:
 | 
			
		||||
    engines: {node: '>=0.11'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  registry.nlark.com/dayjs/1.10.6:
 | 
			
		||||
    resolution: {integrity: sha1-KIsqqC8thBimydTfWJjAc3rQKmM=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/dayjs/download/dayjs-1.10.6.tgz}
 | 
			
		||||
  registry.nlark.com/dayjs/1.10.7:
 | 
			
		||||
    resolution: {integrity: sha1-LPX5Gt0oEWdIRAhmoKHSbzps5Gg=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/dayjs/download/dayjs-1.10.7.tgz}
 | 
			
		||||
    name: dayjs
 | 
			
		||||
    version: 1.10.6
 | 
			
		||||
    version: 1.10.7
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  registry.nlark.com/debug/2.6.9:
 | 
			
		||||
@@ -5176,10 +5176,10 @@ packages:
 | 
			
		||||
      tslib: registry.nlark.com/tslib/1.14.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  registry.nlark.com/sass/1.39.0:
 | 
			
		||||
    resolution: {integrity: sha1-bGRpXRxDd2fI8aTkcSiOgx+B0DU=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/sass/download/sass-1.39.0.tgz}
 | 
			
		||||
  registry.nlark.com/sass/1.39.2:
 | 
			
		||||
    resolution: {integrity: sha1-FoGWQ3j1jXb8ZKalAmGb1ayZ9mA=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/sass/download/sass-1.39.2.tgz?cache=0&sync_timestamp=1631232791563&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsass%2Fdownload%2Fsass-1.39.2.tgz}
 | 
			
		||||
    name: sass
 | 
			
		||||
    version: 1.39.0
 | 
			
		||||
    version: 1.39.2
 | 
			
		||||
    engines: {node: '>=8.9.0'}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
@@ -5532,7 +5532,7 @@ packages:
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  registry.nlark.com/unplugin-icons/0.7.6_5d72f6392975d02ee45d0acbc066efa3:
 | 
			
		||||
  registry.nlark.com/unplugin-icons/0.7.6_ab88f3ed6cd34af3ce86467cf77018e8:
 | 
			
		||||
    resolution: {integrity: sha1-CLYc+b2imJyKfcbU6oBStluwkKA=, registry: https://registry.npm.taobao.org/, tarball: https://registry.nlark.com/unplugin-icons/download/unplugin-icons-0.7.6.tgz}
 | 
			
		||||
    id: registry.nlark.com/unplugin-icons/0.7.6
 | 
			
		||||
    name: unplugin-icons
 | 
			
		||||
@@ -5553,7 +5553,7 @@ packages:
 | 
			
		||||
      vue-template-es2015-compiler:
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@iconify/json': registry.nlark.com/@iconify/json/1.1.399
 | 
			
		||||
      '@iconify/json': registry.nlark.com/@iconify/json/1.1.400
 | 
			
		||||
      '@iconify/json-tools': registry.nlark.com/@iconify/json-tools/1.0.10
 | 
			
		||||
      '@vue/compiler-sfc': registry.nlark.com/@vue/compiler-sfc/3.2.11
 | 
			
		||||
      has-pkg: registry.nlark.com/has-pkg/0.0.1
 | 
			
		||||
@@ -5858,6 +5858,9 @@ packages:
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@vue/composition-api': ^1.0.0-rc.1
 | 
			
		||||
      vue: ^3.0.0-0 || ^2.6.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@vue/composition-api':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      vue: registry.nlark.com/vue/3.2.10
 | 
			
		||||
    dev: false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/common/logo-fill.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/img/common/logo-fill.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 7.8 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 705 KiB  | 
							
								
								
									
										39
									
								
								src/components/common/LoginBg/components/CornerBottom.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/components/common/LoginBg/components/CornerBottom.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <svg
 | 
			
		||||
    version="1.1"
 | 
			
		||||
    xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
    xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
    height="896"
 | 
			
		||||
    width="967.8852157128662"
 | 
			
		||||
  >
 | 
			
		||||
    <defs>
 | 
			
		||||
      <path
 | 
			
		||||
        id="path-2"
 | 
			
		||||
        opacity="1"
 | 
			
		||||
        fill-rule="evenodd"
 | 
			
		||||
        d="M896,448 C1142.6325445712241,465.5747656464056 695.2579309733121,896 448,896 C200.74206902668806,896 5.684341886080802e-14,695.2579309733121 0,448.0000000000001 C0,200.74206902668806 200.74206902668791,5.684341886080802e-14 447.99999999999994,0 C695.2579309733121,0 475,418 896,448Z"
 | 
			
		||||
      />
 | 
			
		||||
      <linearGradient id="linearGradient-3" x1="0.5" y1="0" x2="0.5" y2="1">
 | 
			
		||||
        <stop offset="0" :stop-color="startColor" stop-opacity="1" />
 | 
			
		||||
        <stop offset="1" :stop-color="endColor" stop-opacity="1" />
 | 
			
		||||
      </linearGradient>
 | 
			
		||||
    </defs>
 | 
			
		||||
    <g opacity="1">
 | 
			
		||||
      <use xlink:href="#path-2" fill="url(#linearGradient-3)" fill-opacity="1" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </svg>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
defineProps({
 | 
			
		||||
  startColor: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '#28aff0'
 | 
			
		||||
  },
 | 
			
		||||
  endColor: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '#120fc4'
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										33
									
								
								src/components/common/LoginBg/components/CornerTop.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/components/common/LoginBg/components/CornerTop.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <svg height="1337" width="1337">
 | 
			
		||||
    <defs>
 | 
			
		||||
      <path
 | 
			
		||||
        id="path-1"
 | 
			
		||||
        opacity="1"
 | 
			
		||||
        fill-rule="evenodd"
 | 
			
		||||
        d="M1337,668.5 C1337,1037.455193874239 1037.455193874239,1337 668.5,1337 C523.6725684305388,1337 337,1236 370.50000000000006,1094 C434.03835568300906,824.6732385973953 6.906089672974592e-14,892.6277623047779 0,668.5000000000001 C0,299.5448061257611 299.5448061257609,1.1368683772161603e-13 668.4999999999999,0 C1037.455193874239,0 1337,299.544806125761 1337,668.5Z"
 | 
			
		||||
      />
 | 
			
		||||
      <linearGradient id="linearGradient-2" x1="0.79" y1="0.62" x2="0.21" y2="0.86">
 | 
			
		||||
        <stop offset="0" :stop-color="startColor" stop-opacity="1" />
 | 
			
		||||
        <stop offset="1" :stop-color="endColor" stop-opacity="1" />
 | 
			
		||||
      </linearGradient>
 | 
			
		||||
    </defs>
 | 
			
		||||
    <g opacity="1">
 | 
			
		||||
      <use xlink:href="#path-1" fill="url(#linearGradient-2)" fill-opacity="1" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </svg>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
defineProps({
 | 
			
		||||
  startColor: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '#583ed5'
 | 
			
		||||
  },
 | 
			
		||||
  endColor: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '#17d7fa'
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										4
									
								
								src/components/common/LoginBg/components/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/components/common/LoginBg/components/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
import CornerTop from './CornerTop.vue';
 | 
			
		||||
import CornerBottom from './CornerBottom.vue';
 | 
			
		||||
 | 
			
		||||
export { CornerTop, CornerBottom };
 | 
			
		||||
							
								
								
									
										15
									
								
								src/components/common/LoginBg/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/components/common/LoginBg/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="absolute-lt w-full h-full overflow-hidden">
 | 
			
		||||
    <div class="absolute -right-300px -top-900px">
 | 
			
		||||
      <corner-top />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="absolute -left-200px -bottom-400px">
 | 
			
		||||
      <corner-bottom />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { CornerTop, CornerBottom } from './components';
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										19
									
								
								src/components/common/SystemLogo/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/components/common/SystemLogo/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <img :src="logoSrc" alt="" />
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import logo from '@/assets/img/common/logo.png';
 | 
			
		||||
import logoFill from '@/assets/img/common/logo-fill.png';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  fill: {
 | 
			
		||||
    type: Boolean,
 | 
			
		||||
    default: false
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const logoSrc = computed(() => (props.fill ? logoFill : logo));
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import AppProviderContent from './AppProviderContent/index.vue';
 | 
			
		||||
import SystemLogo from './SystemLogo/index.vue';
 | 
			
		||||
import ExceptionSvg from './ExceptionSvg/index.vue';
 | 
			
		||||
import LoginBg from './LoginBg/index.vue';
 | 
			
		||||
import BannerSvg from './BannerSvg/index.vue';
 | 
			
		||||
import CountTo from './CountTo/index.vue';
 | 
			
		||||
 | 
			
		||||
export { AppProviderContent, ExceptionSvg, BannerSvg, CountTo };
 | 
			
		||||
export { AppProviderContent, SystemLogo, ExceptionSvg, LoginBg, BannerSvg };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/components/custom/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/components/custom/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
import CountTo from './CountTo/index.vue';
 | 
			
		||||
 | 
			
		||||
export { CountTo };
 | 
			
		||||
@@ -1 +1,2 @@
 | 
			
		||||
export { AppProviderContent, ExceptionSvg, BannerSvg, CountTo } from './common';
 | 
			
		||||
export { AppProviderContent, SystemLogo, ExceptionSvg, LoginBg, BannerSvg } from './common';
 | 
			
		||||
export { CountTo } from './custom';
 | 
			
		||||
 
 | 
			
		||||
@@ -19,3 +19,12 @@ export enum EnumDataType {
 | 
			
		||||
  set = '[object Set]',
 | 
			
		||||
  map = '[object Map]'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 登录模块 */
 | 
			
		||||
export enum EnumLoginModule {
 | 
			
		||||
  'pwd-login' = '账密登录',
 | 
			
		||||
  'code-login' = '手机验证码登录',
 | 
			
		||||
  'register' = '注册',
 | 
			
		||||
  'reset-pwd' = '重置密码',
 | 
			
		||||
  'bind-wechat' = '微信绑定'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
export { ContentType, EnumDataType } from './common';
 | 
			
		||||
export { ContentType, EnumDataType, EnumLoginModule } from './common';
 | 
			
		||||
export { EnumAnimate } from './animate';
 | 
			
		||||
export { EnumNavMode, EnumNavTheme } from './theme';
 | 
			
		||||
export { EnumRoutePaths } from './route';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								src/hooks/business/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/hooks/business/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
import useCountDown from './useCountDown';
 | 
			
		||||
import useSmsCode from './useSmsCode';
 | 
			
		||||
 | 
			
		||||
export { useCountDown, useSmsCode };
 | 
			
		||||
							
								
								
									
										46
									
								
								src/hooks/business/useCountDown.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/hooks/business/useCountDown.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
import { ref, computed } from 'vue';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 倒计时
 | 
			
		||||
 * @param second - 倒计时的时间(s)
 | 
			
		||||
 */
 | 
			
		||||
export default function useCountDown(second: number) {
 | 
			
		||||
  if (second <= 0 && second % 1 !== 0) {
 | 
			
		||||
    throw Error('倒计时的时间应该为一个正整数!');
 | 
			
		||||
  }
 | 
			
		||||
  const counts = ref(0);
 | 
			
		||||
  const isCounting = computed(() => Boolean(counts.value));
 | 
			
		||||
 | 
			
		||||
  let intervalId: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 开始计时
 | 
			
		||||
   * @param updateSecond - 更改初时传入的倒计时时间
 | 
			
		||||
   */
 | 
			
		||||
  function start(updateSecond: number = second) {
 | 
			
		||||
    if (!counts.value) {
 | 
			
		||||
      counts.value = updateSecond;
 | 
			
		||||
      intervalId = setInterval(() => {
 | 
			
		||||
        counts.value -= 1;
 | 
			
		||||
        if (counts.value <= 0) {
 | 
			
		||||
          clearInterval(intervalId);
 | 
			
		||||
        }
 | 
			
		||||
      }, 1000);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 停止计时
 | 
			
		||||
   */
 | 
			
		||||
  function stop() {
 | 
			
		||||
    intervalId = clearInterval(intervalId);
 | 
			
		||||
    counts.value = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    counts,
 | 
			
		||||
    isCounting,
 | 
			
		||||
    start,
 | 
			
		||||
    stop
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/hooks/business/useSmsCode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/hooks/business/useSmsCode.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import useCountDown from './useCountDown';
 | 
			
		||||
 | 
			
		||||
export default function useSmsCode() {
 | 
			
		||||
  const { counts, start, isCounting } = useCountDown(60);
 | 
			
		||||
  const initLabel = '获取验证码';
 | 
			
		||||
  const countingLabel = (second: number) => `${second}秒后重新获取`;
 | 
			
		||||
  const label = computed(() => (isCounting.value ? countingLabel(counts.value) : initLabel));
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    label,
 | 
			
		||||
    start,
 | 
			
		||||
    isCounting
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								src/hooks/common/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/hooks/common/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
import useAppTitle from './useAppTitle';
 | 
			
		||||
import useCreateContext from './useCreateContext';
 | 
			
		||||
import useRouterChange from './useRouterChange';
 | 
			
		||||
 | 
			
		||||
export { useAppTitle, useCreateContext, useRouterChange };
 | 
			
		||||
							
								
								
									
										6
									
								
								src/hooks/common/useAppTitle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/hooks/common/useAppTitle.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/** 项目名称 */
 | 
			
		||||
export default function useAppTitle() {
 | 
			
		||||
  const title = import.meta.env.VITE_APP_TITLE as string;
 | 
			
		||||
 | 
			
		||||
  return title;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/hooks/common/useCreateContext.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/hooks/common/useCreateContext.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import { provide, inject } from 'vue';
 | 
			
		||||
import type { InjectionKey } from 'vue';
 | 
			
		||||
 | 
			
		||||
/** 创建共享上下文状态 */
 | 
			
		||||
export default function useCreateContext<T>(contextName: string = 'context') {
 | 
			
		||||
  const injectKey: InjectionKey<T> = Symbol(contextName);
 | 
			
		||||
 | 
			
		||||
  function useProvider(shareState: T) {
 | 
			
		||||
    provide(injectKey, shareState);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function useContext() {
 | 
			
		||||
    return inject(injectKey);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    useProvider,
 | 
			
		||||
    useContext
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/hooks/common/useRouterChange.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/hooks/common/useRouterChange.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import { EnumRoutePaths } from '@/enum';
 | 
			
		||||
import { RouteNameMap } from '@/router';
 | 
			
		||||
import type { LoginModuleType } from '@/interface';
 | 
			
		||||
 | 
			
		||||
export default function useRouterChange() {
 | 
			
		||||
  const router = useRouter();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 跳转登录页面(通过vue路由)
 | 
			
		||||
   * @param module - 展示的登录模块
 | 
			
		||||
   * @param redirectUrl - 登录后重定向的页面路径
 | 
			
		||||
   */
 | 
			
		||||
  function toLogin(module: LoginModuleType = 'pwd-login', redirectUrl?: string) {
 | 
			
		||||
    router.push({
 | 
			
		||||
      name: RouteNameMap.get('login'),
 | 
			
		||||
      params: {
 | 
			
		||||
        module
 | 
			
		||||
      },
 | 
			
		||||
      query: {
 | 
			
		||||
        redirectUrl
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * 跳转登录页面(通过window.location)
 | 
			
		||||
   * @param module - 展示的登录模块
 | 
			
		||||
   * @param redirectUrl - 登录后重定向的页面路径
 | 
			
		||||
   */
 | 
			
		||||
  function toLoginByLocation(module: LoginModuleType = 'pwd-login', redirectUrl?: string) {
 | 
			
		||||
    let href = `${window.location.origin + EnumRoutePaths.login}/${module}`;
 | 
			
		||||
    if (redirectUrl) {
 | 
			
		||||
      href += redirectUrl;
 | 
			
		||||
    }
 | 
			
		||||
    window.location.href = href;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    toLogin,
 | 
			
		||||
    toLoginByLocation
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,2 @@
 | 
			
		||||
export { useAppTitle, useCreateContext, useRouterChange } from './common';
 | 
			
		||||
export { useCountDown, useSmsCode } from './business';
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
import { EnumRoutePaths, EnumLoginModule } from '@/enum';
 | 
			
		||||
 | 
			
		||||
export type RoutePathKey = keyof typeof EnumRoutePaths;
 | 
			
		||||
export type LoginModuleType = keyof typeof EnumLoginModule;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,3 @@
 | 
			
		||||
export { UserInfo } from './business';
 | 
			
		||||
export { ThemeSettings, NavMode, AnimateType } from './theme';
 | 
			
		||||
export { RoutePathKey, LoginModuleType } from './common';
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,11 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { useAppStore, useThemeStore } from '@/store';
 | 
			
		||||
import { useAppTitle } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
const app = useAppStore();
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
const showTitle = computed(() => !app.menu.collapsed && theme.navStyle.mode !== 'vertical-mix');
 | 
			
		||||
const title = import.meta.env.VITE_APP_TITLE as string;
 | 
			
		||||
const title = useAppTitle();
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,9 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <h3 class="text-center text-18px text-error">菜单</h3>
 | 
			
		||||
    <div class="flex-center h-48px">
 | 
			
		||||
      <router-link to="/login" class="text-primary text-18px">登录页</router-link>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import BasicLayout from './BasicLayout/index.vue';
 | 
			
		||||
import BlankLayout from './BlankLayout/index.vue';
 | 
			
		||||
import BlankChildLayout from './BlankChildLayout/index.vue';
 | 
			
		||||
 | 
			
		||||
export { BasicLayout, BlankLayout, BlankChildLayout };
 | 
			
		||||
export { BasicLayout, BlankLayout };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
 | 
			
		||||
import type { App } from 'vue';
 | 
			
		||||
import type { RouteRecordRaw } from 'vue-router';
 | 
			
		||||
import { constantRoutes, customRoutes } from './routes';
 | 
			
		||||
import { constantRoutes, customRoutes, RouteNameMap } from './routes';
 | 
			
		||||
import createRouterGuide from './permission';
 | 
			
		||||
 | 
			
		||||
const routes: Array<RouteRecordRaw> = [...customRoutes, ...constantRoutes];
 | 
			
		||||
@@ -19,3 +19,5 @@ export async function setupRouter(app: App) {
 | 
			
		||||
  createRouterGuide(router);
 | 
			
		||||
  await router.isReady();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { RouteNameMap };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,15 @@
 | 
			
		||||
import type { RouteRecordRaw } from 'vue-router';
 | 
			
		||||
import { BasicLayout, BlankLayout, BlankChildLayout } from '@/layouts';
 | 
			
		||||
import { BasicLayout, BlankLayout } from '@/layouts';
 | 
			
		||||
import { EnumRoutePaths } from '@/enum';
 | 
			
		||||
 | 
			
		||||
type RouteKey = keyof typeof EnumRoutePaths;
 | 
			
		||||
import type { RoutePathKey, LoginModuleType } from '@/interface';
 | 
			
		||||
import { getLoginModuleRegExp } from '@/utils';
 | 
			
		||||
 | 
			
		||||
/** 路由名称 */
 | 
			
		||||
export const RouteNameMap = new Map<RouteKey, RouteKey>((Object.keys(EnumRoutePaths) as RouteKey[]).map(v => [v, v]));
 | 
			
		||||
export const RouteNameMap = new Map<RoutePathKey, RoutePathKey>(
 | 
			
		||||
  (Object.keys(EnumRoutePaths) as RoutePathKey[]).map(v => [v, v])
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const loginModuleRegExp = getLoginModuleRegExp();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 固定不变的路由
 | 
			
		||||
@@ -21,8 +25,14 @@ export const constantRoutes: Array<RouteRecordRaw> = [
 | 
			
		||||
      // 登录
 | 
			
		||||
      {
 | 
			
		||||
        name: RouteNameMap.get('login'),
 | 
			
		||||
        path: EnumRoutePaths.login,
 | 
			
		||||
        path: `${EnumRoutePaths.login}/:module(/${loginModuleRegExp}/)?`,
 | 
			
		||||
        component: () => import('@/views/system/login/index.vue'),
 | 
			
		||||
        props: route => {
 | 
			
		||||
          const moduleType: LoginModuleType = (route.params.module as LoginModuleType) || 'pwd-login';
 | 
			
		||||
          return {
 | 
			
		||||
            module: moduleType
 | 
			
		||||
          };
 | 
			
		||||
        },
 | 
			
		||||
        meta: {
 | 
			
		||||
          fullPage: true
 | 
			
		||||
        }
 | 
			
		||||
@@ -70,47 +80,45 @@ export const customRoutes: Array<RouteRecordRaw> = [
 | 
			
		||||
  {
 | 
			
		||||
    name: 'root',
 | 
			
		||||
    path: '/',
 | 
			
		||||
    redirect: { name: RouteNameMap.get('dashboard-analysis') }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'dashboard',
 | 
			
		||||
    path: '/dashboard',
 | 
			
		||||
    component: BasicLayout,
 | 
			
		||||
    redirect: { name: RouteNameMap.get('dashboard-analysis') },
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: RouteNameMap.get('dashboard-analysis'),
 | 
			
		||||
        path: EnumRoutePaths['dashboard-analysis'],
 | 
			
		||||
        component: () => import('@/views/dashboard/analysis/index.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: RouteNameMap.get('dashboard-workbench'),
 | 
			
		||||
        path: EnumRoutePaths['dashboard-workbench'],
 | 
			
		||||
        component: () => import('@/views/dashboard/workbench/index.vue')
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'exception',
 | 
			
		||||
    path: '/exception',
 | 
			
		||||
    component: BasicLayout,
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'dashboard',
 | 
			
		||||
        path: '/dashboard',
 | 
			
		||||
        component: BlankChildLayout,
 | 
			
		||||
        children: [
 | 
			
		||||
          {
 | 
			
		||||
            name: RouteNameMap.get('dashboard-analysis'),
 | 
			
		||||
            path: EnumRoutePaths['dashboard-analysis'],
 | 
			
		||||
            component: () => import('@/views/dashboard/analysis/index.vue')
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: RouteNameMap.get('dashboard-workbench'),
 | 
			
		||||
            path: EnumRoutePaths['dashboard-workbench'],
 | 
			
		||||
            component: () => import('@/views/dashboard/workbench/index.vue')
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
        name: RouteNameMap.get('exception-403'),
 | 
			
		||||
        path: EnumRoutePaths['exception-403'],
 | 
			
		||||
        component: () => import('@/views/system/exception/403.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'exception',
 | 
			
		||||
        path: '/exception',
 | 
			
		||||
        component: BlankChildLayout,
 | 
			
		||||
        children: [
 | 
			
		||||
          {
 | 
			
		||||
            name: RouteNameMap.get('exception-403'),
 | 
			
		||||
            path: EnumRoutePaths['exception-403'],
 | 
			
		||||
            component: () => import('@/views/system/exception/403.vue')
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: RouteNameMap.get('exception-404'),
 | 
			
		||||
            path: EnumRoutePaths['exception-404'],
 | 
			
		||||
            component: () => import('@/views/system/exception/404.vue')
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: RouteNameMap.get('exception-500'),
 | 
			
		||||
            path: EnumRoutePaths['exception-500'],
 | 
			
		||||
            component: () => import('@/views/system/exception/500.vue')
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
        name: RouteNameMap.get('exception-404'),
 | 
			
		||||
        path: EnumRoutePaths['exception-404'],
 | 
			
		||||
        component: () => import('@/views/system/exception/404.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: RouteNameMap.get('exception-500'),
 | 
			
		||||
        path: EnumRoutePaths['exception-500'],
 | 
			
		||||
        component: () => import('@/views/system/exception/500.vue')
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,3 @@ html {
 | 
			
		||||
  font-size: 14px;
 | 
			
		||||
  color: rgba(0, 0, 0, 0.65);
 | 
			
		||||
}
 | 
			
		||||
svg {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,13 @@
 | 
			
		||||
export function getStorageToken() {
 | 
			
		||||
import type { LoginModuleType } from '@/interface';
 | 
			
		||||
 | 
			
		||||
export function getToken() {
 | 
			
		||||
  return '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getStorageUserInfo() {}
 | 
			
		||||
export function getUserInfo() {}
 | 
			
		||||
 | 
			
		||||
/** 获取登录模块的正则字符串 */
 | 
			
		||||
export function getLoginModuleRegExp() {
 | 
			
		||||
  const arr: LoginModuleType[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
 | 
			
		||||
  return arr.join('|');
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
export { getStorageToken, getStorageUserInfo } from './auth';
 | 
			
		||||
export { getToken, getUserInfo, getLoginModuleRegExp } from './auth';
 | 
			
		||||
export {
 | 
			
		||||
  isNumber,
 | 
			
		||||
  isString,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,14 @@
 | 
			
		||||
  <div class="p-10px">
 | 
			
		||||
    <data-card :loading="loading" />
 | 
			
		||||
    <nav-card :loading="loading" />
 | 
			
		||||
    <router-link :to="EnumRoutePaths['dashboard-workbench']">workbench</router-link>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { DataCard, NavCard } from './components';
 | 
			
		||||
import { EnumRoutePaths } from '@/enum';
 | 
			
		||||
 | 
			
		||||
const loading = ref(true);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,21 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div></div>
 | 
			
		||||
  <div>
 | 
			
		||||
    <h2>工作台</h2>
 | 
			
		||||
    <router-link :to="EnumRoutePaths['dashboard-analysis']">analysis</router-link>
 | 
			
		||||
    <n-button @click="removeCurrent">去除</n-button>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup></script>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { NButton } from 'naive-ui';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import { EnumRoutePaths } from '@/enum';
 | 
			
		||||
import { RouteNameMap } from '@/router';
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 | 
			
		||||
function removeCurrent() {
 | 
			
		||||
  router.removeRoute(RouteNameMap.get('dashboard-workbench')!);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,65 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="min-h-500px">
 | 
			
		||||
    <n-space>
 | 
			
		||||
      <n-button v-for="item in actions" :key="item.key" type="primary" @click="handleClick(item.key)">
 | 
			
		||||
        {{ item.label }}
 | 
			
		||||
      </n-button>
 | 
			
		||||
    </n-space>
 | 
			
		||||
    <n-space>
 | 
			
		||||
      <n-button>Default</n-button>
 | 
			
		||||
      <n-button type="primary">Primary</n-button>
 | 
			
		||||
      <n-button type="info">Info</n-button>
 | 
			
		||||
      <n-button type="success">Success</n-button>
 | 
			
		||||
      <n-button type="warning">Warning</n-button>
 | 
			
		||||
      <n-button type="error">Error</n-button>
 | 
			
		||||
    </n-space>
 | 
			
		||||
    <router-link class="text-primary" to="/system">system</router-link>
 | 
			
		||||
    <div>
 | 
			
		||||
      <span class="text-primary">primary</span>
 | 
			
		||||
      <span class="text-info">info</span>
 | 
			
		||||
      <span class="text-success">success</span>
 | 
			
		||||
      <span class="text-warning">warning</span>
 | 
			
		||||
      <span class="text-error">error</span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <p v-for="i in 100" :key="i">{{ i }}</p>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { NButton, NSpace, useDialog, useNotification, useMessage } from 'naive-ui';
 | 
			
		||||
 | 
			
		||||
type ActionType = 'dialog' | 'notification' | 'message';
 | 
			
		||||
interface Action {
 | 
			
		||||
  key: ActionType;
 | 
			
		||||
  label: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineProps({
 | 
			
		||||
  code: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: ''
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const dialog = useDialog();
 | 
			
		||||
const notification = useNotification();
 | 
			
		||||
const message = useMessage();
 | 
			
		||||
 | 
			
		||||
const actions: Action[] = [
 | 
			
		||||
  { key: 'dialog', label: 'dialog' },
 | 
			
		||||
  { key: 'notification', label: 'notification' },
 | 
			
		||||
  { key: 'message', label: 'message' }
 | 
			
		||||
];
 | 
			
		||||
function handleClick(type: ActionType) {
 | 
			
		||||
  if (type === 'dialog') {
 | 
			
		||||
    dialog.info({ content: '弹窗示例!' });
 | 
			
		||||
  }
 | 
			
		||||
  if (type === 'notification') {
 | 
			
		||||
    notification.info({ content: '通知示例!' });
 | 
			
		||||
  }
 | 
			
		||||
  if (type === 'message') {
 | 
			
		||||
    message.info('消息示例!');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <router-view />
 | 
			
		||||
  <div></div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup></script>
 | 
			
		||||
							
								
								
									
										67
									
								
								src/views/system/login/components/CodeLogin/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/views/system/login/components/CodeLogin/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="pt-24px">
 | 
			
		||||
    <n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
 | 
			
		||||
      <n-form-item path="phone">
 | 
			
		||||
        <n-input v-model:value="model.phone" placeholder="手机号码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="code">
 | 
			
		||||
        <div class="flex-y-center w-full">
 | 
			
		||||
          <n-input v-model:value="model.code" placeholder="验证码" />
 | 
			
		||||
          <div class="w-18px"></div>
 | 
			
		||||
          <n-button size="large" :disabled="isCounting" @click="handleSmsCode">{{ label }}</n-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-space :vertical="true" size="large">
 | 
			
		||||
        <n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
 | 
			
		||||
        <n-button size="large" :block="true" :round="true" @click="toLogin('pwd-login')">返回</n-button>
 | 
			
		||||
      </n-space>
 | 
			
		||||
    </n-form>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { reactive, ref } from 'vue';
 | 
			
		||||
import { NForm, NFormItem, NInput, NSpace, NButton, useMessage } from 'naive-ui';
 | 
			
		||||
import type { FormInst } from 'naive-ui';
 | 
			
		||||
import { useRouterChange, useSmsCode } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
const message = useMessage();
 | 
			
		||||
const { toLogin } = useRouterChange();
 | 
			
		||||
const { label, isCounting, start } = useSmsCode();
 | 
			
		||||
 | 
			
		||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
 | 
			
		||||
const model = reactive({
 | 
			
		||||
  phone: '',
 | 
			
		||||
  code: ''
 | 
			
		||||
});
 | 
			
		||||
const rules = {
 | 
			
		||||
  phone: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入手机号'
 | 
			
		||||
  },
 | 
			
		||||
  code: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入验证码'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function handleSmsCode() {
 | 
			
		||||
  start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSubmit(e: MouseEvent) {
 | 
			
		||||
  if (!formRef.value) return;
 | 
			
		||||
  e.preventDefault();
 | 
			
		||||
 | 
			
		||||
  formRef.value.validate(errors => {
 | 
			
		||||
    if (!errors) {
 | 
			
		||||
      message.success('验证成功');
 | 
			
		||||
    } else {
 | 
			
		||||
      message.error('验证失败');
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										72
									
								
								src/views/system/login/components/PwdLogin/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/views/system/login/components/PwdLogin/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="pt-24px">
 | 
			
		||||
    <n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
 | 
			
		||||
      <n-form-item path="phone">
 | 
			
		||||
        <n-input v-model:value="model.phone" placeholder="手机号码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="pwd">
 | 
			
		||||
        <n-input v-model:value="model.pwd" placeholder="密码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-space :vertical="true" size="large">
 | 
			
		||||
        <div class="flex-y-center justify-between">
 | 
			
		||||
          <n-checkbox v-model:checked="rememberMe">记住我</n-checkbox>
 | 
			
		||||
          <span class="text-primary cursor-pointer" @click="toLogin('reset-pwd')">忘记密码?</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
 | 
			
		||||
        <div class="flex-y-center justify-between">
 | 
			
		||||
          <n-button class="flex-1" :block="true" @click="toLogin('code-login')">
 | 
			
		||||
            {{ EnumLoginModule['code-login'] }}
 | 
			
		||||
          </n-button>
 | 
			
		||||
          <div class="w-12px"></div>
 | 
			
		||||
          <n-button class="flex-1" :block="true" @click="toLogin('register')">{{ EnumLoginModule.register }}</n-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </n-space>
 | 
			
		||||
    </n-form>
 | 
			
		||||
    <other-login />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { reactive, ref } from 'vue';
 | 
			
		||||
import { NForm, NFormItem, NInput, NSpace, NCheckbox, NButton, useMessage } from 'naive-ui';
 | 
			
		||||
import type { FormInst } from 'naive-ui';
 | 
			
		||||
import { EnumLoginModule } from '@/enum';
 | 
			
		||||
import { useRouterChange } from '@/hooks';
 | 
			
		||||
import { OtherLogin } from '../common';
 | 
			
		||||
 | 
			
		||||
const { toLogin } = useRouterChange();
 | 
			
		||||
const message = useMessage();
 | 
			
		||||
 | 
			
		||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
 | 
			
		||||
const model = reactive({
 | 
			
		||||
  phone: '',
 | 
			
		||||
  pwd: ''
 | 
			
		||||
});
 | 
			
		||||
const rules = {
 | 
			
		||||
  phone: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入手机号'
 | 
			
		||||
  },
 | 
			
		||||
  pwd: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入密码'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
const rememberMe = ref(false);
 | 
			
		||||
 | 
			
		||||
function handleSubmit(e: MouseEvent) {
 | 
			
		||||
  if (!formRef.value) return;
 | 
			
		||||
  e.preventDefault();
 | 
			
		||||
 | 
			
		||||
  formRef.value.validate(errors => {
 | 
			
		||||
    if (!errors) {
 | 
			
		||||
      message.success('验证成功');
 | 
			
		||||
    } else {
 | 
			
		||||
      message.error('验证失败');
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										87
									
								
								src/views/system/login/components/Register/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/views/system/login/components/Register/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="pt-24px">
 | 
			
		||||
    <n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
 | 
			
		||||
      <n-form-item path="phone">
 | 
			
		||||
        <n-input v-model:value="model.phone" placeholder="手机号码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="code">
 | 
			
		||||
        <div class="flex-y-center w-full">
 | 
			
		||||
          <n-input v-model:value="model.code" placeholder="验证码" />
 | 
			
		||||
          <div class="w-18px"></div>
 | 
			
		||||
          <n-button size="large" :disabled="isCounting" @click="handleSmsCode">{{ label }}</n-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="pwd">
 | 
			
		||||
        <n-input v-model:value="model.pwd" placeholder="密码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="confirmPwd">
 | 
			
		||||
        <n-input v-model:value="model.confirmPwd" placeholder="确认密码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-space :vertical="true" size="large">
 | 
			
		||||
        <n-checkbox v-model:checked="agreement">我已经仔细阅读并接受用户协议和隐私政策</n-checkbox>
 | 
			
		||||
        <n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
 | 
			
		||||
        <n-button size="large" :block="true" :round="true" @click="toLogin('pwd-login')">返回</n-button>
 | 
			
		||||
      </n-space>
 | 
			
		||||
    </n-form>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { reactive, ref } from 'vue';
 | 
			
		||||
import { NForm, NFormItem, NInput, NSpace, NCheckbox, NButton, useMessage } from 'naive-ui';
 | 
			
		||||
import type { FormInst } from 'naive-ui';
 | 
			
		||||
import { useRouterChange, useSmsCode } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
const message = useMessage();
 | 
			
		||||
const { toLogin } = useRouterChange();
 | 
			
		||||
const { label, isCounting, start } = useSmsCode();
 | 
			
		||||
 | 
			
		||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
 | 
			
		||||
const model = reactive({
 | 
			
		||||
  phone: '',
 | 
			
		||||
  code: '',
 | 
			
		||||
  pwd: '',
 | 
			
		||||
  confirmPwd: ''
 | 
			
		||||
});
 | 
			
		||||
const rules = {
 | 
			
		||||
  phone: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入手机号'
 | 
			
		||||
  },
 | 
			
		||||
  code: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入验证码'
 | 
			
		||||
  },
 | 
			
		||||
  pwd: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入密码'
 | 
			
		||||
  },
 | 
			
		||||
  confirmPwd: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入确认密码'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
const agreement = ref(false);
 | 
			
		||||
 | 
			
		||||
function handleSmsCode() {
 | 
			
		||||
  start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSubmit(e: MouseEvent) {
 | 
			
		||||
  if (!formRef.value) return;
 | 
			
		||||
  e.preventDefault();
 | 
			
		||||
 | 
			
		||||
  formRef.value.validate(errors => {
 | 
			
		||||
    if (!errors) {
 | 
			
		||||
      message.success('验证成功');
 | 
			
		||||
    } else {
 | 
			
		||||
      message.error('验证失败');
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										85
									
								
								src/views/system/login/components/ResetPwd/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/views/system/login/components/ResetPwd/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="pt-24px">
 | 
			
		||||
    <n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
 | 
			
		||||
      <n-form-item path="phone">
 | 
			
		||||
        <n-input v-model:value="model.phone" placeholder="手机号码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="code">
 | 
			
		||||
        <div class="flex-y-center w-full">
 | 
			
		||||
          <n-input v-model:value="model.code" placeholder="验证码" />
 | 
			
		||||
          <div class="w-18px"></div>
 | 
			
		||||
          <n-button size="large" :disabled="isCounting" @click="handleSmsCode">{{ label }}</n-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="pwd">
 | 
			
		||||
        <n-input v-model:value="model.pwd" placeholder="密码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-form-item path="confirmPwd">
 | 
			
		||||
        <n-input v-model:value="model.confirmPwd" placeholder="确认密码" />
 | 
			
		||||
      </n-form-item>
 | 
			
		||||
      <n-space :vertical="true" size="large">
 | 
			
		||||
        <n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
 | 
			
		||||
        <n-button size="large" :block="true" :round="true" @click="toLogin('pwd-login')">返回</n-button>
 | 
			
		||||
      </n-space>
 | 
			
		||||
    </n-form>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { reactive, ref } from 'vue';
 | 
			
		||||
import { NForm, NFormItem, NInput, NSpace, NButton, useMessage } from 'naive-ui';
 | 
			
		||||
import type { FormInst } from 'naive-ui';
 | 
			
		||||
import { useRouterChange, useSmsCode } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
const message = useMessage();
 | 
			
		||||
const { toLogin } = useRouterChange();
 | 
			
		||||
const { label, isCounting, start } = useSmsCode();
 | 
			
		||||
 | 
			
		||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
 | 
			
		||||
const model = reactive({
 | 
			
		||||
  phone: '',
 | 
			
		||||
  code: '',
 | 
			
		||||
  pwd: '',
 | 
			
		||||
  confirmPwd: ''
 | 
			
		||||
});
 | 
			
		||||
const rules = {
 | 
			
		||||
  phone: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入手机号'
 | 
			
		||||
  },
 | 
			
		||||
  code: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入验证码'
 | 
			
		||||
  },
 | 
			
		||||
  pwd: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入密码'
 | 
			
		||||
  },
 | 
			
		||||
  confirmPwd: {
 | 
			
		||||
    required: true,
 | 
			
		||||
    trigger: ['blur', 'input'],
 | 
			
		||||
    message: '请输入确认密码'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function handleSmsCode() {
 | 
			
		||||
  start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSubmit(e: MouseEvent) {
 | 
			
		||||
  if (!formRef.value) return;
 | 
			
		||||
  e.preventDefault();
 | 
			
		||||
 | 
			
		||||
  formRef.value.validate(errors => {
 | 
			
		||||
    if (!errors) {
 | 
			
		||||
      message.success('验证成功');
 | 
			
		||||
    } else {
 | 
			
		||||
      message.error('验证失败');
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -0,0 +1,13 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <n-space :vertical="true">
 | 
			
		||||
    <n-divider class="!mb-0 text-14px text-[#666]">其他登录方式</n-divider>
 | 
			
		||||
    <div class="flex-center">
 | 
			
		||||
      <icon-mdi-wechat class="text-22px text-[#888] hover:text-[#52BF5E] cursor-pointer" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </n-space>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { NSpace, NDivider } from 'naive-ui';
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										3
									
								
								src/views/system/login/components/common/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/views/system/login/components/common/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
import OtherLogin from './OtherLogin/index.vue';
 | 
			
		||||
 | 
			
		||||
export { OtherLogin };
 | 
			
		||||
							
								
								
									
										7
									
								
								src/views/system/login/components/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/views/system/login/components/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
import PwdLogin from './PwdLogin/index.vue';
 | 
			
		||||
import CodeLogin from './CodeLogin/index.vue';
 | 
			
		||||
import Register from './Register/index.vue';
 | 
			
		||||
import ResetPwd from './ResetPwd/index.vue';
 | 
			
		||||
import BindWechat from './BindWechat/index.vue';
 | 
			
		||||
 | 
			
		||||
export { PwdLogin, CodeLogin, Register, ResetPwd, BindWechat };
 | 
			
		||||
							
								
								
									
										17
									
								
								src/views/system/login/composable.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/views/system/login/composable.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import type { Ref } from 'vue';
 | 
			
		||||
import { useCreateContext } from '@/hooks';
 | 
			
		||||
import { LoginModuleType } from '@/interface';
 | 
			
		||||
 | 
			
		||||
interface ShareState {
 | 
			
		||||
  activeModule: Ref<LoginModuleType>;
 | 
			
		||||
  handleLoginModule(module: LoginModuleType): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const { useContext, useProvider } = useCreateContext<ShareState>();
 | 
			
		||||
 | 
			
		||||
export function useLoginContext() {
 | 
			
		||||
  return {
 | 
			
		||||
    useContext,
 | 
			
		||||
    useProvider
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +1,53 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex w-full h-full">
 | 
			
		||||
    <div class="flex-center w-1/2 h-full">
 | 
			
		||||
      <h3>登录</h3>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="flex-center w-1/2 h-full bg-primary bg-opacity-25">
 | 
			
		||||
      <banner-svg class="w-400px h-400px" type="1" :color="theme.themeColor" />
 | 
			
		||||
  <div class="relative flex-center w-full h-full bg-[#DBE0F9]">
 | 
			
		||||
    <login-bg />
 | 
			
		||||
    <div class="w-400px p-40px bg-white rounded-20px z-10">
 | 
			
		||||
      <header class="flex-y-center justify-between">
 | 
			
		||||
        <div class="w-70px h-70px rounded-35px overflow-hidden">
 | 
			
		||||
          <system-logo class="w-full h-full" :fill="true" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <n-gradient-text type="primary" :size="28">{{ title }}</n-gradient-text>
 | 
			
		||||
      </header>
 | 
			
		||||
      <main class="pt-24px">
 | 
			
		||||
        <div v-for="item in modules" v-show="module === item.key" :key="item.key">
 | 
			
		||||
          <h3 class="text-18px text-primary font-medium">{{ item.label }}</h3>
 | 
			
		||||
          <component :is="item.component" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </main>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { BannerSvg } from '@/components';
 | 
			
		||||
import { useThemeStore } from '@/store';
 | 
			
		||||
import type { Component, PropType } from 'vue';
 | 
			
		||||
import { NGradientText } from 'naive-ui';
 | 
			
		||||
import { SystemLogo, LoginBg } from '@/components';
 | 
			
		||||
import { useAppTitle } from '@/hooks';
 | 
			
		||||
import { EnumLoginModule } from '@/enum';
 | 
			
		||||
import type { LoginModuleType } from '@/interface';
 | 
			
		||||
import { PwdLogin, CodeLogin, Register, ResetPwd, BindWechat } from './components';
 | 
			
		||||
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
interface LoginModule {
 | 
			
		||||
  key: LoginModuleType;
 | 
			
		||||
  label: string;
 | 
			
		||||
  component: Component;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineProps({
 | 
			
		||||
  module: {
 | 
			
		||||
    type: String as PropType<LoginModuleType>,
 | 
			
		||||
    default: 'pwd-login'
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const title = useAppTitle();
 | 
			
		||||
 | 
			
		||||
const modules: LoginModule[] = [
 | 
			
		||||
  { key: 'pwd-login', label: EnumLoginModule['pwd-login'], component: PwdLogin },
 | 
			
		||||
  { key: 'code-login', label: EnumLoginModule['code-login'], component: CodeLogin },
 | 
			
		||||
  { key: 'register', label: EnumLoginModule.register, component: Register },
 | 
			
		||||
  { key: 'reset-pwd', label: EnumLoginModule['reset-pwd'], component: ResetPwd },
 | 
			
		||||
  { key: 'bind-wechat', label: EnumLoginModule['bind-wechat'], component: BindWechat }
 | 
			
		||||
];
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user