mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 15:53:43 +08:00 
			
		
		
		
	Compare commits
	
		
			110 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					114072277f | ||
| 
						 | 
					e65034d946 | ||
| 
						 | 
					a7a269d6a6 | ||
| 
						 | 
					2c9660fdbf | ||
| 
						 | 
					e93b94cb24 | ||
| 
						 | 
					3befb22903 | ||
| 
						 | 
					7ed5d0de2d | ||
| 
						 | 
					47f2871cb5 | ||
| 
						 | 
					852ddb64ad | ||
| 
						 | 
					7e1f9f1138 | ||
| 
						 | 
					554d7fd611 | ||
| 
						 | 
					1797f29a79 | ||
| 
						 | 
					50063187ec | ||
| 
						 | 
					b16721b2b7 | ||
| 
						 | 
					facc00e8b4 | ||
| 
						 | 
					02c51e6fb9 | ||
| 
						 | 
					be374089ba | ||
| 
						 | 
					68b42304d5 | ||
| 
						 | 
					32a7cc408e | ||
| 
						 | 
					651e58dcb6 | ||
| 
						 | 
					b61b0ce25f | ||
| 
						 | 
					21bab1f7c3 | ||
| 
						 | 
					4f9d544d43 | ||
| 
						 | 
					db75c91400 | ||
| 
						 | 
					37092974d3 | ||
| 
						 | 
					1d63a83822 | ||
| 
						 | 
					db3c25ea14 | ||
| 
						 | 
					5eddb4910c | ||
| 
						 | 
					ce531ce5dd | ||
| 
						 | 
					28efbdbc70 | ||
| 
						 | 
					cc290accc2 | ||
| 
						 | 
					579e07400e | ||
| 
						 | 
					b2a4ddf5e3 | ||
| 
						 | 
					28b5d22401 | ||
| 
						 | 
					839b82ba8b | ||
| 
						 | 
					09c7658c21 | ||
| 
						 | 
					e25afe2fad | ||
| 
						 | 
					371fad4f26 | ||
| 
						 | 
					a090d398fc | ||
| 
						 | 
					6d132c5977 | ||
| 
						 | 
					912bfdf439 | ||
| 
						 | 
					bf020a8258 | ||
| 
						 | 
					10e4d81bd6 | ||
| 
						 | 
					0653fb144f | ||
| 
						 | 
					006467a062 | ||
| 
						 | 
					0c5770dfd2 | ||
| 
						 | 
					0e783bcf7b | ||
| 
						 | 
					e5793e1c8d | ||
| 
						 | 
					85b55bb37a | ||
| 
						 | 
					b36a62b150 | ||
| 
						 | 
					c804b21ceb | ||
| 
						 | 
					5bfb8199b4 | ||
| 
						 | 
					ab9a6a2f39 | ||
| 
						 | 
					b93b80cb4b | ||
| 
						 | 
					f5a36a05cb | ||
| 
						 | 
					035fa114c9 | ||
| 
						 | 
					2c196841bd | ||
| 
						 | 
					0d2a5629e8 | ||
| 
						 | 
					de2057f141 | ||
| 
						 | 
					7a0648dba5 | ||
| 
						 | 
					25d3404c9c | ||
| 
						 | 
					29078689b0 | ||
| 
						 | 
					078433da43 | ||
| 
						 | 
					b9ce69130b | ||
| 
						 | 
					90ddf9837c | ||
| 
						 | 
					777cf8e06a | ||
| 
						 | 
					9bc682dae8 | ||
| 
						 | 
					c9c5ca9989 | ||
| 
						 | 
					e776df49e4 | ||
| 
						 | 
					659e460653 | ||
| 
						 | 
					186f53f634 | ||
| 
						 | 
					5aac013597 | ||
| 
						 | 
					1901a0bfb7 | ||
| 
						 | 
					25bead0039 | ||
| 
						 | 
					041012b3ee | ||
| 
						 | 
					9472b51811 | ||
| 
						 | 
					f3c86efbe5 | ||
| 
						 | 
					51c744c8e2 | ||
| 
						 | 
					230a50a4cf | ||
| 
						 | 
					e755caabf2 | ||
| 
						 | 
					ae7ec99a98 | ||
| 
						 | 
					987cef3363 | ||
| 
						 | 
					2ad1ad32b8 | ||
| 
						 | 
					ff9216b621 | ||
| 
						 | 
					225c4fe022 | ||
| 
						 | 
					f29106e480 | ||
| 
						 | 
					21c2f5a857 | ||
| 
						 | 
					e53e7936df | ||
| 
						 | 
					bdc39aff1b | ||
| 
						 | 
					0b10b5056e | ||
| 
						 | 
					d683894beb | ||
| 
						 | 
					9fb641f71e | ||
| 
						 | 
					a6bdc380a8 | ||
| 
						 | 
					8b27fc8bb8 | ||
| 
						 | 
					738964a769 | ||
| 
						 | 
					c429cd0293 | ||
| 
						 | 
					20aa39f14e | ||
| 
						 | 
					93f9aa9584 | ||
| 
						 | 
					8ce627a397 | ||
| 
						 | 
					1ffb75afce | ||
| 
						 | 
					32aa5ee75a | ||
| 
						 | 
					98a7d25cf8 | ||
| 
						 | 
					1b3463d2e7 | ||
| 
						 | 
					cff11d9175 | ||
| 
						 | 
					54577f10fc | ||
| 
						 | 
					6261156c5a | ||
| 
						 | 
					f5a5f44a2b | ||
| 
						 | 
					3fb7a5f709 | ||
| 
						 | 
					e6c9b35ab4 | ||
| 
						 | 
					0569666a8f | 
							
								
								
									
										10
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.env
									
									
									
									
									
								
							@@ -1,5 +1,7 @@
 | 
			
		||||
# 变量需要以VITE开头
 | 
			
		||||
BASE_URL=/
 | 
			
		||||
 | 
			
		||||
VITE_APP_TITLE=SoybeanAdmin
 | 
			
		||||
VITE_APP_TITLE_LABEL=SoybeanAdmin
 | 
			
		||||
VITE_BASE_URL=/
 | 
			
		||||
VITE_APP_NAME=SoybeanAdmin
 | 
			
		||||
 | 
			
		||||
VITE_APP_TITLE=Soybean管理系统
 | 
			
		||||
 | 
			
		||||
VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								.env-config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								.env-config.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
/** 请求环境配置 */
 | 
			
		||||
type ServiceEnv = Record<
 | 
			
		||||
  Service.HttpEnv,
 | 
			
		||||
  {
 | 
			
		||||
    /** 请求环境 */
 | 
			
		||||
    env: Service.HttpEnv;
 | 
			
		||||
    /** 请求地址 */
 | 
			
		||||
    url: string;
 | 
			
		||||
  }
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
/** 请求的环境 */
 | 
			
		||||
export const serviceEnv: ServiceEnv = {
 | 
			
		||||
  test: {
 | 
			
		||||
    env: 'test',
 | 
			
		||||
    url: 'http://120.76.42.91:18888'
 | 
			
		||||
  },
 | 
			
		||||
  prod: {
 | 
			
		||||
    env: 'prod',
 | 
			
		||||
    url: 'http://120.76.42.91:18888'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
#请求的环境
 | 
			
		||||
VITE_HTTP_ENV=DEV
 | 
			
		||||
#请求地址
 | 
			
		||||
VITE_HTTP_URL=https://test.aisuit.com.cn
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
#请求的环境 正式环境
 | 
			
		||||
VITE_HTTP_ENV=PROD
 | 
			
		||||
#请求地址
 | 
			
		||||
VITE_HTTP_URL=http://192.168.100.43:8201
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
VITE_HTTP_ENV=STAGING
 | 
			
		||||
#请求地址
 | 
			
		||||
VITE_HTTP_URL=http://192.168.100.43:8201
 | 
			
		||||
@@ -7,8 +7,9 @@ lib
 | 
			
		||||
.vscode
 | 
			
		||||
.idea
 | 
			
		||||
/dist/
 | 
			
		||||
/mock/
 | 
			
		||||
/public
 | 
			
		||||
/docs
 | 
			
		||||
.vscode
 | 
			
		||||
.local
 | 
			
		||||
!.env-config.ts
 | 
			
		||||
package.json
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										148
									
								
								.eslintrc.js
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								.eslintrc.js
									
									
									
									
									
								
							@@ -20,33 +20,143 @@ module.exports = {
 | 
			
		||||
    sourceType: 'module'
 | 
			
		||||
  },
 | 
			
		||||
  plugins: ['vue', '@typescript-eslint'],
 | 
			
		||||
  extends: [
 | 
			
		||||
    'plugin:vue/vue3-recommended',
 | 
			
		||||
    'airbnb-base',
 | 
			
		||||
    '@vue/typescript/recommended',
 | 
			
		||||
    'plugin:prettier/recommended',
 | 
			
		||||
    '@vue/prettier/@typescript-eslint'
 | 
			
		||||
  ],
 | 
			
		||||
  extends: ['plugin:vue/vue3-recommended', 'airbnb-base', '@vue/typescript/recommended', 'plugin:prettier/recommended'],
 | 
			
		||||
  rules: {
 | 
			
		||||
    'no-unused-vars': 'off',
 | 
			
		||||
    'import/extensions': 'off',
 | 
			
		||||
    'import/no-extraneous-dependencies': 'off',
 | 
			
		||||
    'import/no-unresolved': 0,
 | 
			
		||||
    'no-shadow': 0,
 | 
			
		||||
    'import/order': [
 | 
			
		||||
      'error',
 | 
			
		||||
      {
 | 
			
		||||
        'newlines-between': 'never',
 | 
			
		||||
        groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
 | 
			
		||||
        pathGroups: [
 | 
			
		||||
          {
 | 
			
		||||
            pattern: 'vue',
 | 
			
		||||
            group: 'external',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: 'vue-router',
 | 
			
		||||
            group: 'external',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: 'pinia',
 | 
			
		||||
            group: 'external',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: 'naive-ui',
 | 
			
		||||
            group: 'external',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/config',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/settings',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/enum',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/plugins',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/layouts',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/layouts',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/views',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/components',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/router',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/store',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/composables',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/hooks',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/service',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/utils',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/assets',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/**',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: '@/interface',
 | 
			
		||||
            group: 'internal',
 | 
			
		||||
            position: 'before'
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        pathGroupsExcludedImportTypes: ['vue', 'vue-router', 'pinia', 'naive-ui']
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    'import/prefer-default-export': 0,
 | 
			
		||||
    'no-use-before-define': 'off',
 | 
			
		||||
    'vue/multi-word-component-names': 0,
 | 
			
		||||
    'max-classes-per-file': 0,
 | 
			
		||||
    'no-shadow': 0,
 | 
			
		||||
    'no-unused-vars': 'off',
 | 
			
		||||
    'no-use-before-define': 'off',
 | 
			
		||||
    'vue/comment-directive': 0,
 | 
			
		||||
    'vue/multi-word-component-names': 0,
 | 
			
		||||
    '@typescript-eslint/ban-types': 'off',
 | 
			
		||||
    '@typescript-eslint/ban-ts-ignore': 'off',
 | 
			
		||||
    '@typescript-eslint/explicit-function-return-type': 'off',
 | 
			
		||||
    '@typescript-eslint/explicit-module-boundary-types': 'off',
 | 
			
		||||
    '@typescript-eslint/no-empty-function': 'off',
 | 
			
		||||
    '@typescript-eslint/no-explicit-any': 0,
 | 
			
		||||
    '@typescript-eslint/no-inferrable-types': 0,
 | 
			
		||||
    '@typescript-eslint/ban-ts-ignore': 'off',
 | 
			
		||||
    '@typescript-eslint/ban-types': 'off',
 | 
			
		||||
    '@typescript-eslint/no-var-requires': 'off',
 | 
			
		||||
    '@typescript-eslint/explicit-module-boundary-types': 'off',
 | 
			
		||||
    '@typescript-eslint/explicit-function-return-type': 'off',
 | 
			
		||||
    '@typescript-eslint/no-empty-function': 'off',
 | 
			
		||||
    '@typescript-eslint/no-non-null-assertion': 'off',
 | 
			
		||||
    '@typescript-eslint/no-unused-vars': ['warn', { ignoreRestSiblings: true }],
 | 
			
		||||
    '@typescript-eslint/no-use-before-define': ['error', { classes: true, functions: false, typedefs: false }]
 | 
			
		||||
    '@typescript-eslint/no-unused-vars': ['warn', { ignoreRestSiblings: true, varsIgnorePattern: 'Ignored' }],
 | 
			
		||||
    '@typescript-eslint/no-use-before-define': ['error', { classes: true, functions: false, typedefs: false }],
 | 
			
		||||
    '@typescript-eslint/no-var-requires': 'off'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,7 +1,27 @@
 | 
			
		||||
# Logs
 | 
			
		||||
logs
 | 
			
		||||
*.log
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
yarn-error.log*
 | 
			
		||||
pnpm-debug.log*
 | 
			
		||||
lerna-debug.log*
 | 
			
		||||
 | 
			
		||||
node_modules
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist
 | 
			
		||||
dist-ssr
 | 
			
		||||
*.local
 | 
			
		||||
 | 
			
		||||
/cypress/videos/
 | 
			
		||||
/cypress/screenshots/
 | 
			
		||||
 | 
			
		||||
# Editor directories and files
 | 
			
		||||
.idea
 | 
			
		||||
*.suo
 | 
			
		||||
*.ntvs*
 | 
			
		||||
*.njsproj
 | 
			
		||||
*.sln
 | 
			
		||||
*.sw?
 | 
			
		||||
 | 
			
		||||
stats.html
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.husky/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.husky/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
_
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
. "$(dirname "$0")/_/husky.sh"
 | 
			
		||||
 | 
			
		||||
pnpm lint:fix
 | 
			
		||||
pnpm lint && pnpm typecheck
 | 
			
		||||
 
 | 
			
		||||
@@ -15,5 +15,13 @@ module.exports = {
 | 
			
		||||
  requireConfig: false, // Require a 'prettierconfig' to format prettier
 | 
			
		||||
  stylelintIntegration: false, //不让prettier使用stylelint的代码格式进行校验
 | 
			
		||||
  trailingComma: 'none', // 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)
 | 
			
		||||
  tslintIntegration: false // 不让prettier使用tslint的代码格式进行校验
 | 
			
		||||
  tslintIntegration: false, // 不让prettier使用tslint的代码格式进行校验
 | 
			
		||||
  overrides: [
 | 
			
		||||
    {
 | 
			
		||||
      files: '*.html',
 | 
			
		||||
      options: {
 | 
			
		||||
        parser: 'html'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							@@ -4,7 +4,8 @@
 | 
			
		||||
		"formulahendry.auto-complete-tag",
 | 
			
		||||
		"steoates.autoimport",
 | 
			
		||||
		"formulahendry.auto-rename-tag",
 | 
			
		||||
		"coenraads.bracket-pair-colorizer",
 | 
			
		||||
		"coenraads.bracket-pair-colorizer-2",
 | 
			
		||||
		"naumovs.color-highlight",
 | 
			
		||||
		"pranaygp.vscode-css-peek",
 | 
			
		||||
		"mikestead.dotenv",
 | 
			
		||||
		"editorconfig.editorconfig",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -9,6 +9,8 @@
 | 
			
		||||
	"editor.codeActionsOnSave": {
 | 
			
		||||
		"source.fixAll.eslint": true
 | 
			
		||||
	},
 | 
			
		||||
	"editor.bracketPairColorization.enabled": true,
 | 
			
		||||
	"editor.guides.bracketPairs": "active",
 | 
			
		||||
	"git.enableSmartCommit": true,
 | 
			
		||||
	"path-intellisense.mappings": {
 | 
			
		||||
		"@": "${workspaceFolder}/src",
 | 
			
		||||
@@ -54,15 +56,19 @@
 | 
			
		||||
	"[markdown]": {
 | 
			
		||||
		"editor.defaultFormatter": "yzhang.markdown-all-in-one"
 | 
			
		||||
	},
 | 
			
		||||
	"workbench.productIconTheme": "fluent-icons",
 | 
			
		||||
	"vue3snippets.enable-compile-vue-file-on-did-save-code": false,
 | 
			
		||||
	"editor.formatOnSave": false,
 | 
			
		||||
	"material-icon-theme.activeIconPack": "angular",
 | 
			
		||||
	"material-icon-theme.files.associations": {},
 | 
			
		||||
	"material-icon-theme.folders.associations": {
 | 
			
		||||
		"enum": "typescript",
 | 
			
		||||
		"enums": "typescript",
 | 
			
		||||
		"store": "context",
 | 
			
		||||
		"stores": "context",
 | 
			
		||||
		"composable": "hook",
 | 
			
		||||
		"composables": "hook",
 | 
			
		||||
		"business": "core",
 | 
			
		||||
		"directive": "tools",
 | 
			
		||||
		"directives": "tools",
 | 
			
		||||
		"business": "core"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -2,110 +2,87 @@
 | 
			
		||||
 | 
			
		||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
 | 
			
		||||
 | 
			
		||||
### [0.0.3](https://github.com/honghuangdc/soybean-admin/compare/v0.0.2...v0.0.3) (2021-11-23)
 | 
			
		||||
 | 
			
		||||
### 0.0.2 (2021-11-21)
 | 
			
		||||
### [0.9.2](https://github.com/honghuangdc/soybean-admin/compare/v0.9.1...v0.9.2) (2022-02-11)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
* **component:** 增加剪贴板示例 ([f1cd995](https://github.com/honghuangdc/soybean-admin/commit/f1cd9955d9ef0dd06e6eb0ab88ab6be80db789a3))
 | 
			
		||||
* **components:** 添加面包屑 ([c1cdc3a](https://github.com/honghuangdc/soybean-admin/commit/c1cdc3a9ed673d0fd84aa1eaa9fc72468bd5aaf9))
 | 
			
		||||
* **components:** 添加图片验证码 ([336c776](https://github.com/honghuangdc/soybean-admin/commit/336c7766f9130619b7076e832d7ade7cbc3049f2))
 | 
			
		||||
* **components:** 添加主题配置抽屉,添加暗黑主题 ([a87593f](https://github.com/honghuangdc/soybean-admin/commit/a87593f58a1185d6360b8e49ffe1c9fff768770e))
 | 
			
		||||
* **components:** 添加vertical-mix的导航模式下的菜单 ([f24ec1c](https://github.com/honghuangdc/soybean-admin/commit/f24ec1c5326c117e618aed8b3e1867c24fcd84f4))
 | 
			
		||||
* **projects:** 布局调整 ([eda87f0](https://github.com/honghuangdc/soybean-admin/commit/eda87f041d5d87ae9612f369608e486a8e563f17))
 | 
			
		||||
* **projects:** 菜单数据及组件接入 ([3226a72](https://github.com/honghuangdc/soybean-admin/commit/3226a724be65935ce89fe6ae67f49a20d255c6ac))
 | 
			
		||||
* **projects:** 导航栏模式配置:界面实现及主题配置布局调整 ([f002124](https://github.com/honghuangdc/soybean-admin/commit/f002124ee11bc93e6b9955549143b695417e7f8d))
 | 
			
		||||
* **projects:** 登录页面实现 ([f1e7cf6](https://github.com/honghuangdc/soybean-admin/commit/f1e7cf608ea7d61dcd24f8780cde9cc4c59658ce))
 | 
			
		||||
* **projects:** 多页签绑定路由 ([f29bc05](https://github.com/honghuangdc/soybean-admin/commit/f29bc05dd9f53144ef56440033a6f747c112e83d))
 | 
			
		||||
* **projects:** 分析页更新,添加关于页面 ([8e18218](https://github.com/honghuangdc/soybean-admin/commit/8e18218196c52e6a34b96bc313044b6e47886f85))
 | 
			
		||||
* **projects:** 工作台页面:添加技术栈官网链接 ([364c64b](https://github.com/honghuangdc/soybean-admin/commit/364c64b4641e48bcf8cc8600680bcaa39a1a9413))
 | 
			
		||||
* **projects:** 工作台页面布局 ([4c85569](https://github.com/honghuangdc/soybean-admin/commit/4c85569b764b176c9c3a7f9ba3092ff3567e5512))
 | 
			
		||||
* **projects:** 首页更新 ([5c01006](https://github.com/honghuangdc/soybean-admin/commit/5c01006306873944671a4f1d863ced6ba23f6245))
 | 
			
		||||
* **projects:** 四种基本布局完成 ([86d4a20](https://github.com/honghuangdc/soybean-admin/commit/86d4a207eef8daf01c6336e8aaedf3aebb90e7a7))
 | 
			
		||||
* **projects:** 添加百度地图插件 ([6abe094](https://github.com/honghuangdc/soybean-admin/commit/6abe094ff23f52fdd62c025bce17debd9ea2f907))
 | 
			
		||||
* **projects:** 添加多级菜单页面 ([3f49d6d](https://github.com/honghuangdc/soybean-admin/commit/3f49d6db30aee0a6c1007cb00069835b102deb70))
 | 
			
		||||
* **projects:** 添加多页签风格:按钮和浏览器两种风格 ([3cfa0f1](https://github.com/honghuangdc/soybean-admin/commit/3cfa0f103cf788e57ee26743e89bf5fe33a09660))
 | 
			
		||||
* **projects:** 添加多页签右键菜单 ([d6f5237](https://github.com/honghuangdc/soybean-admin/commit/d6f5237c8c167314d578312dcad7505737f0b4c8))
 | 
			
		||||
* **projects:** 添加富文本和markdown编辑器插件及示例页面 ([60c2064](https://github.com/honghuangdc/soybean-admin/commit/60c20647a0d8e6d877a0f23a6e7da05ff09d14a0))
 | 
			
		||||
* **projects:** 添加固定路由 ([ff4a09c](https://github.com/honghuangdc/soybean-admin/commit/ff4a09c452c98791f7d67ba5f135e9cf5099c29c))
 | 
			
		||||
* **projects:** 添加环境文件env对应的类型 ([4f05095](https://github.com/honghuangdc/soybean-admin/commit/4f050953363b364815a08103047df3fe377d8f56))
 | 
			
		||||
* **projects:** 添加判断是否是移动端的hooks ([0a9fba9](https://github.com/honghuangdc/soybean-admin/commit/0a9fba90b5e51fd2d39c47490f49dac7599a9742))
 | 
			
		||||
* **projects:** 添加全屏显示 ([0a1711d](https://github.com/honghuangdc/soybean-admin/commit/0a1711d5b1d8e863d24a55690fa8696c79acaaf9))
 | 
			
		||||
* **projects:** 添加项目配置拷贝 ([2d9d5c0](https://github.com/honghuangdc/soybean-admin/commit/2d9d5c0353ca6d2dc86965fe383bf2925a47d239))
 | 
			
		||||
* **projects:** 添加exception页面:403,404,500 ([d012c4e](https://github.com/honghuangdc/soybean-admin/commit/d012c4ecf2cd325567d419684153955560ce90da))
 | 
			
		||||
* **projects:** 添加multiTab标签页 ([eec0b36](https://github.com/honghuangdc/soybean-admin/commit/eec0b36f594e0d337f13d3d0ce30b1f768614f5c))
 | 
			
		||||
* **projects:** 添加reload context ([03ebd49](https://github.com/honghuangdc/soybean-admin/commit/03ebd49c8639bf7f4f88b1a0523d2caec2d248ee))
 | 
			
		||||
* **projects:** 添加svg logo自适应主题颜色 ([e1e5579](https://github.com/honghuangdc/soybean-admin/commit/e1e5579e8fe71ed97e2ce11d907705157874bd71))
 | 
			
		||||
* **projects:** 添加swiper插件 ([27f600c](https://github.com/honghuangdc/soybean-admin/commit/27f600c4677afeacd3e67f189df139db5cde0aa3))
 | 
			
		||||
* **projects:** 头部添加菜单折叠按钮和github地址 ([3ec1fc8](https://github.com/honghuangdc/soybean-admin/commit/3ec1fc8f0c23fcba56d4bffb20028948f985659c))
 | 
			
		||||
* **projects:** 项目初始化搭建,集成eslint规范,集成代码提交规范 ([6754da4](https://github.com/honghuangdc/soybean-admin/commit/6754da4d83976a02eced801220320d8c9aa1da85))
 | 
			
		||||
* **projects:** 新增导航模式配置 ([49c2dc4](https://github.com/honghuangdc/soybean-admin/commit/49c2dc4f23913c9ef86ee046c6ae53d4406cbca7))
 | 
			
		||||
* **projects:** 新增顶部菜单 ([221d2cc](https://github.com/honghuangdc/soybean-admin/commit/221d2cc02dfdf3f78cb415f26c88f1f274942222))
 | 
			
		||||
* **projects:** 新增多页签缓存功能 ([d86f891](https://github.com/honghuangdc/soybean-admin/commit/d86f891c64f802bbca50e31e3e4f7ccdad65eed1))
 | 
			
		||||
* **projects:** 新增高德地图插件 ([ea82edc](https://github.com/honghuangdc/soybean-admin/commit/ea82edc1146fefa208bb9e6f985dfb000d197d16))
 | 
			
		||||
* **projects:** 新增视频插件 ([6a692d4](https://github.com/honghuangdc/soybean-admin/commit/6a692d4f99942389cd2a5e72ebc852a92e80f742))
 | 
			
		||||
* **projects:** 新增腾讯地图插件 ([3f02c21](https://github.com/honghuangdc/soybean-admin/commit/3f02c215c54fde4c85bf13e92c2620553d5a1840))
 | 
			
		||||
* **projects:** 新增文档页面 ([7654b2a](https://github.com/honghuangdc/soybean-admin/commit/7654b2adf3d0bf051d13b401dfa3534ca7ee3e0c))
 | 
			
		||||
* **projects:** 新增主题配置 ([ed67b79](https://github.com/honghuangdc/soybean-admin/commit/ed67b797c215fe165808505f4b0b9400f3182383))
 | 
			
		||||
* **projects:** 新增主题配置:页面功能 ([8601ce2](https://github.com/honghuangdc/soybean-admin/commit/8601ce2ea184455fcba1d17d759cd4b933b31d96))
 | 
			
		||||
* **projects:** 新增主题颜色配置 ([d93493b](https://github.com/honghuangdc/soybean-admin/commit/d93493b91ca856573c306e890e8c6f6a46b5bda3))
 | 
			
		||||
* **projects:** 增加Icon以及打印功能示例 ([d5bce26](https://github.com/honghuangdc/soybean-admin/commit/d5bce26454c7d7c9da29e01675624f985755779f))
 | 
			
		||||
* **projects:** 主题配置:页面功能和页面显示 ([a0392b3](https://github.com/honghuangdc/soybean-admin/commit/a0392b3d28f89f2b5fcf5b4d2b82ab7a068a23b8))
 | 
			
		||||
* **projects:** vertical-mix的导航模式的二级菜单显示 ([736f314](https://github.com/honghuangdc/soybean-admin/commit/736f3146cb7cb3f56e06a8185ec8532f25c40b13))
 | 
			
		||||
* **route:** 增加功能示例模块 ([efd29bc](https://github.com/honghuangdc/soybean-admin/commit/efd29bc331f630b57eab800bba08b22c53115d76))
 | 
			
		||||
* **projects:** 迁移全局搜索菜单功能 ([554d7fd](https://github.com/honghuangdc/soybean-admin/commit/554d7fd6114b9cf6df571c3cb02f4cb0cc6dcfd4))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
* **多页签:** 在pc模式下右键某个多页签会切换路由 ([a4394dc](https://github.com/honghuangdc/soybean-admin/commit/a4394dc3ee81ea2abc9a9fd243714309a1b4e6ab))
 | 
			
		||||
* **components:** 修复按钮Tab自适应主题颜色 ([3d1f419](https://github.com/honghuangdc/soybean-admin/commit/3d1f41925d54ebe89f1bbbdfe916be59bb97c9cf))
 | 
			
		||||
* **components:** 修复BaseLayout的HorizontalLayout ([0344f46](https://github.com/honghuangdc/soybean-admin/commit/0344f46c9377acfb52c28cf373a5416845d1aa1b))
 | 
			
		||||
* **components:** 修复tab组件适应暗黑主题模式 ([2fe3d27](https://github.com/honghuangdc/soybean-admin/commit/2fe3d27a36b641339fd87eaa7acad8c3424b97b4))
 | 
			
		||||
* **components:** tab组件在黑暗模式下泛白的颜色问题以及chromeTab的重叠问题 ([6797dbf](https://github.com/honghuangdc/soybean-admin/commit/6797dbf1b0617dcca662a25cf663d93dc4ad5807))
 | 
			
		||||
* **deps:** 降低vite版本,新版本有些许问题 ([b429c8b](https://github.com/honghuangdc/soybean-admin/commit/b429c8b8ca61191c6bed1c52742ddd5fcf9ddc3a))
 | 
			
		||||
* **deps:** 去除图片验证码依赖 ([76a1afa](https://github.com/honghuangdc/soybean-admin/commit/76a1afae4e87c3c08f7fd31b20323c0456565f64))
 | 
			
		||||
* **deps:** vite依赖放入devDependencies ([7527b1f](https://github.com/honghuangdc/soybean-admin/commit/7527b1f07cdc2d82ec0104ed7317c7ff731da0b7))
 | 
			
		||||
* **hooks:** 修复登录页切换登录页参数丢失问题 ([789855a](https://github.com/honghuangdc/soybean-admin/commit/789855a3786623893aa55a2f6c977155394a8a44))
 | 
			
		||||
* **hooks:** 修复toLogin函数导致登录重定向地址过多 ([b4adf67](https://github.com/honghuangdc/soybean-admin/commit/b4adf678a4f96f670f9cbdcaebe21378fa94c77c))
 | 
			
		||||
* **projects:** 布局修复:从填充屏幕高的页面切换至滚动页面导致布局坍塌 ([2fdb5f5](https://github.com/honghuangdc/soybean-admin/commit/2fdb5f563f7d9fa00d8e5343d992342ff34e3a5a))
 | 
			
		||||
* **projects:** 更正dashboard的布局文件 ([31fda0c](https://github.com/honghuangdc/soybean-admin/commit/31fda0ce992457972205db3a39e4c7327d21c087))
 | 
			
		||||
* **projects:** 关于页面:开发环境依赖更正 ([3b3baf9](https://github.com/honghuangdc/soybean-admin/commit/3b3baf93ee36423bfe4fc0ab24eda0f99ce92363))
 | 
			
		||||
* **projects:** 腾讯地图容器高自适应 ([d7054c5](https://github.com/honghuangdc/soybean-admin/commit/d7054c599b1ce59a123667443863a8054ba19a90))
 | 
			
		||||
* **projects:** 头部logo链接更正 ([5d8c3f5](https://github.com/honghuangdc/soybean-admin/commit/5d8c3f54a3e414cdeff35bf5ddb2a1e13d7d703a))
 | 
			
		||||
* **projects:** 完善侧边菜单展开逻辑 ([b5f0512](https://github.com/honghuangdc/soybean-admin/commit/b5f05128abcf2403181b7cc7800d9e6593844657))
 | 
			
		||||
* **projects:** 修复百度地图sdk地址 ([9a97d23](https://github.com/honghuangdc/soybean-admin/commit/9a97d23c755b7fa7c3166d783e99cac10a0a9753))
 | 
			
		||||
* **projects:** 修复登录的重定向地址 ([f97f226](https://github.com/honghuangdc/soybean-admin/commit/f97f2266566164cad912e7ffcdebee1c1b2f4324))
 | 
			
		||||
* **projects:** 修复登录页刷新跳404 ([358d4e8](https://github.com/honghuangdc/soybean-admin/commit/358d4e8a1992aa040b909ae580470a0fd2142f5f))
 | 
			
		||||
* **projects:** 修复顶部加载条主题 ([ea5917d](https://github.com/honghuangdc/soybean-admin/commit/ea5917d2258356bbcb296420ea1d017f5ad05b7a))
 | 
			
		||||
* **projects:** 修复多级菜单页面multitab显示问题 ([f0474bd](https://github.com/honghuangdc/soybean-admin/commit/f0474bd96104dcca332d35d8202eedc3df00eb10))
 | 
			
		||||
* **projects:** 修复多页签删除功能 ([99adbc5](https://github.com/honghuangdc/soybean-admin/commit/99adbc5a30c9128d005dc8096d58c5b320f67fef))
 | 
			
		||||
* **projects:** 修复分析页折线图表布局问题 ([43b832b](https://github.com/honghuangdc/soybean-admin/commit/43b832bee0dc1d852f3e435f16eaa37f27b0f66c))
 | 
			
		||||
* **projects:** 修复富文本编辑器在亮色主题下全屏后背景色丢失 ([4ab7702](https://github.com/honghuangdc/soybean-admin/commit/4ab7702186e1121e50f1d4725b73f28498aba312))
 | 
			
		||||
* **projects:** 修复没有子页面的路由写法问题 ([b80c224](https://github.com/honghuangdc/soybean-admin/commit/b80c2246641d44b9ad35dfbfb3d17500cfcb6e43))
 | 
			
		||||
* **projects:** 修复同时显示两种multiTab ([5be2e2a](https://github.com/honghuangdc/soybean-admin/commit/5be2e2a2e5658e09c47a4dc1331129e14ed6d761))
 | 
			
		||||
* **projects:** 修复页面滚动和页面100%视高占比 ([fa2cc78](https://github.com/honghuangdc/soybean-admin/commit/fa2cc789371999de6b2f698ba7ed87a4d740ad37))
 | 
			
		||||
* **projects:** 修复页面滚动行为 ([57e00e6](https://github.com/honghuangdc/soybean-admin/commit/57e00e64177bc9925ca95785335786836571766a))
 | 
			
		||||
* **projects:** 修复页面缓存 ([fa0a907](https://github.com/honghuangdc/soybean-admin/commit/fa0a907941a90ed72288205fef14b0923a0ffd8e))
 | 
			
		||||
* **projects:** 修复页面缓存,添加多页签删除 ([2489374](https://github.com/honghuangdc/soybean-admin/commit/248937479cc9ccb936116300d628dfa734014b37))
 | 
			
		||||
* **projects:** 修复在暗黑模式下第一次进入网页不会触发暗黑模式监听 ([c4a652e](https://github.com/honghuangdc/soybean-admin/commit/c4a652e21e4c3e2ee6e86e04e46d5dccd579d584))
 | 
			
		||||
* **projects:** 修复主题配置 ([ff24fda](https://github.com/honghuangdc/soybean-admin/commit/ff24fda5ee12074e7130122ca311d0ce174cc184))
 | 
			
		||||
* **projects:** 修复主题相关,自适应操作系统暗黑模式 ([bfa42d7](https://github.com/honghuangdc/soybean-admin/commit/bfa42d769d464dbc8d51689c5fc8c59a348941fb))
 | 
			
		||||
* **projects:** 修复globalFooter适应暗黑模式 ([93f08d9](https://github.com/honghuangdc/soybean-admin/commit/93f08d90671b3ddfbdb969d5b13f4a3fa9903a19))
 | 
			
		||||
* **projects:** 修复multiTab关闭逻辑,添加关闭左边和右边的标签右键操作 ([ed90cb8](https://github.com/honghuangdc/soybean-admin/commit/ed90cb8f8e8d3bbf594757caa950f8521869ece4))
 | 
			
		||||
* **projects:** 修复tab过多时样式坍塌,添加tab横向滚动 ([0ec4d21](https://github.com/honghuangdc/soybean-admin/commit/0ec4d218e365f54ab0c138a955dcd990cbf2d9bc))
 | 
			
		||||
* **projects:** 修复tab在移动端无法点击 ([1a76de0](https://github.com/honghuangdc/soybean-admin/commit/1a76de04463b0344b39c09df0e0762825d66653b))
 | 
			
		||||
* **projects:** 修复vertical sider自适应主题 ([9097fa3](https://github.com/honghuangdc/soybean-admin/commit/9097fa386687d077a480033d9978cfbd59e0e3a0))
 | 
			
		||||
* **projects:** 修复vertical-mix导航模式的二级菜单显示问题 ([6f286e6](https://github.com/honghuangdc/soybean-admin/commit/6f286e674724db12d6c5a4339ba6f3db720b781d))
 | 
			
		||||
* **projects:** 页面各部分背景颜色添加自然过渡 ([1c5fdca](https://github.com/honghuangdc/soybean-admin/commit/1c5fdca59637c141ae1f0b47d9bcf05788a631c2))
 | 
			
		||||
* **projects:** wangEditor在暗黑模式下的背景色问题 ([a7de314](https://github.com/honghuangdc/soybean-admin/commit/a7de31404508a2d4436435d06cdb63f851a86029))
 | 
			
		||||
* **types:** 数据类型 EnumDataType.boolean 为 [object Boolean] ([e9b5560](https://github.com/honghuangdc/soybean-admin/commit/e9b55608f960c0d3cdeca91af6f2777a23fd20dd))
 | 
			
		||||
* **types:** 修复naive组件回调函数参数类型错误 ([667282f](https://github.com/honghuangdc/soybean-admin/commit/667282f81a8822006242d612a08ac59571e3508e))
 | 
			
		||||
* **types:** 修复TS类型错误 ([45d31a0](https://github.com/honghuangdc/soybean-admin/commit/45d31a0f5625784423bea463b2373b0cd35b37f5))
 | 
			
		||||
* **utils:** utils函数名称更正 ([68f4d01](https://github.com/honghuangdc/soybean-admin/commit/68f4d012cc3cce1df5cb61dfa0212126ea0b202e))
 | 
			
		||||
* **components:** 修复Tab在移动端设备无法点击的问题 ([2c9660f](https://github.com/honghuangdc/soybean-admin/commit/2c9660fdbf9a84e980db0aff5cd0aed0f75963ca))
 | 
			
		||||
* **projects:** 修复分析页和工作台的布局问题 ([e93b94c](https://github.com/honghuangdc/soybean-admin/commit/e93b94cb2435a130bb1d94a703328af342cd24c9))
 | 
			
		||||
* **projects:** 修复项目配置拷贝功能 ([a7a269d](https://github.com/honghuangdc/soybean-admin/commit/a7a269d6a61ccd25883e6bb69639d39e0260587d))
 | 
			
		||||
* **projects:** vite配置修复 ([facc00e](https://github.com/honghuangdc/soybean-admin/commit/facc00e8b4998dc8bd338e3b63a652b4bfe2ed3e))
 | 
			
		||||
 | 
			
		||||
### [0.9.1](https://github.com/honghuangdc/soybean-admin/compare/v0.1.3...v0.9.1) (2022-01-23)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Performance Improvements
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
* **projects:** 添加windicss指定的扫描目录,提升构建性能 ([8e6b0b2](https://github.com/honghuangdc/soybean-admin/commit/8e6b0b299d2ef50f2b85e67b7a1aa7fd2ac1bce1))
 | 
			
		||||
* **projects:** 新版重构完成 ([68b4230](https://github.com/honghuangdc/soybean-admin/commit/68b42304d5964246775c7a82dcc1406c5db7a4e4))
 | 
			
		||||
 | 
			
		||||
### [0.1.3](https://github.com/honghuangdc/soybean-admin/compare/v0.1.2...v0.1.3) (2022-01-23)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
* **projects:** 修复未登录时会调用获取用户路由的接口 ([21bab1f](https://github.com/honghuangdc/soybean-admin/commit/21bab1f7c30611fe59dc91c7a73050ccb49a4658))
 | 
			
		||||
* **projects:** 修复路由守卫的动态路由逻辑 ([b61b0ce](https://github.com/honghuangdc/soybean-admin/commit/b61b0ce25fdcbaf29ca64cbcc467e12faa947625))
 | 
			
		||||
 | 
			
		||||
### [0.1.2](https://github.com/honghuangdc/soybean-admin/compare/v0.1.1...v0.1.2) (2022-01-21)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
* **projects:** 添加缓存主题色 ([3709297](https://github.com/honghuangdc/soybean-admin/commit/37092974d37b2e661d4cbf9d27c89b5e99119cd7))
 | 
			
		||||
* **projects:** 添加页面缓存、记录在tab中的缓存页面的滚动条位置 ([1d63a83](https://github.com/honghuangdc/soybean-admin/commit/1d63a838226df4f48e7f2a15b5a05d4b496d3c69))
 | 
			
		||||
 | 
			
		||||
### [0.1.1](https://github.com/honghuangdc/soybean-admin/compare/v0.0.5...v0.1.1) (2022-01-20)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
* **projects:** theme store完成 ([bf020a8](https://github.com/honghuangdc/soybean-admin/commit/bf020a82580e6b1fbda1cc1e0bd6176770434884))
 | 
			
		||||
* **projects:** 主题配置抽屉: 迁移其他功能 ([6d132c5](https://github.com/honghuangdc/soybean-admin/commit/6d132c59770e925cfc61217dcefa5b4d937604df))
 | 
			
		||||
* **projects:** 主题配置抽屉:迁移暗黑模式、布局模式、添加颜色选择面板 ([912bfdf](https://github.com/honghuangdc/soybean-admin/commit/912bfdf4390ab624d3f8e343be88e8c1cf7ab5b6))
 | 
			
		||||
* **projects:** 创建自定义布局组件SoybeanLayout ([0653fb1](https://github.com/honghuangdc/soybean-admin/commit/0653fb144fe9d49f24ef4fe6e4a58de6de342b78))
 | 
			
		||||
* **projects:** 初始化加载效果:应用主题颜色 ([035fa11](https://github.com/honghuangdc/soybean-admin/commit/035fa114c9fd638cf467e6a73a8e4c558f503deb))
 | 
			
		||||
* **projects:** 图标选择器增加扩展树形 ([041012b](https://github.com/honghuangdc/soybean-admin/commit/041012b3ee04d960c1e38895839225613f7af377))
 | 
			
		||||
* **projects:** 增加Icon选择器组件 ([9472b51](https://github.com/honghuangdc/soybean-admin/commit/9472b51811f419e9139de81c73f2c71d170700c2))
 | 
			
		||||
* **projects:** 增加全局搜索菜单功能 ([b9ce691](https://github.com/honghuangdc/soybean-admin/commit/b9ce69130b12712013228326f883e2d973e4e46a))
 | 
			
		||||
* **projects:** 增加项目文档外链 ([1901a0b](https://github.com/honghuangdc/soybean-admin/commit/1901a0bfb7bfa516dfda552675397ddec96b8d4b))
 | 
			
		||||
* **projects:** 多级路由的所有子路由转换成二级路由 ([85b55bb](https://github.com/honghuangdc/soybean-admin/commit/85b55bb37a0a06e2645b96ed81aefe463127121a))
 | 
			
		||||
* **projects:** 引入mockjs ([9bc682d](https://github.com/honghuangdc/soybean-admin/commit/9bc682dae878c084e38a0e2c9a4a2de171023c48))
 | 
			
		||||
* **projects:** 新增BasicLayout布局 ([006467a](https://github.com/honghuangdc/soybean-admin/commit/006467a0626f427da3f516d90c15bf1e1eef0e55))
 | 
			
		||||
* **projects:** 添加cryptojs,对本地缓存数据进行加密 ([7a0648d](https://github.com/honghuangdc/soybean-admin/commit/7a0648dba55a98f61f4d81696307d86c82a1d34d))
 | 
			
		||||
* **projects:** 添加NaiveProvider组件 ([c804b21](https://github.com/honghuangdc/soybean-admin/commit/c804b21ceb92133c6ea7cc64c87521cc164e40ce))
 | 
			
		||||
* **projects:** 添加侧边菜单 ([e25afe2](https://github.com/honghuangdc/soybean-admin/commit/e25afe2fadfe86b9330ee02190a4e40b8321714c))
 | 
			
		||||
* **projects:** 添加头部折叠按钮 ([a090d39](https://github.com/honghuangdc/soybean-admin/commit/a090d398fc071e246b92d0da80883cf5cbedba0e))
 | 
			
		||||
* **projects:** 添加常用组件、composables函数 ([230a50a](https://github.com/honghuangdc/soybean-admin/commit/230a50a4cf4d2ebb62b19d6324234243cf6b2f0d))
 | 
			
		||||
* **projects:** 添加抽屉 ([10e4d81](https://github.com/honghuangdc/soybean-admin/commit/10e4d81bd6a0b35d8cfb4f7a1e981f8ef6ab87cc))
 | 
			
		||||
* **projects:** 添加表格页面示例 ([51c744c](https://github.com/honghuangdc/soybean-admin/commit/51c744c8e2c8ed9691e92e35b6a88582f22c30d8))
 | 
			
		||||
* **projects:** 添加路由跳转浏览器新标签 ([987cef3](https://github.com/honghuangdc/soybean-admin/commit/987cef336338987f2e6f0d5aba8f6d4602b297ca))
 | 
			
		||||
* **projects:** 登录页面开始迁移 ([f5a36a0](https://github.com/honghuangdc/soybean-admin/commit/f5a36a05cb626ec62115283f1d2c534b2a787bdd))
 | 
			
		||||
* **projects:** 细节完善 ([cc290ac](https://github.com/honghuangdc/soybean-admin/commit/cc290accc29282e9ba655356e2695b6ca4b23605))
 | 
			
		||||
* **projects:** 细节完善、迁移页面 ([ce531ce](https://github.com/honghuangdc/soybean-admin/commit/ce531ce5dda0b4a1024aa6bd3d68835b59760d57))
 | 
			
		||||
* **projects:** 菜单搜索增加大小写转换 ([2907868](https://github.com/honghuangdc/soybean-admin/commit/29078689b0652cf4ae852c93d8601a157579adcc))
 | 
			
		||||
* **projects:** 请求拦截器添加刷新token ([839b82b](https://github.com/honghuangdc/soybean-admin/commit/839b82ba8b052b02e24bcfe6da54160609a4fd4b))
 | 
			
		||||
* **projects:** 路由页面跳转权限完成 ([0d2a562](https://github.com/honghuangdc/soybean-admin/commit/0d2a5629e89c73a32d6c79f04b51543e1513e006))
 | 
			
		||||
* **projects:** 迁移多页签 ([28efbdb](https://github.com/honghuangdc/soybean-admin/commit/28efbdbc70733d22011a0eee084d35711429d188))
 | 
			
		||||
* **projects:** 迁移登录完成 ([b93b80c](https://github.com/honghuangdc/soybean-admin/commit/b93b80cb4b35268dfb6a09517a2494af24748dac))
 | 
			
		||||
* **projects:** 集成naiveUI主题配置,将css vars添加至html ([2c19684](https://github.com/honghuangdc/soybean-admin/commit/2c196841bd8527d7acccefe6a7545e0a49d532f7))
 | 
			
		||||
* **projects:** 面包屑 ([09c7658](https://github.com/honghuangdc/soybean-admin/commit/09c7658c21c7dda461dbb528e85b638b5a7dfacd))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
* **deps:** 降低vite版本 ([c9c5ca9](https://github.com/honghuangdc/soybean-admin/commit/c9c5ca9989eddb084f2706155473123c5dcfc334))
 | 
			
		||||
* **projects:** 修复redirect-not-found子路由 ([5bfb819](https://github.com/honghuangdc/soybean-admin/commit/5bfb8199b463d9ca6430577b5c493c0b78967aa9))
 | 
			
		||||
* **projects:** 修复vertical-mix布局、重构初始化的loading ([579e074](https://github.com/honghuangdc/soybean-admin/commit/579e07400e1b9a52934ed808a37c8579a41e8e74))
 | 
			
		||||
* **projects:** 修复网络请求错误空信息的提示 ([ff9216b](https://github.com/honghuangdc/soybean-admin/commit/ff9216b621aaef0a8203386fa1c3ca5477a2edea))
 | 
			
		||||
* **projects:** 修复面包屑数据 ([28b5d22](https://github.com/honghuangdc/soybean-admin/commit/28b5d224010a28669ad3a1919fc49f6e2dc808cd))
 | 
			
		||||
* **projects:** 去除Layout组件冗余代码 ([0e783bc](https://github.com/honghuangdc/soybean-admin/commit/0e783bcf7be0b3a083fe950adfb0afc72b510f97))
 | 
			
		||||
* **projects:** 请求相关细节修复 ([2ad1ad3](https://github.com/honghuangdc/soybean-admin/commit/2ad1ad32b8410d84902a33d825032c282ca6df86))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								README.md
									
									
									
									
									
								
							@@ -1,50 +1,56 @@
 | 
			
		||||
<div align="center">
 | 
			
		||||
  <a href="https://github.com/honghuangdc/soybean-admin">
 | 
			
		||||
    <img alt="SoybeanAdmin Logo" width="200" height="200" src="https://s3.bmp.ovh/imgs/2021/09/088571214c76b1e5.png">
 | 
			
		||||
  </a><br /><br />
 | 
			
		||||
	<img src="https://i.loli.net/2021/11/24/x5lLfuSnEawBAgi.png"/>
 | 
			
		||||
	<h1>Soybean Admin</h1>
 | 
			
		||||
  <br />
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
[](LICENSE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[](./LICENSE)
 | 
			
		||||
 | 
			
		||||
## 简介
 | 
			
		||||
 | 
			
		||||
Soybean Admin 是一个基于 Vue3、Vite、Naive UI、TypeScript 的免费中后台模版,它使用了最新的前端技术栈,内置丰富的插件,有着极高的代码规范,开箱即用的中后台前端解决方案,也可用于学习参考。
 | 
			
		||||
Soybean Admin  是一个基于 Vue3、Vite、TypeScript、Naive UI 的免费中后台模版,它使用了最新的前端技术栈,内置丰富的主题配置,有着极高的代码规范,基于mock实现的动态权限路由,开箱即用的中后台前端解决方案,也可用于学习参考。
 | 
			
		||||
 | 
			
		||||
## 特性
 | 
			
		||||
 | 
			
		||||
- **最新技术栈**:使用 Vue3/vite2 等前端前沿技术开发, 使用高效率的npm包管理器pnpm
 | 
			
		||||
- **TypeScript**: 应用程序级 JavaScript 的语言
 | 
			
		||||
- **主题**:丰富可配置的主题
 | 
			
		||||
- **主题**:丰富可配置的主题、暗黑模式,基于windicss的动态主题颜色
 | 
			
		||||
- **代码规范**:丰富的规范插件及极高的代码规范
 | 
			
		||||
- **路由配置**:简易的路由配置
 | 
			
		||||
- **权限路由**:简易的路由配置、基于mock的动态路由能快速实现后端动态路由
 | 
			
		||||
- **请求函数**:完善的请求函数封装,提供Promise和hooks两种请求函数
 | 
			
		||||
 | 
			
		||||
## 预览
 | 
			
		||||
 | 
			
		||||
- [soybean-admin](https://soybean.pro/)
 | 
			
		||||
 | 
			
		||||
<p align="center">
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
    <img alt="SoybeanAdmin" width="100%" src="https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/02.png">
 | 
			
		||||
 | 
			
		||||
    <img alt="SoybeanAdmin" width="100%" src="https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/03.png">
 | 
			
		||||
 | 
			
		||||
    <img alt="SoybeanAdmin" width="100%" src="https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/01.png">
 | 
			
		||||
 | 
			
		||||
<img alt="SoybeanAdmin" width="100%" src="https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/04.png">
 | 
			
		||||
 | 
			
		||||
<img alt="SoybeanAdmin" width="100%" src="https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/05.png">
 | 
			
		||||
 | 
			
		||||
</p>
 | 
			
		||||
- [项目文档](https://docs.soybean.pro)
 | 
			
		||||
 | 
			
		||||
### 代码仓库
 | 
			
		||||
 | 
			
		||||
**github**:https://github.com/honghuangdc/soybean-admin
 | 
			
		||||
- [github](https://github.com/honghuangdc/soybean-admin)
 | 
			
		||||
 | 
			
		||||
**gitee**:https://gitee.com/honghuangdc/soybean-admin
 | 
			
		||||
- [gitee](https://gitee.com/honghuangdc/soybean-admin)
 | 
			
		||||
 | 
			
		||||
### 温馨提示(老用户)
 | 
			
		||||
 | 
			
		||||
旧版代码在old分支,等main分支稳定下来,再删除old分支。
 | 
			
		||||
 | 
			
		||||
如果不是第一次进预览地址 soybean.pro,新版的发布会导致有缓存,退出用户,重新登录即可。
 | 
			
		||||
 | 
			
		||||
thin分支相对于main分支少了插件示例,其他都一样,后面会适当精简一些代码。
 | 
			
		||||
 | 
			
		||||
## 项目示例图
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 使用 Gitpod
 | 
			
		||||
 | 
			
		||||
@@ -52,10 +58,6 @@ Soybean Admin 是一个基于 Vue3、Vite、Naive UI、TypeScript 的免费中
 | 
			
		||||
 | 
			
		||||
[](https://gitpod.io/#https://github.com/honghuangdc/soybean-admin)
 | 
			
		||||
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
[项目相关文档](./doc)
 | 
			
		||||
 | 
			
		||||
## 安装使用
 | 
			
		||||
 | 
			
		||||
- 克隆代码
 | 
			
		||||
@@ -102,9 +104,10 @@ pnpm i -g commitizen
 | 
			
		||||
 | 
			
		||||
支持现代浏览器, 不支持 IE
 | 
			
		||||
 | 
			
		||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
 | 
			
		||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px"  />](http://godban.github.io/browsers-support-badges/)IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Safari |
 | 
			
		||||
| :-: | :-: | :-: | :-: | :-: |
 | 
			
		||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
 | 
			
		||||
 | 
			
		||||
## 开源作者
 | 
			
		||||
 | 
			
		||||
[@Soybean](https://github.com/honghuangdc)
 | 
			
		||||
@@ -113,15 +116,18 @@ pnpm i -g commitizen
 | 
			
		||||
 | 
			
		||||
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供 QQ 交流群使用问题欢迎在群内提问。
 | 
			
		||||
 | 
			
		||||
- QQ 群 `711301266`
 | 
			
		||||
 | 
			
		||||
  <div style="text-align:left;">
 | 
			
		||||
    <img src="https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/qq_qrcode.JPG" style="width:200px" />
 | 
			
		||||
- 微信交流群:
 | 
			
		||||
  <div style="text-align:left">
 | 
			
		||||
    <img src="https://s2.loli.net/2022/02/07/hJWkOUAjpCQNwdf.jpg" style="width:200px" />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
- QQ 群 `711301266`
 | 
			
		||||
 | 
			
		||||
  <div style="text-align:left">
 | 
			
		||||
    <img src="https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg" style="width:200px" />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
- 本人微信号:honghuangdc,欢迎来技术交流。
 | 
			
		||||
- 本人微信号:honghuangdc,欢迎来技术交流,业务咨询。
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,6 @@ import dayjs from 'dayjs';
 | 
			
		||||
/** 项目构建时间 */
 | 
			
		||||
const PROJECT_BUILD_TIME = JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss'));
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
export const define = {
 | 
			
		||||
  PROJECT_BUILD_TIME
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								build/env/index.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								build/env/index.ts
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +0,0 @@
 | 
			
		||||
import dotenv from 'dotenv';
 | 
			
		||||
 | 
			
		||||
const { parsed: viteEnv } = dotenv.config(); // 加载环境
 | 
			
		||||
 | 
			
		||||
export default viteEnv;
 | 
			
		||||
@@ -1,5 +1,2 @@
 | 
			
		||||
import viteEnv from './env';
 | 
			
		||||
import plugins from './plugins';
 | 
			
		||||
import define from './define';
 | 
			
		||||
 | 
			
		||||
export { viteEnv, plugins, define };
 | 
			
		||||
export * from './plugins';
 | 
			
		||||
export * from './define';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,17 @@
 | 
			
		||||
import { loadEnv } from 'vite';
 | 
			
		||||
import type { ConfigEnv, PluginOption } from 'vite';
 | 
			
		||||
import { minifyHtml, injectHtml } from 'vite-plugin-html'; // html插件(使用变量、压缩)
 | 
			
		||||
import viteEnv from '../env';
 | 
			
		||||
 | 
			
		||||
export default [
 | 
			
		||||
export default (config: ConfigEnv): PluginOption[] => {
 | 
			
		||||
  const viteEnv = loadEnv(config.mode, `.env.${config.mode}`);
 | 
			
		||||
 | 
			
		||||
  return [
 | 
			
		||||
    minifyHtml(),
 | 
			
		||||
    injectHtml({
 | 
			
		||||
      injectData: {
 | 
			
		||||
      title: viteEnv.VITE_APP_TITLE,
 | 
			
		||||
      appName: viteEnv.VITE_APP_TITLE_LABEL
 | 
			
		||||
        appName: viteEnv.VITE_APP_NAME,
 | 
			
		||||
        appTitle: viteEnv.VITE_APP_TITLE
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  ];
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,8 @@ import Components from 'unplugin-vue-components/vite'; // 从指定目录自动
 | 
			
		||||
 | 
			
		||||
export default [
 | 
			
		||||
  Components({
 | 
			
		||||
    dts: false,
 | 
			
		||||
    resolvers: [IconsResolver({ componentPrefix: 'icon' })]
 | 
			
		||||
  }),
 | 
			
		||||
  Icons()
 | 
			
		||||
  Icons({ scale: 1, defaultClass: 'inline-block' })
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,17 @@
 | 
			
		||||
import type { ConfigEnv, PluginOption } from 'vite';
 | 
			
		||||
import vue from './vue';
 | 
			
		||||
import html from './html';
 | 
			
		||||
import iconify from './iconify';
 | 
			
		||||
import windicss from './windicss';
 | 
			
		||||
import mock from './mock';
 | 
			
		||||
import visualizer from './visualizer';
 | 
			
		||||
 | 
			
		||||
const plugins = [vue, ...html, ...iconify, windicss, visualizer];
 | 
			
		||||
export function setupVitePlugins(configEnv: ConfigEnv): (PluginOption | PluginOption[])[] {
 | 
			
		||||
  const plugins = [vue, ...html(configEnv), ...iconify, windicss, mock];
 | 
			
		||||
 | 
			
		||||
export default plugins;
 | 
			
		||||
  if (configEnv.command === 'build') {
 | 
			
		||||
    plugins.push(visualizer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return plugins;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								build/plugins/mock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								build/plugins/mock.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { viteMockServe } from 'vite-plugin-mock';
 | 
			
		||||
 | 
			
		||||
export default viteMockServe({
 | 
			
		||||
  mockPath: 'mock',
 | 
			
		||||
  injectCode: `
 | 
			
		||||
		import { setupMockServer } from '../mock';
 | 
			
		||||
		setupMockServer();
 | 
			
		||||
	`
 | 
			
		||||
});
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import { visualizer } from 'rollup-plugin-visualizer';
 | 
			
		||||
 | 
			
		||||
export default visualizer({
 | 
			
		||||
  open: true,
 | 
			
		||||
  gzipSize: true,
 | 
			
		||||
  brotliSize: true
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
### 1.interface和type
 | 
			
		||||
 | 
			
		||||
##### interface和type使用优先级:能用interface表示的类型就用interface。
 | 
			
		||||
 | 
			
		||||
### 2.请求函数
 | 
			
		||||
 | 
			
		||||
#### api接口:
 | 
			
		||||
 | 
			
		||||
统一以 **fetch** 开头,例如:
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
/**
 | 
			
		||||
 * 获取用户信息
 | 
			
		||||
 * @param id - 用户唯一标识id
 | 
			
		||||
 */
 | 
			
		||||
function fetchUserInfo(id:string) {
 | 
			
		||||
	// ***
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 删除列表项
 | 
			
		||||
 * @param id - 列表id
 | 
			
		||||
 */
 | 
			
		||||
function fetchDeleteListItem(id:string) {
 | 
			
		||||
	// ***
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### middleware中间件:
 | 
			
		||||
 | 
			
		||||
统一以 **handle** 开头,例如
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
/**接口返回的用户信息 */
 | 
			
		||||
interface ResponseUserInfo {
 | 
			
		||||
  userId: string;
 | 
			
		||||
  userName: string;
 | 
			
		||||
  userAge: number;
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 获取用户信息 中间件
 | 
			
		||||
 @param data - 返回的用户信息
 | 
			
		||||
 */
 | 
			
		||||
function handleUserInfo(data: ResponseUserInfo): UserInfo {
 | 
			
		||||
  // ***
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
### css书写顺序
 | 
			
		||||
 | 
			
		||||
1. 定位属性:
 | 
			
		||||
 | 
			
		||||
   `position display float left top right bottom overflow clear z-index`
 | 
			
		||||
 | 
			
		||||
2. 自身属性:
 | 
			
		||||
 | 
			
		||||
   `width height padding border margin background`
 | 
			
		||||
 | 
			
		||||
3. 文字样式:
 | 
			
		||||
 | 
			
		||||
   `font-family font-size font-style font-weight font-varient color`
 | 
			
		||||
 | 
			
		||||
4. 文本属性:
 | 
			
		||||
 | 
			
		||||
   `text-align vertical-align text-wrap text-transform text-indent text-decoration letter-spacing word-spacing white-space text-overflow`
 | 
			
		||||
 | 
			
		||||
5. css3中新增属性:
 | 
			
		||||
 | 
			
		||||
   `content box-shadow border-radius transform`
 | 
			
		||||
 | 
			
		||||
#### class类名的顺序:
 | 
			
		||||
 | 
			
		||||
1. 自定义的class类名(遵循BEM命名法)
 | 
			
		||||
2. css插件提供的类名按照以上的css属性对应的顺序
 | 
			
		||||
 | 
			
		||||
例如:自定义类名结合tailwind css
 | 
			
		||||
 | 
			
		||||
<div class="demo-container absolute flex justify-center items-center left-10px top-12px overflow-hidden wh-full p-10px border-1px border-[#f00] m-24px bg-[#fff] text-32px text-[#0f0]"></div>
 | 
			
		||||
<style>
 | 
			
		||||
	.demo-container {
 | 
			
		||||
		box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
### iconify用法
 | 
			
		||||
 | 
			
		||||
#### 一、静态用法:直接用图标的组件名称
 | 
			
		||||
 | 
			
		||||
1. 安装vscode智能提示的插件: Iconify IntelliSense
 | 
			
		||||
2. 找图标:网址 https://icones.js.org/ 或者 vscode安装 icones插件
 | 
			
		||||
3. 确定图标名字:找到图标后复制名字 如:**'mdi:emoticon'** 组件为: `<icon-mdi:emoticon />`, icon-为设置的前缀
 | 
			
		||||
4. 设置样式:同html标签一样直接应用style属性或者class属性; 通过设置color和font-size属性设置对应的颜色和大小
 | 
			
		||||
 | 
			
		||||
#### 二、多个图标动态渲染
 | 
			
		||||
 | 
			
		||||
1. 确定图标名字,如:'mdi:emoticon'
 | 
			
		||||
 | 
			
		||||
2. 引入Icon组件:
 | 
			
		||||
 | 
			
		||||
   `import { Icon } from '@iconify/vue';`
 | 
			
		||||
 | 
			
		||||
3. 动态渲染
 | 
			
		||||
 | 
			
		||||
    `<Icon icon="mdi:emoticon" />`
 | 
			
		||||
 | 
			
		||||
*ps:Icon组件属性 https://docs.iconify.design/icon-components/vue/*
 | 
			
		||||
 | 
			
		||||
#### 三、结合naiveUI组件动态渲染
 | 
			
		||||
 | 
			
		||||
1. 确定图标名字,如:**'mdi:emoticon'**
 | 
			
		||||
 | 
			
		||||
2. 引入vue的h函数:
 | 
			
		||||
 | 
			
		||||
   `import { h } from 'vue';`
 | 
			
		||||
 | 
			
		||||
3. 引入Icon组件
 | 
			
		||||
 | 
			
		||||
   `import { Icon } from '@iconify/vue';`
 | 
			
		||||
 | 
			
		||||
4. 动态渲染
 | 
			
		||||
 | 
			
		||||
   `() => h(Icon, { icon: 'mdi:emoticon', style: { color: '#f00', fontSize: '16px' } })`
 | 
			
		||||
 | 
			
		||||
*ps:@/uitls已封装好了函数:iconifyRender*
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										209
									
								
								doc/vue书写规范.md
									
									
									
									
									
								
							
							
						
						
									
										209
									
								
								doc/vue书写规范.md
									
									
									
									
									
								
							@@ -1,209 +0,0 @@
 | 
			
		||||
### script-setup写法
 | 
			
		||||
 | 
			
		||||
#### 第一部分
 | 
			
		||||
 | 
			
		||||
##### template
 | 
			
		||||
 | 
			
		||||
#### 第二部分
 | 
			
		||||
 | 
			
		||||
##### script
 | 
			
		||||
 | 
			
		||||
##### 一、import的顺序, 依次按照下面的顺序。
 | 
			
		||||
 | 
			
		||||
1. vue模块
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import {  } from 'vue';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
2. vue相关类型
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import type {  } from 'vue';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
3. vue-router模块
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import {  } from 'vue-router';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
4. vue-router相关类型
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import type {  } from 'vue-router';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
5. UI框架模块
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import {  } from 'naive-ui';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
6. UI框架相关类型
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import type {  } from 'naive-ui';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
7. 第三方依赖
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import BScroll from 'bscroll';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
8. 第三方依赖相关类型
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import type {  } from 'bscroll';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
9. @/enum
 | 
			
		||||
 | 
			
		||||
   ```typescript
 | 
			
		||||
   import {  } from '@/enum';
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
10. @/setting
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/setting';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
11. @/plugins
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/plugins';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
12. @/layouts
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/layouts';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
13. @/views
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/views';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
14. @/components
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/components';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
15. @/hooks
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/hooks';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
16. @/store
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/store';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
17. @/context
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/context';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
18. @/router
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/router';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
19. @/service
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/service';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
20. @/utils
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/utils';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
21. @/interface
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/interface';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
22. @/assets
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from '@/assets';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
23. 相对路径依赖
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import {  } from './components';
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
##### 二、TS类型声明
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
interface Props {
 | 
			
		||||
	/**姓名 */
 | 
			
		||||
  name: string;
 | 
			
		||||
	/**年龄 */
 | 
			
		||||
  age?: number;
 | 
			
		||||
}
 | 
			
		||||
interface Emits {
 | 
			
		||||
  /**
 | 
			
		||||
   * 删除事件
 | 
			
		||||
   * @param id - 删除项的id
 | 
			
		||||
   */
 | 
			
		||||
	(e: 'delete', id: number): void;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
##### 三、defineProps、defineEmits、withDefaults
 | 
			
		||||
 | 
			
		||||
1. 定义属性,如:
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  age: 24
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
其中name是必须的属性,age是可选属性,通过withDefaults添加默认值
 | 
			
		||||
 | 
			
		||||
2. 定义emit事件
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
##### 四、响应式use函数
 | 
			
		||||
 | 
			
		||||
有些use函数需要传入响应式的变量参数时,则书写在声明的变量下面。
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
/**dom引用 */
 | 
			
		||||
const domRef = ref<HTMLElement | null>(null);
 | 
			
		||||
const { height: domRefHeight } = useElementSize(domRef); //获取domRef的响应式高度
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##### 五、变量、函数声明
 | 
			
		||||
 | 
			
		||||
##### 六、vue生命周期函数、nextTick执行
 | 
			
		||||
 | 
			
		||||
##### 七、defineExpose
 | 
			
		||||
							
								
								
									
										72
									
								
								doc/命名规范.md
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								doc/命名规范.md
									
									
									
									
									
								
							@@ -1,72 +0,0 @@
 | 
			
		||||
### 命名法:
 | 
			
		||||
 | 
			
		||||
#### 1.驼峰命名法(小驼峰)
 | 
			
		||||
 | 
			
		||||
**getUser**
 | 
			
		||||
 | 
			
		||||
#### 2.帕斯卡命名法(大驼峰)
 | 
			
		||||
 | 
			
		||||
 **GlobalHeader**
 | 
			
		||||
 | 
			
		||||
#### 3.短横线命名法
 | 
			
		||||
 | 
			
		||||
**user-center**
 | 
			
		||||
 | 
			
		||||
#### 4.下划线命名法
 | 
			
		||||
 | 
			
		||||
 **MAX_LENGTH**
 | 
			
		||||
 | 
			
		||||
### 文件、文件夹命名:
 | 
			
		||||
 | 
			
		||||
1. 文件夹作为**路由页面**时用小写字母,包含多个单词时,单词之间建议使用半角的连词线 ( - ) 分隔, 即**短横线命名法**,此时vue文件为**index.vue**。
 | 
			
		||||
2. 文件夹作为**vue组件**时用**大写驼峰命名法**。
 | 
			
		||||
3. 文件作为**vue组件**时用**大写驼峰命名法**。
 | 
			
		||||
4. 文件作为**use函数**时用**小驼峰命名法**。
 | 
			
		||||
5. 其余文件用**短横线命名法**。
 | 
			
		||||
 | 
			
		||||
### 变量命名:
 | 
			
		||||
#### 命名方式 : 小驼峰式命名方法
 | 
			
		||||
**命名规范 : 类型+对象描述的方式,如果没有明确的类型,就可以使前缀为名词**
 | 
			
		||||
 | 
			
		||||
动词 | 含义 | 返回值
 | 
			
		||||
---|---|---
 | 
			
		||||
can | 判断是否可执行某个动作 | 函数返回一个布尔值。true:可执行;false:不可执行。
 | 
			
		||||
has | 判断是否含有某个值 | 函数返回一个布尔值。true:含有此值;false:不含有此值。
 | 
			
		||||
is | 判断是否为某个值 | 函数返回一个布尔值。true:为某个值;false:不为某个值。
 | 
			
		||||
get | 获取某个值 | 函数返回一个非布尔值。
 | 
			
		||||
set | 设置某个值 | 无返回值、返回是否设置成功或者返回链式对象。
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
/** 是否可读 */
 | 
			
		||||
function canRead(){
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 获取姓名 */
 | 
			
		||||
function getName(){
 | 
			
		||||
  return this.name;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 常量
 | 
			
		||||
#### 命名方法 : 使用大写字母和下划线来组合命名,下划线用以分割单词。
 | 
			
		||||
```javascript
 | 
			
		||||
const MAX_COUNT = 10;
 | 
			
		||||
const URL = 'http://www.baidu.com';
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### TS类型接口interface和type
 | 
			
		||||
 | 
			
		||||
##### 命名方法:大写驼峰
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
interface PersonInfo {
 | 
			
		||||
  /**姓名 */
 | 
			
		||||
  name: string;
 | 
			
		||||
  /**性别 '0':男; '1': 女; '2': 未知 */
 | 
			
		||||
  gender: '0' | '1' | '2';
 | 
			
		||||
  /**年龄 */
 | 
			
		||||
  age: 25;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										101
									
								
								doc/目录.md
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								doc/目录.md
									
									
									
									
									
								
							@@ -1,101 +0,0 @@
 | 
			
		||||
 | 
			
		||||
## 目录规范
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
qitan-pc
 | 
			
		||||
├── build                      //vite构建相关配置和插件
 | 
			
		||||
│   ├── define                 //定义的全局常量,通过vite构建时注入
 | 
			
		||||
│   ├── env                    //.env环境文件内容加载插件
 | 
			
		||||
│   └── plugins                //构建插件
 | 
			
		||||
│       ├── html.ts            //html插件(注入变量,压缩代码等)
 | 
			
		||||
│       ├── iconify.ts         //iconify图标插件
 | 
			
		||||
│       ├── visualizer.ts      //构建的依赖大小占比分析插件
 | 
			
		||||
│       ├── vue.ts             //vue相关vite插件
 | 
			
		||||
│       └── windicss.ts        //css框架插件
 | 
			
		||||
├── doc                        //项目相关说明文档
 | 
			
		||||
├── public                     //公共目录
 | 
			
		||||
│   ├── resource               //资源文件夹(不会被打包)
 | 
			
		||||
│   └── favicon.ico            //网站标签图标
 | 
			
		||||
├── src
 | 
			
		||||
│   ├── assets                 //静态资源
 | 
			
		||||
│   ├── components             //全局组件
 | 
			
		||||
│   │   ├── business           //业务相关组件
 | 
			
		||||
│   │   ├── common             //公共组件
 | 
			
		||||
│   │   └── custom             //自定义组件
 | 
			
		||||
│   ├── context                //全局上下文(通过provide和inject实现)
 | 
			
		||||
│   │   ├── app                //从app.vue注入的上下文
 | 
			
		||||
│   │   └── part               //局部组件注入的上下文
 | 
			
		||||
│   ├── enum                   //TS枚举
 | 
			
		||||
│   │   ├── animate.ts         //动画枚举
 | 
			
		||||
│   │   ├── business.ts        //业务相关枚举
 | 
			
		||||
│   │   ├── common.ts          //通用枚举
 | 
			
		||||
│   │   ├── route.ts           //路由相关枚举
 | 
			
		||||
│   │   ├── storage.ts         //存储相关枚举
 | 
			
		||||
│   │   └── theme.ts           //系统主题配置相关枚举
 | 
			
		||||
│   ├── hooks                  //组合式的钩子函数hooks
 | 
			
		||||
│   │   ├── business           //业务相关hooks
 | 
			
		||||
│   │   └── common             //通用hooks
 | 
			
		||||
│   ├── interface              //TS类型接口
 | 
			
		||||
│   │   ├── business.ts        //业务相关类型接口
 | 
			
		||||
│   │   ├── common.ts          //通用类型接口
 | 
			
		||||
│   │   └── theme.ts           //系统主题配置相关类型接口
 | 
			
		||||
│   ├── layouts                //布局组件
 | 
			
		||||
│   │   ├── BasicLayout        //基本布局(包含全局头部、侧边栏、底部等公共部分)
 | 
			
		||||
│   │   ├── BlankLayout        //空白布局组件(单个页面)
 | 
			
		||||
│   │   └── RouterViewLayout   //路由组件(用于多级路由之间的桥接)
 | 
			
		||||
│   ├── plugins                //插件
 | 
			
		||||
│   │   └── dark-mode.ts       //windicss暗黑模式插件
 | 
			
		||||
│   ├── router                 //vue路由
 | 
			
		||||
│   │   ├── modules            //路由页面(按模块划分)
 | 
			
		||||
│   │   ├── permission         //路由权限(路由守卫)
 | 
			
		||||
│   │   ├── routes         		 //声明的路由
 | 
			
		||||
│   │   └── setup              //路由挂载函数
 | 
			
		||||
│   ├── service                //网络请求
 | 
			
		||||
│   │   ├── api                //接口api
 | 
			
		||||
│   │   ├── middleware         //请求结果的处理中间件
 | 
			
		||||
│   │   └── request            //封装的请求函数
 | 
			
		||||
│   ├── settings               //项目初始配置
 | 
			
		||||
│   │   └── theme.ts           //项目主题初始配置
 | 
			
		||||
│   ├── store                  //状态管理
 | 
			
		||||
│   │   └── modules            //状态管理划分的模块
 | 
			
		||||
│   ├── styles                 //样式
 | 
			
		||||
│   │   ├── css                //css
 | 
			
		||||
│   │   └── scss               //scss
 | 
			
		||||
│   ├── typings                //TS类型声明文件(*.d.ts)
 | 
			
		||||
│   ├── utils                  //全局工具函数
 | 
			
		||||
│   │   ├── auth               //用户鉴权
 | 
			
		||||
│   │   ├── common             //通用工具函数
 | 
			
		||||
│   │   ├── package            //npm依赖
 | 
			
		||||
│   │   ├── router             //路由
 | 
			
		||||
│   │   ├── request            //请求工具函数
 | 
			
		||||
│   │   └── storage            //存储
 | 
			
		||||
│   ├── views                  //页面
 | 
			
		||||
│   │   ├── about
 | 
			
		||||
│   │   ├── component
 | 
			
		||||
│   │   ├── dashboard
 | 
			
		||||
│   │   ├── document
 | 
			
		||||
│   │   ├── multi-menu
 | 
			
		||||
│   │   └── system             //系统内置页面:登录、异常页等
 | 
			
		||||
│   ├── App.vue                //vue文件入口
 | 
			
		||||
│   ├── AppProvider.vue        //配置naive UI的vue文件(国际化,loadingBar、message等组件)
 | 
			
		||||
│   └── main.ts                //项目入口ts文件
 | 
			
		||||
├── .cz-config.js              //git cz提交配置
 | 
			
		||||
├── .editorconfig              //统一编辑器配置
 | 
			
		||||
├── .env                       //环境文件
 | 
			
		||||
├── .env.development           //环境文件(开发模式)
 | 
			
		||||
├── .env.production            //环境文件(生产模式)
 | 
			
		||||
├── .env.staging               //环境文件(自定义staging模式)
 | 
			
		||||
├── .eslintignore              //忽略eslint检查的配置文件
 | 
			
		||||
├── .eslintrc.js               //eslint配置文件
 | 
			
		||||
├── .gitignore                 //忽略git提交的配置文件
 | 
			
		||||
├── .husky                     //git commit提交钩子,提交前检查代码格式和提交commit内容的格式
 | 
			
		||||
├── .prettierrc.js             //prettier代码格式插件配置
 | 
			
		||||
├── commitlint.config.js       //commitlint提交规范插件配置
 | 
			
		||||
├── index.html
 | 
			
		||||
├── package.json               //npm依赖描述文件
 | 
			
		||||
├── pnpm-lock.yaml             //npm包管理器pnpm依赖锁定文件
 | 
			
		||||
├── README.md                  //项目介绍文档
 | 
			
		||||
├── tsconfig.json              //TS配置
 | 
			
		||||
├── vite.config.ts             //vite配置
 | 
			
		||||
└── windi.config.ts            //windicss框架配置
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										26
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								index.html
									
									
									
									
									
								
							@@ -4,28 +4,26 @@
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <link rel="icon" href="/favicon.ico" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title><%= title %></title>
 | 
			
		||||
    <title><%= appName %></title>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
		<div id="appProvider" style="display: none"></div>
 | 
			
		||||
    <div id="app">
 | 
			
		||||
			<!-- 页面渲染之前加载动画 -->
 | 
			
		||||
      <div class="app-loading">
 | 
			
		||||
        <img class="app-loading_logo" src="/resource/logo.png" />
 | 
			
		||||
        <div class="app-loading__dot-wrapper">
 | 
			
		||||
          <div class="app-loading__dot">
 | 
			
		||||
            <i class="left top"></i>
 | 
			
		||||
            <i class="left bottom delay-400"></i>
 | 
			
		||||
            <i class="right top delay-800"></i>
 | 
			
		||||
            <i class="right bottom delay-1200"></i>
 | 
			
		||||
      <div class="loading-container">
 | 
			
		||||
        <div id="loadingLogo" class="loading-svg"></div>
 | 
			
		||||
        <div class="loading-spin__container">
 | 
			
		||||
          <div class="loading-spin">
 | 
			
		||||
            <div class="left-0 top-0 loading-spin-item"></div>
 | 
			
		||||
            <div class="left-0 bottom-0 loading-spin-item loading-delay-500"></div>
 | 
			
		||||
            <div class="right-0 top-0 loading-spin-item loading-delay-1000"></div>
 | 
			
		||||
            <div class="right-0 bottom-0 loading-spin-item loading-delay-1500"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <h2 class="app-loading_title"><%= appName %></h2>
 | 
			
		||||
        <h2 class="loading-title"><%= appTitle %></h2>
 | 
			
		||||
      </div>
 | 
			
		||||
      <style>
 | 
			
		||||
        @import '/resource/loading.css';
 | 
			
		||||
      </style>
 | 
			
		||||
      </div>
 | 
			
		||||
      <!-- End -->
 | 
			
		||||
      <script src="/resource/loading.js"></script>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script type="module" src="/src/main.ts"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								mock/api/auth.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								mock/api/auth.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
import type { MockMethod } from 'vite-plugin-mock';
 | 
			
		||||
 | 
			
		||||
const token: ApiAuth.Token = {
 | 
			
		||||
  token: '__TEMP_TOKEN__',
 | 
			
		||||
  refreshToken: '__TEMP_REFRESH_TOKEN__'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const apis: MockMethod[] = [
 | 
			
		||||
  // 获取验证码
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/getSmsCode',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    response: (): Service.MockServiceResult<boolean> => {
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: true
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  // 密码登录
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/loginByPwd',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    response: (): Service.MockServiceResult<ApiAuth.Token> => {
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: token
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  // 验证码登录
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/loginByCode',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    response: (): Service.MockServiceResult<ApiAuth.Token> => {
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: token
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  // 获取用户信息(请求头携带token)
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/getUserInfo',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    response: (): Service.MockServiceResult<ApiAuth.UserInfo> => {
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: {
 | 
			
		||||
          userId: '0',
 | 
			
		||||
          userName: 'Soybean',
 | 
			
		||||
          userPhone: '15170283876',
 | 
			
		||||
          userRole: 'super'
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/testToken',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    response: (option: any): Service.MockServiceResult<true | null> => {
 | 
			
		||||
      if (option.headers?.authorization !== token.token) {
 | 
			
		||||
        return {
 | 
			
		||||
          code: 66666,
 | 
			
		||||
          message: 'token 失效',
 | 
			
		||||
          data: null
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: true
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/updateToken',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    response: (): Service.MockServiceResult<string> => {
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: token.token
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export default apis;
 | 
			
		||||
							
								
								
									
										4
									
								
								mock/api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								mock/api/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
import auth from './auth';
 | 
			
		||||
import route from './route';
 | 
			
		||||
 | 
			
		||||
export default [...auth, ...route];
 | 
			
		||||
							
								
								
									
										355
									
								
								mock/api/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								mock/api/route.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,355 @@
 | 
			
		||||
import type { MockMethod } from 'vite-plugin-mock';
 | 
			
		||||
 | 
			
		||||
const routes: AuthRoute.Route[] = [
 | 
			
		||||
  {
 | 
			
		||||
    name: 'dashboard',
 | 
			
		||||
    path: '/dashboard',
 | 
			
		||||
    component: 'basic',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'dashboard_analysis',
 | 
			
		||||
        path: '/dashboard/analysis',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '分析页',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'dashboard_workbench',
 | 
			
		||||
        path: '/dashboard/workbench',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '工作台',
 | 
			
		||||
          requiresAuth: true,
 | 
			
		||||
          permissions: ['super', 'admin']
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '仪表盘',
 | 
			
		||||
      icon: 'carbon:dashboard',
 | 
			
		||||
      order: 1
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'document',
 | 
			
		||||
    path: '/document',
 | 
			
		||||
    component: 'basic',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'document_vue',
 | 
			
		||||
        path: '/document/vue',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'vue文档',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'document_vue-new',
 | 
			
		||||
        path: '/document/vue-new',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'vue文档(新版)',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'document_vite',
 | 
			
		||||
        path: '/document/vite',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'vite文档',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'document_naive',
 | 
			
		||||
        path: '/document/naive',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'naive文档',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'document_project',
 | 
			
		||||
        path: '/document/project',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '项目文档(外链)',
 | 
			
		||||
          requiresAuth: true,
 | 
			
		||||
          href: 'https://docs.soybean.pro/'
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '文档',
 | 
			
		||||
      icon: 'carbon:document',
 | 
			
		||||
      order: 2
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'component',
 | 
			
		||||
    path: '/component',
 | 
			
		||||
    component: 'basic',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'component_button',
 | 
			
		||||
        path: '/component/button',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '按钮',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'component_card',
 | 
			
		||||
        path: '/component/card',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '卡片',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'component_table',
 | 
			
		||||
        path: '/component/table',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '表格',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '组件示例',
 | 
			
		||||
      icon: 'fluent:app-store-24-regular',
 | 
			
		||||
      order: 3
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'plugin',
 | 
			
		||||
    path: '/plugin',
 | 
			
		||||
    component: 'basic',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_map',
 | 
			
		||||
        path: '/plugin/map',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '地图',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_video',
 | 
			
		||||
        path: '/plugin/video',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '视频',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_editor',
 | 
			
		||||
        path: '/plugin/editor',
 | 
			
		||||
        component: 'multi',
 | 
			
		||||
        children: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'plugin_editor_quill',
 | 
			
		||||
            path: '/plugin/editor/quill',
 | 
			
		||||
            component: 'self',
 | 
			
		||||
            meta: {
 | 
			
		||||
              title: '富文本编辑器'
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: 'plugin_editor_markdown',
 | 
			
		||||
            path: '/plugin/editor/markdown',
 | 
			
		||||
            component: 'self',
 | 
			
		||||
            meta: {
 | 
			
		||||
              title: 'markdown编辑器'
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '编辑器',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_swiper',
 | 
			
		||||
        path: '/plugin/swiper',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'Swiper插件',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_copy',
 | 
			
		||||
        path: '/plugin/copy',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '剪贴板',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_icon',
 | 
			
		||||
        path: '/plugin/icon',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '图标',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'plugin_print',
 | 
			
		||||
        path: '/plugin/print',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '打印',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '插件示例',
 | 
			
		||||
      icon: 'clarity:plugin-line',
 | 
			
		||||
      order: 4
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'exception',
 | 
			
		||||
    path: '/exception',
 | 
			
		||||
    component: 'basic',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'exception_403',
 | 
			
		||||
        path: '/exception/403',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '异常页403',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'exception_404',
 | 
			
		||||
        path: '/exception/404',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '异常页404',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'exception_500',
 | 
			
		||||
        path: '/exception/500',
 | 
			
		||||
        component: 'self',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '异常页500',
 | 
			
		||||
          requiresAuth: true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '异常页',
 | 
			
		||||
      icon: 'ant-design:exception-outlined',
 | 
			
		||||
      order: 5
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'multi-menu',
 | 
			
		||||
    path: '/multi-menu',
 | 
			
		||||
    component: 'basic',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'multi-menu_first',
 | 
			
		||||
        path: '/multi-menu/first',
 | 
			
		||||
        component: 'multi',
 | 
			
		||||
        children: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'multi-menu_first_second',
 | 
			
		||||
            path: '/multi-menu/first/second',
 | 
			
		||||
            component: 'self',
 | 
			
		||||
            meta: {
 | 
			
		||||
              title: '二级菜单',
 | 
			
		||||
              requiresAuth: true
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: 'multi-menu_first_second-new',
 | 
			
		||||
            path: '/multi-menu/first/second-new',
 | 
			
		||||
            component: 'multi',
 | 
			
		||||
            children: [
 | 
			
		||||
              {
 | 
			
		||||
                name: 'multi-menu_first_second-new_third',
 | 
			
		||||
                path: '/multi-menu/first/second-new/third',
 | 
			
		||||
                component: 'self',
 | 
			
		||||
                meta: {
 | 
			
		||||
                  title: '三级菜单',
 | 
			
		||||
                  requiresAuth: true
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            ],
 | 
			
		||||
            meta: {
 | 
			
		||||
              title: '二级菜单(有子菜单)'
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '一级菜单'
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '多级菜单',
 | 
			
		||||
      icon: 'carbon:menu',
 | 
			
		||||
      order: 6
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'about',
 | 
			
		||||
    path: '/about',
 | 
			
		||||
    component: 'self',
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '关于',
 | 
			
		||||
      requiresAuth: true,
 | 
			
		||||
      singleLayout: 'basic',
 | 
			
		||||
      permissions: ['super', 'admin', 'test'],
 | 
			
		||||
      icon: 'fluent:book-information-24-regular',
 | 
			
		||||
      order: 7
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function dataMiddleware(data: AuthRoute.Route[]): ApiRoute.Route {
 | 
			
		||||
  const routeHomeName: AuthRoute.RouteKey = 'dashboard_analysis';
 | 
			
		||||
 | 
			
		||||
  function sortRoutes(sorts: AuthRoute.Route[]) {
 | 
			
		||||
    return sorts.sort((next, pre) => Number(next.meta?.order) - Number(pre.meta?.order));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    routes: sortRoutes(data),
 | 
			
		||||
    home: routeHomeName
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const apis: MockMethod[] = [
 | 
			
		||||
  {
 | 
			
		||||
    url: '/mock/getUserRoutes',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    response: (): Service.MockServiceResult => {
 | 
			
		||||
      return {
 | 
			
		||||
        code: 200,
 | 
			
		||||
        message: 'ok',
 | 
			
		||||
        data: dataMiddleware(routes)
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export default apis;
 | 
			
		||||
							
								
								
									
										6
									
								
								mock/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mock/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
 | 
			
		||||
import api from './api';
 | 
			
		||||
 | 
			
		||||
export function setupMockServer() {
 | 
			
		||||
  createProdMockServer(api);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										122
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								package.json
									
									
									
									
									
								
							@@ -1,23 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "soybean-admin",
 | 
			
		||||
  "version": "0.0.3",
 | 
			
		||||
  "author": {
 | 
			
		||||
    "name": "Soybean",
 | 
			
		||||
    "email": "honghuangdc@gmail.com",
 | 
			
		||||
    "url": "https://github.com/honghuangdc"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "0.9.2",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "cross-env VITE_HTTP_ENV=test vite",
 | 
			
		||||
    "dev:prod": "cross-env VITE_HTTP_ENV=prod vite",
 | 
			
		||||
    "typecheck": "vue-tsc",
 | 
			
		||||
    "build": "npm run typecheck && cross-env VITE_HTTP_ENV=prod vite build",
 | 
			
		||||
    "build:test": "npm run typecheck && cross-env VITE_HTTP_ENV=test vite build",
 | 
			
		||||
    "build:vercel": "npm run typecheck && cross-env VITE_HTTP_ENV=prod VITE_IS_VERCEL=1 vite build",
 | 
			
		||||
    "preview": "vite preview --port 5050",
 | 
			
		||||
    "release": "standard-version",
 | 
			
		||||
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
 | 
			
		||||
    "dev": "vite",
 | 
			
		||||
    "dev:prod": "vite --mode production",
 | 
			
		||||
    "dev:staging": "vite --mode staging",
 | 
			
		||||
    "build": "vue-tsc --noEmit --skipLibCheck && vite build",
 | 
			
		||||
    "build:dev": "vue-tsc --noEmit --skipLibCheck && vite build --mode development",
 | 
			
		||||
    "build:staging": "vue-tsc --noEmit --skipLibCheck && vite build --mode staging",
 | 
			
		||||
    "serve": "vite preview",
 | 
			
		||||
    "lint": "eslint ./src --ext .vue,.js,jsx,.ts,tsx",
 | 
			
		||||
    "lint:fix": "eslint --fix ./src --ext .vue,.js,jsx,.ts,tsx",
 | 
			
		||||
    "lint": "eslint --fix ./ --ext .vue,.js,jsx,.ts,tsx",
 | 
			
		||||
    "prepare": "husky install",
 | 
			
		||||
    "postinstall": "patch-package"
 | 
			
		||||
  },
 | 
			
		||||
@@ -30,73 +24,71 @@
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@antv/g2plot": "^2.3.40",
 | 
			
		||||
    "@antv/g2plot": "^2.4.7",
 | 
			
		||||
    "@better-scroll/core": "^2.4.2",
 | 
			
		||||
    "@vueuse/core": "^7.0.0",
 | 
			
		||||
    "axios": "^0.24.0",
 | 
			
		||||
    "chroma-js": "^2.1.2",
 | 
			
		||||
    "clipboard": "^2.0.8",
 | 
			
		||||
    "@vueuse/core": "^7.5.5",
 | 
			
		||||
    "axios": "^0.25.0",
 | 
			
		||||
    "clipboard": "^2.0.10",
 | 
			
		||||
    "colord": "^2.9.2",
 | 
			
		||||
    "crypto-js": "^4.1.1",
 | 
			
		||||
    "dayjs": "^1.10.7",
 | 
			
		||||
    "form-data": "^4.0.0",
 | 
			
		||||
    "naive-ui": "^2.21.0",
 | 
			
		||||
    "pinia": "^2.0.4",
 | 
			
		||||
    "lodash-es": "^4.17.21",
 | 
			
		||||
    "naive-ui": "^2.25.1",
 | 
			
		||||
    "pinia": "^2.0.11",
 | 
			
		||||
    "print-js": "^1.6.0",
 | 
			
		||||
    "qs": "^6.10.1",
 | 
			
		||||
    "swiper": "^7.3.0",
 | 
			
		||||
    "vditor": "^3.8.7",
 | 
			
		||||
    "vue": "^3.2.22",
 | 
			
		||||
    "qs": "^6.10.3",
 | 
			
		||||
    "swiper": "^8.0.3",
 | 
			
		||||
    "ua-parser-js": "^1.0.2",
 | 
			
		||||
    "vditor": "^3.8.11",
 | 
			
		||||
    "vue": "^3.2.29",
 | 
			
		||||
    "vue-router": "^4.0.12",
 | 
			
		||||
    "wangeditor": "^4.7.9",
 | 
			
		||||
    "xgplayer": "^2.31.2"
 | 
			
		||||
    "wangeditor": "^4.7.11",
 | 
			
		||||
    "xgplayer": "^2.31.4"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@amap/amap-jsapi-types": "^0.0.8",
 | 
			
		||||
    "@commitlint/cli": "^15.0.0",
 | 
			
		||||
    "@commitlint/config-conventional": "^15.0.0",
 | 
			
		||||
    "@iconify/json": "^1.1.431",
 | 
			
		||||
    "@iconify/vue": "^3.1.1",
 | 
			
		||||
    "@types/bmapgl": "^0.0.4",
 | 
			
		||||
    "@types/chroma-js": "^2.1.3",
 | 
			
		||||
    "@commitlint/cli": "^16.1.0",
 | 
			
		||||
    "@commitlint/config-conventional": "^16.0.0",
 | 
			
		||||
    "@iconify/json": "^2.0.33",
 | 
			
		||||
    "@iconify/vue": "^3.1.3",
 | 
			
		||||
    "@types/bmapgl": "^0.0.5",
 | 
			
		||||
    "@types/crypto-js": "^4.1.0",
 | 
			
		||||
    "@types/node": "^17.0.15",
 | 
			
		||||
    "@types/qs": "^6.9.7",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^5.4.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^5.4.0",
 | 
			
		||||
    "@vitejs/plugin-vue": "^1.9.4",
 | 
			
		||||
    "@vue/compiler-sfc": "^3.2.22",
 | 
			
		||||
    "@vue/eslint-config-prettier": "^6.0.0",
 | 
			
		||||
    "@vue/eslint-config-typescript": "^9.1.0",
 | 
			
		||||
    "@types/ua-parser-js": "^0.7.36",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^5.10.2",
 | 
			
		||||
    "@typescript-eslint/parser": "^5.10.2",
 | 
			
		||||
    "@vitejs/plugin-vue": "^2.1.0",
 | 
			
		||||
    "@vue/eslint-config-prettier": "^7.0.0",
 | 
			
		||||
    "@vue/eslint-config-typescript": "^10.0.0",
 | 
			
		||||
    "commitizen": "^4.2.4",
 | 
			
		||||
    "cross-env": "^7.0.3",
 | 
			
		||||
    "cz-conventional-changelog": "^3.3.0",
 | 
			
		||||
    "cz-customizable": "^6.3.0",
 | 
			
		||||
    "dotenv": "^10.0.0",
 | 
			
		||||
    "eslint": "^8.3.0",
 | 
			
		||||
    "eslint": "^8.8.0",
 | 
			
		||||
    "eslint-config-airbnb-base": "^15.0.0",
 | 
			
		||||
    "eslint-config-prettier": "^8.3.0",
 | 
			
		||||
    "eslint-plugin-import": "^2.25.3",
 | 
			
		||||
    "eslint-plugin-import": "^2.25.4",
 | 
			
		||||
    "eslint-plugin-prettier": "^4.0.0",
 | 
			
		||||
    "eslint-plugin-vue": "^8.1.1",
 | 
			
		||||
    "eslint-plugin-vue": "^8.4.1",
 | 
			
		||||
    "husky": "^7.0.4",
 | 
			
		||||
    "lint-staged": "^12.1.0",
 | 
			
		||||
    "lint-staged": "^12.3.3",
 | 
			
		||||
    "mockjs": "^1.1.0",
 | 
			
		||||
    "patch-package": "^6.4.7",
 | 
			
		||||
    "postinstall-postinstall": "^2.1.0",
 | 
			
		||||
    "prettier": "^2.4.1",
 | 
			
		||||
    "rollup-plugin-visualizer": "^5.5.2",
 | 
			
		||||
    "sass": "^1.43.4",
 | 
			
		||||
    "typescript": "^4.5.2",
 | 
			
		||||
    "unplugin-icons": "^0.12.18",
 | 
			
		||||
    "unplugin-vue-components": "^0.17.2",
 | 
			
		||||
    "vite": "~2.5.10",
 | 
			
		||||
    "vite-plugin-html": "^2.1.1",
 | 
			
		||||
    "vite-plugin-windicss": "^1.5.1",
 | 
			
		||||
    "vue-tsc": "^0.29.6",
 | 
			
		||||
    "vueuc": "^0.4.15",
 | 
			
		||||
    "windicss": "^3.2.1"
 | 
			
		||||
  },
 | 
			
		||||
  "homepage": "https://github.com/honghuangdc/soybean-admin",
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git+https://github.com/honghuangdc/soybean-admin.git"
 | 
			
		||||
  },
 | 
			
		||||
  "bugs": {
 | 
			
		||||
    "url": "https://github.com/honghuangdc/soybean-admin/issues"
 | 
			
		||||
    "prettier": "^2.5.1",
 | 
			
		||||
    "rollup-plugin-visualizer": "^5.5.4",
 | 
			
		||||
    "sass": "^1.49.7",
 | 
			
		||||
    "typescript": "^4.5.5",
 | 
			
		||||
    "unplugin-icons": "^0.13.0",
 | 
			
		||||
    "unplugin-vue-components": "^0.17.17",
 | 
			
		||||
    "vite": "^2.7.13",
 | 
			
		||||
    "vite-plugin-html": "^2.1.2",
 | 
			
		||||
    "vite-plugin-mock": "^2.9.6",
 | 
			
		||||
    "vite-plugin-windicss": "^1.6.3",
 | 
			
		||||
    "vue-tsc": "^0.31.2",
 | 
			
		||||
    "vueuc": "^0.4.25",
 | 
			
		||||
    "windicss": "^3.4.3"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8927
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8927
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,91 +1,91 @@
 | 
			
		||||
.app-loading {
 | 
			
		||||
.loading-container {
 | 
			
		||||
	position: fixed;
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: 0;
 | 
			
		||||
  z-index: -1;
 | 
			
		||||
	display: flex;
 | 
			
		||||
	flex-direction: column;
 | 
			
		||||
	justify-content: center;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	height: 100%;
 | 
			
		||||
	background-color:#f5f7f9;
 | 
			
		||||
}
 | 
			
		||||
.app-loading_logo {
 | 
			
		||||
 | 
			
		||||
.loading-svg {
 | 
			
		||||
	width: 128px;
 | 
			
		||||
	height: 128px;
 | 
			
		||||
	color: var(--primary-color);
 | 
			
		||||
}
 | 
			
		||||
.app-loading__dot-wrapper {
 | 
			
		||||
 | 
			
		||||
.loading-spin__container {
 | 
			
		||||
	width: 56px;
 | 
			
		||||
	height: 56px;
 | 
			
		||||
	margin: 36px 0;
 | 
			
		||||
}
 | 
			
		||||
.app-loading__dot {
 | 
			
		||||
 | 
			
		||||
.loading-spin {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	height: 100%;
 | 
			
		||||
  transform: rotate(45deg);
 | 
			
		||||
  animation-name: loadingRotate;
 | 
			
		||||
  animation-duration: 1.2s;
 | 
			
		||||
  animation-iteration-count: infinite;
 | 
			
		||||
  animation-timing-function: linear;
 | 
			
		||||
	animation: loadingSpin 1s linear infinite;
 | 
			
		||||
}
 | 
			
		||||
@keyframes loadingRotate {
 | 
			
		||||
  to {
 | 
			
		||||
    -webkit-transform: rotate(405deg);
 | 
			
		||||
    transform: rotate(405deg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@-webkit-keyframes loadingRotate {
 | 
			
		||||
  to {
 | 
			
		||||
    -webkit-transform: rotate(405deg);
 | 
			
		||||
    transform: rotate(405deg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.app-loading__dot > i {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  display: block;
 | 
			
		||||
  width: 18px;
 | 
			
		||||
  height: 18px;
 | 
			
		||||
  background: #1890ff;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  -webkit-transform: scale(0.75);
 | 
			
		||||
  transform: scale(0.75);
 | 
			
		||||
  transform-origin: 50% 50%;
 | 
			
		||||
  opacity: 0.3;
 | 
			
		||||
  animation: spinOpacity 1s infinite linear alternate;
 | 
			
		||||
}
 | 
			
		||||
@keyframes spinOpacity {
 | 
			
		||||
  to {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@-webkit-keyframes spinOpacity {
 | 
			
		||||
  to {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.delay-400 {
 | 
			
		||||
  animation-delay: 0.4s !important;
 | 
			
		||||
}
 | 
			
		||||
.delay-800 {
 | 
			
		||||
  animation-delay: 0.8s !important;
 | 
			
		||||
}
 | 
			
		||||
.delay-1200 {
 | 
			
		||||
  animation-delay: 1.2s !important;
 | 
			
		||||
}
 | 
			
		||||
.left {
 | 
			
		||||
 | 
			
		||||
.left-0 {
 | 
			
		||||
  left: 0;
 | 
			
		||||
}
 | 
			
		||||
.right {
 | 
			
		||||
.right-0 {
 | 
			
		||||
  right: 0;
 | 
			
		||||
}
 | 
			
		||||
.top {
 | 
			
		||||
.top-0 {
 | 
			
		||||
  top: 0;
 | 
			
		||||
}
 | 
			
		||||
.bottom {
 | 
			
		||||
.bottom-0 {
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
.app-loading_title {
 | 
			
		||||
 | 
			
		||||
.loading-spin-item {
 | 
			
		||||
	position: absolute;
 | 
			
		||||
  height: 16px;
 | 
			
		||||
  width: 16px;
 | 
			
		||||
	background-color: var(--primary-color);
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  -webkit-animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 | 
			
		||||
  animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes loadingSpin {
 | 
			
		||||
  from {
 | 
			
		||||
    -webkit-transform: rotate(0deg);
 | 
			
		||||
    transform: rotate(0deg);
 | 
			
		||||
  }
 | 
			
		||||
  to {
 | 
			
		||||
    -webkit-transform: rotate(360deg);
 | 
			
		||||
    transform: rotate(360deg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes loadingPulse {
 | 
			
		||||
  0%, 100% {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
  }
 | 
			
		||||
  50% {
 | 
			
		||||
    opacity: .5;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.loading-delay-500 {
 | 
			
		||||
  -webkit-animation-delay: 500ms;
 | 
			
		||||
  animation-delay: 500ms;
 | 
			
		||||
}
 | 
			
		||||
.loading-delay-1000 {
 | 
			
		||||
  -webkit-animation-delay: 1000ms;
 | 
			
		||||
  animation-delay: 1000ms;
 | 
			
		||||
}
 | 
			
		||||
.loading-delay-1500 {
 | 
			
		||||
  -webkit-animation-delay: 1500ms;
 | 
			
		||||
  animation-delay: 1500ms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.loading-title {
 | 
			
		||||
  font-size: 28px;
 | 
			
		||||
	font-weight: 500;
 | 
			
		||||
  color: #646464;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								public/resource/loading.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								public/resource/loading.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 初始化加载效果的svg格式logo
 | 
			
		||||
 * @param { string }id - 元素id
 | 
			
		||||
 */
 | 
			
		||||
function initSvgLogo(id) {
 | 
			
		||||
  const svgStr = `<svg width="128px" height="128px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
 | 
			
		||||
	y="0px" viewBox="0 0 158.9 158.9" style="enable-background:new 0 0 158.9 158.9;" xml:space="preserve">
 | 
			
		||||
	<path style="fill:none" d="M0,158.9C0,106.3,0,53.7,0,1.1C0,0.2,0.2,0,1.1,0c52.2,0,104.5,0,156.7,0c0.9,0,1.1,0.2,1.1,1.1
 | 
			
		||||
	c0,52.2,0,104.5,0,156.7c0,0.9-0.2,1.1-1.1,1.1C105.2,158.8,52.6,158.8,0,158.9z" />
 | 
			
		||||
	<path style="fill:currentColor" d="M81.3,55.9c-0.1-11.7-2.9-22.5-9.4-32.4c-1-1.5-2.1-2.9-2.5-4.7c-0.7-3.4,0.9-6.9,4-8.6c3-1.7,6.8-1.2,9.3,1.2
 | 
			
		||||
	c2.4,2.6,4.4,5.6,5.9,8.8c4.7,8.9,7.6,18.6,8.4,28.6c1,12.5-0.7,25-5.2,36.7c-0.9,2.5-1.9,4.9-3,7.3c-0.3,0.4-0.3,1,0,1.4
 | 
			
		||||
	c9.6,13.3,21.8,23,37.8,27.2c6.4,1.7,13.1,2.3,19.7,1.6c4.2-0.4,7.9,2.7,8.4,6.9c0.7,4.3-2.3,8.3-6.6,9c0,0,0,0-0.1,0
 | 
			
		||||
	c-7.7,0.9-15.5,0.5-23-1.3c-13.9-3.1-26.7-10-36.9-19.9c-4.4-4.2-8.4-8.8-11.9-13.7c-0.5-0.8-1.4-1.2-2.3-1.1
 | 
			
		||||
	c-9.5,0.7-18.8,3.3-27.4,7.6c-11.6,6-20.7,14.6-26.4,26.4c-0.7,1.9-2,3.5-3.7,4.7c-2.9,1.7-6.6,1.5-9.2-0.7c-2.8-2.2-3.8-6-2.4-9.3
 | 
			
		||||
	c2.2-5.2,5.1-10.1,8.7-14.5c12.2-15.4,28.2-24.6,47.3-28.6c4-0.8,8.1-1.4,12.2-1.6c0.5,0,1-0.3,1.2-0.8c3.3-7.1,5.5-14.6,6.5-22.3
 | 
			
		||||
	C81.1,61.2,81.3,58.6,81.3,55.9z" />
 | 
			
		||||
	<path style="fill:currentColor" d="M136.3,108.3c-3.8-0.5-7.6-1.4-11.1-2.9c-7.7-2.8-14.4-7.5-19.7-13.8c-2.9-3.3-2.5-8.4,0.8-11.3
 | 
			
		||||
	c1.4-1.2,3.1-1.9,4.9-1.9c2.5-0.1,5,1,6.5,2.9c4.9,5.6,11.6,9.4,18.9,10.8c1.5,0.2,3.1,0.6,4.5,1.2c3.2,1.8,4.8,5.6,3.8,9.2
 | 
			
		||||
	C144,106.1,140.8,108.4,136.3,108.3z" />
 | 
			
		||||
	<path style="fill:currentColor" d="M55.7,33.3c3,0.2,5.6,2.2,6.6,5c2.2,5.4,3.4,11.2,3.6,17c0.3,5.9-0.6,11.7-2.5,17.3c-2,5.8-8.2,7.8-12.9,4.2
 | 
			
		||||
	c-2.6-2.2-3.6-5.8-2.4-9c1.4-4,1.9-8.2,1.7-12.4c-0.2-3.8-1-7.5-2.4-11C45.3,38.9,49.2,33.3,55.7,33.3z" />
 | 
			
		||||
	<path style="fill:currentColor" d="M77.9,126.6c0,3.9-2.8,7.2-6.7,7.9c-7.8,1.5-14.8,5.9-19.7,12.2c-2.7,3.5-7.6,4.2-11.2,1.6
 | 
			
		||||
	c-3.6-2.6-4.3-7.6-1.7-11.2c0.1-0.1,0.2-0.3,0.3-0.4c4.1-5.2,9.3-9.6,15.1-12.8c4.4-2.5,9.1-4.2,14-5.1
 | 
			
		||||
	C73.3,117.7,77.9,121.3,77.9,126.6z" />
 | 
			
		||||
</svg>
 | 
			
		||||
`;
 | 
			
		||||
  const appEl = document.querySelector(id);
 | 
			
		||||
  const div = document.createElement('div');
 | 
			
		||||
  div.innerHTML = svgStr;
 | 
			
		||||
  if (appEl) {
 | 
			
		||||
    appEl.appendChild(div);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addThemeColorCssVars() {
 | 
			
		||||
  const key = '__THEME_COLOR__';
 | 
			
		||||
  const themeColor = window.localStorage.getItem(key) || '#1890ff';
 | 
			
		||||
  const cssVars = `--primary-color: ${themeColor}`;
 | 
			
		||||
  document.documentElement.style.cssText = cssVars;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
initSvgLogo('#loadingLogo');
 | 
			
		||||
 | 
			
		||||
addThemeColorCssVars();
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										24
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/App.vue
									
									
									
									
									
								
							@@ -1,10 +1,24 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <app-provider>
 | 
			
		||||
  <n-config-provider
 | 
			
		||||
    :theme="theme.naiveTheme"
 | 
			
		||||
    :theme-overrides="theme.naiveThemeOverrides"
 | 
			
		||||
    :locale="zhCN"
 | 
			
		||||
    :date-locale="dateZhCN"
 | 
			
		||||
    class="h-full"
 | 
			
		||||
  >
 | 
			
		||||
    <naive-provider>
 | 
			
		||||
      <router-view />
 | 
			
		||||
  </app-provider>
 | 
			
		||||
    </naive-provider>
 | 
			
		||||
  </n-config-provider>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import AppProvider from './AppProvider.vue';
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NConfigProvider, zhCN, dateZhCN } from 'naive-ui';
 | 
			
		||||
import { NaiveProvider } from '@/components';
 | 
			
		||||
import { useThemeStore, subscribeStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
 | 
			
		||||
subscribeStore();
 | 
			
		||||
</script>
 | 
			
		||||
<style></style>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <n-config-provider
 | 
			
		||||
    class="h-full"
 | 
			
		||||
    :locale="zhCN"
 | 
			
		||||
    :date-locale="dateZhCN"
 | 
			
		||||
    :theme="naiveTheme"
 | 
			
		||||
    :theme-overrides="theme.themeOverrids"
 | 
			
		||||
  >
 | 
			
		||||
    <n-element class="h-full">
 | 
			
		||||
      <naive-provider>
 | 
			
		||||
        <slot></slot>
 | 
			
		||||
      </naive-provider>
 | 
			
		||||
    </n-element>
 | 
			
		||||
  </n-config-provider>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { NConfigProvider, NElement, zhCN, dateZhCN } from 'naive-ui';
 | 
			
		||||
import { NaiveProvider } from '@/components';
 | 
			
		||||
import { useThemeStore } from '@/store';
 | 
			
		||||
import { useDarkMode } from '@/composables';
 | 
			
		||||
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
const { naiveTheme } = useDarkMode();
 | 
			
		||||
</script>
 | 
			
		||||
<style></style>
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 7.8 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 11 KiB  | 
| 
		 Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB  | 
							
								
								
									
										97
									
								
								src/components/business/LoadingEmptyWrapper/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/components/business/LoadingEmptyWrapper/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div v-if="reloadFlag" class="relative">
 | 
			
		||||
    <slot></slot>
 | 
			
		||||
    <div v-show="showPlaceholder" class="absolute-lt w-full h-full" :class="placeholderClass">
 | 
			
		||||
      <div v-show="loading" class="absolute-center">
 | 
			
		||||
        <n-spin :show="true" :size="loadingSize" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-show="isEmpty" class="absolute-center">
 | 
			
		||||
        <div class="relative" :class="emptyNetworkClass">
 | 
			
		||||
          <svg-empty-data class="text-primary" />
 | 
			
		||||
          <p class="absolute-lb w-full text-center">{{ emptyDesc }}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-show="!network" class="absolute-center">
 | 
			
		||||
        <div
 | 
			
		||||
          class="relative"
 | 
			
		||||
          :class="[{ 'cursor-pointer': showNetworkReload }, emptyNetworkClass]"
 | 
			
		||||
          @click="handleReload"
 | 
			
		||||
        >
 | 
			
		||||
          <svg-network-error class="text-primary" />
 | 
			
		||||
          <p class="absolute-lb w-full text-center">{{ networkErrorDesc }}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed, watch, nextTick, onUnmounted } from 'vue';
 | 
			
		||||
import { NSpin } from 'naive-ui';
 | 
			
		||||
import { NETWORK_ERROR_MSG } from '@/config';
 | 
			
		||||
import { SvgEmptyData, SvgNetworkError } from '@/components';
 | 
			
		||||
import { useBoolean } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 是否加载 */
 | 
			
		||||
  loading: boolean;
 | 
			
		||||
  /** 是否为空 */
 | 
			
		||||
  empty?: boolean;
 | 
			
		||||
  /** 加载图标的大小 */
 | 
			
		||||
  loadingSize?: 'small' | 'medium' | 'large';
 | 
			
		||||
  /** 中间占位符的class */
 | 
			
		||||
  placeholderClass?: string;
 | 
			
		||||
  /** 空数据描述文本 */
 | 
			
		||||
  emptyDesc?: string;
 | 
			
		||||
  /** 空数据和网络异常占位class */
 | 
			
		||||
  emptyNetworkClass?: string;
 | 
			
		||||
  /** 显示网络异常的重试点击按钮 */
 | 
			
		||||
  showNetworkReload?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  loading: false,
 | 
			
		||||
  empty: false,
 | 
			
		||||
  loadingSize: 'medium',
 | 
			
		||||
  placeholderClass: 'bg-white',
 | 
			
		||||
  emptyDesc: '暂无数据',
 | 
			
		||||
  emptyNetworkClass: 'w-320px h-320px text-16px text-[#666]',
 | 
			
		||||
  showNetworkReload: false
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 网络状态
 | 
			
		||||
const { bool: network, setBool: setNetwork } = useBoolean(window.navigator.onLine);
 | 
			
		||||
const { bool: reloadFlag, setBool: setReload } = useBoolean(true);
 | 
			
		||||
 | 
			
		||||
// 数据是否为空
 | 
			
		||||
const isEmpty = computed(() => props.empty && !props.loading && network.value);
 | 
			
		||||
 | 
			
		||||
const showPlaceholder = computed(() => props.loading || isEmpty.value || !network.value);
 | 
			
		||||
 | 
			
		||||
const networkErrorDesc = computed(() =>
 | 
			
		||||
  props.showNetworkReload ? `${NETWORK_ERROR_MSG}, 点击重试` : NETWORK_ERROR_MSG
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
function handleReload() {
 | 
			
		||||
  if (!props.showNetworkReload) return;
 | 
			
		||||
  setReload(false);
 | 
			
		||||
  nextTick(() => {
 | 
			
		||||
    setReload(true);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const stopHandle = watch(
 | 
			
		||||
  () => props.loading,
 | 
			
		||||
  newValue => {
 | 
			
		||||
    // 结束加载判断一下网络状态
 | 
			
		||||
    if (!newValue) {
 | 
			
		||||
      setNetwork(window.navigator.onLine);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  stopHandle();
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										48
									
								
								src/components/business/LoginAgreement/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/components/business/LoginAgreement/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="w-full text-14px">
 | 
			
		||||
    <n-checkbox v-model:checked="checked">我已经仔细阅读并接受</n-checkbox>
 | 
			
		||||
    <n-button :text="true" type="primary" @click="handleClickProtocol">《用户协议》</n-button>
 | 
			
		||||
    <n-button :text="true" type="primary" @click="handleClickPolicy">《隐私权政策》</n-button>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { NCheckbox, NButton } from 'naive-ui';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 是否勾选 */
 | 
			
		||||
  value?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Emits {
 | 
			
		||||
  (e: 'update:value', value: boolean): void;
 | 
			
		||||
  /** 点击协议 */
 | 
			
		||||
  (e: 'click-protocol'): void;
 | 
			
		||||
  /** 点击隐私政策 */
 | 
			
		||||
  (e: 'click-policy'): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  value: true
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
 | 
			
		||||
const checked = computed({
 | 
			
		||||
  get() {
 | 
			
		||||
    return props.value;
 | 
			
		||||
  },
 | 
			
		||||
  set(newValue: boolean) {
 | 
			
		||||
    emit('update:value', newValue);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handleClickProtocol() {
 | 
			
		||||
  emit('click-protocol');
 | 
			
		||||
}
 | 
			
		||||
function handleClickPolicy() {
 | 
			
		||||
  emit('click-policy');
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
import LoadingEmptyWrapper from './LoadingEmptyWrapper/index.vue';
 | 
			
		||||
import LoginAgreement from './LoginAgreement/index.vue';
 | 
			
		||||
 | 
			
		||||
export { LoadingEmptyWrapper, LoginAgreement };
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,5 +0,0 @@
 | 
			
		||||
import Banner1 from './Banner1.vue';
 | 
			
		||||
import Banner2 from './Banner2.vue';
 | 
			
		||||
import Banner3 from './Banner3.vue';
 | 
			
		||||
 | 
			
		||||
export { Banner1, Banner2, Banner3 };
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div :style="{ color }">
 | 
			
		||||
    <banner1 v-if="type === '1'" />
 | 
			
		||||
    <banner2 v-if="type === '2'" />
 | 
			
		||||
    <banner3 v-if="type === '3'" />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { Banner1, Banner2, Banner3 } from './components';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** banner类型 */
 | 
			
		||||
  type?: '1' | '2' | '3';
 | 
			
		||||
  /** 主题颜色 */
 | 
			
		||||
  color?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
  type: '1',
 | 
			
		||||
  color: '#409eff'
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										10
									
								
								src/components/common/DarkModeContainer/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/components/common/DarkModeContainer/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    class="bg-white text-[#333639] dark:(bg-[#18181c] text-white text-opacity-82) transition-all duration-300 ease-in-out"
 | 
			
		||||
  >
 | 
			
		||||
    <slot></slot>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts"></script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										39
									
								
								src/components/common/DarkModeSwitch/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/components/common/DarkModeSwitch/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex-center text-18px hover:text-primary cursor-pointer" @click="handleSwitch">
 | 
			
		||||
    <icon-mdi-moon-waning-crescent v-if="darkMode" />
 | 
			
		||||
    <icon-mdi-white-balance-sunny v-else />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 暗黑模式 */
 | 
			
		||||
  dark?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Emits {
 | 
			
		||||
  (e: 'update:dark', darkMode: boolean): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  dark: false
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
 | 
			
		||||
const darkMode = computed({
 | 
			
		||||
  get() {
 | 
			
		||||
    return props.dark;
 | 
			
		||||
  },
 | 
			
		||||
  set(newValue: boolean) {
 | 
			
		||||
    emit('update:dark', newValue);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handleSwitch() {
 | 
			
		||||
  darkMode.value = !darkMode.value;
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
import SvgNoPermission from './SvgNoPermission.vue';
 | 
			
		||||
import SvgNotFound from './SvgNotFound.vue';
 | 
			
		||||
import SvgServiceError from './SvgServiceError.vue';
 | 
			
		||||
 | 
			
		||||
export { SvgNoPermission, SvgNotFound, SvgServiceError };
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="w-400px h-400px" :style="{ color }">
 | 
			
		||||
    <svg-no-permission v-if="type === '403'" />
 | 
			
		||||
    <svg-not-found v-if="type === '404'" />
 | 
			
		||||
    <svg-service-error v-if="type === '500'" />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { SvgNoPermission, SvgNotFound, SvgServiceError } from './components';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 异常类型 */
 | 
			
		||||
  type?: '403' | '404' | '500';
 | 
			
		||||
  /** 主题颜色 */
 | 
			
		||||
  color?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
  type: '404',
 | 
			
		||||
  color: '#409eff'
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="absolute-lt wh-full overflow-hidden">
 | 
			
		||||
    <div class="absolute -right-300px -top-900px">
 | 
			
		||||
      <corner-top :start-color="themeColor" :end-color="stopColor" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="absolute -left-200px -bottom-400px">
 | 
			
		||||
      <corner-bottom :start-color="themeColor" :end-color="stopColor" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { mixColor } from '@/utils';
 | 
			
		||||
import { CornerTop, CornerBottom } from './components';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 主题颜色 */
 | 
			
		||||
  themeColor?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  themeColor: '#409EFF'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const COLOR_WHITE = '#ffffff';
 | 
			
		||||
const stopColor = computed(() => mixColor(COLOR_WHITE, props.themeColor, 0.7));
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div :style="{ color }">
 | 
			
		||||
  <div>
 | 
			
		||||
    <svg-fill-logo v-if="fill" />
 | 
			
		||||
    <svg-logo v-else />
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -11,13 +11,10 @@ import { SvgLogo, SvgFillLogo } from './components';
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** logo是否填充 */
 | 
			
		||||
  fill?: boolean;
 | 
			
		||||
  /** logo的主题颜色 */
 | 
			
		||||
  color?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
  fill: false,
 | 
			
		||||
  color: '#409EFF'
 | 
			
		||||
  fill: false
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
import NaiveProvider from './NaiveProvider/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 DarkModeSwitch from './DarkModeSwitch/index.vue';
 | 
			
		||||
import DarkModeContainer from './DarkModeContainer/index.vue';
 | 
			
		||||
import HoverContainer from './HoverContainer/index.vue';
 | 
			
		||||
 | 
			
		||||
export { NaiveProvider, SystemLogo, ExceptionSvg, LoginBg, BannerSvg, HoverContainer };
 | 
			
		||||
export { NaiveProvider, SystemLogo, DarkModeSwitch, DarkModeContainer, HoverContainer };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div ref="scrollbar" class="wh-full text-left">
 | 
			
		||||
    <div ref="scrollbarContent" class="inline-block" :class="{ 'h-full': !isScrollY }">
 | 
			
		||||
  <div ref="bsWrap" class="h-full text-left">
 | 
			
		||||
    <div ref="bsContent" class="inline-block" :class="{ 'h-full': !isScrollY }">
 | 
			
		||||
      <slot></slot>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -8,38 +8,40 @@
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, computed, watch, onMounted } from 'vue';
 | 
			
		||||
import { useElementSize } from '@vueuse/core';
 | 
			
		||||
import BScroll from '@better-scroll/core';
 | 
			
		||||
import type { Options } from '@better-scroll/core';
 | 
			
		||||
import { useElementSize } from '@vueuse/core';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** better-scroll的配置: https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html */
 | 
			
		||||
  options?: Options;
 | 
			
		||||
  options: Options;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  options: undefined
 | 
			
		||||
});
 | 
			
		||||
const props = defineProps<Props>();
 | 
			
		||||
 | 
			
		||||
const scrollbar = ref<HTMLElement | null>(null);
 | 
			
		||||
const bsInstance = ref<BScroll | null>(null);
 | 
			
		||||
const scrollbarContent = ref<HTMLElement | null>(null);
 | 
			
		||||
const isScrollY = computed(() => Boolean(props.options?.scrollY));
 | 
			
		||||
const bsWrap = ref<HTMLElement>();
 | 
			
		||||
const instance = ref<BScroll>();
 | 
			
		||||
const bsContent = ref<HTMLElement>();
 | 
			
		||||
const isScrollY = computed(() => Boolean(props.options.scrollY));
 | 
			
		||||
 | 
			
		||||
function initBetterScroll() {
 | 
			
		||||
  bsInstance.value = new BScroll(scrollbar.value!, props.options);
 | 
			
		||||
  if (!bsWrap.value) return;
 | 
			
		||||
  instance.value = new BScroll(bsWrap.value, props.options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 滚动元素发生变化,刷新BS
 | 
			
		||||
const { width, height } = useElementSize(scrollbarContent);
 | 
			
		||||
watch([() => width.value, () => height.value], () => {
 | 
			
		||||
  if (bsInstance.value) {
 | 
			
		||||
    bsInstance.value.refresh();
 | 
			
		||||
const { width: wrapWidth } = useElementSize(bsWrap);
 | 
			
		||||
const { width, height } = useElementSize(bsContent);
 | 
			
		||||
watch([() => wrapWidth.value, () => width.value, () => height.value], () => {
 | 
			
		||||
  if (instance.value) {
 | 
			
		||||
    instance.value.refresh();
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  initBetterScroll();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
defineExpose({ instance });
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    class="
 | 
			
		||||
      relative
 | 
			
		||||
      flex-center
 | 
			
		||||
      h-30px
 | 
			
		||||
      pl-14px
 | 
			
		||||
      border-1px border-[#e5e7eb]
 | 
			
		||||
      dark:border-[#ffffff3d]
 | 
			
		||||
      rounded-2px
 | 
			
		||||
      transition-border-color
 | 
			
		||||
      duration-300
 | 
			
		||||
      ease-in-out
 | 
			
		||||
      cursor-pointer
 | 
			
		||||
    "
 | 
			
		||||
    class="relative flex-center h-30px pl-14px border-1px border-[#e5e7eb] dark:border-[#ffffff3d] rounded-2px cursor-pointer transition-colors duration-300 ease-in-out"
 | 
			
		||||
    :class="[closable ? 'pr-6px' : 'pr-14px']"
 | 
			
		||||
    :style="buttonStyle"
 | 
			
		||||
    @mouseenter="setTrue"
 | 
			
		||||
@@ -22,7 +10,7 @@
 | 
			
		||||
      <slot></slot>
 | 
			
		||||
    </span>
 | 
			
		||||
    <div v-if="closable" class="pl-10px">
 | 
			
		||||
      <icon-close :is-primary="isActive || isHover" :primary-color="primaryColor" @click="handleClose" />
 | 
			
		||||
      <icon-close :is-active="isIconActive" :primary-color="primaryColor" @click="handleClose" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -30,8 +18,8 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { useBoolean } from '@/hooks';
 | 
			
		||||
import { IconClose } from '@/components';
 | 
			
		||||
import { addColorAlpha } from '@/utils';
 | 
			
		||||
import IconClose from '../IconClose/index.vue';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 激活状态 */
 | 
			
		||||
@@ -44,27 +32,27 @@ interface Props {
 | 
			
		||||
  darkMode?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Emits {
 | 
			
		||||
  /** 点击关闭图标 */
 | 
			
		||||
  (e: 'close'): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  isActive: false,
 | 
			
		||||
  primaryColor: '#409EFF',
 | 
			
		||||
  primaryColor: '#1890ff',
 | 
			
		||||
  closable: true,
 | 
			
		||||
  darkMode: false
 | 
			
		||||
});
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
  /** 点击关闭图标 */
 | 
			
		||||
  (e: 'close'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
 | 
			
		||||
const { bool: isHover, setTrue, setFalse } = useBoolean();
 | 
			
		||||
 | 
			
		||||
function handleClose(e: MouseEvent) {
 | 
			
		||||
  e.stopPropagation();
 | 
			
		||||
  emit('close');
 | 
			
		||||
}
 | 
			
		||||
const isIconActive = computed(() => props.isActive || isHover.value);
 | 
			
		||||
 | 
			
		||||
const buttonStyle = computed(() => {
 | 
			
		||||
  const style: { [key: string]: string } = {};
 | 
			
		||||
  if (props.isActive || isHover.value) {
 | 
			
		||||
  const style: Record<string, string> = {};
 | 
			
		||||
  if (isIconActive.value) {
 | 
			
		||||
    style.color = props.primaryColor;
 | 
			
		||||
    style.borderColor = addColorAlpha(props.primaryColor, 0.3);
 | 
			
		||||
    if (props.isActive) {
 | 
			
		||||
@@ -74,5 +62,10 @@ const buttonStyle = computed(() => {
 | 
			
		||||
  }
 | 
			
		||||
  return style;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handleClose(e: MouseEvent) {
 | 
			
		||||
  e.stopPropagation();
 | 
			
		||||
  emit('close');
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    class="relative flex-y-center h-34px px-24px cursor-pointer -mr-18px"
 | 
			
		||||
    class="relative flex-y-center h-34px px-24px -mr-18px cursor-pointer"
 | 
			
		||||
    :class="{ 'z-10': isActive, 'z-9': isHover }"
 | 
			
		||||
    @mouseenter="setTrue"
 | 
			
		||||
    @mouseleave="setFalse"
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
      <slot></slot>
 | 
			
		||||
    </span>
 | 
			
		||||
    <div v-if="closable" class="pl-18px">
 | 
			
		||||
      <icon-close :is-primary="isActive" :primary-color="primaryColor" @click="handleClose" />
 | 
			
		||||
      <icon-close :is-active="isActive" :primary-color="primaryColor" @click="handleClose" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <n-divider v-if="!isHover && !isActive" :vertical="true" class="absolute right-0 !bg-[#a4abb8] z-2" />
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -41,16 +41,20 @@ interface Props {
 | 
			
		||||
  darkMode?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Emits {
 | 
			
		||||
  /** 点击关闭图标 */
 | 
			
		||||
  (e: 'close'): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
  isActive: false,
 | 
			
		||||
  primaryColor: '#409EFF',
 | 
			
		||||
  closable: true,
 | 
			
		||||
  darkMode: false
 | 
			
		||||
  darkMode: false,
 | 
			
		||||
  isLast: false
 | 
			
		||||
});
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
  /** 点击关闭图标 */
 | 
			
		||||
  (e: 'close'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
 | 
			
		||||
const { bool: isHover, setTrue, setFalse } = useBoolean();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -95,9 +95,11 @@ watch([() => props.startValue, () => props.endValue], () => {
 | 
			
		||||
    start();
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watchEffect(() => {
 | 
			
		||||
  source.value = props.startValue;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  if (props.autoplay) {
 | 
			
		||||
    start();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    class="relative flex-center w-18px h-18px text-14px"
 | 
			
		||||
    :style="{ color: isPrimary ? primaryColor : defaultColor }"
 | 
			
		||||
    :style="{ color: isActive ? primaryColor : defaultColor }"
 | 
			
		||||
    @mouseenter="setTrue"
 | 
			
		||||
    @mouseleave="setFalse"
 | 
			
		||||
  >
 | 
			
		||||
    <transition name="transition-opacity">
 | 
			
		||||
      <icon-carbon-close-filled v-if="isHover" key="hover" class="absolute" />
 | 
			
		||||
      <icon-carbon-close v-else key="unhover" class="absolute" />
 | 
			
		||||
    <transition name="fade">
 | 
			
		||||
      <icon-mdi:close-circle v-if="isHover" key="hover" class="absolute" />
 | 
			
		||||
      <icon-mdi:close v-else key="unhover" class="absolute" />
 | 
			
		||||
    </transition>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -17,7 +17,7 @@ import { useBoolean } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 激活状态 */
 | 
			
		||||
  isPrimary?: boolean;
 | 
			
		||||
  isActive?: boolean;
 | 
			
		||||
  /** 主题颜色 */
 | 
			
		||||
  primaryColor?: string;
 | 
			
		||||
  /** 默认颜色 */
 | 
			
		||||
@@ -26,7 +26,7 @@ interface Props {
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
  isPrimary: false,
 | 
			
		||||
  primaryColor: '#409EFF',
 | 
			
		||||
  primaryColor: '#1890ff',
 | 
			
		||||
  defaultColor: '#9ca3af'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										77
									
								
								src/components/custom/IconSelect/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/components/custom/IconSelect/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <n-popover placement="bottom-end" trigger="click">
 | 
			
		||||
    <template #trigger>
 | 
			
		||||
      <n-input v-model:value="modelValue" readonly placeholder="点击选择图标">
 | 
			
		||||
        <template #suffix>
 | 
			
		||||
          <Icon :icon="modelValue ? modelValue : emptyIcon" class="text-30px p-5px" />
 | 
			
		||||
        </template>
 | 
			
		||||
      </n-input>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #header>
 | 
			
		||||
      <n-input v-model:value="searchValue" placeholder="搜索图标"></n-input>
 | 
			
		||||
    </template>
 | 
			
		||||
    <div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto">
 | 
			
		||||
      <template v-for="iconItem in iconsList" :key="iconItem">
 | 
			
		||||
        <Icon
 | 
			
		||||
          :icon="iconItem"
 | 
			
		||||
          class="border-1px border-[#d9d9d9] text-30px m-2px p-5px"
 | 
			
		||||
          :style="{ 'border-color': modelValue === iconItem ? theme.themeColor : '' }"
 | 
			
		||||
          @click="handleChange(iconItem)"
 | 
			
		||||
        />
 | 
			
		||||
      </template>
 | 
			
		||||
    </div>
 | 
			
		||||
    <n-empty v-else class="w-306px" description="你什么也找不到" />
 | 
			
		||||
  </n-popover>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ref, computed } from 'vue';
 | 
			
		||||
import { NPopover, NInput, NEmpty } from 'naive-ui';
 | 
			
		||||
import { Icon } from '@iconify/vue';
 | 
			
		||||
import { useThemeStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  /** 选中的图标 */
 | 
			
		||||
  value: string;
 | 
			
		||||
  /** 图标列表 */
 | 
			
		||||
  icons: string[];
 | 
			
		||||
  /** 未选中图标 */
 | 
			
		||||
  emptyIcon?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Emits {
 | 
			
		||||
  (e: 'update:value', val: string): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  emptyIcon: 'mdi:apps'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
 | 
			
		||||
const searchValue = ref('');
 | 
			
		||||
const iconsList = computed(() => props.icons.filter(v => v.includes(searchValue.value)));
 | 
			
		||||
 | 
			
		||||
const modelValue = computed({
 | 
			
		||||
  get() {
 | 
			
		||||
    return props.value;
 | 
			
		||||
  },
 | 
			
		||||
  set(val: string) {
 | 
			
		||||
    emit('update:value', val);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handleChange(iconItem: string) {
 | 
			
		||||
  modelValue.value = iconItem;
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
:deep(.n-input-wrapper) {
 | 
			
		||||
  padding-right: 0;
 | 
			
		||||
}
 | 
			
		||||
:deep(.n-input__suffix) {
 | 
			
		||||
  border: 1px solid #d9d9d9;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										39
									
								
								src/components/custom/ImageVerify/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/components/custom/ImageVerify/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <canvas ref="domRef" width="152" height="40" class="cursor-pointer" @click="getImgCode"></canvas>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { watch } from 'vue';
 | 
			
		||||
import { useImageVerify } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  code?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Emits {
 | 
			
		||||
  (e: 'update:code', code: string): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  code: ''
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<Emits>();
 | 
			
		||||
 | 
			
		||||
const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify();
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.code,
 | 
			
		||||
  newValue => {
 | 
			
		||||
    setImgCode(newValue);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
watch(imgCode, newValue => {
 | 
			
		||||
  emit('update:code', newValue);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
defineExpose({ getImgCode });
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
import CountTo from './CountTo/index.vue';
 | 
			
		||||
import IconClose from './IconClose/index.vue';
 | 
			
		||||
import BetterScroll from './BetterScroll/index.vue';
 | 
			
		||||
import ButtonTab from './ButtonTab/index.vue';
 | 
			
		||||
import ChromeTab from './ChromeTab/index.vue';
 | 
			
		||||
import BetterScroll from './BetterScroll/index.vue';
 | 
			
		||||
import CountTo from './CountTo/index.vue';
 | 
			
		||||
import ImageVerify from './ImageVerify/index.vue';
 | 
			
		||||
import WebSiteLink from './WebSiteLink/index.vue';
 | 
			
		||||
import GithubLink from './GithubLink/index.vue';
 | 
			
		||||
import IconSelect from './IconSelect/index.vue';
 | 
			
		||||
 | 
			
		||||
export { CountTo, IconClose, ButtonTab, ChromeTab, BetterScroll, WebSiteLink, GithubLink };
 | 
			
		||||
export { BetterScroll, ButtonTab, ChromeTab, CountTo, ImageVerify, WebSiteLink, GithubLink, IconSelect };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,5 @@
 | 
			
		||||
export * from './common';
 | 
			
		||||
export * from './custom';
 | 
			
		||||
export * from './svg';
 | 
			
		||||
export * from './custom';
 | 
			
		||||
export * from './common';
 | 
			
		||||
export * from './business';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1447
									
								
								src/components/svg/SvgEmptyData.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1447
									
								
								src/components/svg/SvgEmptyData.vue
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										408
									
								
								src/components/svg/SvgNetworkError.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										408
									
								
								src/components/svg/SvgNetworkError.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,408 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
 | 
			
		||||
    <g id="freepik--background-simple--inject-163">
 | 
			
		||||
      <path
 | 
			
		||||
        d="M464.5,113.47q-2.38-4-5-7.9C436.33,71.22,396.44,47.73,357.2,37.11a231.87,231.87,0,0,0-52.71-7.69c-116.62-4-163.07,88-201.61,111.67S5,205.33,9,290.32s65.22,165,183.81,170,126.5-48.42,192.71-68.19c43.82-13.08,75-57.26,90.2-98.09C496.5,238.38,495.52,166.11,464.5,113.47Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M464.5,113.47q-2.38-4-5-7.9C436.33,71.22,396.44,47.73,357.2,37.11a231.87,231.87,0,0,0-52.71-7.69c-116.62-4-163.07,88-201.61,111.67S5,205.33,9,290.32s65.22,165,183.81,170,126.5-48.42,192.71-68.19c43.82-13.08,75-57.26,90.2-98.09C496.5,238.38,495.52,166.11,464.5,113.47Z"
 | 
			
		||||
        style="fill: #fff; opacity: 0.7000000000000001"
 | 
			
		||||
      ></path>
 | 
			
		||||
    </g>
 | 
			
		||||
    <g id="freepik--Window--inject-163">
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="438.56 121.49 438.56 118.2 377.7 118.2 377.7 69.42 375.72 69.42 375.72 118.2 315.29 118.2 315.29 121.49 375.72 121.49 375.72 166.65 315.29 166.65 315.29 169.95 375.72 169.95 375.72 215.11 315.29 215.11 315.29 218.4 375.72 218.4 375.72 265.85 377.7 265.85 377.7 218.4 438.56 218.4 438.56 215.11 377.7 215.11 377.7 169.95 438.56 169.95 438.56 166.65 377.7 166.65 377.7 121.49 438.56 121.49"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M311.12,65.46V271.13H442.3V65.46ZM437.87,264.18H315.55V72.4H437.87Z"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
    </g>
 | 
			
		||||
    <g id="freepik--Plant--inject-163">
 | 
			
		||||
      <path
 | 
			
		||||
        d="M254,323.92h0a.1.1,0,0,0-.06-.13.1.1,0,0,0-.13.06,132,132,0,0,0-6.94,21.75A202,202,0,0,0,243,368.15l-.54,5.7c-.08.95-.19,1.9-.26,2.85l-.15,2.86c-.08,1.91-.2,3.82-.26,5.73l.06,5.73.06,2.86c0,.95.11,1.91.18,2.86.14,1.9.26,3.81.43,5.71.37,3.8.94,7.57,1.52,11.34a.11.11,0,0,0,.1.08.09.09,0,0,0,.1-.1h0c0-1.91-.09-3.8-.13-5.71s-.22-3.78-.19-5.68,0-3.8-.06-5.69l-.06-2.84,0-2.84,0-5.68.15-5.67.06-2.84c0-.95.11-1.89.16-2.84l.35-5.67a200.9,200.9,0,0,1,3.21-22.49A131.72,131.72,0,0,1,254,323.92Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M254.81,353.6a.1.1,0,0,0-.06-.12.1.1,0,0,0-.13.06,132.44,132.44,0,0,0-6.43,29.24c-.5,5-1.06,9.94-1.13,14.94a127.13,127.13,0,0,0,.53,15,.11.11,0,0,0,.09.08.09.09,0,0,0,.11-.08c.58-5,.93-9.94,1.28-14.9s.4-9.93.72-14.89a148.32,148.32,0,0,1,5-29.31Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M271.27,364.05a.1.1,0,0,0-.14,0,50.06,50.06,0,0,0-11,9.59,44.64,44.64,0,0,0-4.3,6l-.88,1.67-.7,1.74a15.71,15.71,0,0,0-.6,1.75c-.18.6-.38,1.18-.54,1.78a71,71,0,0,0-2.27,14.52,84,84,0,0,0,.54,14.62.11.11,0,0,0,.1.08.09.09,0,0,0,.1-.09l1.26-14.47a127.64,127.64,0,0,1,1.83-14.27c.13-.59.3-1.15.45-1.73a16,16,0,0,1,.5-1.71l.59-1.67.76-1.58a43.25,43.25,0,0,1,3.94-6,49.72,49.72,0,0,1,10.33-10.05h0A.11.11,0,0,0,271.27,364.05Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M263.3,382.64a16.06,16.06,0,0,0-5,6,35.33,35.33,0,0,0-2.81,7.37c-.19.63-.35,1.27-.49,1.91l-.45,1.92a34.52,34.52,0,0,0-.49,3.91c0,1.32-.05,2.63,0,3.94a34.57,34.57,0,0,0,.51,3.91.13.13,0,0,0,.08.08.1.1,0,0,0,.12-.08,35.91,35.91,0,0,0,.7-3.85c.2-1.28.4-2.54.55-3.81s.35-2.52.45-3.79.27-2.54.55-3.78a34.19,34.19,0,0,1,2.14-7.3,16.18,16.18,0,0,1,4.2-6.27l0,0a.09.09,0,0,0,0-.13A.11.11,0,0,0,263.3,382.64Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M240.22,397.14c-.54-6.42-1.6-12.79-2.6-19.14s-2.12-12.7-3.28-19-2.46-12.63-3.83-18.91a.1.1,0,1,0-.19,0c1.1,6.34,2.09,12.69,3,19.05s1.87,12.71,2.68,19.08,1.4,12.77,2.19,19.13l2.35,19.13a.12.12,0,0,0,.1.09.11.11,0,0,0,.11-.1c.07-1.61.07-3.22.11-4.84s-.07-3.22-.09-4.83C240.74,403.57,240.46,400.36,240.22,397.14Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M236.37,397.05c-.35-2.55-.81-5.08-1.3-7.6l-.9-3.75c-.3-1.25-.58-2.5-.94-3.74a105.84,105.84,0,0,0-4.92-14.61l-.77-1.77-.85-1.73c-.55-1.16-1.2-2.27-1.83-3.4a28.58,28.58,0,0,0-4.64-6.14.09.09,0,0,0-.13,0,.1.1,0,0,0,0,.14h0a28,28,0,0,1,4.24,6.29c.56,1.14,1.15,2.26,1.63,3.44l.76,1.75.67,1.79a105.45,105.45,0,0,1,4.28,14.61c.58,2.47,1,5,1.45,7.49l.6,3.76c.22,1.25.49,2.5.66,3.76.4,2.51.87,5,1.3,7.53s.85,5,1.39,7.55a.11.11,0,0,0,.09.09.11.11,0,0,0,.11-.1q0-3.85-.18-7.7C236.94,402.15,236.63,399.6,236.37,397.05Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M231.05,397.38c-1.19-3.38-2.57-6.68-4.07-9.91a86.8,86.8,0,0,0-5.12-9.38.1.1,0,1,0-.17.1,87.62,87.62,0,0,1,4.37,9.68c1.3,3.28,2.48,6.62,3.46,10s1.73,6.83,2.63,10.23,1.71,6.83,2.76,10.27a.1.1,0,0,0,.09.07.11.11,0,0,0,.11-.1,51.83,51.83,0,0,0-1-10.69A66.72,66.72,0,0,0,231.05,397.38Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M230.83,408.61a23.15,23.15,0,0,0-2.83-5.94,24.44,24.44,0,0,0-4.12-5.11,19.36,19.36,0,0,0-2.62-2,10.92,10.92,0,0,0-3-1.34.1.1,0,0,0-.11.06.1.1,0,0,0,0,.13h0a14.89,14.89,0,0,1,4.93,3.83,23.66,23.66,0,0,1,3.43,5.2c1,1.84,1.48,3.88,2.33,5.79.37,1,.74,1.95,1.18,2.93a23.09,23.09,0,0,0,1.29,3h0a.11.11,0,0,0,.09.05.09.09,0,0,0,.1-.09,22.57,22.57,0,0,0-.12-3.29C231.31,410.74,231.09,409.67,230.83,408.61Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M231.23,349.25a18.29,18.29,0,0,1-13-13.76c-2.43-11.34,5.4-6.48-1.62-24.3s-5.4-21.87-3-27,3.78-3.51,5.94,3.24,10,17.28,13,37S231.23,349.25,231.23,349.25Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M252,330.09s-7-2.43-7.29-15.66,5.13-10.53,5.94-27,1.89-28.08,4.86-28.35,10.8,8.1,13.5,26.46-3.51,30.51-8.91,35.91S252,330.09,252,330.09Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M227.45,365.18s-1.62-9.72-4.86-14.31-10.26-8.64-14.85-17.54-9.72-13.23-9.45-.54,5.94,19.16,13,23.48S227.45,365.18,227.45,365.18Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M224.48,381.92s-6.48-12.42-15.93-21.6-17-13.77-14.31-9.18,5.94,4.32,10.53,12.42,3.24,14,9.72,18.63S224.48,381.92,224.48,381.92Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M223.67,397.85c-.27-.81-.54-6.75-9.72-11.61s-17.82-3-16.74-.54,6.75,6.48,12.42,8.91S223.67,397.85,223.67,397.85Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M251.47,365.72s-4.05-5.13-1.89-15.93,7-8.1,12.42-13.77,4.32-6.47,9.72-12.41,10.26-9.18,12.42-6.75-1.08,10-4.32,14.31S271.45,337.64,269,343s-1.35,14-7.56,18.09S251.47,365.72,251.47,365.72Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M265,368.69s9.72-11.07,16.74-19.17,9.72-14.3,10-8.91,2.43,9.72-3.24,16.74S265,368.69,265,368.69Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M259.84,386.51s-.27-4.32,15.12-14.31,21.87-11.88,20.25-7.83-8.37,8.1-15.66,15.39S259.84,386.51,259.84,386.51Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M231.87,348.1s-3.61-19.36-7.39-33.13-6.21-22.14-6.21-22.14"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-linecap: round; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M251.39,330.29s6.56-21,8.72-33.68-.27-22.14-1.35-27.81"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-linecap: round; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M226.6,364.78a101.76,101.76,0,0,0-9.95-14.18c-5.94-7-10.53-10.8-13.23-17.54"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-linecap: round; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M251.47,365.72s2.16-13,7.29-19.71,15.39-17.27,19.44-24"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-linecap: round; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M223,397.55s-9.08-5.91-15.56-9.15"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-linecap: round; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="252.89 454.81 234.13 454.81 224.75 407.84 262.27 407.84 252.89 454.81"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-linecap: round; stroke-linejoin: round"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
    </g>
 | 
			
		||||
    <g id="freepik--Floor--inject-163">
 | 
			
		||||
      <line
 | 
			
		||||
        x1="64.8"
 | 
			
		||||
        y1="454.81"
 | 
			
		||||
        x2="489.98"
 | 
			
		||||
        y2="454.81"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></line>
 | 
			
		||||
      <line
 | 
			
		||||
        x1="31.18"
 | 
			
		||||
        y1="454.81"
 | 
			
		||||
        x2="57.55"
 | 
			
		||||
        y2="454.81"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></line>
 | 
			
		||||
    </g>
 | 
			
		||||
    <g id="freepik--Device--inject-163">
 | 
			
		||||
      <path
 | 
			
		||||
        d="M216.14,167v-31.5h-2.85V95.19a24.85,24.85,0,0,0-24.85-24.86H54.92A24.86,24.86,0,0,0,30.06,95.19V412.62a24.86,24.86,0,0,0,24.86,24.86H188.44a24.85,24.85,0,0,0,24.85-24.86V193.85h2.85V175.24h-2.85V167Z"
 | 
			
		||||
        style="fill: #707070; stroke: #263238; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M184.08,434.05H59.27a26.33,26.33,0,0,1-26.33-26.33V100.09c0-14.55,7.46-26.34,26.33-26.34H184.08c17.31-.23,26.34,11.79,26.34,26.34V407.72A26.33,26.33,0,0,1,184.08,434.05Z"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-linejoin: round"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M131.34,77.8l-.05.19c-1.46,5.44-4.82,9-8.54,9h-2.14c-3.72,0-7.08-3.52-8.54-9l0-.19-52.08-.18A21.92,21.92,0,0,0,38,99.53l-.07,304.94c0,14.21,11.12,25.73,24.85,25.73H180.62c13.73,0,24.86-11.52,24.86-25.73l0-307.13a19,19,0,0,0-18.78-19Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M205.46,102.88V97.34a19,19,0,0,0-18.78-19l-55.34-.57-.05.19c-1.46,5.44-4.82,9-8.54,9h-2.14c-3.72,0-7.08-3.52-8.54-9l0-.19-52.08-.18A21.92,21.92,0,0,0,38,99.53v3.35Z"
 | 
			
		||||
        style="fill: #dbdbdb; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M123.5,77.2a2.34,2.34,0,1,0-2.33,2.34A2.33,2.33,0,0,0,123.5,77.2Z" style="fill: #707070"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M54.4,83.14c0,2.37-2.65,3.22-2.66,5v.12h2.57v.79H50.88V88.4c0-2.53,2.65-3,2.65-5.22,0-.81-.27-1.23-.92-1.23s-.92.46-.92,1.15v.69h-.81v-.63c0-1.2.54-2,1.75-2S54.4,82,54.4,83.14Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M58.69,83.13v.2a1.57,1.57,0,0,1-1,1.62,1.55,1.55,0,0,1,1,1.62v.6c0,1.2-.56,2-1.77,2s-1.76-.78-1.76-2v-.53H56v.59c0,.7.29,1.13.92,1.13s.91-.42.91-1.21v-.6c0-.78-.32-1.15-1-1.17h-.47V84.6h.51a.94.94,0,0,0,.94-1.08v-.35c0-.81-.28-1.22-.91-1.22s-.92.43-.92,1.14v.4h-.82v-.36c0-1.19.56-2,1.76-2S58.69,81.94,58.69,83.13Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M60.38,83.73v1.08h-.84V83.73Zm0,4.26v1.08h-.84V88Z" style="fill: #263238"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M62.14,84.43a1.28,1.28,0,0,1,1.17-.65c1,0,1.45.74,1.45,1.86v1.53c0,1.2-.57,2-1.76,2s-1.76-.78-1.76-2v-.52h.81v.58c0,.7.3,1.13.92,1.13s.92-.43.92-1.13V85.71c0-.71-.29-1.13-.92-1.13a.84.84,0,0,0-.88.84v.17h-.82l.21-4.35h3.09V82h-2.3Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M69.12,83.12v.15H68.3v-.2c0-.71-.29-1.12-.93-1.12s-1,.42-1,1.26V85a1.26,1.26,0,0,1,1.26-.84c1,0,1.46.73,1.46,1.86v1.18c0,1.2-.59,2-1.79,2s-1.8-.78-1.8-2v-4c0-1.24.56-2,1.8-2S69.12,81.92,69.12,83.12Zm-2.7,2.93v1.18c0,.7.29,1.13.93,1.13s.93-.43.93-1.13V86.05c0-.7-.3-1.13-.93-1.13S66.42,85.35,66.42,86.05Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <rect x="152.41" y="83.47" width="2.21" height="5.53" style="fill: #263238"></rect>
 | 
			
		||||
      <rect x="155.36" y="82.36" width="2.21" height="6.64" style="fill: #263238"></rect>
 | 
			
		||||
      <rect x="158.31" y="81.57" width="2.21" height="7.42" style="fill: #263238"></rect>
 | 
			
		||||
      <rect x="161.25" y="80.2" width="2.21" height="8.79" style="fill: #263238"></rect>
 | 
			
		||||
      <path d="M190.83,88.84H177.56V80.73h13.27Zm-12.53-.73h11.79V81.47H178.3Z" style="fill: #263238"></path>
 | 
			
		||||
      <rect x="179.41" y="82.27" width="7" height="5.03" style="fill: #263238"></rect>
 | 
			
		||||
      <rect x="190.37" y="83.83" width="1.67" height="1.91" style="fill: #263238"></rect>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M172.7,87.08a.48.48,0,0,1-.26-.07c-2.7-1.61-4.48-.12-4.56-.05a.49.49,0,0,1-.69,0,.5.5,0,0,1,0-.7c.1-.08,2.38-2,5.72,0a.5.5,0,0,1-.25.92Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M173.7,85.41a.48.48,0,0,1-.26-.07c-3.9-2.32-6.53-.07-6.55,0a.5.5,0,0,1-.66-.74s3.2-2.74,7.72-.06a.49.49,0,0,1,.17.68A.48.48,0,0,1,173.7,85.41Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M174.6,83.48a.47.47,0,0,1-.25-.07c-4.94-2.94-8.23-.17-8.37,0a.49.49,0,0,1-.7,0,.48.48,0,0,1,.05-.69s3.94-3.38,9.53-.06a.5.5,0,0,1,.17.68A.49.49,0,0,1,174.6,83.48Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M171.21,87.91A1.25,1.25,0,1,1,170,86.67,1.24,1.24,0,0,1,171.21,87.91Z" style="fill: #263238"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M121.53,217.66c22,.34,40.33,8.68,54.78,25.36a5.08,5.08,0,0,1-.47,7.33,5,5,0,0,1-7.15-.66,62.94,62.94,0,0,0-18.47-14.9,61.21,61.21,0,0,0-68.4,7.45,92.38,92.38,0,0,0-7.42,7.39,5.17,5.17,0,0,1-7.32.66,5.1,5.1,0,0,1-.36-7.33,71.44,71.44,0,0,1,45.7-24.87c1.44-.19,2.89-.35,4.34-.41S119.94,217.66,121.53,217.66Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M123.35,238.05c13.9.36,27,6.29,37.24,18.17a5,5,0,0,1-.46,7.3,5,5,0,0,1-7.22-.7,39.62,39.62,0,0,0-19.8-12.9C117,245.56,103,249.54,91.3,261.49c-.44.45-.84.93-1.25,1.39a5.08,5.08,0,1,1-7.68-6.65,51.11,51.11,0,0,1,16.38-12.85C105.94,239.81,113.57,238.08,123.35,238.05Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M146.21,272.81a5.08,5.08,0,0,1-3.25,4.76,5,5,0,0,1-5.66-1.42,21.34,21.34,0,0,0-7.76-5.77C121.2,266.84,112,269,105.79,276a4.92,4.92,0,0,1-5.43,1.63A5.07,5.07,0,0,1,98,269.52a30.52,30.52,0,0,1,47,.08,14.27,14.27,0,0,1,1.5,3.09Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M121.52,299.5a10.2,10.2,0,1,1,10.17-10.15A10.14,10.14,0,0,1,121.52,299.5Z" style="fill: #263238"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M121.58,182a72.72,72.72,0,1,0,72.71,72.71A72.8,72.8,0,0,0,121.58,182ZM58.86,254.75A62.68,62.68,0,0,1,164,208.56L75.39,297.13A62.52,62.52,0,0,1,58.86,254.75Zm62.72,62.72a62.4,62.4,0,0,1-38.85-13.54l88-88a62.67,62.67,0,0,1-49.18,101.57Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
    </g>
 | 
			
		||||
    <g id="freepik--Sofa--inject-163">
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="427.5 454.81 418.02 454.81 415.99 432.25 429.53 432.25 427.5 454.81"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="466.98 454.81 457.5 454.81 455.47 432.25 469.01 432.25 466.98 454.81"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="372.23 454.81 362.75 454.81 360.73 432.25 374.26 432.25 372.23 454.81"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="329.93 454.81 320.46 454.81 318.43 432.25 331.96 432.25 329.93 454.81"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M375.17,286.9h98.52s.39,34.25-4.68,58.5-11.28,38.91-11.28,38.91H364.11S378,330.67,375.17,286.9Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M416.06,292.13c-8.64-.68-12.07-2.42-18.53-6-8.41-4.61-10.21-1.54-9.61,3.07s-8.17,35-10,50.3-1.21,23.53-1.21,23.53-5.4,4.61-1.8,6.14a32.28,32.28,0,0,0,7.21,2.05h69.66s12.38-63.61,13.58-73.84-4.2-11.25-7.21-9.72-3.6,2.56-7.81,3.07C440.5,292,427.9,293.05,416.06,292.13Z"
 | 
			
		||||
        style="fill: currentColor"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M393.63,293.16a16.26,16.26,0,0,0,3.69-7.1c-.84-.45-1.61-.82-2.32-1.12a13.69,13.69,0,0,1-3.26,6.61,14.3,14.3,0,0,1-4.54,3.65c-.18.91-.38,1.9-.6,3A16.06,16.06,0,0,0,393.63,293.16Z"
 | 
			
		||||
        style="fill: #fff"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M456,313.06c-4.41,5.18-4.24,9.78-4.08,14.24.16,4.16.3,8.08-3.48,12.53s-7.69,4.92-11.82,5.44c-4.42.54-9,1.11-13.4,6.29s-4.25,9.78-4.08,14.23a28.53,28.53,0,0,1-.15,5.47h2.52a33.48,33.48,0,0,0,.11-5.56c-.15-4.16-.3-8.09,3.49-12.53s7.68-4.93,11.82-5.44c4.42-.54,9-1.11,13.4-6.29s4.24-9.78,4.08-14.24c-.16-4.16-.3-8.09,3.49-12.53a14,14,0,0,1,5.34-4c.17-1,.34-1.92.5-2.83A16.07,16.07,0,0,0,456,313.06Z"
 | 
			
		||||
        style="fill: #fff"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M385.23,370.14c4.43-.55,9-1.12,13.41-6.3s4.24-9.78,4.08-14.23c-.16-4.16-.3-8.09,3.48-12.53s7.69-4.93,11.82-5.44c4.42-.55,9-1.11,13.4-6.29s4.25-9.79,4.08-14.24c-.15-4.16-.3-8.09,3.49-12.53s7.69-4.93,11.82-5.44,8.26-1,12.34-5.14a4.35,4.35,0,0,0-2.74-.86,14.82,14.82,0,0,1-7.92,3.27,19.67,19.67,0,0,1-2.12.36l-3,.35A16.3,16.3,0,0,0,437.1,297c-4.42,5.19-4.25,9.79-4.08,14.24.15,4.16.3,8.09-3.49,12.54s-7.68,4.92-11.82,5.43c-4.42.55-9,1.11-13.4,6.29s-4.24,9.79-4.08,14.24c.16,4.16.3,8.09-3.48,12.53s-7.69,4.93-11.82,5.44a23.21,23.21,0,0,0-8.38,2.16,32.7,32.7,0,0,0,4,1.12A32.06,32.06,0,0,1,385.23,370.14Z"
 | 
			
		||||
        style="fill: #fff"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M379.74,347.75c4.41-5.18,4.24-9.78,4.08-14.23-.15-4.17-.3-8.09,3.49-12.54s7.68-4.92,11.81-5.43c4.42-.55,9-1.12,13.41-6.3s4.24-9.78,4.08-14.23c0-1-.07-1.91-.06-2.86l-.49,0-2-.19c0,1.07,0,2.12.05,3.17.16,4.16.3,8.09-3.48,12.53s-7.69,4.93-11.82,5.44c-4.42.55-9,1.11-13.41,6.29s-4.24,9.78-4.07,14.24c.15,4.16.29,8.09-3.49,12.53-.21.25-.42.48-.64.7-.1,1.27-.19,2.47-.26,3.59A18.26,18.26,0,0,0,379.74,347.75Z"
 | 
			
		||||
        style="fill: #fff"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M416.06,292.13c-8.64-.68-12.07-2.42-18.53-6-8.41-4.61-10.21-1.54-9.61,3.07s-8.17,35-10,50.3-1.21,23.53-1.21,23.53-5.4,4.61-1.8,6.14a32.28,32.28,0,0,0,7.21,2.05h69.66s12.38-63.61,13.58-73.84-4.2-11.25-7.21-9.72-3.6,2.56-7.81,3.07C440.5,292,427.9,293.05,416.06,292.13Z"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M375.17,286.9H369.6a4.49,4.49,0,0,0-4.37,3.45l-6.92,29c-1.28,5.38-5.66,20.88-10.48,23.59h0a19.79,19.79,0,0,1-9.69,2.53H317.26l8.88,90.77h38l10-144.53a5.11,5.11,0,0,0,0-1h1.13Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M322.83,345.46h20.89a19.82,19.82,0,0,0,9.69-2.53h0c4.82-2.71,9.19-18.21,10.48-23.59l6.91-29a4.5,4.5,0,0,1,4.37-3.45h0a4.49,4.49,0,0,1,4.48,4.8L369.4,436.23h-38Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="412.04 436.28 414.21 371.26 327.1 370.49 329.28 436.28 412.04 436.28"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M479.26,286.9h-5.57a4.5,4.5,0,0,0-4.37,3.45l-6.92,29c-1.28,5.38-5.65,20.88-10.48,23.59h0a19.76,19.76,0,0,1-9.69,2.53H421.35L425,436.23h42.82L478.17,291.7a4.39,4.39,0,0,0,0-1h1.13Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M426.92,345.46h20.89a19.82,19.82,0,0,0,9.69-2.53h0c4.82-2.71,9.2-18.21,10.48-23.59l6.92-29a4.49,4.49,0,0,1,4.36-3.45h0a4.48,4.48,0,0,1,4.48,4.8L473.58,434a2.39,2.39,0,0,1-2.37,2.21H430.33Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M414.21,352,412,436.28h18.29L433.47,352a6.54,6.54,0,0,0-6.55-6.54h-6.16A6.54,6.54,0,0,0,414.21,352Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M307.94,352l3.14,84.28h18.29L327.19,352a6.54,6.54,0,0,0-6.54-6.54h-6.16A6.54,6.54,0,0,0,307.94,352Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <line
 | 
			
		||||
        x1="327.54"
 | 
			
		||||
        y1="399.23"
 | 
			
		||||
        x2="413.38"
 | 
			
		||||
        y2="399.23"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></line>
 | 
			
		||||
    </g>
 | 
			
		||||
    <g id="freepik--Character--inject-163">
 | 
			
		||||
      <path
 | 
			
		||||
        d="M285.55,287.23s-7.94,44.12-9.68,51.58-6,45.49-6,54.94.74,38,.74,38-3.23,1.49-3.23,2.73,2.49,9.2,2.49,9.2l14.66,5.47s3-7,3.23-9.45-1.74-3.48-1.74-7.71,7.46-53.44,9.7-61.64,8.7-32.81,9.94-35.05,7.46-18.64,7.46-18.64l3.48,1.74s2.23,51.7,3.48,54.93S338,429.2,338,429.2s-2.73,1.24-1,4.23,4.72,9.69,4.72,9.69,2.74-3.73,6-4.47,8.7,2.48,8.7,2.48,3.23-7.46,2.48-10.93-4-9.45-4.22-11.19-7-51.86-7-51.86,1.74-70.1,1.74-74.82a31.49,31.49,0,0,0-1.74-9.45,117.57,117.57,0,0,1-32.32,6.22c-17.65.74-27.34-6.46-27.34-6.46Z"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M269.91,443.71s-8.21,7.45-9.7,8.45-11.93,5-11.68,7,3,3.23,3,3.23h33.81s.24-1,.74-5.72a18.86,18.86,0,0,0-1.49-8.95Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M285.83,458.76l-37.12,1a6.16,6.16,0,0,0,2.8,2.57h33.81S285.49,461.66,285.83,458.76Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M341.74,443.12s-.25,6.21-.25,9.45-.49,7-.25,7.7.75,2,.75,2h19.64s.74-2-.25-5.72-2.74-5.71-3.23-9a36.31,36.31,0,0,0-1.74-6.46S346.71,433.43,341.74,443.12Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M361.89,459.91c-7.47,0-16.77-.24-20.72-.34a3.34,3.34,0,0,0,.07.7c.25.75.75,2,.75,2h19.64A7.22,7.22,0,0,0,361.89,459.91Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M278.5,193.64s-21.12,19.83-20.2,21.68S265,232.18,265,232.18s1.66,10,.74,16.31a64.54,64.54,0,0,0-.56,12.05,25.59,25.59,0,0,0,7,6.11c4.08,2.23,7.23,6.12,9.83-.18s1.48-17.61,1.48-17.61S287,262.58,287.59,265s-2,22.24-2,22.24S298.33,298,320,295.56s28.54-10,28.54-10-2.23-24.28-2.23-27.06a54,54,0,0,0-.37-5.75s7.6,7.79,11.31,7.42,10.19-15.57,8.52-18-7.41-12.25-10.93-19.29a85.73,85.73,0,0,1-5.28-13.19s-6.12-20.76-8-22.24-22.7-1.25-26.41-2.17a124.1,124.1,0,0,0-14.83-2C299.08,183.26,282.58,187.15,278.5,193.64Z"
 | 
			
		||||
        style="fill: currentColor; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M340.8,221.39c-.1-5.32-.12-9.46-.12-9.46"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M346,252.75a78.12,78.12,0,0,1-3.7-8.89c-.74-2.21-1.13-10.1-1.34-17.49"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M272.93,230.8c-1.89-3.3-3.33-5.88-3.51-6.4"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M283.51,248.86s-4.74-8-8.72-14.84" style="fill: none; stroke: #263238; stroke-miterlimit: 10"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M307.6,185.85a92.16,92.16,0,0,1,.19,19.65"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M320.07,277.32c2.23.45,4.62.86,7.17,1.19"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M293.14,265.36s6,6.58,22,10.8" style="fill: none; stroke: #263238; stroke-miterlimit: 10"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M309.45,279.44a50.88,50.88,0,0,0,21.87,4.45"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M265.16,163.61s2.22,10.38,2.41,12.61.74,5.19,3.52,9.63,10,14.27,11.49,14.83,7.23-.93,7.23-.93,6.11,12.05,7.78,11.68,5.93-14.09,6.49-19.65a39.64,39.64,0,0,0,.18-8.34l-2.59-9.26s-2.22-13-2.22-13.9,1.29-7.6-5.93-10.38-16.13-3-22.06,3.89S265.16,163.61,265.16,163.61Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M285,151.94a33.32,33.32,0,0,1-10,9.82c-6.85,4.45-3.52-.37-3.52-.37s-4.82,3-8.71,5-2.41,2.6-3.89,2-7-16.38,5.39-24,21.43-5.61,21.43-5.61-5.38-2.41-.56-2.41S292,139,292,139s2.59-.74,3.7.74a4.4,4.4,0,0,0,1.77,1.17c6,2.54,8.09,8.22,9.22,14.15.74,3.89-2.41,13.16-2.41,13.16L299.08,171a12.88,12.88,0,0,1-3-4.08,15.94,15.94,0,0,1-.74-4.63s-3.15,4.07-3.71,1.11-2-10.56-2-10.56.56,3-1.29,3S285,151.94,285,151.94Z"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M298.7,172.32s-.18-6.3,3.53-8.34,6.3.19,5.18,6.12-7.78,8.15-7.78,8.15"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <polygon
 | 
			
		||||
        points="289.81 199.75 299.63 189.93 293.14 204.94 289.81 199.75"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></polygon>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M285.27,189.82a1.66,1.66,0,0,1-.51,2.2c-.67.27-1.52-.28-1.9-1.23s-.15-1.94.51-2.21S284.88,188.87,285.27,189.82Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M287.83,172.66c.36,1.18.1,2.3-.57,2.51s-1.53-.58-1.89-1.75-.1-2.29.57-2.5S287.47,171.49,287.83,172.66Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M275.66,174.72c.36,1.18.1,2.3-.58,2.51s-1.52-.57-1.88-1.75-.11-2.29.57-2.5S275.29,173.55,275.66,174.72Z"
 | 
			
		||||
        style="fill: #263238"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M276.1,170.1a7.06,7.06,0,0,1,2.59,5.56c0,3.71-.56,9.27-.56,9.27l6.68-.93"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path d="M280.54,167.69s3.34-5.93,8.53-4.82" style="fill: none; stroke: #263238; stroke-miterlimit: 10"></path>
 | 
			
		||||
      <path d="M274.61,168.8s-4.07-1.29-6.85.37" style="fill: none; stroke: #263238; stroke-miterlimit: 10"></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M388,216.43s4.08-1.48,5.56-2,3,.37,5.56-.92,5.56-3.9,5.75-2-.75,2.59-2.23,3.52-3.89,2.78-3.89,2.78l-.74,1.85-11.12-.92S387.1,217,388,216.43Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M410.35,221.22s5.11-4.05,5.85-5.9-.56-2-1.85-1.3S409,218.1,409,218.1Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M406.17,222.1s3.8.4,4.18-.88,3.38-9.34,2.89-10.35-1.44-.92-2.49.75a57.18,57.18,0,0,0-2.9,7.07s-2.54.57-3.18,2.31S406.17,222.1,406.17,222.1Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M401.37,223.1s3.53,1.49,4.27.37,5.93-8,5.74-9.08-1.11-1.29-2.59,0a55.84,55.84,0,0,0-4.82,5.93s-2.6-.18-3.71,1.3S401.37,223.1,401.37,223.1Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M349.67,253.5a23.39,23.39,0,0,1,10.38-14.27C368.94,233.48,381,223.1,381,223.1s4.08-5.74,7-6.67,12.6,1.85,12.6,1.85,7.79-7.78,8-6.11-3.7,7.6-5.56,9.82-7.6,2.78-8.89,2.78a67,67,0,0,0-6.86.56c-.74.18-13,16.86-18.53,25s-8,9.63-11.49,9.82-7.23-2.6-8.16-3.89S349.67,253.5,349.67,253.5Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M233.77,222.82l11.68,25.34a1.86,1.86,0,0,0,2,1l9.69-1.79a1.85,1.85,0,0,0,1.36-2.52L248,220.12a1.82,1.82,0,0,0-1.89-1.11l-10.89,1.21A1.84,1.84,0,0,0,233.77,222.82Z"
 | 
			
		||||
        style="fill: #263238; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <path
 | 
			
		||||
        d="M241.44,244.51s-4.19-6.42-4.19-7.86,2.44-4.85,3.87-7.93,1.65-5.14,2.88-4.72,1,1.64.61,3.69a79.85,79.85,0,0,1-2.66,7.8l.41,2.47a26.4,26.4,0,0,1,3.69-5.55c1.85-1.84,3.9-4.92,5.34-4.31s2.05,1,.82,2.47a42.71,42.71,0,0,0-4.14,5.91,36.87,36.87,0,0,0-2.79,5.58,42.73,42.73,0,0,1,4.37-3.66c2-1.44,4.82-3.52,6-2.7s1.44,1.64.21,2.67-2.35,1.73-4,3.17a60.68,60.68,0,0,0-4.84,5.45,17.87,17.87,0,0,1,4.84-2.66c3.49-1.43,6.68-1.27,6.81-.17s-3.67,2.1-3.67,2.1c-2.72.62-2.85,1.55-2.85,1.55s2.26,4.52,1.85,6c0,0,9.12-1.43,12.61-2.45s8.94,1,10.59,2.65c3.49,3.49,5.13,8.83,3.29,13.35s-1.85,3.08-16.43-1-14.3-5.54-17.86-11.5C244.91,252.54,241.44,244.51,241.44,244.51Z"
 | 
			
		||||
        style="fill: #fff; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></path>
 | 
			
		||||
      <line
 | 
			
		||||
        x1="254.06"
 | 
			
		||||
        y1="253.77"
 | 
			
		||||
        x2="253.75"
 | 
			
		||||
        y2="257.53"
 | 
			
		||||
        style="fill: none; stroke: #263238; stroke-miterlimit: 10"
 | 
			
		||||
      ></line>
 | 
			
		||||
    </g>
 | 
			
		||||
  </svg>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts"></script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										8
									
								
								src/components/svg/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/components/svg/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
import SvgNoPermission from './SvgNoPermission.vue';
 | 
			
		||||
import SvgNotFound from './SvgNotFound.vue';
 | 
			
		||||
import SvgServiceError from './SvgServiceError.vue';
 | 
			
		||||
import SvgEmptyData from './SvgEmptyData.vue';
 | 
			
		||||
import SvgNetworkError from './SvgNetworkError.vue';
 | 
			
		||||
import SvgBanner from './SvgBanner.vue';
 | 
			
		||||
 | 
			
		||||
export { SvgNoPermission, SvgNotFound, SvgServiceError, SvgEmptyData, SvgNetworkError, SvgBanner };
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
export * from './route';
 | 
			
		||||
export * from './router';
 | 
			
		||||
export * from './system';
 | 
			
		||||
export * from './router';
 | 
			
		||||
export * from './layout';
 | 
			
		||||
export * from './theme';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,82 +1,70 @@
 | 
			
		||||
import { ref, computed, watch } from 'vue';
 | 
			
		||||
import type { ScrollbarInst } from 'naive-ui';
 | 
			
		||||
import { useThemeStore, useAppStore } from '@/store';
 | 
			
		||||
import { useRouteProps } from './route';
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { useAppStore, useThemeStore } from '@/store';
 | 
			
		||||
import type { ThemeLayoutMode, GlobalHeaderProps } from '@/interface';
 | 
			
		||||
 | 
			
		||||
export function useLayoutConfig() {
 | 
			
		||||
  const theme = useThemeStore();
 | 
			
		||||
type LayoutHeaderProps = Record<ThemeLayoutMode, GlobalHeaderProps>;
 | 
			
		||||
 | 
			
		||||
export function useBasicLayout() {
 | 
			
		||||
  const app = useAppStore();
 | 
			
		||||
  const { setScrollbarInstance } = useAppStore();
 | 
			
		||||
  const routeProps = useRouteProps();
 | 
			
		||||
  const theme = useThemeStore();
 | 
			
		||||
 | 
			
		||||
  /** 反转sider */
 | 
			
		||||
  const siderInverted = computed(() => theme.navStyle.theme !== 'light');
 | 
			
		||||
 | 
			
		||||
  /** 侧边菜单宽度 */
 | 
			
		||||
  const siderMenuWidth = computed(() => {
 | 
			
		||||
    const { collapsed } = app.menu;
 | 
			
		||||
    const { collapsedWidth, width } = theme.menuStyle;
 | 
			
		||||
    return collapsed ? collapsedWidth : width;
 | 
			
		||||
  type LayoutMode = 'vertical' | 'horizontal';
 | 
			
		||||
  const mode = computed(() => {
 | 
			
		||||
    const vertical: LayoutMode = 'vertical';
 | 
			
		||||
    const horizontal: LayoutMode = 'horizontal';
 | 
			
		||||
    return theme.layout.mode.includes(vertical) ? vertical : horizontal;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  /** 反转header */
 | 
			
		||||
  const headerInverted = computed(() => (theme.navStyle.theme !== 'dark' ? siderInverted.value : !siderInverted.value));
 | 
			
		||||
 | 
			
		||||
  /** 头部定位 */
 | 
			
		||||
  const headerPosition = computed(() => (theme.fixedHeaderAndTab ? 'absolute' : 'static'));
 | 
			
		||||
 | 
			
		||||
  /** 全局头部的高度(px) */
 | 
			
		||||
  const headerHeight = computed(() => `${theme.headerStyle.height}px`);
 | 
			
		||||
 | 
			
		||||
  /** 多页签Tab的高度(px) */
 | 
			
		||||
  const multiTabHeight = computed(() => `${theme.multiTabStyle.height}px`);
 | 
			
		||||
 | 
			
		||||
  /** 全局头部和多页签的总高度 */
 | 
			
		||||
  const headerAndMultiTabHeight = computed(() => {
 | 
			
		||||
    const {
 | 
			
		||||
      multiTabStyle: { visible, height: tabHeight },
 | 
			
		||||
      headerStyle: { height: headerHeight }
 | 
			
		||||
    } = theme;
 | 
			
		||||
    const height = visible ? headerHeight + tabHeight : headerHeight;
 | 
			
		||||
    return `${height}px`;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  /** 全局侧边栏的样式 */
 | 
			
		||||
  const globalSiderClassAndStyle = {
 | 
			
		||||
    class: 'transition-all duration-300 ease-in-out',
 | 
			
		||||
    style: 'z-index:12;box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);'
 | 
			
		||||
  const layoutHeaderProps: LayoutHeaderProps = {
 | 
			
		||||
    vertical: {
 | 
			
		||||
      showLogo: false,
 | 
			
		||||
      showHeaderMenu: false,
 | 
			
		||||
      showMenuCollape: true
 | 
			
		||||
    },
 | 
			
		||||
    'vertical-mix': {
 | 
			
		||||
      showLogo: false,
 | 
			
		||||
      showHeaderMenu: false,
 | 
			
		||||
      showMenuCollape: false
 | 
			
		||||
    },
 | 
			
		||||
    horizontal: {
 | 
			
		||||
      showLogo: true,
 | 
			
		||||
      showHeaderMenu: true,
 | 
			
		||||
      showMenuCollape: false
 | 
			
		||||
    },
 | 
			
		||||
    'horizontal-mix': {
 | 
			
		||||
      showLogo: true,
 | 
			
		||||
      showHeaderMenu: false,
 | 
			
		||||
      showMenuCollape: true
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** 纵向flex布局样式 */
 | 
			
		||||
  const flexColumnStyle = 'display:flex;flex-direction:column;height:100%;';
 | 
			
		||||
  const headerProps = computed(() => layoutHeaderProps[theme.layout.mode]);
 | 
			
		||||
 | 
			
		||||
  /** scrollbar的content的样式 */
 | 
			
		||||
  const scrollbarContentStyle = computed(() => {
 | 
			
		||||
    const { fullPage } = routeProps.value;
 | 
			
		||||
    const height = fullPage ? '100%' : 'auto';
 | 
			
		||||
    return `display:flex;flex-direction:column;height:${height};min-height:100%;`;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  /** 滚动条实例 */
 | 
			
		||||
  const scrollbar = ref<ScrollbarInst | null>(null);
 | 
			
		||||
 | 
			
		||||
  watch(scrollbar, newValue => {
 | 
			
		||||
    if (newValue) {
 | 
			
		||||
      setScrollbarInstance(newValue);
 | 
			
		||||
  const siderVisible = computed(() => theme.layout.mode !== 'horizontal');
 | 
			
		||||
  const siderWidth = computed(() => {
 | 
			
		||||
    const { width, mixWidth, mixChildMenuWidth } = theme.sider;
 | 
			
		||||
    const isVerticalMix = theme.layout.mode === 'vertical-mix';
 | 
			
		||||
    let w = isVerticalMix ? mixWidth : width;
 | 
			
		||||
    if (isVerticalMix && app.mixSiderFixed) {
 | 
			
		||||
      w += mixChildMenuWidth;
 | 
			
		||||
    }
 | 
			
		||||
    return w;
 | 
			
		||||
  });
 | 
			
		||||
  const siderCollapsedWidth = computed(() => {
 | 
			
		||||
    const { collapsedWidth, mixCollapsedWidth, mixChildMenuWidth } = theme.sider;
 | 
			
		||||
    const isVerticalMix = theme.layout.mode === 'vertical-mix';
 | 
			
		||||
    let w = isVerticalMix ? mixCollapsedWidth : collapsedWidth;
 | 
			
		||||
    if (isVerticalMix && app.mixSiderFixed) {
 | 
			
		||||
      w += mixChildMenuWidth;
 | 
			
		||||
    }
 | 
			
		||||
    return w;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    siderInverted,
 | 
			
		||||
    siderMenuWidth,
 | 
			
		||||
    headerInverted,
 | 
			
		||||
    headerPosition,
 | 
			
		||||
    headerHeight,
 | 
			
		||||
    multiTabHeight,
 | 
			
		||||
    headerAndMultiTabHeight,
 | 
			
		||||
    globalSiderClassAndStyle,
 | 
			
		||||
    flexColumnStyle,
 | 
			
		||||
    scrollbarContentStyle,
 | 
			
		||||
    scrollbar
 | 
			
		||||
    mode,
 | 
			
		||||
    headerProps,
 | 
			
		||||
    siderVisible,
 | 
			
		||||
    siderWidth,
 | 
			
		||||
    siderCollapsedWidth
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
import { computed, watch } from 'vue';
 | 
			
		||||
import { useRoute } from 'vue-router';
 | 
			
		||||
import { routeName } from '@/router';
 | 
			
		||||
import type { RouteKey } from '@/interface';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 路由属性
 | 
			
		||||
 */
 | 
			
		||||
export function useRouteProps() {
 | 
			
		||||
  const route = useRoute();
 | 
			
		||||
  const props = computed(() => {
 | 
			
		||||
    /** 路由名称 */
 | 
			
		||||
    const name = route.name as string;
 | 
			
		||||
    /** 缓存页面 */
 | 
			
		||||
    const keepAlive = Boolean(route.meta?.keepAlive);
 | 
			
		||||
    /** 视高100% */
 | 
			
		||||
    const fullPage = Boolean(route.meta?.fullPage);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      name,
 | 
			
		||||
      keepAlive,
 | 
			
		||||
      fullPage
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return props;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 路由查询参数
 | 
			
		||||
 */
 | 
			
		||||
export function useRouteQuery() {
 | 
			
		||||
  const route = useRoute();
 | 
			
		||||
 | 
			
		||||
  /** 登录跳转链接 */
 | 
			
		||||
  const loginRedirectUrl = computed(() => {
 | 
			
		||||
    let url: string | undefined;
 | 
			
		||||
    if (route.name === routeName('login')) {
 | 
			
		||||
      url = (route.query?.redirectUrl as string) || '';
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    loginRedirectUrl
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 路由名称变化后的回调
 | 
			
		||||
 * @param callback
 | 
			
		||||
 */
 | 
			
		||||
export function routeNameWatcher(callback: (name: RouteKey) => void) {
 | 
			
		||||
  const route = useRoute();
 | 
			
		||||
  watch(
 | 
			
		||||
    () => route.name,
 | 
			
		||||
    newValue => {
 | 
			
		||||
      callback(newValue as RouteKey);
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 路由全路径变化后的回调
 | 
			
		||||
 * @param callback
 | 
			
		||||
 */
 | 
			
		||||
export function routeFullPathWatcher(callback: (fullPath: string) => void) {
 | 
			
		||||
  const route = useRoute();
 | 
			
		||||
  watch(
 | 
			
		||||
    () => route.fullPath,
 | 
			
		||||
    newValue => {
 | 
			
		||||
      callback(newValue);
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -1,69 +1,86 @@
 | 
			
		||||
import { useRouter, useRoute } from 'vue-router';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import type { RouteLocationRaw } from 'vue-router';
 | 
			
		||||
import { router as globalRouter, routePath } from '@/router';
 | 
			
		||||
import type { LoginModuleType } from '@/interface';
 | 
			
		||||
import { router as globalRouter, routeName } from '@/router';
 | 
			
		||||
import { LoginModuleKey } from '@/interface';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 路由跳转
 | 
			
		||||
 * @param inSetup - 是否在vue页面/组件的setup里面调用
 | 
			
		||||
 * @param inSetup - 是否在vue页面/组件的setup里面调用,在axios里面无法使用useRouter和useRoute
 | 
			
		||||
 */
 | 
			
		||||
export function useRouterPush(inSetup: boolean = true) {
 | 
			
		||||
  const router = inSetup ? useRouter() : globalRouter;
 | 
			
		||||
  const route = inSetup ? useRoute() : null;
 | 
			
		||||
 | 
			
		||||
  /** 跳转首页 */
 | 
			
		||||
  function toHome() {
 | 
			
		||||
    router.push('/');
 | 
			
		||||
  }
 | 
			
		||||
  const route = globalRouter.currentRoute;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 重定向地址
 | 
			
		||||
   * - current: 取当前的path作为重定向地址
 | 
			
		||||
   * 路由跳转
 | 
			
		||||
   * @param to - 需要跳转的路由
 | 
			
		||||
   * @param newTab - 是否在新的浏览器Tab标签打开
 | 
			
		||||
   */
 | 
			
		||||
  type LoginRedirect = 'current' | string;
 | 
			
		||||
  /**
 | 
			
		||||
   * 跳转登录页面(通过vue路由)
 | 
			
		||||
   * @param module - 展示的登录模块
 | 
			
		||||
   * @param redirectUrl - 重定向地址
 | 
			
		||||
   */
 | 
			
		||||
  function toLogin(module: LoginModuleType = 'pwd-login', redirectUrl: LoginRedirect = 'current') {
 | 
			
		||||
    const routeLocation: RouteLocationRaw = {
 | 
			
		||||
      path: routePath('login'),
 | 
			
		||||
      query: { module }
 | 
			
		||||
    };
 | 
			
		||||
    if (redirectUrl) {
 | 
			
		||||
      let url = redirectUrl;
 | 
			
		||||
      if (redirectUrl === 'current') {
 | 
			
		||||
        url = router.currentRoute.value.fullPath;
 | 
			
		||||
      }
 | 
			
		||||
      routeLocation.query!.redirectUrl = url;
 | 
			
		||||
    }
 | 
			
		||||
    router.push(routeLocation);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 登陆页跳转登陆页
 | 
			
		||||
   * @param module - 展示的登录模块
 | 
			
		||||
   * @param query - 查询参数
 | 
			
		||||
   */
 | 
			
		||||
  function toCurrentLogin(module: LoginModuleType) {
 | 
			
		||||
    if (route) {
 | 
			
		||||
      const { query } = route;
 | 
			
		||||
      router.push({ path: routePath('login'), query: { ...query, module } });
 | 
			
		||||
  function routerPush(to: RouteLocationRaw, newTab = false) {
 | 
			
		||||
    if (newTab) {
 | 
			
		||||
      const routerData = router.resolve(to);
 | 
			
		||||
      window.open(routerData.href, '_blank');
 | 
			
		||||
    } else {
 | 
			
		||||
      throw Error('该函数必须在setup里面调用!');
 | 
			
		||||
      router.push(to);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 登录后跳转重定向的地址 */
 | 
			
		||||
  function toLoginRedirectUrl(path: string) {
 | 
			
		||||
    router.push(path);
 | 
			
		||||
  /** 返回上一级路由 */
 | 
			
		||||
  function routerBack() {
 | 
			
		||||
    router.go(-1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 跳转首页
 | 
			
		||||
   * @param newTab - 在新的浏览器标签打开
 | 
			
		||||
   */
 | 
			
		||||
  function toHome(newTab = false) {
 | 
			
		||||
    routerPush({ name: routeName('root') }, newTab);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 跳转登录页面
 | 
			
		||||
   * @param loginModule - 展示的登录模块
 | 
			
		||||
   * @param redirectUrl - 重定向地址(登录成功后跳转的地址),默认undefined表示取当前地址为重定向地址
 | 
			
		||||
   */
 | 
			
		||||
  function toLogin(loginModule?: LoginModuleKey, redirectUrl?: string) {
 | 
			
		||||
    const module: LoginModuleKey = loginModule || 'pwd-login';
 | 
			
		||||
    const routeLocation: RouteLocationRaw = {
 | 
			
		||||
      name: routeName('login'),
 | 
			
		||||
      params: { module }
 | 
			
		||||
    };
 | 
			
		||||
    const redirect = redirectUrl || route.value.fullPath;
 | 
			
		||||
    Object.assign(routeLocation, { query: { redirect } });
 | 
			
		||||
    routerPush(routeLocation);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 登录页切换其他模块
 | 
			
		||||
   * @param module - 切换后的登录模块
 | 
			
		||||
   */
 | 
			
		||||
  function toLoginModule(module: LoginModuleKey) {
 | 
			
		||||
    const { query } = route.value;
 | 
			
		||||
    routerPush({ name: routeName('login'), params: { module }, query });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 登录成功后跳转重定向的地址
 | 
			
		||||
   */
 | 
			
		||||
  function toLoginRedirect() {
 | 
			
		||||
    const { query } = route.value;
 | 
			
		||||
    if (query?.redirect) {
 | 
			
		||||
      routerPush(query.redirect as string);
 | 
			
		||||
    } else {
 | 
			
		||||
      toHome();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    routerPush,
 | 
			
		||||
    routerBack,
 | 
			
		||||
    toHome,
 | 
			
		||||
    toLogin,
 | 
			
		||||
    toCurrentLogin,
 | 
			
		||||
    toLoginRedirectUrl
 | 
			
		||||
    toLoginModule,
 | 
			
		||||
    toLoginRedirect
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,28 @@
 | 
			
		||||
import { useBreakpoints, breakpointsTailwind } from '@vueuse/core';
 | 
			
		||||
import UAParser from 'ua-parser-js';
 | 
			
		||||
 | 
			
		||||
interface AppInfo {
 | 
			
		||||
  /** 项目名称 */
 | 
			
		||||
export function useAppTitle() {
 | 
			
		||||
  return import.meta.env.VITE_APP_TITLE as string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  /** 项目标题 */
 | 
			
		||||
  title: string;
 | 
			
		||||
  /** 项目描述 */
 | 
			
		||||
  desc: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 是否是移动端 */
 | 
			
		||||
export function useIsMobile() {
 | 
			
		||||
  const breakpoints = useBreakpoints(breakpointsTailwind);
 | 
			
		||||
  const isMobile = breakpoints.smaller('lg');
 | 
			
		||||
  return isMobile;
 | 
			
		||||
/** 项目信息 */
 | 
			
		||||
export function useAppInfo(): AppInfo {
 | 
			
		||||
  const { VITE_APP_NAME: name, VITE_APP_TITLE: title, VITE_APP_DESC: desc } = import.meta.env;
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    name,
 | 
			
		||||
    title,
 | 
			
		||||
    desc
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 获取设备信息 */
 | 
			
		||||
export function useDeviceInfo() {
 | 
			
		||||
  const parser = new UAParser();
 | 
			
		||||
  const result = parser.getResult();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
import { computed, watch } from 'vue';
 | 
			
		||||
import { darkTheme } from 'naive-ui';
 | 
			
		||||
import { useDark } from '@vueuse/core';
 | 
			
		||||
import { useThemeStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
/** 系统暗黑模式 */
 | 
			
		||||
export function useDarkMode() {
 | 
			
		||||
  const osDark = useDark();
 | 
			
		||||
  const theme = useThemeStore();
 | 
			
		||||
  const { handleDarkMode } = useThemeStore();
 | 
			
		||||
 | 
			
		||||
  /** naive-ui暗黑主题 */
 | 
			
		||||
  const naiveTheme = computed(() => (theme.darkMode ? darkTheme : undefined));
 | 
			
		||||
 | 
			
		||||
  // 监听操作系统主题模式
 | 
			
		||||
  watch(
 | 
			
		||||
    osDark,
 | 
			
		||||
    newValue => {
 | 
			
		||||
      handleDarkMode(newValue);
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      immediate: true
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    naiveTheme
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								src/composables/events/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/composables/events/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export {};
 | 
			
		||||
							
								
								
									
										1
									
								
								src/config/business/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/config/business/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export {};
 | 
			
		||||
							
								
								
									
										3
									
								
								src/config/common/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/config/common/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
export * from './service';
 | 
			
		||||
export * from './regexp';
 | 
			
		||||
export * from './map-sdk';
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
/** 百度地图sdk地址 */
 | 
			
		||||
export const BAIDU_MAP_SDK_URL =
 | 
			
		||||
  'https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1';
 | 
			
		||||
 | 
			
		||||
/** 高德地图sdk地址 */
 | 
			
		||||
export const GAODE_MAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d';
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								src/config/common/regexp.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/config/common/regexp.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
/** 手机号码正则 */
 | 
			
		||||
export const REGEXP_PHONE =
 | 
			
		||||
  /^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/;
 | 
			
		||||
 | 
			
		||||
/** 邮箱正则 */
 | 
			
		||||
export const REGEXP_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
 | 
			
		||||
 | 
			
		||||
/** 密码正则(密码为8-18位数字/字符/符号的组合) */
 | 
			
		||||
export const REGEXP_PWD =
 | 
			
		||||
  /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
 | 
			
		||||
 | 
			
		||||
/** 6位数字验证码正则 */
 | 
			
		||||
export const REGEXP_CODE_SIX = /^\d{6}$/;
 | 
			
		||||
 | 
			
		||||
/** 4位数字验证码正则 */
 | 
			
		||||
export const REGEXP_CODE_FOUR = /^\d{4}$/;
 | 
			
		||||
 | 
			
		||||
/** url链接正则 */
 | 
			
		||||
export const REGEXP_URL =
 | 
			
		||||
  /(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
 | 
			
		||||
@@ -4,24 +4,27 @@ export const REQUEST_TIMEOUT = 60 * 1000;
 | 
			
		||||
/** 错误信息的显示时间 */
 | 
			
		||||
export const ERROR_MSG_DURATION = 3 * 1000;
 | 
			
		||||
 | 
			
		||||
/** 兜底的请求错误code */
 | 
			
		||||
/** 默认的请求错误code */
 | 
			
		||||
export const DEFAULT_REQUEST_ERROR_CODE = 'DEFAULT';
 | 
			
		||||
/** 兜底的请求错误文本 */
 | 
			
		||||
 | 
			
		||||
/** 默认的请求错误文本 */
 | 
			
		||||
export const DEFAULT_REQUEST_ERROR_MSG = '请求错误~';
 | 
			
		||||
 | 
			
		||||
/** 请求超时的错误code(为固定值:ECONNABORTED) */
 | 
			
		||||
export const REQUEST_TIMEOUT_CODE = 'ECONNABORTED';
 | 
			
		||||
 | 
			
		||||
/** 请求超时的错误文本 */
 | 
			
		||||
export const REQUEST_TIMEOUT_MSG = '请求超时~';
 | 
			
		||||
 | 
			
		||||
/** 网络不可用的code */
 | 
			
		||||
export const NETWORK_ERROR_CODE = 'NETWORK_ERROR';
 | 
			
		||||
 | 
			
		||||
/** 网络不可用的错误文本 */
 | 
			
		||||
export const NETWORK_ERROR_MSG = '网络不可用~';
 | 
			
		||||
 | 
			
		||||
/** 请求不成功各种状态的错误 */
 | 
			
		||||
export const ERROR_STATUS = {
 | 
			
		||||
  400: '400: 请求出现语法错误',
 | 
			
		||||
  400: '400: 请求出现语法错误~',
 | 
			
		||||
  401: '401: 用户未授权~',
 | 
			
		||||
  403: '403: 服务器拒绝访问~',
 | 
			
		||||
  404: '404: 请求的资源不存在~',
 | 
			
		||||
@@ -32,8 +35,12 @@ export const ERROR_STATUS = {
 | 
			
		||||
  502: '502: 错误网关~',
 | 
			
		||||
  503: '503: 服务不可用~',
 | 
			
		||||
  504: '504: 网关超时~',
 | 
			
		||||
  505: '505: http版本不支持该请求~'
 | 
			
		||||
  505: '505: http版本不支持该请求~',
 | 
			
		||||
  [DEFAULT_REQUEST_ERROR_CODE]: DEFAULT_REQUEST_ERROR_MSG
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 不弹出错误信息的code */
 | 
			
		||||
export const NO_ERROR_MSG_CODE: (string | number)[] = [];
 | 
			
		||||
 | 
			
		||||
/** token失效需要刷新token的code */
 | 
			
		||||
export const REFRESH_TOKEN_CODE: (string | number)[] = [66666];
 | 
			
		||||
							
								
								
									
										1
									
								
								src/config/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/config/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export * from './common';
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
import useReloadContext from './useReloadContext';
 | 
			
		||||
 | 
			
		||||
const { useReloadProvide, useReloadInject } = useReloadContext();
 | 
			
		||||
 | 
			
		||||
/** 从App组件注入provide */
 | 
			
		||||
function setupAppContext() {
 | 
			
		||||
  useReloadProvide();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { setupAppContext, useReloadInject };
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
import { ref, nextTick } from 'vue';
 | 
			
		||||
import type { Ref } from 'vue';
 | 
			
		||||
import { useContext } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
interface ReloadContext {
 | 
			
		||||
  reload: Ref<boolean>;
 | 
			
		||||
  handleReload(): void;
 | 
			
		||||
}
 | 
			
		||||
const { useProvide, useInject: useReloadInject } = useContext<ReloadContext>();
 | 
			
		||||
 | 
			
		||||
/** 重载上下文 */
 | 
			
		||||
export default function useReloadContext() {
 | 
			
		||||
  const reload = ref(true);
 | 
			
		||||
  function handleReload() {
 | 
			
		||||
    reload.value = false;
 | 
			
		||||
    nextTick(() => {
 | 
			
		||||
      reload.value = true;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const context: ReloadContext = {
 | 
			
		||||
    reload,
 | 
			
		||||
    handleReload
 | 
			
		||||
  };
 | 
			
		||||
  function useReloadProvide() {
 | 
			
		||||
    useProvide(context);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    useReloadProvide,
 | 
			
		||||
    useReloadInject
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
export * from './app';
 | 
			
		||||
export * from './part';
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
import useVerticalMixSiderContext from './useVerticalMixSiderContext';
 | 
			
		||||
 | 
			
		||||
export { useVerticalMixSiderContext };
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import type { Ref } from 'vue';
 | 
			
		||||
import { useContext, useBoolean } from '@/hooks';
 | 
			
		||||
 | 
			
		||||
interface VerticalMixSiderContext {
 | 
			
		||||
  /** 子菜单可见性 */
 | 
			
		||||
  childMenuVisible: Ref<boolean>;
 | 
			
		||||
  /** 展示子菜单 */
 | 
			
		||||
  showChildMenu(): void;
 | 
			
		||||
  /** 隐藏子菜单 */
 | 
			
		||||
  hideChildMenu(): void;
 | 
			
		||||
  /** 鼠标悬浮的一级菜单对应的路由名称 */
 | 
			
		||||
  hoverRouteName: Ref<string>;
 | 
			
		||||
  /** 设置悬浮路由名称 */
 | 
			
		||||
  setHoverRouteName(name: string): void;
 | 
			
		||||
  isMouseEnterChildMenu: Ref<boolean>;
 | 
			
		||||
  setMouseEnterChildMenu(): void;
 | 
			
		||||
  setMouseLeaveChildMenu(): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const { useProvide, useInject: useVerticalMixSiderInject } = useContext<VerticalMixSiderContext>();
 | 
			
		||||
 | 
			
		||||
export default function useVerticalMixSiderContext() {
 | 
			
		||||
  const { bool: childMenuVisible, setTrue: showChildMenu, setFalse: hideChildMenu } = useBoolean();
 | 
			
		||||
  const {
 | 
			
		||||
    bool: isMouseEnterChildMenu,
 | 
			
		||||
    setTrue: setMouseEnterChildMenu,
 | 
			
		||||
    setFalse: setMouseLeaveChildMenu
 | 
			
		||||
  } = useBoolean();
 | 
			
		||||
 | 
			
		||||
  const hoverRouteName = ref('');
 | 
			
		||||
  function setHoverRouteName(name: string) {
 | 
			
		||||
    hoverRouteName.value = name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const context: VerticalMixSiderContext = {
 | 
			
		||||
    childMenuVisible,
 | 
			
		||||
    showChildMenu,
 | 
			
		||||
    hideChildMenu,
 | 
			
		||||
    hoverRouteName,
 | 
			
		||||
    setHoverRouteName,
 | 
			
		||||
    isMouseEnterChildMenu,
 | 
			
		||||
    setMouseEnterChildMenu,
 | 
			
		||||
    setMouseLeaveChildMenu
 | 
			
		||||
  };
 | 
			
		||||
  function useVerticalMixSiderProvide() {
 | 
			
		||||
    useProvide(context);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    useVerticalMixSiderProvide,
 | 
			
		||||
    useVerticalMixSiderInject
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/directives/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/directives/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
import type { App } from 'vue';
 | 
			
		||||
import setupNetworkDirective from './network';
 | 
			
		||||
import setupLoginDirective from './login';
 | 
			
		||||
 | 
			
		||||
export function setupDirectives(app: App) {
 | 
			
		||||
  setupNetworkDirective(app);
 | 
			
		||||
  setupLoginDirective(app);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/directives/login.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/directives/login.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import type { App, Directive } from 'vue';
 | 
			
		||||
import { useAuthStore } from '@/store';
 | 
			
		||||
import { useRouterPush } from '@/composables';
 | 
			
		||||
 | 
			
		||||
export default function setupLoginDirective(app: App) {
 | 
			
		||||
  const auth = useAuthStore();
 | 
			
		||||
  const { toLogin } = useRouterPush(false);
 | 
			
		||||
  function listenerHandler(event: MouseEvent) {
 | 
			
		||||
    if (!auth.isLogin) {
 | 
			
		||||
      event.stopPropagation();
 | 
			
		||||
      toLogin();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const loginDirective: Directive<HTMLElement, boolean | undefined> = {
 | 
			
		||||
    mounted(el: HTMLElement, binding) {
 | 
			
		||||
      if (binding.value === false) return;
 | 
			
		||||
      el.addEventListener('click', listenerHandler, { capture: true });
 | 
			
		||||
    },
 | 
			
		||||
    unmounted(el: HTMLElement, binding) {
 | 
			
		||||
      if (binding.value === false) return;
 | 
			
		||||
      el.removeEventListener('click', listenerHandler);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  app.directive('login', loginDirective);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/directives/network.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/directives/network.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import type { App, Directive } from 'vue';
 | 
			
		||||
import { NETWORK_ERROR_MSG } from '@/config';
 | 
			
		||||
 | 
			
		||||
export default function setupNetworkDirective(app: App) {
 | 
			
		||||
  function listenerHandler(event: MouseEvent) {
 | 
			
		||||
    const hasNetwork = window.navigator.onLine;
 | 
			
		||||
    if (!hasNetwork) {
 | 
			
		||||
      window.$message?.error(NETWORK_ERROR_MSG);
 | 
			
		||||
      event.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const networkDirective: Directive<HTMLElement, boolean | undefined> = {
 | 
			
		||||
    mounted(el: HTMLElement, binding) {
 | 
			
		||||
      if (binding.value === false) return;
 | 
			
		||||
      el.addEventListener('click', listenerHandler, { capture: true });
 | 
			
		||||
    },
 | 
			
		||||
    unmounted(el: HTMLElement, binding) {
 | 
			
		||||
      if (binding.value === false) return;
 | 
			
		||||
      el.removeEventListener('click', listenerHandler);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  app.directive('network', networkDirective);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
export {};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
/** 动画类型 */
 | 
			
		||||
export enum EnumAnimate {
 | 
			
		||||
  'zoom-fade' = '渐变',
 | 
			
		||||
  'zoom-out' = '闪现',
 | 
			
		||||
  'fade-slide' = '滑动',
 | 
			
		||||
  'fade' = '消退',
 | 
			
		||||
  'fade-bottom' = '底部消退',
 | 
			
		||||
  'fade-scale' = '缩放消退'
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
export * from './system';
 | 
			
		||||
export * from './theme';
 | 
			
		||||
export * from './animate';
 | 
			
		||||
export * from './typeof';
 | 
			
		||||
export * from './storage';
 | 
			
		||||
export * from './service';
 | 
			
		||||
export * from './system';
 | 
			
		||||
export * from './theme';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								src/enum/common/service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/enum/common/service.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/** http请求头的content-type类型 */
 | 
			
		||||
export enum ContentType {
 | 
			
		||||
  json = 'application/json',
 | 
			
		||||
  formUrlencoded = 'application/x-www-form-urlencoded',
 | 
			
		||||
  formData = 'multipart/form-data'
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
export enum EnumStorageKey {
 | 
			
		||||
  /** 主题颜色 */
 | 
			
		||||
  'theme-color' = '__THEME_COLOR__',
 | 
			
		||||
  /** 用户token */
 | 
			
		||||
  'token' = '__TOKEN__',
 | 
			
		||||
  /** 用户刷新token */
 | 
			
		||||
@@ -6,5 +8,5 @@ export enum EnumStorageKey {
 | 
			
		||||
  /** 用户信息 */
 | 
			
		||||
  'user-info' = '__USER_INFO__',
 | 
			
		||||
  /** 多页签路由信息 */
 | 
			
		||||
  'tab-route' = '__TAB_ROUTE__'
 | 
			
		||||
  'tab-routes' = '__TAB_ROUTES__'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user