mirror of
https://github.com/xiangshu233/vue3-vant4-mobile.git
synced 2025-04-06 03:57:47 +08:00
style: 💄 eslint --fix
This commit is contained in:
parent
147022076f
commit
cb91140ab8
build
commitlint.config.cjsindex.htmlmock
postcss.config.jssrc
App.vue
tsconfig.jsonapi/system
components
enums
hooks
layout
main.tsrouter
settings
store
styles
theme
utils
views
dashboard
exception
login
message
my
AccountSetting.vueChangePassword.vueEditNickname.vueEditSign.vueEditUserInfo.vueThemeSetting.vue
components
index.vuepickColumns.tswelcome
types
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* The name of the configuration file entered in the production environment
|
* The name of the configuration file entered in the production environment
|
||||||
*/
|
*/
|
||||||
export const GLOB_CONFIG_FILE_NAME = 'app.config.js';
|
export const GLOB_CONFIG_FILE_NAME = 'app.config.js'
|
||||||
|
|
||||||
export const OUTPUT_DIR = 'dist/vant-mobile';
|
export const OUTPUT_DIR = 'dist/vant-mobile'
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
* Get the configuration file variable name
|
* Get the configuration file variable name
|
||||||
* @param env
|
* @param env
|
||||||
*/
|
*/
|
||||||
export const getConfigFileName = (env: Record<string, any>) => {
|
export function getConfigFileName(env: Record<string, any>) {
|
||||||
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
|
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
|
||||||
.toUpperCase()
|
.toUpperCase()
|
||||||
.replace(/\s/g, '');
|
.replace(/\s/g, '')
|
||||||
};
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
/**
|
/**
|
||||||
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
|
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
|
||||||
*/
|
*/
|
||||||
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
|
import fs from 'fs-extra'
|
||||||
import fs from 'fs-extra';
|
import colors from 'picocolors'
|
||||||
import colors from 'picocolors';
|
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant'
|
||||||
|
|
||||||
import { getRootPath, getEnvConfig } from '../utils';
|
import { getEnvConfig, getRootPath } from '../utils'
|
||||||
import { getConfigFileName } from '../getConfigFileName';
|
import { getConfigFileName } from '../getConfigFileName'
|
||||||
|
|
||||||
import pkg from '../../package.json';
|
import pkg from '../../package.json'
|
||||||
|
|
||||||
function createConfig(
|
function createConfig(
|
||||||
{
|
{
|
||||||
configName,
|
configName,
|
||||||
config,
|
config,
|
||||||
configFileName = GLOB_CONFIG_FILE_NAME,
|
configFileName = GLOB_CONFIG_FILE_NAME,
|
||||||
}: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} }
|
}: { configName: string, config: any, configFileName?: string } = { configName: '', config: {} },
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const windowConf = `window.${configName}`;
|
const windowConf = `window.${configName}`
|
||||||
// Ensure that the variable will not be modified
|
// Ensure that the variable will not be modified
|
||||||
const configStr = `${windowConf}=${JSON.stringify(config)};
|
const configStr = `${windowConf}=${JSON.stringify(config)};
|
||||||
Object.freeze(${windowConf});
|
Object.freeze(${windowConf});
|
||||||
@ -26,19 +26,20 @@ function createConfig(
|
|||||||
configurable: false,
|
configurable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
});
|
});
|
||||||
`.replace(/\s/g, '');
|
`.replace(/\s/g, '')
|
||||||
fs.mkdirp(getRootPath(OUTPUT_DIR));
|
fs.mkdirp(getRootPath(OUTPUT_DIR))
|
||||||
fs.writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
|
fs.writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr)
|
||||||
|
|
||||||
console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
|
console.log(`${colors.cyan(`✨ [${pkg.name}]`)} - configuration file is build successfully:`)
|
||||||
console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n');
|
console.log(`${colors.gray(`${OUTPUT_DIR}/${colors.green(configFileName)}`)}\n`)
|
||||||
} catch (error) {
|
}
|
||||||
console.log(colors.red('configuration file configuration file failed to package:\n' + error));
|
catch (error) {
|
||||||
|
console.log(colors.red(`configuration file configuration file failed to package:\n${error}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runBuildConfig() {
|
export function runBuildConfig() {
|
||||||
const config = getEnvConfig();
|
const config = getEnvConfig()
|
||||||
const configFileName = getConfigFileName(config);
|
const configFileName = getConfigFileName(config)
|
||||||
createConfig({ config, configName: configFileName });
|
createConfig({ config, configName: configFileName })
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
// #!/usr/bin/env node
|
// #!/usr/bin/env node
|
||||||
|
|
||||||
import { runBuildConfig } from './buildConf';
|
import colors from 'picocolors'
|
||||||
import colors from 'picocolors';
|
|
||||||
|
|
||||||
import pkg from '../../package.json';
|
import pkg from '../../package.json'
|
||||||
|
import { runBuildConfig } from './buildConf'
|
||||||
|
|
||||||
export const runBuild = async () => {
|
export async function runBuild() {
|
||||||
try {
|
try {
|
||||||
const argvList = process.argv.splice(2);
|
const argvList = process.argv.splice(2)
|
||||||
|
|
||||||
// Generate configuration file
|
// Generate configuration file
|
||||||
if (!argvList.includes('disabled-config')) {
|
if (!argvList.includes('disabled-config')) {
|
||||||
await runBuildConfig();
|
await runBuildConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
|
console.log(`✨ ${colors.cyan(`[${pkg.name}]`)} - build successfully!`)
|
||||||
} catch (error) {
|
|
||||||
console.log(colors.red('vite build error:\n' + error));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
};
|
catch (error) {
|
||||||
runBuild();
|
console.log(colors.red(`vite build error:\n${error}`))
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runBuild()
|
||||||
|
@ -1,44 +1,45 @@
|
|||||||
import fs from 'fs';
|
import fs from 'node:fs'
|
||||||
import path from 'path';
|
import path from 'node:path'
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
export function isDevFn(mode: string): boolean {
|
export function isDevFn(mode: string): boolean {
|
||||||
return mode === 'development';
|
return mode === 'development'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isProdFn(mode: string): boolean {
|
export function isProdFn(mode: string): boolean {
|
||||||
return mode === 'production';
|
return mode === 'production'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to generate package preview
|
* Whether to generate package preview
|
||||||
*/
|
*/
|
||||||
export function isReportMode(): boolean {
|
export function isReportMode(): boolean {
|
||||||
return process.env.REPORT === 'true';
|
return process.env.REPORT === 'true'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read all environment variable configuration files to process.env
|
// Read all environment variable configuration files to process.env
|
||||||
// 读取并处理所有环境变量配置文件 .env
|
// 读取并处理所有环境变量配置文件 .env
|
||||||
export function wrapperEnv(envConf: Recordable): ViteEnv {
|
export function wrapperEnv(envConf: Recordable): ViteEnv {
|
||||||
const ret: any = {};
|
const ret: any = {}
|
||||||
|
|
||||||
for (const envName of Object.keys(envConf)) {
|
for (const envName of Object.keys(envConf)) {
|
||||||
// 去除空格
|
// 去除空格
|
||||||
let realName = envConf[envName].replace(/\\n/g, '\n');
|
let realName = envConf[envName].replace(/\\n/g, '\n')
|
||||||
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
|
realName = realName === 'true' ? true : realName === 'false' ? false : realName
|
||||||
|
|
||||||
if (envName === 'VITE_PORT') {
|
if (envName === 'VITE_PORT') {
|
||||||
realName = Number(realName);
|
realName = Number(realName)
|
||||||
}
|
}
|
||||||
if (envName === 'VITE_PROXY') {
|
if (envName === 'VITE_PROXY') {
|
||||||
try {
|
try {
|
||||||
realName = JSON.parse(realName);
|
realName = JSON.parse(realName)
|
||||||
} catch (error) {}
|
}
|
||||||
|
catch (error) {}
|
||||||
}
|
}
|
||||||
ret[envName] = realName;
|
ret[envName] = realName
|
||||||
process.env[envName] = realName;
|
process.env[envName] = realName
|
||||||
}
|
}
|
||||||
return ret;
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,21 +48,22 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
|
|||||||
* @param confFiles ext
|
* @param confFiles ext
|
||||||
*/
|
*/
|
||||||
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {
|
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {
|
||||||
let envConfig = {};
|
let envConfig = {}
|
||||||
confFiles.forEach((item) => {
|
confFiles.forEach((item) => {
|
||||||
try {
|
try {
|
||||||
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
|
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)))
|
||||||
envConfig = { ...envConfig, ...env };
|
envConfig = { ...envConfig, ...env }
|
||||||
} catch (error) {}
|
}
|
||||||
});
|
catch (error) {}
|
||||||
|
})
|
||||||
|
|
||||||
Object.keys(envConfig).forEach((key) => {
|
Object.keys(envConfig).forEach((key) => {
|
||||||
const reg = new RegExp(`^(${match})`);
|
const reg = new RegExp(`^(${match})`)
|
||||||
if (!reg.test(key)) {
|
if (!reg.test(key)) {
|
||||||
Reflect.deleteProperty(envConfig, key);
|
Reflect.deleteProperty(envConfig, key)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return envConfig;
|
return envConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,5 +71,5 @@ export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.pr
|
|||||||
* @param dir file path
|
* @param dir file path
|
||||||
*/
|
*/
|
||||||
export function getRootPath(...dir: string[]) {
|
export function getRootPath(...dir: string[]) {
|
||||||
return path.resolve(process.cwd(), ...dir);
|
return path.resolve(process.cwd(), ...dir)
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,25 @@
|
|||||||
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
|
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
|
||||||
* https://github.com/anncwb/vite-plugin-compression
|
* https://github.com/anncwb/vite-plugin-compression
|
||||||
*/
|
*/
|
||||||
import type { PluginOption } from 'vite';
|
import type { PluginOption } from 'vite'
|
||||||
|
|
||||||
import compressPlugin from 'vite-plugin-compression';
|
import compressPlugin from 'vite-plugin-compression'
|
||||||
|
|
||||||
export function configCompressPlugin(
|
export function configCompressPlugin(
|
||||||
compress: 'gzip' | 'brotli' | 'none',
|
compress: 'gzip' | 'brotli' | 'none',
|
||||||
deleteOriginFile = false
|
deleteOriginFile = false,
|
||||||
): PluginOption | PluginOption[] {
|
): PluginOption | PluginOption[] {
|
||||||
const compressList = compress.split(',');
|
const compressList = compress.split(',')
|
||||||
|
|
||||||
const plugins: PluginOption[] = [];
|
const plugins: PluginOption[] = []
|
||||||
|
|
||||||
if (compressList.includes('gzip')) {
|
if (compressList.includes('gzip')) {
|
||||||
plugins.push(
|
plugins.push(
|
||||||
compressPlugin({
|
compressPlugin({
|
||||||
ext: '.gz',
|
ext: '.gz',
|
||||||
deleteOriginFile,
|
deleteOriginFile,
|
||||||
})
|
}),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
if (compressList.includes('brotli')) {
|
if (compressList.includes('brotli')) {
|
||||||
plugins.push(
|
plugins.push(
|
||||||
@ -28,8 +28,8 @@ export function configCompressPlugin(
|
|||||||
ext: '.br',
|
ext: '.br',
|
||||||
algorithm: 'brotliCompress',
|
algorithm: 'brotliCompress',
|
||||||
deleteOriginFile,
|
deleteOriginFile,
|
||||||
})
|
}),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
return plugins;
|
return plugins
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,19 @@
|
|||||||
* Plugin to minimize and use ejs template syntax in index.html.
|
* Plugin to minimize and use ejs template syntax in index.html.
|
||||||
* https://github.com/anncwb/vite-plugin-html
|
* https://github.com/anncwb/vite-plugin-html
|
||||||
*/
|
*/
|
||||||
import type { PluginOption } from 'vite';
|
import type { PluginOption } from 'vite'
|
||||||
import { createHtmlPlugin } from 'vite-plugin-html';
|
import { createHtmlPlugin } from 'vite-plugin-html'
|
||||||
import pkg from '../../../package.json';
|
import pkg from '../../../package.json'
|
||||||
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
|
import { GLOB_CONFIG_FILE_NAME } from '../../constant'
|
||||||
|
|
||||||
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
|
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
|
||||||
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
|
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env
|
||||||
|
|
||||||
const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
|
const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`
|
||||||
|
|
||||||
const getAppConfigSrc = () => {
|
const getAppConfigSrc = () => {
|
||||||
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
|
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`
|
||||||
};
|
}
|
||||||
|
|
||||||
// 当执行 yarn build 构建项目之后,会自动生成 _app.config.js 文件并插入 index.html
|
// 当执行 yarn build 构建项目之后,会自动生成 _app.config.js 文件并插入 index.html
|
||||||
// _app.config.js 用于项目在打包后,需要动态修改配置的需求,如接口地址
|
// _app.config.js 用于项目在打包后,需要动态修改配置的需求,如接口地址
|
||||||
@ -41,6 +41,6 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
|
|||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
return htmlPlugin;
|
return htmlPlugin
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import type { PluginOption } from 'vite';
|
import type { PluginOption } from 'vite'
|
||||||
import Components from 'unplugin-vue-components/vite';
|
import Components from 'unplugin-vue-components/vite'
|
||||||
import { VantResolver } from 'unplugin-vue-components/resolvers';
|
import { VantResolver } from 'unplugin-vue-components/resolvers'
|
||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue'
|
||||||
import UnoCSS from 'unocss/vite';
|
import UnoCSS from 'unocss/vite'
|
||||||
|
|
||||||
import { configHtmlPlugin } from './html';
|
import { configHtmlPlugin } from './html'
|
||||||
import { configMockPlugin } from './mock';
|
import { configMockPlugin } from './mock'
|
||||||
import { configCompressPlugin } from './compress';
|
import { configCompressPlugin } from './compress'
|
||||||
import { configVisualizerConfig } from './visualizer';
|
import { configVisualizerConfig } from './visualizer'
|
||||||
import { configSvgIconsPlugin } from './svgSprite';
|
import { configSvgIconsPlugin } from './svgSprite'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置 vite 插件
|
* 配置 vite 插件
|
||||||
@ -22,7 +22,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, prodMock:
|
|||||||
// 如果你需要多种形式,你可以用','来分隔
|
// 如果你需要多种形式,你可以用','来分隔
|
||||||
|
|
||||||
// VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE 打包使用压缩时是否删除原始文件,默认为 false
|
// VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE 打包使用压缩时是否删除原始文件,默认为 false
|
||||||
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
|
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv
|
||||||
|
|
||||||
const vitePlugins: (PluginOption | PluginOption[])[] = [
|
const vitePlugins: (PluginOption | PluginOption[])[] = [
|
||||||
// have to
|
// have to
|
||||||
@ -33,30 +33,30 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, prodMock:
|
|||||||
resolvers: [VantResolver()],
|
resolvers: [VantResolver()],
|
||||||
types: [],
|
types: [],
|
||||||
}),
|
}),
|
||||||
];
|
]
|
||||||
|
|
||||||
// UnoCSS
|
// UnoCSS
|
||||||
vitePlugins.push(UnoCSS());
|
vitePlugins.push(UnoCSS())
|
||||||
|
|
||||||
// 加载 html 插件 vite-plugin-html
|
// 加载 html 插件 vite-plugin-html
|
||||||
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
|
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild))
|
||||||
|
|
||||||
// rollup-plugin-visualizer
|
// rollup-plugin-visualizer
|
||||||
vitePlugins.push(configVisualizerConfig());
|
vitePlugins.push(configVisualizerConfig())
|
||||||
|
|
||||||
// vite-plugin-mock
|
// vite-plugin-mock
|
||||||
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild, prodMock));
|
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild, prodMock))
|
||||||
|
|
||||||
// vite-plugin-svg-icons
|
// vite-plugin-svg-icons
|
||||||
vitePlugins.push(configSvgIconsPlugin(isBuild));
|
vitePlugins.push(configSvgIconsPlugin(isBuild))
|
||||||
|
|
||||||
if (isBuild) {
|
if (isBuild) {
|
||||||
// rollup-plugin-gzip
|
// rollup-plugin-gzip
|
||||||
// 加载 gzip 打包
|
// 加载 gzip 打包
|
||||||
vitePlugins.push(
|
vitePlugins.push(
|
||||||
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE)
|
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return vitePlugins;
|
return vitePlugins
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Mock plugin for development and production.
|
* Mock plugin for development and production.
|
||||||
* https://github.com/anncwb/vite-plugin-mock
|
* https://github.com/anncwb/vite-plugin-mock
|
||||||
*/
|
*/
|
||||||
import { viteMockServe } from 'vite-plugin-mock';
|
import { viteMockServe } from 'vite-plugin-mock'
|
||||||
|
|
||||||
export function configMockPlugin(isBuild: boolean, prodMock: boolean) {
|
export function configMockPlugin(isBuild: boolean, prodMock: boolean) {
|
||||||
return viteMockServe({
|
return viteMockServe({
|
||||||
@ -15,5 +15,5 @@ export function configMockPlugin(isBuild: boolean, prodMock: boolean) {
|
|||||||
|
|
||||||
setupProdMockServer();
|
setupProdMockServer();
|
||||||
`,
|
`,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
* https://github.com/anncwb/vite-plugin-svg-icons
|
* https://github.com/anncwb/vite-plugin-svg-icons
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
import path from 'node:path'
|
||||||
import path from 'path';
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
|
|
||||||
export function configSvgIconsPlugin(isBuild: boolean) {
|
export function configSvgIconsPlugin(isBuild: boolean) {
|
||||||
// 指定需要缓存的图标文件夹
|
// 指定需要缓存的图标文件夹
|
||||||
@ -14,6 +14,6 @@ export function configSvgIconsPlugin(isBuild: boolean) {
|
|||||||
svgoOptions: isBuild,
|
svgoOptions: isBuild,
|
||||||
// 指定symbolId格式
|
// 指定symbolId格式
|
||||||
symbolId: 'icon-[dir]-[name]',
|
symbolId: 'icon-[dir]-[name]',
|
||||||
});
|
})
|
||||||
return svgIconsPlugin;
|
return svgIconsPlugin
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Package file volume analysis
|
* Package file volume analysis
|
||||||
*/
|
*/
|
||||||
import visualizer from 'rollup-plugin-visualizer';
|
import visualizer from 'rollup-plugin-visualizer'
|
||||||
import type { PluginOption } from 'vite';
|
import type { PluginOption } from 'vite'
|
||||||
import { isReportMode } from '../../utils';
|
import { isReportMode } from '../../utils'
|
||||||
|
|
||||||
export function configVisualizerConfig() {
|
export function configVisualizerConfig() {
|
||||||
if (isReportMode()) {
|
if (isReportMode()) {
|
||||||
@ -12,7 +12,7 @@ export function configVisualizerConfig() {
|
|||||||
open: true,
|
open: true,
|
||||||
gzipSize: true,
|
gzipSize: true,
|
||||||
brotliSize: true,
|
brotliSize: true,
|
||||||
}) as PluginOption;
|
}) as PluginOption
|
||||||
}
|
}
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
/**
|
/**
|
||||||
* Used to parse the .env.development proxy configuration
|
* Used to parse the .env.development proxy configuration
|
||||||
*/
|
*/
|
||||||
import type { ProxyOptions } from 'vite';
|
import type { ProxyOptions } from 'vite'
|
||||||
|
|
||||||
type ProxyItem = [string, string];
|
type ProxyItem = [string, string]
|
||||||
|
|
||||||
type ProxyList = ProxyItem[];
|
type ProxyList = ProxyItem[]
|
||||||
|
|
||||||
type ProxyTargetList = Record<string, ProxyOptions & { rewrite: (path: string) => string }>;
|
type ProxyTargetList = Record<string, ProxyOptions & { rewrite: (path: string) => string }>
|
||||||
|
|
||||||
const httpsRE = /^https:\/\//;
|
const httpsRE = /^https:\/\//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate proxy
|
* Generate proxy
|
||||||
* @param list
|
* @param list
|
||||||
*/
|
*/
|
||||||
export function createProxy(list: ProxyList = []) {
|
export function createProxy(list: ProxyList = []) {
|
||||||
const ret: ProxyTargetList = {};
|
const ret: ProxyTargetList = {}
|
||||||
for (const [prefix, target] of list) {
|
for (const [prefix, target] of list) {
|
||||||
const isHttps = httpsRE.test(target);
|
const isHttps = httpsRE.test(target)
|
||||||
|
|
||||||
// https://github.com/http-party/node-http-proxy#options
|
// https://github.com/http-party/node-http-proxy#options
|
||||||
ret[prefix] = {
|
ret[prefix] = {
|
||||||
target: target,
|
target,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
ws: true,
|
ws: true,
|
||||||
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
|
rewrite: path => path.replace(new RegExp(`^${prefix}`), ''),
|
||||||
// https is require secure=false
|
// https is require secure=false
|
||||||
// 如果您secure="true"只允许来自 HTTPS 的请求,则secure="false"意味着允许来自 HTTP 和 HTTPS 的请求。
|
// 如果您secure="true"只允许来自 HTTPS 的请求,则secure="false"意味着允许来自 HTTP 和 HTTPS 的请求。
|
||||||
...(isHttps ? { secure: false } : {}),
|
...(isHttps ? { secure: false } : {}),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret
|
||||||
|
|
||||||
// ret
|
// ret
|
||||||
// {
|
// {
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
// commitlint.config.js
|
// commitlint.config.js
|
||||||
const fs = require('fs');
|
const fs = require('node:fs')
|
||||||
const path = require('path');
|
const path = require('node:path')
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('node:child_process')
|
||||||
|
|
||||||
const scopes = fs
|
const scopes = fs
|
||||||
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
|
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
|
||||||
.filter((dirent) => dirent.isDirectory())
|
.filter(dirent => dirent.isDirectory())
|
||||||
.map((dirent) => dirent.name.replace(/s$/, ''));
|
.map(dirent => dirent.name.replace(/s$/, ''))
|
||||||
|
|
||||||
// precomputed scope
|
// precomputed scope
|
||||||
const scopeComplete = execSync('git status --porcelain || true')
|
const scopeComplete = execSync('git status --porcelain || true')
|
||||||
.toString()
|
.toString()
|
||||||
.trim()
|
.trim()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.find((r) => ~r.indexOf('M src'))
|
.find(r => ~r.indexOf('M src'))
|
||||||
?.replace(/(\/)/g, '%%')
|
?.replace(/(\/)/g, '%%')
|
||||||
?.match(/src%%((\w|-)*)/)?.[1]
|
?.match(/src%%((\w|-)*)/)?.[1]
|
||||||
?.replace(/s$/, '');
|
?.replace(/s$/, '')
|
||||||
|
|
||||||
/** @type {import('cz-git').UserConfig} */
|
/** @type {import('cz-git').UserConfig} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ignores: [(commit) => commit.includes('init')],
|
ignores: [commit => commit.includes('init')],
|
||||||
extends: ['@commitlint/config-conventional'],
|
extends: ['@commitlint/config-conventional'],
|
||||||
rules: {
|
rules: {
|
||||||
'body-leading-blank': [2, 'always'],
|
'body-leading-blank': [2, 'always'],
|
||||||
@ -144,12 +144,12 @@ module.exports = {
|
|||||||
emptyIssuePrefixsAlias: 'skip',
|
emptyIssuePrefixsAlias: 'skip',
|
||||||
customIssuePrefixsAlias: 'custom',
|
customIssuePrefixsAlias: 'custom',
|
||||||
confirmColorize: true,
|
confirmColorize: true,
|
||||||
maxHeaderLength: Infinity,
|
maxHeaderLength: Number.POSITIVE_INFINITY,
|
||||||
maxSubjectLength: Infinity,
|
maxSubjectLength: Number.POSITIVE_INFINITY,
|
||||||
minSubjectLength: 0,
|
minSubjectLength: 0,
|
||||||
scopeOverrides: undefined,
|
scopeOverrides: undefined,
|
||||||
defaultBody: '',
|
defaultBody: '',
|
||||||
defaultIssues: '',
|
defaultIssues: '',
|
||||||
defaultSubject: '',
|
defaultSubject: '',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
55
index.html
55
index.html
@ -1,27 +1,26 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="zh-cmn-Hans" id="htmlRoot" class>
|
<html lang="zh-cmn-Hans" id="htmlRoot" class>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>
|
<title><%= title %></title>
|
||||||
<%= title %>
|
|
||||||
</title>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<script>
|
<script>
|
||||||
(() => {
|
;(() => {
|
||||||
let htmlRoot = document.getElementById('htmlRoot');
|
let htmlRoot = document.getElementById('htmlRoot')
|
||||||
const appDesignSetting = window.localStorage.getItem('DESIGN-SETTING')
|
const appDesignSetting = window.localStorage.getItem('DESIGN-SETTING')
|
||||||
let darkMode = appDesignSetting && JSON.parse(appDesignSetting).darkMode
|
let darkMode =
|
||||||
|
appDesignSetting && JSON.parse(appDesignSetting).darkMode
|
||||||
if (htmlRoot && darkMode) {
|
if (htmlRoot && darkMode) {
|
||||||
htmlRoot.setAttribute('data-theme', darkMode);
|
htmlRoot.setAttribute('data-theme', darkMode)
|
||||||
darkMode = htmlRoot = null;
|
darkMode = htmlRoot = null
|
||||||
} else {
|
} else {
|
||||||
htmlRoot.setAttribute('data-theme', 'light');
|
htmlRoot.setAttribute('data-theme', 'light')
|
||||||
}
|
}
|
||||||
})();
|
})()
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
@ -38,14 +37,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.first-loading-wrap>h1 {
|
.first-loading-wrap > h1 {
|
||||||
font-size: 128px
|
font-size: 128px;
|
||||||
}
|
}
|
||||||
.first-loading-wrap .loading-wrap {
|
.first-loading-wrap .loading-wrap {
|
||||||
padding: 98px;
|
padding: 98px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center
|
align-items: center;
|
||||||
}
|
}
|
||||||
.dot {
|
.dot {
|
||||||
animation: antRotate 1.2s infinite linear;
|
animation: antRotate 1.2s infinite linear;
|
||||||
@ -55,7 +54,7 @@
|
|||||||
font-size: 52px;
|
font-size: 52px;
|
||||||
width: 52px;
|
width: 52px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
box-sizing: border-box
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.dot i {
|
.dot i {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
@ -64,53 +63,53 @@
|
|||||||
display: block;
|
display: block;
|
||||||
background-color: #1890ff;
|
background-color: #1890ff;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
transform: scale(.75);
|
transform: scale(0.75);
|
||||||
transform-origin: 50% 50%;
|
transform-origin: 50% 50%;
|
||||||
opacity: .3;
|
opacity: 0.3;
|
||||||
animation: antSpinMove 1s infinite linear alternate
|
animation: antSpinMove 1s infinite linear alternate;
|
||||||
}
|
}
|
||||||
.dot i:nth-child(1) {
|
.dot i:nth-child(1) {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0
|
left: 0;
|
||||||
}
|
}
|
||||||
.dot i:nth-child(2) {
|
.dot i:nth-child(2) {
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
-webkit-animation-delay: .4s;
|
-webkit-animation-delay: 0.4s;
|
||||||
animation-delay: .4s
|
animation-delay: 0.4s;
|
||||||
}
|
}
|
||||||
.dot i:nth-child(3) {
|
.dot i:nth-child(3) {
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
-webkit-animation-delay: .8s;
|
-webkit-animation-delay: 0.8s;
|
||||||
animation-delay: .8s
|
animation-delay: 0.8s;
|
||||||
}
|
}
|
||||||
.dot i:nth-child(4) {
|
.dot i:nth-child(4) {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
-webkit-animation-delay: 1.2s;
|
-webkit-animation-delay: 1.2s;
|
||||||
animation-delay: 1.2s
|
animation-delay: 1.2s;
|
||||||
}
|
}
|
||||||
@keyframes antRotate {
|
@keyframes antRotate {
|
||||||
to {
|
to {
|
||||||
-webkit-transform: rotate(405deg);
|
-webkit-transform: rotate(405deg);
|
||||||
transform: rotate(405deg)
|
transform: rotate(405deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-webkit-keyframes antRotate {
|
@-webkit-keyframes antRotate {
|
||||||
to {
|
to {
|
||||||
-webkit-transform: rotate(405deg);
|
-webkit-transform: rotate(405deg);
|
||||||
transform: rotate(405deg)
|
transform: rotate(405deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@keyframes antSpinMove {
|
@keyframes antSpinMove {
|
||||||
to {
|
to {
|
||||||
opacity: 1
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-webkit-keyframes antSpinMove {
|
@-webkit-keyframes antSpinMove {
|
||||||
to {
|
to {
|
||||||
opacity: 1
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
|
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
|
||||||
|
|
||||||
const modules = import.meta.glob('./**/*.ts', { eager: true }) as any;
|
const modules = import.meta.glob('./**/*.ts', { eager: true }) as any
|
||||||
|
|
||||||
const mockModules: any[] = [];
|
const mockModules: any[] = []
|
||||||
Object.keys(modules).forEach((key) => {
|
Object.keys(modules).forEach((key) => {
|
||||||
if (key.includes('/_')) {
|
if (key.includes('/_')) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
mockModules.push(...modules[key].default);
|
mockModules.push(...modules[key].default)
|
||||||
});
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in a production environment. Need to manually import all modules
|
* Used in a production environment. Need to manually import all modules
|
||||||
*/
|
*/
|
||||||
export function setupProdMockServer() {
|
export function setupProdMockServer() {
|
||||||
createProdMockServer(mockModules);
|
createProdMockServer(mockModules)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Mock from 'mockjs';
|
import Mock from 'mockjs'
|
||||||
import { ResultEnum } from '@/enums/httpEnum';
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
|
|
||||||
export function resultSuccess<T = Recordable>(result: T, { message = 'ok' } = {}) {
|
export function resultSuccess<T = Recordable>(result: T, { message = 'ok' } = {}) {
|
||||||
return Mock.mock({
|
return Mock.mock({
|
||||||
@ -7,16 +7,16 @@ export function resultSuccess<T = Recordable>(result: T, { message = 'ok' } = {}
|
|||||||
result,
|
result,
|
||||||
message,
|
message,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resultPageSuccess<T = any>(
|
export function resultPageSuccess<T = any>(
|
||||||
page: number,
|
page: number,
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
list: T[],
|
list: T[],
|
||||||
{ message = 'ok' } = {}
|
{ message = 'ok' } = {},
|
||||||
) {
|
) {
|
||||||
const pageData = pagination(page, pageSize, list);
|
const pageData = pagination(page, pageSize, list)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...resultSuccess({
|
...resultSuccess({
|
||||||
@ -26,46 +26,46 @@ export function resultPageSuccess<T = any>(
|
|||||||
list: pageData,
|
list: pageData,
|
||||||
}),
|
}),
|
||||||
message,
|
message,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resultError(
|
export function resultError(
|
||||||
message = 'Request failed',
|
message = 'Request failed',
|
||||||
{ code = ResultEnum.ERROR, result = null } = {}
|
{ code = ResultEnum.ERROR, result = null } = {},
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
result,
|
result,
|
||||||
message,
|
message,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]): T[] {
|
export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]): T[] {
|
||||||
const offset = (pageNo - 1) * Number(pageSize);
|
const offset = (pageNo - 1) * Number(pageSize)
|
||||||
const ret =
|
const ret
|
||||||
offset + Number(pageSize) >= array.length
|
= offset + Number(pageSize) >= array.length
|
||||||
? array.slice(offset, array.length)
|
? array.slice(offset, array.length)
|
||||||
: array.slice(offset, offset + Number(pageSize));
|
: array.slice(offset, offset + Number(pageSize))
|
||||||
return ret;
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Number} times 回调函数需要执行的次数
|
* @param {number} times 回调函数需要执行的次数
|
||||||
* @param {Function} callback 回调函数
|
* @param {Function} callback 回调函数
|
||||||
*/
|
*/
|
||||||
export function doCustomTimes(times: number, callback: any) {
|
export function doCustomTimes(times: number, callback: any) {
|
||||||
let i = -1;
|
let i = -1
|
||||||
while (++i < times) {
|
while (++i < times) {
|
||||||
callback(i);
|
callback(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface requestParams {
|
export interface requestParams {
|
||||||
method: string;
|
method: string
|
||||||
body: any;
|
body: any
|
||||||
headers?: { authorization?: string };
|
headers?: { authorization?: string }
|
||||||
query: any;
|
query: any
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,5 +73,5 @@ export interface requestParams {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function getRequestToken({ headers }: requestParams): string | undefined {
|
export function getRequestToken({ headers }: requestParams): string | undefined {
|
||||||
return headers?.authorization;
|
return headers?.authorization
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MockMethod } from 'vite-plugin-mock';
|
import type { MockMethod } from 'vite-plugin-mock'
|
||||||
import { getRequestToken, requestParams, resultError, resultSuccess } from '../_util';
|
import type { requestParams } from '../_util'
|
||||||
import { ResultEnum } from '@/enums/httpEnum';
|
import { getRequestToken, resultError, resultSuccess } from '../_util'
|
||||||
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
|
|
||||||
const fakeUserList = [
|
const fakeUserList = [
|
||||||
{
|
{
|
||||||
@ -32,7 +33,7 @@ const fakeUserList = [
|
|||||||
phone: '18822137893',
|
phone: '18822137893',
|
||||||
token: 'fakeToken2',
|
token: 'fakeToken2',
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
@ -40,21 +41,21 @@ export default [
|
|||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
response: ({ body }) => {
|
response: ({ body }) => {
|
||||||
const { username, password } = body;
|
const { username, password } = body
|
||||||
const checkUser = fakeUserList.find(
|
const checkUser = fakeUserList.find(
|
||||||
(item) => item.username === username && password === item.password
|
item => item.username === username && password === item.password,
|
||||||
);
|
)
|
||||||
if (!checkUser) {
|
if (!checkUser) {
|
||||||
return resultError('帐号或密码不正确');
|
return resultError('帐号或密码不正确')
|
||||||
}
|
}
|
||||||
const { userId, username: _username, token, realname, sign } = checkUser;
|
const { userId, username: _username, token, realname, sign } = checkUser
|
||||||
return resultSuccess({
|
return resultSuccess({
|
||||||
userId,
|
userId,
|
||||||
username: _username,
|
username: _username,
|
||||||
token,
|
token,
|
||||||
realname,
|
realname,
|
||||||
sign,
|
sign,
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -62,15 +63,17 @@ export default [
|
|||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
response: (request: requestParams) => {
|
response: (request: requestParams) => {
|
||||||
const token = getRequestToken(request);
|
const token = getRequestToken(request)
|
||||||
if (!token) return resultError('无效令牌');
|
if (!token) {
|
||||||
const checkUser = fakeUserList.find((item) => item.token === token);
|
return resultError('无效令牌')
|
||||||
|
}
|
||||||
|
const checkUser = fakeUserList.find(item => item.token === token)
|
||||||
if (!checkUser) {
|
if (!checkUser) {
|
||||||
return resultError('没有获取到对应的用户信息', {
|
return resultError('没有获取到对应的用户信息', {
|
||||||
code: ResultEnum.TOKEN_EXPIRED,
|
code: ResultEnum.TOKEN_EXPIRED,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
return resultSuccess(checkUser);
|
return resultSuccess(checkUser)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -78,13 +81,15 @@ export default [
|
|||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
response: (request: requestParams) => {
|
response: (request: requestParams) => {
|
||||||
const token = getRequestToken(request);
|
const token = getRequestToken(request)
|
||||||
if (!token) return resultError('无效令牌');
|
if (!token) {
|
||||||
const checkUser = fakeUserList.find((item) => item.token === token);
|
return resultError('无效令牌')
|
||||||
if (!checkUser) {
|
|
||||||
return resultError('无效令牌');
|
|
||||||
}
|
}
|
||||||
return resultSuccess(undefined, { message: '令牌已被销毁' });
|
const checkUser = fakeUserList.find(item => item.token === token)
|
||||||
|
if (!checkUser) {
|
||||||
|
return resultError('无效令牌')
|
||||||
|
}
|
||||||
|
return resultSuccess(undefined, { message: '令牌已被销毁' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as MockMethod[];
|
] as MockMethod[]
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
// 所有*.js文件现在都解释为 ESM,并且需要使用 ESM 语法。您可以使用扩展名重命名文件.cjs来继续使用 CJS。
|
// 所有*.js文件现在都解释为 ESM,并且需要使用 ESM 语法。您可以使用扩展名重命名文件.cjs来继续使用 CJS。
|
||||||
// require 是cjs 语法
|
// require 是cjs 语法
|
||||||
|
|
||||||
const autoprefixer = require('autoprefixer');
|
import autoprefixer from 'autoprefixer'
|
||||||
const viewport = require('postcss-mobile-forever');
|
import viewport from 'postcss-mobile-forever'
|
||||||
|
|
||||||
const baseViewportOpts = {
|
const baseViewportOpts = {
|
||||||
appSelector: '#app', // 根元素选择器,用于设置桌面端和横屏时的居中样式
|
appSelector: '#app', // 根元素选择器,用于设置桌面端和横屏时的居中样式
|
||||||
@ -37,16 +37,16 @@ const baseViewportOpts = {
|
|||||||
// exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件
|
// exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件
|
||||||
// include: [/src/], // 如果设置了include,那将只有匹配到的文件才会被转换
|
// include: [/src/], // 如果设置了include,那将只有匹配到的文件才会被转换
|
||||||
mobileUnit: 'vw', // 指定需要转换成的视口单位,建议使用 vw
|
mobileUnit: 'vw', // 指定需要转换成的视口单位,建议使用 vw
|
||||||
rootContainingBlockSelectorList: ["van-popup--bottom"], // 指定包含块是根包含块的选择器,这种选择器的定位通常是 `fixed`,但是选择器内没有 `position: fixed`
|
rootContainingBlockSelectorList: ['van-popup--bottom'], // 指定包含块是根包含块的选择器,这种选择器的定位通常是 `fixed`,但是选择器内没有 `position: fixed`
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
plugins: [
|
plugins: [
|
||||||
autoprefixer(),
|
autoprefixer(),
|
||||||
viewport({
|
viewport({
|
||||||
...baseViewportOpts,
|
...baseViewportOpts,
|
||||||
// 只将 vant 转为 350 设计稿的 viewport,其它样式的视图宽度为 750
|
// 只将 vant 转为 350 设计稿的 viewport,其它样式的视图宽度为 750
|
||||||
viewportWidth: (file) => (file.includes('node_modules/vant/') ? 375 : 750),
|
viewportWidth: file => (file.includes('node_modules/vant/') ? 375 : 750),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
}
|
108
src/App.vue
108
src/App.vue
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<vanConfigProvider :theme="getDarkMode" :theme-vars="getThemeVars()">
|
<vanConfigProvider :theme="getDarkMode" :theme-vars="getThemeVars()">
|
||||||
<routerView v-slot="{ Component }">
|
<routerView v-slot="{ Component }">
|
||||||
<div class="absolute top-0 bottom-0 w-full overflow-hidden">
|
<div class="absolute bottom-0 top-0 w-full overflow-hidden">
|
||||||
<transition :name="getTransitionName" mode="out-in" appear>
|
<transition :name="getTransitionName" mode="out-in" appear>
|
||||||
<keep-alive v-if="keepAliveComponents" :include="keepAliveComponents">
|
<keep-alive v-if="keepAliveComponents" :include="keepAliveComponents">
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
@ -13,64 +13,64 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, unref } from 'vue';
|
import { computed, unref } from 'vue'
|
||||||
import { darken, lighten } from '@/utils/index';
|
import { darken, lighten } from '@/utils/index'
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route'
|
||||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting'
|
||||||
|
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore()
|
||||||
const { getDarkMode, getAppTheme, getIsPageAnimate, getPageAnimateType } = useDesignSetting();
|
const { getDarkMode, getAppTheme, getIsPageAnimate, getPageAnimateType } = useDesignSetting()
|
||||||
|
|
||||||
// 需要缓存的路由组件
|
// 需要缓存的路由组件
|
||||||
const keepAliveComponents = computed(() => routeStore.keepAliveComponents);
|
const keepAliveComponents = computed(() => routeStore.keepAliveComponents)
|
||||||
|
|
||||||
const getThemeVars = () => {
|
function getThemeVars() {
|
||||||
const appTheme = unref(getAppTheme);
|
const appTheme = unref(getAppTheme)
|
||||||
const darkenStr = darken(appTheme, 25);
|
const darkenStr = darken(appTheme, 25)
|
||||||
const lightenStr = lighten(appTheme, 10);
|
const lightenStr = lighten(appTheme, 10)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
actionSheetCancelTextColor: appTheme,
|
actionSheetCancelTextColor: appTheme,
|
||||||
buttonPrimaryBackground: appTheme,
|
buttonPrimaryBackground: appTheme,
|
||||||
buttonPrimaryBorderColor: appTheme,
|
buttonPrimaryBorderColor: appTheme,
|
||||||
radioCheckedIconColor: appTheme,
|
radioCheckedIconColor: appTheme,
|
||||||
sliderActiveBackground: appTheme,
|
sliderActiveBackground: appTheme,
|
||||||
cascaderActiveColor: appTheme,
|
cascaderActiveColor: appTheme,
|
||||||
checkboxCheckedIconColor: appTheme,
|
checkboxCheckedIconColor: appTheme,
|
||||||
numberKeyboardButtonBackground: appTheme,
|
numberKeyboardButtonBackground: appTheme,
|
||||||
pickerLoadingIconColor: appTheme,
|
pickerLoadingIconColor: appTheme,
|
||||||
calendarRangeEdgeBackground: appTheme,
|
calendarRangeEdgeBackground: appTheme,
|
||||||
calendarRangeMiddleColor: appTheme,
|
calendarRangeMiddleColor: appTheme,
|
||||||
calendarSelectedDayBackground: appTheme,
|
calendarSelectedDayBackground: appTheme,
|
||||||
stepperButtonRoundThemeColor: appTheme,
|
stepperButtonRoundThemeColor: appTheme,
|
||||||
switchOnBackground: appTheme,
|
switchOnBackground: appTheme,
|
||||||
dialogConfirmButtonTextColor: appTheme,
|
dialogConfirmButtonTextColor: appTheme,
|
||||||
dropdownMenuOptionActiveColor: appTheme,
|
dropdownMenuOptionActiveColor: appTheme,
|
||||||
dropdownMenuTitleActiveTextColor: appTheme,
|
dropdownMenuTitleActiveTextColor: appTheme,
|
||||||
notifyPrimaryBackground: appTheme,
|
notifyPrimaryBackground: appTheme,
|
||||||
circleColor: appTheme,
|
circleColor: appTheme,
|
||||||
noticeBarBackground: lightenStr,
|
noticeBarBackground: lightenStr,
|
||||||
noticeBarTextColor: darkenStr,
|
noticeBarTextColor: darkenStr,
|
||||||
progressColor: appTheme,
|
progressColor: appTheme,
|
||||||
progressPivotBackground: appTheme,
|
progressPivotBackground: appTheme,
|
||||||
stepActiveColor: appTheme,
|
stepActiveColor: appTheme,
|
||||||
stepFinishLineColor: appTheme,
|
stepFinishLineColor: appTheme,
|
||||||
swipeIndicatorActiveBackground: appTheme,
|
swipeIndicatorActiveBackground: appTheme,
|
||||||
tagPrimaryColor: appTheme,
|
tagPrimaryColor: appTheme,
|
||||||
navBarIconColor: appTheme,
|
navBarIconColor: appTheme,
|
||||||
navBarTextColor: appTheme,
|
navBarTextColor: appTheme,
|
||||||
paginationItemDefaultColor: appTheme,
|
paginationItemDefaultColor: appTheme,
|
||||||
sidebarSelectedBorderColor: appTheme,
|
sidebarSelectedBorderColor: appTheme,
|
||||||
tabsDefaultColor: appTheme,
|
tabsDefaultColor: appTheme,
|
||||||
tabsBottomBarColor: appTheme,
|
tabsBottomBarColor: appTheme,
|
||||||
tabbarItemActiveColor: appTheme,
|
tabbarItemActiveColor: appTheme,
|
||||||
treeSelectItemActiveColor: appTheme,
|
treeSelectItemActiveColor: appTheme,
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const getTransitionName = computed(() => {
|
const getTransitionName = computed(() => {
|
||||||
return unref(getIsPageAnimate) ? unref(getPageAnimateType) : undefined;
|
return unref(getIsPageAnimate) ? unref(getPageAnimateType) : undefined
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { http } from '@/utils/http/axios';
|
import { http } from '@/utils/http/axios'
|
||||||
|
|
||||||
export interface BasicResponseModel<T = any> {
|
export interface BasicResponseModel<T = any> {
|
||||||
code: number;
|
code: number
|
||||||
message: string;
|
message: string
|
||||||
result: T;
|
result: T
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,8 +18,8 @@ export function login(params: any) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
isTransformResponse: false,
|
isTransformResponse: false,
|
||||||
}
|
},
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,7 +29,7 @@ export function getUserInfo() {
|
|||||||
return http.request({
|
return http.request({
|
||||||
url: '/getUserInfo',
|
url: '/getUserInfo',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +39,7 @@ export function doLogout() {
|
|||||||
return http.request({
|
return http.request({
|
||||||
url: '/logout',
|
url: '/logout',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,6 +54,6 @@ export function changePassword(params: any, uid: any) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
isTransformResponse: false,
|
isTransformResponse: false,
|
||||||
}
|
},
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,44 +5,45 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue'
|
||||||
import { defineComponent, computed } from 'vue';
|
import { computed, defineComponent } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SvgIcon',
|
name: 'SvgIcon',
|
||||||
props: {
|
props: {
|
||||||
prefix: {
|
prefix: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'icon',
|
default: 'icon',
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: 16,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: '#333',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
setup(props) {
|
name: {
|
||||||
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
|
type: String,
|
||||||
|
required: true,
|
||||||
const getStyle = computed((): CSSProperties => {
|
|
||||||
const { size } = props;
|
|
||||||
let s = `${size}`;
|
|
||||||
s = `${s.replace('px', '')}px`;
|
|
||||||
return {
|
|
||||||
width: s,
|
|
||||||
height: s,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return { symbolId, getStyle };
|
|
||||||
},
|
},
|
||||||
});
|
size: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 16,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#333',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
|
||||||
|
|
||||||
|
const getStyle = computed((): CSSProperties => {
|
||||||
|
const { size } = props
|
||||||
|
let s = `${size}`
|
||||||
|
s = `${s.replace('px', '')}px`
|
||||||
|
return {
|
||||||
|
width: s,
|
||||||
|
height: s,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { symbolId, getStyle }
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -16,13 +16,13 @@ export enum screenEnum {
|
|||||||
XXL = 1600,
|
XXL = 1600,
|
||||||
}
|
}
|
||||||
|
|
||||||
const screenMap = new Map<sizeEnum, number>();
|
const screenMap = new Map<sizeEnum, number>()
|
||||||
|
|
||||||
screenMap.set(sizeEnum.XS, screenEnum.XS);
|
screenMap.set(sizeEnum.XS, screenEnum.XS)
|
||||||
screenMap.set(sizeEnum.SM, screenEnum.SM);
|
screenMap.set(sizeEnum.SM, screenEnum.SM)
|
||||||
screenMap.set(sizeEnum.MD, screenEnum.MD);
|
screenMap.set(sizeEnum.MD, screenEnum.MD)
|
||||||
screenMap.set(sizeEnum.LG, screenEnum.LG);
|
screenMap.set(sizeEnum.LG, screenEnum.LG)
|
||||||
screenMap.set(sizeEnum.XL, screenEnum.XL);
|
screenMap.set(sizeEnum.XL, screenEnum.XL)
|
||||||
screenMap.set(sizeEnum.XXL, screenEnum.XXL);
|
screenMap.set(sizeEnum.XXL, screenEnum.XXL)
|
||||||
|
|
||||||
export { screenMap };
|
export { screenMap }
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
// token key
|
// token key
|
||||||
export const TOKEN_KEY = 'TOKEN';
|
export const TOKEN_KEY = 'TOKEN'
|
||||||
|
|
||||||
// user info key
|
// user info key
|
||||||
export const USER_INFO_KEY = 'USER__INFO__';
|
export const USER_INFO_KEY = 'USER__INFO__'
|
||||||
|
|
||||||
// role info key
|
// role info key
|
||||||
export const ROLES_KEY = 'ROLES__KEY__';
|
export const ROLES_KEY = 'ROLES__KEY__'
|
||||||
|
|
||||||
// base global local key
|
// base global local key
|
||||||
export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__';
|
export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__'
|
||||||
|
|
||||||
// base global session key
|
// base global session key
|
||||||
export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__';
|
export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__'
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable ts/no-duplicate-enum-values */
|
||||||
export enum PageEnum {
|
export enum PageEnum {
|
||||||
// 登录
|
// 登录
|
||||||
BASE_LOGIN = '/login',
|
BASE_LOGIN = '/login',
|
||||||
|
@ -1,47 +1,48 @@
|
|||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue'
|
||||||
import { tryOnUnmounted } from '@vueuse/core';
|
import { tryOnUnmounted } from '@vueuse/core'
|
||||||
import { isFunction } from '@/utils/is';
|
import { isFunction } from '@/utils/is'
|
||||||
|
|
||||||
export function useTimeoutFn(handle: Fn<any>, wait: number, native = false) {
|
export function useTimeoutFn(handle: Fn<any>, wait: number, native = false) {
|
||||||
if (!isFunction(handle)) {
|
if (!isFunction(handle)) {
|
||||||
throw new Error('handle is not Function!');
|
throw new Error('handle is not Function!')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { readyRef, stop, start } = useTimeoutRef(wait);
|
const { readyRef, stop, start } = useTimeoutRef(wait)
|
||||||
if (native) {
|
if (native) {
|
||||||
handle();
|
handle()
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
watch(
|
watch(
|
||||||
readyRef,
|
readyRef,
|
||||||
(maturity) => {
|
(maturity) => {
|
||||||
maturity && handle();
|
maturity && handle()
|
||||||
},
|
},
|
||||||
{ immediate: false }
|
{ immediate: false },
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
return { readyRef, stop, start };
|
return { readyRef, stop, start }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTimeoutRef(wait: number) {
|
export function useTimeoutRef(wait: number) {
|
||||||
const readyRef = ref(false);
|
const readyRef = ref(false)
|
||||||
|
|
||||||
let timer: TimeoutHandle;
|
let timer: TimeoutHandle
|
||||||
|
|
||||||
function stop(): void {
|
function stop(): void {
|
||||||
readyRef.value = false;
|
readyRef.value = false
|
||||||
timer && window.clearTimeout(timer);
|
timer && window.clearTimeout(timer)
|
||||||
}
|
}
|
||||||
|
|
||||||
function start(): void {
|
function start(): void {
|
||||||
stop();
|
stop()
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
readyRef.value = true;
|
readyRef.value = true
|
||||||
}, wait);
|
}, wait)
|
||||||
}
|
}
|
||||||
|
|
||||||
start();
|
start()
|
||||||
|
|
||||||
tryOnUnmounted(stop);
|
tryOnUnmounted(stop)
|
||||||
|
|
||||||
return { readyRef, stop, start };
|
return { readyRef, stop, start }
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import { ref, computed, ComputedRef, unref } from 'vue';
|
import type { ComputedRef } from 'vue'
|
||||||
import { useEventListener } from '@/hooks/event/useEventListener';
|
import { computed, ref, unref } from 'vue'
|
||||||
import { screenMap, sizeEnum, screenEnum } from '@/enums/breakpointEnum';
|
import { useEventListener } from '@/hooks/event/useEventListener'
|
||||||
|
import { screenEnum, screenMap, sizeEnum } from '@/enums/breakpointEnum'
|
||||||
|
|
||||||
let globalScreenRef: ComputedRef<sizeEnum | undefined>;
|
let globalScreenRef: ComputedRef<sizeEnum | undefined>
|
||||||
let globalWidthRef: ComputedRef<number>;
|
let globalWidthRef: ComputedRef<number>
|
||||||
let globalRealWidthRef: ComputedRef<number>;
|
let globalRealWidthRef: ComputedRef<number>
|
||||||
|
|
||||||
export interface CreateCallbackParams {
|
export interface CreateCallbackParams {
|
||||||
screen: ComputedRef<sizeEnum | undefined>;
|
screen: ComputedRef<sizeEnum | undefined>
|
||||||
width: ComputedRef<number>;
|
width: ComputedRef<number>
|
||||||
realWidth: ComputedRef<number>;
|
realWidth: ComputedRef<number>
|
||||||
screenEnum: typeof screenEnum;
|
screenEnum: typeof screenEnum
|
||||||
screenMap: Map<sizeEnum, number>;
|
screenMap: Map<sizeEnum, number>
|
||||||
sizeEnum: typeof sizeEnum;
|
sizeEnum: typeof sizeEnum
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBreakpoint() {
|
export function useBreakpoint() {
|
||||||
@ -21,35 +22,40 @@ export function useBreakpoint() {
|
|||||||
widthRef: globalWidthRef,
|
widthRef: globalWidthRef,
|
||||||
screenEnum,
|
screenEnum,
|
||||||
realWidthRef: globalRealWidthRef,
|
realWidthRef: globalRealWidthRef,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just call it once
|
// Just call it once
|
||||||
export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void) {
|
export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void) {
|
||||||
const screenRef = ref<sizeEnum>(sizeEnum.XL);
|
const screenRef = ref<sizeEnum>(sizeEnum.XL)
|
||||||
const realWidthRef = ref(window.innerWidth);
|
const realWidthRef = ref(window.innerWidth)
|
||||||
|
|
||||||
function getWindowWidth() {
|
function getWindowWidth() {
|
||||||
const width = document.body.clientWidth;
|
const width = document.body.clientWidth
|
||||||
const xs = screenMap.get(sizeEnum.XS)!;
|
const xs = screenMap.get(sizeEnum.XS)!
|
||||||
const sm = screenMap.get(sizeEnum.SM)!;
|
const sm = screenMap.get(sizeEnum.SM)!
|
||||||
const md = screenMap.get(sizeEnum.MD)!;
|
const md = screenMap.get(sizeEnum.MD)!
|
||||||
const lg = screenMap.get(sizeEnum.LG)!;
|
const lg = screenMap.get(sizeEnum.LG)!
|
||||||
const xl = screenMap.get(sizeEnum.XL)!;
|
const xl = screenMap.get(sizeEnum.XL)!
|
||||||
if (width < xs) {
|
if (width < xs) {
|
||||||
screenRef.value = sizeEnum.XS;
|
screenRef.value = sizeEnum.XS
|
||||||
} else if (width < sm) {
|
|
||||||
screenRef.value = sizeEnum.SM;
|
|
||||||
} else if (width < md) {
|
|
||||||
screenRef.value = sizeEnum.MD;
|
|
||||||
} else if (width < lg) {
|
|
||||||
screenRef.value = sizeEnum.LG;
|
|
||||||
} else if (width < xl) {
|
|
||||||
screenRef.value = sizeEnum.XL;
|
|
||||||
} else {
|
|
||||||
screenRef.value = sizeEnum.XXL;
|
|
||||||
}
|
}
|
||||||
realWidthRef.value = width;
|
else if (width < sm) {
|
||||||
|
screenRef.value = sizeEnum.SM
|
||||||
|
}
|
||||||
|
else if (width < md) {
|
||||||
|
screenRef.value = sizeEnum.MD
|
||||||
|
}
|
||||||
|
else if (width < lg) {
|
||||||
|
screenRef.value = sizeEnum.LG
|
||||||
|
}
|
||||||
|
else if (width < xl) {
|
||||||
|
screenRef.value = sizeEnum.XL
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
screenRef.value = sizeEnum.XXL
|
||||||
|
}
|
||||||
|
realWidthRef.value = width
|
||||||
}
|
}
|
||||||
|
|
||||||
useEventListener({
|
useEventListener({
|
||||||
@ -57,16 +63,16 @@ export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void)
|
|||||||
name: 'resize',
|
name: 'resize',
|
||||||
|
|
||||||
listener: () => {
|
listener: () => {
|
||||||
getWindowWidth();
|
getWindowWidth()
|
||||||
resizeFn();
|
resizeFn()
|
||||||
},
|
},
|
||||||
// wait: 100,
|
// wait: 100,
|
||||||
});
|
})
|
||||||
|
|
||||||
getWindowWidth();
|
getWindowWidth()
|
||||||
globalScreenRef = computed(() => unref(screenRef));
|
globalScreenRef = computed(() => unref(screenRef))
|
||||||
globalWidthRef = computed((): number => screenMap.get(unref(screenRef)!)!);
|
globalWidthRef = computed((): number => screenMap.get(unref(screenRef)!)!)
|
||||||
globalRealWidthRef = computed((): number => unref(realWidthRef));
|
globalRealWidthRef = computed((): number => unref(realWidthRef))
|
||||||
|
|
||||||
function resizeFn() {
|
function resizeFn() {
|
||||||
fn?.({
|
fn?.({
|
||||||
@ -76,14 +82,14 @@ export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void)
|
|||||||
screenEnum,
|
screenEnum,
|
||||||
screenMap,
|
screenMap,
|
||||||
sizeEnum,
|
sizeEnum,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeFn();
|
resizeFn()
|
||||||
return {
|
return {
|
||||||
screenRef: globalScreenRef,
|
screenRef: globalScreenRef,
|
||||||
screenEnum,
|
screenEnum,
|
||||||
widthRef: globalWidthRef,
|
widthRef: globalWidthRef,
|
||||||
realWidthRef: globalRealWidthRef,
|
realWidthRef: globalRealWidthRef,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
import { ref, watch, unref } from 'vue';
|
import { ref, unref, watch } from 'vue'
|
||||||
import { useThrottleFn, useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn, useThrottleFn } from '@vueuse/core'
|
||||||
|
|
||||||
export type RemoveEventFn = () => void;
|
export type RemoveEventFn = () => void
|
||||||
|
|
||||||
export interface UseEventParams {
|
export interface UseEventParams {
|
||||||
el?: Element | Ref<Element | undefined> | Window | any;
|
el?: Element | Ref<Element | undefined> | Window | any
|
||||||
name: string;
|
name: string
|
||||||
listener: EventListener;
|
listener: EventListener
|
||||||
options?: boolean | AddEventListenerOptions;
|
options?: boolean | AddEventListenerOptions
|
||||||
autoRemove?: boolean;
|
autoRemove?: boolean
|
||||||
isDebounce?: boolean;
|
isDebounce?: boolean
|
||||||
wait?: number;
|
wait?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useEventListener({
|
export function useEventListener({
|
||||||
@ -24,39 +24,38 @@ export function useEventListener({
|
|||||||
isDebounce = true,
|
isDebounce = true,
|
||||||
wait = 80,
|
wait = 80,
|
||||||
}: UseEventParams): { removeEvent: RemoveEventFn } {
|
}: UseEventParams): { removeEvent: RemoveEventFn } {
|
||||||
/* eslint-disable-next-line */
|
let remove: RemoveEventFn = () => {
|
||||||
let remove: RemoveEventFn = () => {
|
}
|
||||||
};
|
const isAddRef = ref(false)
|
||||||
const isAddRef = ref(false);
|
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
const element: Ref<Element> = ref(el as Element);
|
const element: Ref<Element> = ref(el as Element)
|
||||||
|
|
||||||
const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait);
|
const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait)
|
||||||
const realHandler = wait ? handler : listener;
|
const realHandler = wait ? handler : listener
|
||||||
const removeEventListener = (e: Element) => {
|
const removeEventListener = (e: Element) => {
|
||||||
isAddRef.value = true;
|
isAddRef.value = true
|
||||||
e.removeEventListener(name, realHandler, options);
|
e.removeEventListener(name, realHandler, options)
|
||||||
};
|
}
|
||||||
const addEventListener = (e: Element) => e.addEventListener(name, realHandler, options);
|
const addEventListener = (e: Element) => e.addEventListener(name, realHandler, options)
|
||||||
|
|
||||||
const removeWatch = watch(
|
const removeWatch = watch(
|
||||||
element,
|
element,
|
||||||
(v, _ov, cleanUp) => {
|
(v, _ov, cleanUp) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
!unref(isAddRef) && addEventListener(v);
|
!unref(isAddRef) && addEventListener(v)
|
||||||
cleanUp(() => {
|
cleanUp(() => {
|
||||||
autoRemove && removeEventListener(v);
|
autoRemove && removeEventListener(v)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true },
|
||||||
);
|
)
|
||||||
|
|
||||||
remove = () => {
|
remove = () => {
|
||||||
removeEventListener(element.value);
|
removeEventListener(element.value)
|
||||||
removeWatch();
|
removeWatch()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
return { removeEvent: remove };
|
return { removeEvent: remove }
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,35 @@
|
|||||||
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
|
import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core'
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
|
||||||
|
|
||||||
interface WindowSizeOptions {
|
interface WindowSizeOptions {
|
||||||
once?: boolean;
|
once?: boolean
|
||||||
immediate?: boolean;
|
immediate?: boolean
|
||||||
listenerOptions?: AddEventListenerOptions | boolean;
|
listenerOptions?: AddEventListenerOptions | boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWindowSizeFn<T>(fn: Fn<T>, wait = 150, options?: WindowSizeOptions) {
|
export function useWindowSizeFn<T>(fn: Fn<T>, wait = 150, options?: WindowSizeOptions) {
|
||||||
let handler = () => {
|
let handler = () => {
|
||||||
fn();
|
fn()
|
||||||
};
|
}
|
||||||
const handleSize = useDebounceFn(handler, wait);
|
const handleSize = useDebounceFn(handler, wait)
|
||||||
handler = handleSize;
|
handler = handleSize
|
||||||
|
|
||||||
const start = () => {
|
const start = () => {
|
||||||
if (options && options.immediate) {
|
if (options && options.immediate) {
|
||||||
handler();
|
handler()
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', handler);
|
window.addEventListener('resize', handler)
|
||||||
};
|
}
|
||||||
|
|
||||||
const stop = () => {
|
const stop = () => {
|
||||||
window.removeEventListener('resize', handler);
|
window.removeEventListener('resize', handler)
|
||||||
};
|
}
|
||||||
|
|
||||||
tryOnMounted(() => {
|
tryOnMounted(() => {
|
||||||
start();
|
start()
|
||||||
});
|
})
|
||||||
|
|
||||||
tryOnUnmounted(() => {
|
tryOnUnmounted(() => {
|
||||||
stop();
|
stop()
|
||||||
});
|
})
|
||||||
return [start, stop];
|
return [start, stop]
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import { useAsync } from './use-async';
|
import { useAsync } from './use-async'
|
||||||
|
|
||||||
export { useAsync };
|
export { useAsync }
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { warn } from '@/utils/log';
|
import { warn } from '@/utils/log'
|
||||||
import { getAppEnvConfig } from '@/utils/env';
|
import { getAppEnvConfig } from '@/utils/env'
|
||||||
import { GlobConfig } from '#/config';
|
import type { GlobConfig } from '#/config'
|
||||||
|
|
||||||
export const useGlobSetting = (): Readonly<GlobConfig> => {
|
export function useGlobSetting(): Readonly<GlobConfig> {
|
||||||
const {
|
const {
|
||||||
VITE_GLOB_APP_TITLE,
|
VITE_GLOB_APP_TITLE,
|
||||||
VITE_GLOB_APP_TITLE_CN,
|
VITE_GLOB_APP_TITLE_CN,
|
||||||
@ -12,12 +12,12 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
|
|||||||
VITE_GLOB_UPLOAD_URL,
|
VITE_GLOB_UPLOAD_URL,
|
||||||
VITE_GLOB_PROD_MOCK,
|
VITE_GLOB_PROD_MOCK,
|
||||||
VITE_GLOB_IMG_URL,
|
VITE_GLOB_IMG_URL,
|
||||||
} = getAppEnvConfig();
|
} = getAppEnvConfig()
|
||||||
|
|
||||||
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||||
warn(
|
warn(
|
||||||
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
|
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take global configuration
|
// Take global configuration
|
||||||
@ -30,6 +30,6 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
|
|||||||
uploadUrl: VITE_GLOB_UPLOAD_URL,
|
uploadUrl: VITE_GLOB_UPLOAD_URL,
|
||||||
prodMock: VITE_GLOB_PROD_MOCK,
|
prodMock: VITE_GLOB_PROD_MOCK,
|
||||||
imgUrl: VITE_GLOB_IMG_URL,
|
imgUrl: VITE_GLOB_IMG_URL,
|
||||||
};
|
}
|
||||||
return glob as Readonly<GlobConfig>;
|
return glob as Readonly<GlobConfig>
|
||||||
};
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
|
|
||||||
export function useDesignSetting() {
|
export function useDesignSetting() {
|
||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore()
|
||||||
|
|
||||||
const getDarkMode = computed(() => designStore.darkMode);
|
const getDarkMode = computed(() => designStore.darkMode)
|
||||||
|
|
||||||
const getAppTheme = computed(() => designStore.appTheme);
|
const getAppTheme = computed(() => designStore.appTheme)
|
||||||
|
|
||||||
const getAppThemeList = computed(() => designStore.appThemeList);
|
const getAppThemeList = computed(() => designStore.appThemeList)
|
||||||
|
|
||||||
const getIsPageAnimate = computed(() => designStore.isPageAnimate);
|
const getIsPageAnimate = computed(() => designStore.isPageAnimate)
|
||||||
|
|
||||||
const getPageAnimateType = computed(() => designStore.pageAnimateType);
|
const getPageAnimateType = computed(() => designStore.pageAnimateType)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getDarkMode,
|
getDarkMode,
|
||||||
@ -20,5 +20,5 @@ export function useDesignSetting() {
|
|||||||
getAppThemeList,
|
getAppThemeList,
|
||||||
getIsPageAnimate,
|
getIsPageAnimate,
|
||||||
getPageAnimateType,
|
getPageAnimateType,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import { isReactive, isRef } from 'vue';
|
import { isReactive, isRef } from 'vue'
|
||||||
|
|
||||||
function setLoading(loading, val) {
|
function setLoading(loading, val) {
|
||||||
if (loading != undefined && isRef(loading)) {
|
if (loading !== undefined && isRef(loading)) {
|
||||||
loading.value = val;
|
loading.value = val
|
||||||
} else if (loading != undefined && isReactive(loading)) {
|
}
|
||||||
loading.loading = val;
|
else if (loading !== undefined && isReactive(loading)) {
|
||||||
|
loading.loading = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAsync = async (func: Promise<any>, loading: any): Promise<any> => {
|
export async function useAsync(func: Promise<any>, loading: any): Promise<any> {
|
||||||
setLoading(loading, true);
|
setLoading(loading, true)
|
||||||
|
|
||||||
return await func.finally(() => setLoading(loading, false));
|
return await func.finally(() => setLoading(loading, false))
|
||||||
};
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* description: 获取页面宽度
|
* description: 获取页面宽度
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function useDomWidth() {
|
export function useDomWidth() {
|
||||||
const domWidth = ref(window.innerWidth);
|
const domWidth = ref(window.innerWidth)
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
domWidth.value = document.body.clientWidth;
|
domWidth.value = document.body.clientWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', debounce(resize, 80));
|
window.addEventListener('resize', debounce(resize, 80))
|
||||||
});
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('resize', resize);
|
window.removeEventListener('resize', resize)
|
||||||
});
|
})
|
||||||
|
|
||||||
return domWidth;
|
return domWidth
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 用户网络是否可用
|
* @description 用户网络是否可用
|
||||||
* */
|
*/
|
||||||
export function useOnline() {
|
export function useOnline() {
|
||||||
const online = ref(true);
|
const online = ref(true)
|
||||||
|
|
||||||
const showStatus = (val) => {
|
const showStatus = (val) => {
|
||||||
online.value = typeof val == 'boolean' ? val : val.target.online;
|
online.value = typeof val == 'boolean' ? val : val.target.online
|
||||||
};
|
}
|
||||||
|
|
||||||
// 在页面加载后,设置正确的网络状态
|
// 在页面加载后,设置正确的网络状态
|
||||||
navigator.onLine ? showStatus(true) : showStatus(false);
|
navigator.onLine ? showStatus(true) : showStatus(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 开始监听网络状态的变化
|
// 开始监听网络状态的变化
|
||||||
window.addEventListener('online', showStatus);
|
window.addEventListener('online', showStatus)
|
||||||
|
|
||||||
window.addEventListener('offline', showStatus);
|
window.addEventListener('offline', showStatus)
|
||||||
});
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 移除监听网络状态的变化
|
// 移除监听网络状态的变化
|
||||||
window.removeEventListener('online', showStatus);
|
window.removeEventListener('online', showStatus)
|
||||||
|
|
||||||
window.removeEventListener('offline', showStatus);
|
window.removeEventListener('offline', showStatus)
|
||||||
});
|
})
|
||||||
|
|
||||||
return { online };
|
return { online }
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取本地时间
|
* @description 获取本地时间
|
||||||
*/
|
*/
|
||||||
export function useTime() {
|
export function useTime() {
|
||||||
let timer; // 定时器
|
let timer // 定时器
|
||||||
const year = ref(0); // 年份
|
const year = ref(0) // 年份
|
||||||
const month = ref(0); // 月份
|
const month = ref(0) // 月份
|
||||||
const week = ref(''); // 星期几
|
const week = ref('') // 星期几
|
||||||
const day = ref(0); // 天数
|
const day = ref(0) // 天数
|
||||||
const hour = ref<number | string>(0); // 小时
|
const hour = ref<number | string>(0) // 小时
|
||||||
const minute = ref<number | string>(0); // 分钟
|
const minute = ref<number | string>(0) // 分钟
|
||||||
const second = ref(0); // 秒
|
const second = ref(0) // 秒
|
||||||
|
|
||||||
// 更新时间
|
// 更新时间
|
||||||
const updateTime = () => {
|
const updateTime = () => {
|
||||||
const date = new Date();
|
const date = new Date()
|
||||||
year.value = date.getFullYear();
|
year.value = date.getFullYear()
|
||||||
month.value = date.getMonth() + 1;
|
month.value = date.getMonth() + 1
|
||||||
week.value = '日一二三四五六'.charAt(date.getDay());
|
week.value = '日一二三四五六'.charAt(date.getDay())
|
||||||
day.value = date.getDate();
|
day.value = date.getDate()
|
||||||
hour.value =
|
hour.value
|
||||||
(date.getHours() + '')?.padStart(2, '0') ||
|
= (`${date.getHours()}`)?.padStart(2, '0')
|
||||||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours());
|
|| new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours())
|
||||||
minute.value =
|
minute.value
|
||||||
(date.getMinutes() + '')?.padStart(2, '0') ||
|
= (`${date.getMinutes()}`)?.padStart(2, '0')
|
||||||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes());
|
|| new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes())
|
||||||
second.value = date.getSeconds();
|
second.value = date.getSeconds()
|
||||||
};
|
}
|
||||||
|
|
||||||
// 原生时间格式化
|
// 原生时间格式化
|
||||||
// new Intl.DateTimeFormat('zh', {
|
// new Intl.DateTimeFormat('zh', {
|
||||||
@ -40,16 +40,16 @@ export function useTime() {
|
|||||||
// hour12: false
|
// hour12: false
|
||||||
// }).format(new Date())
|
// }).format(new Date())
|
||||||
|
|
||||||
updateTime();
|
updateTime()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
clearInterval(timer);
|
clearInterval(timer)
|
||||||
timer = setInterval(() => updateTime(), 1000);
|
timer = setInterval(() => updateTime(), 1000)
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(timer);
|
clearInterval(timer)
|
||||||
});
|
})
|
||||||
|
|
||||||
return { month, day, hour, minute, second, week };
|
return { month, day, hour, minute, second, week }
|
||||||
}
|
}
|
||||||
|
@ -1,111 +1,116 @@
|
|||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts'
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
import { useTimeoutFn } from '@/hooks/core/useTimeout';
|
import type { Fn } from '@vueuse/core'
|
||||||
import { Fn, tryOnUnmounted } from '@vueuse/core';
|
import { tryOnUnmounted, useDebounceFn } from '@vueuse/core'
|
||||||
import { unref, nextTick, watch, computed, ref } from 'vue';
|
import { computed, nextTick, ref, unref, watch } from 'vue'
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useTimeoutFn } from '@/hooks/core/useTimeout'
|
||||||
import { useEventListener } from '@/hooks/event/useEventListener';
|
|
||||||
import { useBreakpoint } from '@/hooks/event/useBreakpoint';
|
import { useEventListener } from '@/hooks/event/useEventListener'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useBreakpoint } from '@/hooks/event/useBreakpoint'
|
||||||
import echarts from '@/utils/lib/echarts';
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
|
import echarts from '@/utils/lib/echarts'
|
||||||
|
|
||||||
export function useECharts(
|
export function useECharts(
|
||||||
elRef: Ref<HTMLDivElement>,
|
elRef: Ref<HTMLDivElement>,
|
||||||
theme: 'light' | 'dark' | 'default' = 'default'
|
theme: 'light' | 'dark' | 'default' = 'default',
|
||||||
) {
|
) {
|
||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore()
|
||||||
|
|
||||||
const getDarkMode = computed(() => {
|
const getDarkMode = computed(() => {
|
||||||
return theme === 'default' ? designStore.getDarkMode : theme;
|
return theme === 'default' ? designStore.getDarkMode : theme
|
||||||
});
|
})
|
||||||
|
|
||||||
let chartInstance: echarts.ECharts | null = null;
|
let chartInstance: echarts.ECharts | null = null
|
||||||
let resizeFn: Fn = resize;
|
let resizeFn: Fn = resize
|
||||||
const cacheOptions = ref({});
|
const cacheOptions = ref({})
|
||||||
let removeResizeFn: Fn = () => {};
|
let removeResizeFn: Fn = () => {}
|
||||||
resizeFn = useDebounceFn(resize, 200);
|
resizeFn = useDebounceFn(resize, 200)
|
||||||
|
|
||||||
const getOptions = computed((): EChartsOption => {
|
const getOptions = computed((): EChartsOption => {
|
||||||
if (getDarkMode.value !== 'dark') {
|
if (getDarkMode.value !== 'dark') {
|
||||||
return cacheOptions.value;
|
return cacheOptions.value
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
...cacheOptions.value,
|
...cacheOptions.value,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
function initCharts(t = theme) {
|
function initCharts(t = theme) {
|
||||||
const el = unref(elRef);
|
const el = unref(elRef)
|
||||||
if (!el || !unref(el)) {
|
if (!el || !unref(el)) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chartInstance = echarts.init(el, t);
|
chartInstance = echarts.init(el, t)
|
||||||
const { removeEvent } = useEventListener({
|
const { removeEvent } = useEventListener({
|
||||||
el: window,
|
el: window,
|
||||||
name: 'resize',
|
name: 'resize',
|
||||||
listener: resizeFn,
|
listener: resizeFn,
|
||||||
});
|
})
|
||||||
removeResizeFn = removeEvent;
|
removeResizeFn = removeEvent
|
||||||
const { widthRef, screenEnum } = useBreakpoint();
|
const { widthRef, screenEnum } = useBreakpoint()
|
||||||
if (unref(widthRef) <= screenEnum.MD || el.offsetHeight === 0) {
|
if (unref(widthRef) <= screenEnum.MD || el.offsetHeight === 0) {
|
||||||
useTimeoutFn(() => {
|
useTimeoutFn(() => {
|
||||||
resizeFn();
|
resizeFn()
|
||||||
}, 30);
|
}, 30)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOptions(options: EChartsOption, clear = true) {
|
function setOptions(options: EChartsOption, clear = true) {
|
||||||
cacheOptions.value = options;
|
cacheOptions.value = options
|
||||||
if (unref(elRef)?.offsetHeight === 0) {
|
if (unref(elRef)?.offsetHeight === 0) {
|
||||||
useTimeoutFn(() => {
|
useTimeoutFn(() => {
|
||||||
setOptions(unref(getOptions));
|
setOptions(unref(getOptions))
|
||||||
}, 30);
|
}, 30)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
useTimeoutFn(() => {
|
useTimeoutFn(() => {
|
||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
initCharts(getDarkMode.value as 'default');
|
initCharts(getDarkMode.value as 'default')
|
||||||
|
|
||||||
if (!chartInstance) return;
|
if (!chartInstance) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clear && chartInstance?.clear();
|
clear && chartInstance?.clear()
|
||||||
|
|
||||||
chartInstance?.setOption(unref(getOptions));
|
chartInstance?.setOption(unref(getOptions))
|
||||||
}, 30);
|
}, 30)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
chartInstance?.resize();
|
chartInstance?.resize()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => getDarkMode.value,
|
() => getDarkMode.value,
|
||||||
(theme) => {
|
(theme) => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose();
|
chartInstance.dispose()
|
||||||
initCharts(theme as 'default');
|
initCharts(theme as 'default')
|
||||||
setOptions(cacheOptions.value);
|
setOptions(cacheOptions.value)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
tryOnUnmounted(() => {
|
tryOnUnmounted(() => {
|
||||||
if (!chartInstance) return;
|
if (!chartInstance) {
|
||||||
removeResizeFn();
|
return
|
||||||
chartInstance.dispose();
|
}
|
||||||
chartInstance = null;
|
removeResizeFn()
|
||||||
});
|
chartInstance.dispose()
|
||||||
|
chartInstance = null
|
||||||
|
})
|
||||||
|
|
||||||
function getInstance(): echarts.ECharts | null {
|
function getInstance(): echarts.ECharts | null {
|
||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
initCharts(getDarkMode.value as 'default');
|
initCharts(getDarkMode.value as 'default')
|
||||||
}
|
}
|
||||||
return chartInstance;
|
return chartInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -113,5 +118,5 @@ export function useECharts(
|
|||||||
resize,
|
resize,
|
||||||
echarts,
|
echarts,
|
||||||
getInstance,
|
getInstance,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
<!-- eslint-disable prettier/prettier -->
|
||||||
<template>
|
<template>
|
||||||
<div class="h-screen flex flex-col">
|
<div class="h-screen flex flex-col">
|
||||||
<van-nav-bar v-if="getShowHeader" fixed placeholder :title="getTitle" />
|
<van-nav-bar v-if="getShowHeader" placeholder fixed :title="getTitle" />
|
||||||
<routerView class="flex-1 overflow-x-hidden">
|
<routerView class="flex-1 overflow-x-hidden">
|
||||||
<template #default="{ Component, route }">
|
<template #default="{ Component, route }">
|
||||||
<!--
|
<!--
|
||||||
@ -13,42 +14,45 @@
|
|||||||
<keep-alive v-if="keepAliveComponents" :include="keepAliveComponents">
|
<keep-alive v-if="keepAliveComponents" :include="keepAliveComponents">
|
||||||
<component :is="Component" :key="route.fullPath" />
|
<component :is="Component" :key="route.fullPath" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
<component v-else :is="Component" :key="route.fullPath" />
|
<component :is="Component" v-else :key="route.fullPath" />
|
||||||
</template>
|
</template>
|
||||||
</routerView>
|
</routerView>
|
||||||
<van-tabbar route fixed placeholder>
|
<van-tabbar placeholder route fixed>
|
||||||
<van-tabbar-item
|
<van-tabbar-item
|
||||||
replace
|
|
||||||
v-for="menu in getMenus"
|
v-for="menu in getMenus"
|
||||||
:key="menu.name"
|
:key="menu.name"
|
||||||
|
replace
|
||||||
:to="menu.path"
|
:to="menu.path"
|
||||||
:icon="(menu.meta?.icon as string)"
|
:icon="(menu.meta?.icon as string)"
|
||||||
>{{ menu.meta?.title }}
|
>
|
||||||
|
{{ menu.meta?.title }}
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
</van-tabbar>
|
</van-tabbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import type { ComputedRef } from 'vue'
|
||||||
import { useRoute } from 'vue-router';
|
import { computed } from 'vue'
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRoute } from 'vue-router'
|
||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
import { useRouteStore } from '@/store/modules/route'
|
||||||
|
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore()
|
||||||
// 需要缓存的路由组件
|
// 需要缓存的路由组件
|
||||||
const keepAliveComponents = computed(() => routeStore.keepAliveComponents);
|
const keepAliveComponents = computed(() => routeStore.keepAliveComponents)
|
||||||
const currentRoute = useRoute();
|
const currentRoute = useRoute()
|
||||||
|
|
||||||
const getTitle = computed(() => currentRoute.meta.title as string);
|
const getTitle = computed(() => currentRoute.meta.title as string)
|
||||||
|
|
||||||
// 菜单
|
// 菜单
|
||||||
const getMenus = computed(() =>
|
const getMenus: ComputedRef<RouteRecordRaw[]> = computed(() =>
|
||||||
routeStore.menus.filter((item) => {
|
routeStore.menus.filter((item) => {
|
||||||
return !item.meta?.innerPage;
|
return !item.meta?.innerPage
|
||||||
})
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
const getShowHeader = computed(() => !currentRoute.meta.hiddenHeader);
|
const getShowHeader = computed(() => !currentRoute.meta.hiddenHeader)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
40
src/main.ts
40
src/main.ts
@ -1,31 +1,31 @@
|
|||||||
import 'virtual:uno.css';
|
import 'virtual:uno.css'
|
||||||
import 'vant/es/toast/style';
|
import 'vant/es/toast/style'
|
||||||
import 'vant/es/dialog/style';
|
import 'vant/es/dialog/style'
|
||||||
import '@unocss/reset/tailwind.css';
|
import '@unocss/reset/tailwind.css'
|
||||||
import '@unocss/reset/tailwind-compat.css';
|
import '@unocss/reset/tailwind-compat.css'
|
||||||
|
|
||||||
// Register icon sprite
|
// Register icon sprite
|
||||||
import 'virtual:svg-icons-register';
|
import 'virtual:svg-icons-register'
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue';
|
import App from './App.vue'
|
||||||
import { setupStore } from '@/store';
|
import router, { setupRouter } from './router'
|
||||||
import router, { setupRouter } from './router';
|
import { updateDarkSign } from './theme'
|
||||||
import { updateDarkSign } from './theme';
|
import { setupStore } from '@/store'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = createApp(App);
|
const app = createApp(App)
|
||||||
// 挂载状态管理
|
// 挂载状态管理
|
||||||
setupStore(app);
|
setupStore(app)
|
||||||
// 挂载路由
|
// 挂载路由
|
||||||
setupRouter(app);
|
setupRouter(app)
|
||||||
await router.isReady();
|
await router.isReady()
|
||||||
// 路由准备就绪后挂载APP实例
|
// 路由准备就绪后挂载APP实例
|
||||||
app.mount('#app', true);
|
app.mount('#app', true)
|
||||||
|
|
||||||
// 根节点挂载 dark 标识
|
// 根节点挂载 dark 标识
|
||||||
const appDesignSetting = window.localStorage.getItem('DESIGN-SETTING');
|
const appDesignSetting = window.localStorage.getItem('DESIGN-SETTING')
|
||||||
const darkMode = appDesignSetting && JSON.parse(appDesignSetting).darkMode;
|
const darkMode = appDesignSetting && JSON.parse(appDesignSetting).darkMode
|
||||||
updateDarkSign(darkMode);
|
updateDarkSign(darkMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
void bootstrap();
|
void bootstrap()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
|
|
||||||
const Layout = () => import('@/layout/index.vue');
|
const Layout = () => import('@/layout/index.vue')
|
||||||
|
|
||||||
// 404 on a page
|
// 404 on a page
|
||||||
export const ErrorPageRoute: RouteRecordRaw = {
|
export const ErrorPageRoute: RouteRecordRaw = {
|
||||||
@ -23,7 +23,7 @@ export const ErrorPageRoute: RouteRecordRaw = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|
||||||
export const RootRoute: RouteRecordRaw = {
|
export const RootRoute: RouteRecordRaw = {
|
||||||
path: '/',
|
path: '/',
|
||||||
@ -32,7 +32,7 @@ export const RootRoute: RouteRecordRaw = {
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
export const LoginRoute: RouteRecordRaw = {
|
export const LoginRoute: RouteRecordRaw = {
|
||||||
path: '/login',
|
path: '/login',
|
||||||
@ -41,4 +41,4 @@ export const LoginRoute: RouteRecordRaw = {
|
|||||||
meta: {
|
meta: {
|
||||||
title: '登录',
|
title: '登录',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
@ -1,31 +1,32 @@
|
|||||||
import { App } from 'vue';
|
import type { App } from 'vue'
|
||||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { LoginRoute, RootRoute, ErrorPageRoute } from '@/router/base';
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import { createRouterGuards } from './router-guards';
|
import { createRouterGuards } from './router-guards'
|
||||||
import { useRouteStoreWidthOut } from '@/store/modules/route';
|
import routeModuleList from './modules'
|
||||||
|
import { ErrorPageRoute, LoginRoute, RootRoute } from '@/router/base'
|
||||||
|
import { useRouteStoreWidthOut } from '@/store/modules/route'
|
||||||
|
|
||||||
// 菜单
|
// 菜单
|
||||||
import routeModuleList from './modules';
|
|
||||||
|
|
||||||
// 普通路由
|
// 普通路由
|
||||||
export const constantRouter: RouteRecordRaw[] = [LoginRoute, RootRoute, ErrorPageRoute];
|
export const constantRouter: RouteRecordRaw[] = [LoginRoute, RootRoute, ErrorPageRoute]
|
||||||
|
|
||||||
const routeStore = useRouteStoreWidthOut();
|
const routeStore = useRouteStoreWidthOut()
|
||||||
|
|
||||||
routeStore.setMenus(routeModuleList);
|
routeStore.setMenus(routeModuleList)
|
||||||
routeStore.setRouters(constantRouter.concat(routeModuleList));
|
routeStore.setRouters(constantRouter.concat(routeModuleList))
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(''),
|
history: createWebHashHistory(''),
|
||||||
routes: constantRouter.concat(...routeModuleList),
|
routes: constantRouter.concat(...routeModuleList),
|
||||||
strict: true,
|
strict: true,
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
});
|
})
|
||||||
|
|
||||||
export function setupRouter(app: App) {
|
export function setupRouter(app: App) {
|
||||||
app.use(router);
|
app.use(router)
|
||||||
// 创建路由守卫
|
// 创建路由守卫
|
||||||
createRouterGuards(router);
|
createRouterGuards(router)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default router;
|
export default router
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
const Layout = () => import('@/layout/index.vue');
|
const Layout = () => import('@/layout/index.vue')
|
||||||
|
|
||||||
const routeModuleList: Array<RouteRecordRaw> = [
|
const routeModuleList: Array<RouteRecordRaw> = [
|
||||||
{
|
{
|
||||||
@ -121,6 +121,6 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
},
|
},
|
||||||
component: () => import('@/views/my/ThemeSetting.vue'),
|
component: () => import('@/views/my/ThemeSetting.vue'),
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
export default routeModuleList;
|
export default routeModuleList
|
||||||
|
@ -1,88 +1,91 @@
|
|||||||
import { isNavigationFailure, Router } from 'vue-router';
|
import type { Router } from 'vue-router'
|
||||||
import { useRouteStoreWidthOut } from '@/store/modules/route';
|
import { isNavigationFailure } from 'vue-router'
|
||||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
import { useRouteStoreWidthOut } from '@/store/modules/route'
|
||||||
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
import { useUserStoreWidthOut } from '@/store/modules/user'
|
||||||
import { storage } from '@/utils/Storage';
|
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { storage } from '@/utils/Storage'
|
||||||
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
|
|
||||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
const LOGIN_PATH = PageEnum.BASE_LOGIN
|
||||||
|
|
||||||
const whitePathList = [LOGIN_PATH]; // no redirect whitelist
|
const whitePathList = [LOGIN_PATH] // no redirect whitelist
|
||||||
|
|
||||||
export function createRouterGuards(router: Router) {
|
export function createRouterGuards(router: Router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
// to: 即将要进入的目标
|
// to: 即将要进入的目标
|
||||||
// from: 当前导航正要离开的路由
|
// from: 当前导航正要离开的路由
|
||||||
|
|
||||||
const userStore = useUserStoreWidthOut();
|
const userStore = useUserStoreWidthOut()
|
||||||
|
|
||||||
if (from.path === LOGIN_PATH && to.name === PageEnum.ERROR_PAGE_NAME) {
|
if (from.path === LOGIN_PATH && to.name === PageEnum.ERROR_PAGE_NAME) {
|
||||||
next(PageEnum.BASE_HOME);
|
next(PageEnum.BASE_HOME)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whitelist can be directly entered
|
// Whitelist can be directly entered
|
||||||
if (whitePathList.includes(to.path as PageEnum)) {
|
if (whitePathList.includes(to.path as PageEnum)) {
|
||||||
next();
|
next()
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = storage.get(ACCESS_TOKEN);
|
const token = storage.get(ACCESS_TOKEN)
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
// redirect login page
|
// redirect login page
|
||||||
next(LOGIN_PATH);
|
next(LOGIN_PATH)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当上次更新时间为空时获取用户信息
|
// 当上次更新时间为空时获取用户信息
|
||||||
if (userStore.getLastUpdateTime === 0) {
|
if (userStore.getLastUpdateTime === 0) {
|
||||||
try {
|
try {
|
||||||
await userStore.GetUserInfo();
|
await userStore.GetUserInfo()
|
||||||
} catch (err) {
|
}
|
||||||
next();
|
catch (err) {
|
||||||
return;
|
next()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next()
|
||||||
});
|
})
|
||||||
|
|
||||||
// 进入某个路由之后触发的钩子
|
// 进入某个路由之后触发的钩子
|
||||||
router.afterEach((to, _, failure) => {
|
router.afterEach((to, _, failure) => {
|
||||||
// 设置每个页面的 title
|
// 设置每个页面的 title
|
||||||
document.title = (to?.meta?.title as string) || document.title;
|
document.title = (to?.meta?.title as string) || document.title
|
||||||
|
|
||||||
if (isNavigationFailure(failure)) {
|
if (isNavigationFailure(failure)) {
|
||||||
console.log('failed navigation', failure);
|
console.warn('failed navigation', failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
const routeStore = useRouteStoreWidthOut();
|
const routeStore = useRouteStoreWidthOut()
|
||||||
// 在这里设置需要缓存的组件名称
|
// 在这里设置需要缓存的组件名称
|
||||||
const keepAliveComponents = routeStore.keepAliveComponents;
|
const keepAliveComponents = routeStore.keepAliveComponents
|
||||||
// 获取当前组件名
|
// 获取当前组件名
|
||||||
const currentComName: any = to.matched.find((item) => item.name == to.name)?.name;
|
const currentComName: any = to.matched.find(item => item.name === to.name)?.name
|
||||||
|
|
||||||
// 如果 currentComName 且 keepAliveComponents 不包含 currentComName 且 即将要进入的路由 meta 属性里 keepAlive 为 true,则缓存该组件
|
// 如果 currentComName 且 keepAliveComponents 不包含 currentComName 且 即将要进入的路由 meta 属性里 keepAlive 为 true,则缓存该组件
|
||||||
if (currentComName && !keepAliveComponents.includes(currentComName) && to.meta?.keepAlive) {
|
if (currentComName && !keepAliveComponents.includes(currentComName) && to.meta?.keepAlive) {
|
||||||
// 需要缓存的组件
|
// 需要缓存的组件
|
||||||
keepAliveComponents.push(currentComName);
|
keepAliveComponents.push(currentComName)
|
||||||
// keepAlive 为 false 则不缓存
|
// keepAlive 为 false 则不缓存
|
||||||
} else if (!to.meta?.keepAlive) {
|
}
|
||||||
|
else if (!to.meta?.keepAlive) {
|
||||||
// 不需要缓存的组件
|
// 不需要缓存的组件
|
||||||
|
|
||||||
// 这里的作用一开始组件设置为缓存,之后又设置不缓存但是它还是存在 keepAliveComponents 数组中
|
// 这里的作用一开始组件设置为缓存,之后又设置不缓存但是它还是存在 keepAliveComponents 数组中
|
||||||
// keepAliveComponents 使用 findIndex 与 当前路由对比,如果存在则返回具体下标位置,不存在返回 -1
|
// keepAliveComponents 使用 findIndex 与 当前路由对比,如果存在则返回具体下标位置,不存在返回 -1
|
||||||
const index = routeStore.keepAliveComponents.findIndex((name) => name == currentComName);
|
const index = routeStore.keepAliveComponents.findIndex(name => name === currentComName)
|
||||||
if (index != -1) {
|
if (index !== -1) {
|
||||||
// 通过返回具体下标位置删除 keepAliveComponents 数组中缓存的 元素
|
// 通过返回具体下标位置删除 keepAliveComponents 数组中缓存的 元素
|
||||||
keepAliveComponents.splice(index, 1);
|
keepAliveComponents.splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routeStore.setKeepAliveComponents(keepAliveComponents);
|
routeStore.setKeepAliveComponents(keepAliveComponents)
|
||||||
});
|
})
|
||||||
|
|
||||||
router.onError((error) => {
|
router.onError((error) => {
|
||||||
console.error(error, '路由错误');
|
console.error(error, '路由错误')
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,4 @@ export const animates = [
|
|||||||
{ value: 'fade', text: '消退' },
|
{ value: 'fade', text: '消退' },
|
||||||
{ value: 'fade-bottom', text: '底部消退' },
|
{ value: 'fade-bottom', text: '底部消退' },
|
||||||
{ value: 'fade-scale', text: '缩放消退' },
|
{ value: 'fade-scale', text: '缩放消退' },
|
||||||
];
|
]
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
export default {
|
export default {
|
||||||
upload: {
|
upload: {
|
||||||
//考虑接口规范不同
|
// 考虑接口规范不同
|
||||||
apiSetting: {
|
apiSetting: {
|
||||||
// 集合字段名
|
// 集合字段名
|
||||||
infoField: 'result',
|
infoField: 'result',
|
||||||
// 图片地址字段名
|
// 图片地址字段名
|
||||||
imgField: 'imagePath',
|
imgField: 'imagePath',
|
||||||
},
|
},
|
||||||
//最大上传图片大小
|
// 最大上传图片大小
|
||||||
maxSize: 1,
|
maxSize: 1,
|
||||||
//图片上传类型
|
// 图片上传类型
|
||||||
fileType: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml'],
|
fileType: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml'],
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
export interface DesignSettingState {
|
export interface DesignSettingState {
|
||||||
// 系统主题
|
// 系统主题
|
||||||
darkMode: 'light' | 'dark';
|
darkMode: 'light' | 'dark'
|
||||||
// 系统风格
|
// 系统风格
|
||||||
appTheme: string;
|
appTheme: string
|
||||||
// 系统内置风格
|
// 系统内置风格
|
||||||
appThemeList: string[];
|
appThemeList: string[]
|
||||||
// 是否开启路由动画
|
// 是否开启路由动画
|
||||||
isPageAnimate: boolean;
|
isPageAnimate: boolean
|
||||||
// 路由动画类型
|
// 路由动画类型
|
||||||
pageAnimateType: string;
|
pageAnimateType: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appThemeList: string[] = [
|
export const appThemeList: string[] = [
|
||||||
@ -33,19 +33,19 @@ export const appThemeList: string[] = [
|
|||||||
'#FB9300',
|
'#FB9300',
|
||||||
'#FC5404',
|
'#FC5404',
|
||||||
'#8675ff',
|
'#8675ff',
|
||||||
];
|
]
|
||||||
|
|
||||||
const setting: DesignSettingState = {
|
const setting: DesignSettingState = {
|
||||||
//深色主题
|
// 深色主题
|
||||||
darkMode: 'light',
|
darkMode: 'light',
|
||||||
//系统主题色
|
// 系统主题色
|
||||||
appTheme: '#5d9dfe',
|
appTheme: '#5d9dfe',
|
||||||
//系统内置主题色列表
|
// 系统内置主题色列表
|
||||||
appThemeList,
|
appThemeList,
|
||||||
//是否开启路由动画
|
// 是否开启路由动画
|
||||||
isPageAnimate: true,
|
isPageAnimate: true,
|
||||||
//路由动画类型
|
// 路由动画类型
|
||||||
pageAnimateType: 'zoom-fade',
|
pageAnimateType: 'zoom-fade',
|
||||||
};
|
}
|
||||||
|
|
||||||
export default setting;
|
export default setting
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue'
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia'
|
||||||
import piniaPersist from 'pinia-plugin-persist';
|
import piniaPersist from 'pinia-plugin-persist'
|
||||||
|
|
||||||
const store = createPinia();
|
const store = createPinia()
|
||||||
store.use(piniaPersist);
|
store.use(piniaPersist)
|
||||||
|
|
||||||
export function setupStore(app: App<Element>) {
|
export function setupStore(app: App<Element>) {
|
||||||
app.use(store);
|
app.use(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
export { store };
|
export { store }
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia'
|
||||||
import { store } from '@/store';
|
import { store } from '@/store'
|
||||||
import designSetting from '@/settings/designSetting';
|
import designSetting from '@/settings/designSetting'
|
||||||
import type { DesignSettingState } from '@/settings/designSetting';
|
import type { DesignSettingState } from '@/settings/designSetting'
|
||||||
|
|
||||||
const { darkMode, appTheme, appThemeList, isPageAnimate, pageAnimateType } = designSetting;
|
const { darkMode, appTheme, appThemeList, isPageAnimate, pageAnimateType } = designSetting
|
||||||
|
|
||||||
export const useDesignSettingStore = defineStore({
|
export const useDesignSettingStore = defineStore({
|
||||||
id: 'app-design-setting',
|
id: 'app-design-setting',
|
||||||
@ -16,27 +16,27 @@ export const useDesignSettingStore = defineStore({
|
|||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getDarkMode(): 'light' | 'dark' {
|
getDarkMode(): 'light' | 'dark' {
|
||||||
return this.darkMode;
|
return this.darkMode
|
||||||
},
|
},
|
||||||
getAppTheme(): string {
|
getAppTheme(): string {
|
||||||
return this.appTheme;
|
return this.appTheme
|
||||||
},
|
},
|
||||||
getAppThemeList(): string[] {
|
getAppThemeList(): string[] {
|
||||||
return this.appThemeList;
|
return this.appThemeList
|
||||||
},
|
},
|
||||||
getIsPageAnimate(): boolean {
|
getIsPageAnimate(): boolean {
|
||||||
return this.isPageAnimate;
|
return this.isPageAnimate
|
||||||
},
|
},
|
||||||
getPageAnimateType(): string {
|
getPageAnimateType(): string {
|
||||||
return this.pageAnimateType;
|
return this.pageAnimateType
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setDarkMode(mode: 'light' | 'dark'): void {
|
setDarkMode(mode: 'light' | 'dark'): void {
|
||||||
this.darkMode = mode;
|
this.darkMode = mode
|
||||||
},
|
},
|
||||||
setPageAnimateType(type: string): void {
|
setPageAnimateType(type: string): void {
|
||||||
this.pageAnimateType = type;
|
this.pageAnimateType = type
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 持久化
|
// 持久化
|
||||||
@ -49,9 +49,9 @@ export const useDesignSettingStore = defineStore({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
// Need to be used outside the setup
|
// Need to be used outside the setup
|
||||||
export function useDesignSettingWithOut() {
|
export function useDesignSettingWithOut() {
|
||||||
return useDesignSettingStore(store);
|
return useDesignSettingStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia'
|
||||||
import { RouteRecordRaw } from 'vue-router';
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { store } from '@/store';
|
import { store } from '@/store'
|
||||||
|
|
||||||
export interface IRouteState {
|
export interface IRouteState {
|
||||||
menus: RouteRecordRaw[];
|
menus: RouteRecordRaw[]
|
||||||
routers: RouteRecordRaw[];
|
routers: RouteRecordRaw[]
|
||||||
keepAliveComponents: string[];
|
keepAliveComponents: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRouteStore = defineStore({
|
export const useRouteStore = defineStore({
|
||||||
@ -17,24 +17,24 @@ export const useRouteStore = defineStore({
|
|||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getMenus(): RouteRecordRaw[] {
|
getMenus(): RouteRecordRaw[] {
|
||||||
return this.menus;
|
return this.menus
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setRouters(routers: RouteRecordRaw[]) {
|
setRouters(routers: RouteRecordRaw[]) {
|
||||||
this.routers = routers;
|
this.routers = routers
|
||||||
},
|
},
|
||||||
setMenus(menus: RouteRecordRaw[]) {
|
setMenus(menus: RouteRecordRaw[]) {
|
||||||
this.menus = menus;
|
this.menus = menus
|
||||||
},
|
},
|
||||||
setKeepAliveComponents(compNames: string[]) {
|
setKeepAliveComponents(compNames: string[]) {
|
||||||
// 设置需要缓存的组件
|
// 设置需要缓存的组件
|
||||||
this.keepAliveComponents = compNames;
|
this.keepAliveComponents = compNames
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
// Need to be used outside the setup
|
// Need to be used outside the setup
|
||||||
export function useRouteStoreWidthOut() {
|
export function useRouteStoreWidthOut() {
|
||||||
return useRouteStore(store);
|
return useRouteStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,36 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia'
|
||||||
import { createStorage } from '@/utils/Storage';
|
import { createStorage } from '@/utils/Storage'
|
||||||
import { store } from '@/store';
|
import { store } from '@/store'
|
||||||
import { ACCESS_TOKEN, CURRENT_USER } from '@/store/mutation-types';
|
import { ACCESS_TOKEN, CURRENT_USER } from '@/store/mutation-types'
|
||||||
import { ResultEnum } from '@/enums/httpEnum';
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
const Storage = createStorage({ storage: localStorage });
|
import { doLogout, getUserInfo, login } from '@/api/system/user'
|
||||||
import { getUserInfo, login, doLogout } from '@/api/system/user';
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import router from '@/router'
|
||||||
import router from '@/router';
|
|
||||||
|
const Storage = createStorage({ storage: localStorage })
|
||||||
|
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
userId: string | number;
|
userId: string | number
|
||||||
username: string;
|
username: string
|
||||||
realname: string;
|
realname: string
|
||||||
nickname: string;
|
nickname: string
|
||||||
avatar: string;
|
avatar: string
|
||||||
cover: string;
|
cover: string
|
||||||
gender: number;
|
gender: number
|
||||||
phone: string;
|
phone: string
|
||||||
sign?: string;
|
sign?: string
|
||||||
industry?: number;
|
industry?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUserState {
|
interface IUserState {
|
||||||
token?: string;
|
token?: string
|
||||||
userInfo: Nullable<UserInfo>;
|
userInfo: Nullable<UserInfo>
|
||||||
lastUpdateTime: number;
|
lastUpdateTime: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginParams {
|
interface LoginParams {
|
||||||
username: string;
|
username: string
|
||||||
password: string;
|
password: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
@ -41,37 +42,38 @@ export const useUserStore = defineStore({
|
|||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getUserInfo(): UserInfo {
|
getUserInfo(): UserInfo {
|
||||||
return this.userInfo || Storage.get(CURRENT_USER, '') || {};
|
return this.userInfo || Storage.get(CURRENT_USER, '') || {}
|
||||||
},
|
},
|
||||||
getToken(): string {
|
getToken(): string {
|
||||||
return this.token || Storage.get(ACCESS_TOKEN, '');
|
return this.token || Storage.get(ACCESS_TOKEN, '')
|
||||||
},
|
},
|
||||||
getLastUpdateTime(): number {
|
getLastUpdateTime(): number {
|
||||||
return this.lastUpdateTime;
|
return this.lastUpdateTime
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setToken(token: string | undefined) {
|
setToken(token: string | undefined) {
|
||||||
this.token = token ? token : '';
|
this.token = token || ''
|
||||||
Storage.set(ACCESS_TOKEN, token);
|
Storage.set(ACCESS_TOKEN, token)
|
||||||
},
|
},
|
||||||
setUserInfo(info: UserInfo | null) {
|
setUserInfo(info: UserInfo | null) {
|
||||||
this.userInfo = info;
|
this.userInfo = info
|
||||||
this.lastUpdateTime = new Date().getTime();
|
this.lastUpdateTime = new Date().getTime()
|
||||||
Storage.set(CURRENT_USER, info);
|
Storage.set(CURRENT_USER, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
async Login(params: LoginParams) {
|
async Login(params: LoginParams) {
|
||||||
try {
|
try {
|
||||||
const response = await login(params);
|
const response = await login(params)
|
||||||
const { result, code } = response;
|
const { result, code } = response
|
||||||
if (code === ResultEnum.SUCCESS) {
|
if (code === ResultEnum.SUCCESS) {
|
||||||
// save token
|
// save token
|
||||||
this.setToken(result.token);
|
this.setToken(result.token)
|
||||||
}
|
}
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response)
|
||||||
} catch (error) {
|
}
|
||||||
return Promise.reject(error);
|
catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -79,34 +81,35 @@ export const useUserStore = defineStore({
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getUserInfo()
|
getUserInfo()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.setUserInfo(res);
|
this.setUserInfo(res)
|
||||||
resolve(res);
|
resolve(res)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
reject(error);
|
reject(error)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
async Logout() {
|
async Logout() {
|
||||||
if (this.getToken) {
|
if (this.getToken) {
|
||||||
try {
|
try {
|
||||||
await doLogout();
|
await doLogout()
|
||||||
} catch {
|
}
|
||||||
console.error('注销Token失败');
|
catch {
|
||||||
|
console.error('注销Token失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setToken(undefined);
|
this.setToken(undefined)
|
||||||
this.setUserInfo(null);
|
this.setUserInfo(null)
|
||||||
Storage.remove(ACCESS_TOKEN);
|
Storage.remove(ACCESS_TOKEN)
|
||||||
Storage.remove(CURRENT_USER);
|
Storage.remove(CURRENT_USER)
|
||||||
router.push(PageEnum.BASE_LOGIN);
|
router.push(PageEnum.BASE_LOGIN)
|
||||||
location.reload();
|
location.reload()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
// Need to be used outside the setup
|
// Need to be used outside the setup
|
||||||
export function useUserStoreWidthOut() {
|
export function useUserStoreWidthOut() {
|
||||||
return useUserStore(store);
|
return useUserStore(store)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const FIRST_VISIT = 'FIRST-VISIT'; // 是否首次访问
|
export const FIRST_VISIT = 'FIRST-VISIT' // 是否首次访问
|
||||||
export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
|
export const ACCESS_TOKEN = 'ACCESS-TOKEN' // 用户token
|
||||||
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
|
export const CURRENT_USER = 'CURRENT-USER' // 当前用户信息
|
||||||
export const DESIGN_SETTING = 'DESIGN-SETTING'; // 当前用户主题信息
|
export const DESIGN_SETTING = 'DESIGN-SETTING' // 当前用户主题信息
|
||||||
|
@ -11,7 +11,6 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='dark'] {
|
[data-theme='dark'] {
|
||||||
|
|
||||||
&,
|
&,
|
||||||
* {
|
* {
|
||||||
color-scheme: dark !important;
|
color-scheme: dark !important;
|
||||||
@ -24,7 +23,6 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='light'] {
|
[data-theme='light'] {
|
||||||
|
|
||||||
&,
|
&,
|
||||||
* {
|
* {
|
||||||
color-scheme: light !important;
|
color-scheme: light !important;
|
||||||
@ -80,7 +78,9 @@ a:hover {
|
|||||||
|
|
||||||
.zoom-fade-enter-active,
|
.zoom-fade-enter-active,
|
||||||
.zoom-fade-leave-active {
|
.zoom-fade-leave-active {
|
||||||
transition: transform 0.35s, opacity 0.28s ease-in-out;
|
transition:
|
||||||
|
transform 0.35s,
|
||||||
|
opacity 0.28s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-fade-enter-from {
|
.zoom-fade-enter-from {
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
// Speed: 1x
|
// Speed: 1x
|
||||||
.fade-bottom-enter-active,
|
.fade-bottom-enter-active,
|
||||||
.fade-bottom-leave-active {
|
.fade-bottom-leave-active {
|
||||||
transition: opacity 0.25s, transform 0.3s;
|
transition:
|
||||||
|
opacity 0.25s,
|
||||||
|
transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-bottom-enter-from {
|
.fade-bottom-enter-from {
|
||||||
@ -67,7 +69,9 @@
|
|||||||
// Speed: 1x
|
// Speed: 1x
|
||||||
.fade-top-enter-active,
|
.fade-top-enter-active,
|
||||||
.fade-top-leave-active {
|
.fade-top-leave-active {
|
||||||
transition: opacity 0.2s, transform 0.25s;
|
transition:
|
||||||
|
opacity 0.2s,
|
||||||
|
transform 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-top-enter-from {
|
.fade-top-enter-from {
|
||||||
|
@ -6,5 +6,8 @@
|
|||||||
@import './zoom.less';
|
@import './zoom.less';
|
||||||
|
|
||||||
.collapse-transition {
|
.collapse-transition {
|
||||||
transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out;
|
transition:
|
||||||
|
0.2s height ease-in-out,
|
||||||
|
0.2s padding-top ease-in-out,
|
||||||
|
0.2s padding-bottom ease-in-out;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// zoom-out
|
// zoom-out
|
||||||
.zoom-out-enter-active,
|
.zoom-out-enter-active,
|
||||||
.zoom-out-leave-active {
|
.zoom-out-leave-active {
|
||||||
transition: opacity 0.1 ease-in-out, transform 0.15s ease-out;
|
transition:
|
||||||
|
opacity 0.1 ease-in-out,
|
||||||
|
transform 0.15s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-out-enter-from,
|
.zoom-out-enter-from,
|
||||||
@ -13,7 +15,9 @@
|
|||||||
// zoom-fade
|
// zoom-fade
|
||||||
.zoom-fade-enter-active,
|
.zoom-fade-enter-active,
|
||||||
.zoom-fade-leave-active {
|
.zoom-fade-leave-active {
|
||||||
transition: transform 0.2s, opacity 0.3s ease-out;
|
transition:
|
||||||
|
transform 0.2s,
|
||||||
|
opacity 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-fade-enter-from {
|
.zoom-fade-enter-from {
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
import { addClass, removeClass, hasClass } from '@/utils/domUtils';
|
import { addClass, hasClass, removeClass } from '@/utils/domUtils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* html 根标签上挂载 暗/亮 属性标识
|
* html 根标签上挂载 暗/亮 属性标识
|
||||||
*/
|
*/
|
||||||
export function updateDarkSign(mode: 'light' | 'dark') {
|
export function updateDarkSign(mode: 'light' | 'dark') {
|
||||||
const htmlRoot = document.getElementById('htmlRoot');
|
const htmlRoot = document.getElementById('htmlRoot')
|
||||||
if (!htmlRoot) {
|
if (!htmlRoot) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const hasDarkClass = hasClass(htmlRoot, 'dark');
|
const hasDarkClass = hasClass(htmlRoot, 'dark')
|
||||||
|
|
||||||
if (mode === 'dark') {
|
if (mode === 'dark') {
|
||||||
htmlRoot.setAttribute('data-theme', 'dark');
|
htmlRoot.setAttribute('data-theme', 'dark')
|
||||||
if (!hasDarkClass) {
|
if (!hasDarkClass) {
|
||||||
addClass(htmlRoot, 'dark');
|
addClass(htmlRoot, 'dark')
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
htmlRoot.setAttribute('data-theme', 'light');
|
else {
|
||||||
|
htmlRoot.setAttribute('data-theme', 'light')
|
||||||
if (hasDarkClass) {
|
if (hasDarkClass) {
|
||||||
removeClass(htmlRoot, 'dark');
|
removeClass(htmlRoot, 'dark')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
// 默认缓存期限为7天
|
// 默认缓存期限为7天
|
||||||
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
|
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建本地缓存对象
|
* 创建本地缓存对象
|
||||||
* @param {string=} prefixKey -
|
* @param {string} prefixKey -
|
||||||
* @param {Object} [storage=localStorage] - sessionStorage | localStorage
|
|
||||||
*/
|
*/
|
||||||
export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) => {
|
export function createStorage({ prefixKey = '', storage = localStorage } = {}) {
|
||||||
/**
|
/**
|
||||||
* 本地缓存类
|
* 本地缓存类
|
||||||
* @class Storage
|
* @class Storage
|
||||||
*/
|
*/
|
||||||
const Storage = class {
|
const Storage = class {
|
||||||
private storage = storage;
|
private storage = storage
|
||||||
private prefixKey?: string = prefixKey;
|
private prefixKey?: string = prefixKey
|
||||||
|
|
||||||
private getKey(key: string) {
|
private getKey(key: string) {
|
||||||
return `${this.prefixKey}${key}`.toUpperCase();
|
return `${this.prefixKey}${key}`.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,8 +28,8 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
const stringData = JSON.stringify({
|
const stringData = JSON.stringify({
|
||||||
value,
|
value,
|
||||||
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
|
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
|
||||||
});
|
})
|
||||||
this.storage.setItem(this.getKey(key), stringData);
|
this.storage.setItem(this.getKey(key), stringData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,21 +38,22 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
* @param {*=} def 默认值
|
* @param {*=} def 默认值
|
||||||
*/
|
*/
|
||||||
get(key: string, def: any = null) {
|
get(key: string, def: any = null) {
|
||||||
const item = this.storage.getItem(this.getKey(key));
|
const item = this.storage.getItem(this.getKey(key))
|
||||||
if (item) {
|
if (item) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(item);
|
const data = JSON.parse(item)
|
||||||
const { value, expire } = data;
|
const { value, expire } = data
|
||||||
// 在有效期内直接返回
|
// 在有效期内直接返回
|
||||||
if (expire === null || expire >= Date.now()) {
|
if (expire === null || expire >= Date.now()) {
|
||||||
return value;
|
return value
|
||||||
}
|
}
|
||||||
this.remove(key);
|
this.remove(key)
|
||||||
} catch (e) {
|
}
|
||||||
return def;
|
catch (e) {
|
||||||
|
return def
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return def;
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,7 +61,7 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
*/
|
*/
|
||||||
remove(key: string) {
|
remove(key: string) {
|
||||||
this.storage.removeItem(this.getKey(key));
|
this.storage.removeItem(this.getKey(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +69,7 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
* @memberOf Cache
|
* @memberOf Cache
|
||||||
*/
|
*/
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.storage.clear();
|
this.storage.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +81,7 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
* @example
|
* @example
|
||||||
*/
|
*/
|
||||||
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
||||||
document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`;
|
document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,14 +89,14 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
* @param name
|
* @param name
|
||||||
*/
|
*/
|
||||||
getCookie(name: string): string {
|
getCookie(name: string): string {
|
||||||
const cookieArr = document.cookie.split('; ');
|
const cookieArr = document.cookie.split('; ')
|
||||||
for (let i = 0, length = cookieArr.length; i < length; i++) {
|
for (let i = 0, length = cookieArr.length; i < length; i++) {
|
||||||
const kv = cookieArr[i].split('=');
|
const kv = cookieArr[i].split('=')
|
||||||
if (kv[0] === this.getKey(name)) {
|
if (kv[0] === this.getKey(name)) {
|
||||||
return kv[1];
|
return kv[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,24 +104,24 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) =
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
*/
|
*/
|
||||||
removeCookie(key: string) {
|
removeCookie(key: string) {
|
||||||
this.setCookie(key, 1, -1);
|
this.setCookie(key, 1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空cookie,使所有cookie失效
|
* 清空cookie,使所有cookie失效
|
||||||
*/
|
*/
|
||||||
clearCookie(): void {
|
clearCookie(): void {
|
||||||
const keys = document.cookie.match(/[^ =;]+(?==)/g);
|
const keys = document.cookie.match(/[^ =;]+(?==)/g)
|
||||||
if (keys) {
|
if (keys) {
|
||||||
for (let i = keys.length; i--; ) {
|
for (let i = keys.length; i--;) {
|
||||||
document.cookie = keys[i] + '=0;expire=' + new Date(0).toUTCString();
|
document.cookie = `${keys[i]}=0;expire=${new Date(0).toUTCString()}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
return new Storage();
|
return new Storage()
|
||||||
};
|
}
|
||||||
|
|
||||||
export const storage = createStorage();
|
export const storage = createStorage()
|
||||||
|
|
||||||
export default Storage;
|
export default Storage
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns'
|
||||||
|
|
||||||
const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
|
const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss'
|
||||||
const DATE_FORMAT = 'YYYY-MM-DD ';
|
const DATE_FORMAT = 'YYYY-MM-DD '
|
||||||
|
|
||||||
export function formatToDateTime(date: number | Date, formatStr = DATE_TIME_FORMAT): string {
|
export function formatToDateTime(date: number | Date, formatStr = DATE_TIME_FORMAT): string {
|
||||||
return format(date, formatStr);
|
return format(date, formatStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatToDate(date: number | Date, formatStr = DATE_FORMAT): string {
|
export function formatToDate(date: number | Date, formatStr = DATE_FORMAT): string {
|
||||||
return format(date, formatStr);
|
return format(date, formatStr)
|
||||||
}
|
}
|
||||||
|
@ -1,76 +1,92 @@
|
|||||||
import type { FunctionArgs } from '@vueuse/core';
|
/* eslint-disable ts/ban-ts-comment */
|
||||||
import { upperFirst } from 'lodash-es';
|
import type { FunctionArgs } from '@vueuse/core'
|
||||||
|
import { upperFirst } from 'lodash-es'
|
||||||
|
|
||||||
export interface ViewportOffsetResult {
|
export interface ViewportOffsetResult {
|
||||||
left: number;
|
left: number
|
||||||
top: number;
|
top: number
|
||||||
right: number;
|
right: number
|
||||||
bottom: number;
|
bottom: number
|
||||||
rightIncludeBody: number;
|
rightIncludeBody: number
|
||||||
bottomIncludeBody: number;
|
bottomIncludeBody: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBoundingClientRect(element: Element): DOMRect | number {
|
export function getBoundingClientRect(element: Element): DOMRect | number {
|
||||||
if (!element || !element.getBoundingClientRect) {
|
if (!element || !element.getBoundingClientRect) {
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
return element.getBoundingClientRect();
|
return element.getBoundingClientRect()
|
||||||
}
|
}
|
||||||
|
|
||||||
function trim(string: string) {
|
function trim(string: string) {
|
||||||
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
|
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export function hasClass(el: Element, cls: string) {
|
export function hasClass(el: Element, cls: string) {
|
||||||
if (!el || !cls) return false;
|
if (!el || !cls) {
|
||||||
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
|
return false
|
||||||
|
}
|
||||||
|
if (cls.includes(' ')) {
|
||||||
|
throw new Error('className should not contain space.')
|
||||||
|
}
|
||||||
if (el.classList) {
|
if (el.classList) {
|
||||||
return el.classList.contains(cls);
|
return el.classList.contains(cls)
|
||||||
} else {
|
}
|
||||||
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
|
else {
|
||||||
|
return (` ${el.className} `).includes(` ${cls} `)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export function addClass(el: Element, cls: string) {
|
export function addClass(el: Element, cls: string) {
|
||||||
if (!el) return;
|
if (!el) {
|
||||||
let curClass = el.className;
|
return
|
||||||
const classes = (cls || '').split(' ');
|
}
|
||||||
|
let curClass = el.className
|
||||||
|
const classes = (cls || '').split(' ')
|
||||||
|
|
||||||
for (let i = 0, j = classes.length; i < j; i++) {
|
for (let i = 0, j = classes.length; i < j; i++) {
|
||||||
const clsName = classes[i];
|
const clsName = classes[i]
|
||||||
if (!clsName) continue;
|
if (!clsName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if (el.classList) {
|
if (el.classList) {
|
||||||
el.classList.add(clsName);
|
el.classList.add(clsName)
|
||||||
} else if (!hasClass(el, clsName)) {
|
}
|
||||||
curClass += ' ' + clsName;
|
else if (!hasClass(el, clsName)) {
|
||||||
|
curClass += ` ${clsName}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!el.classList) {
|
if (!el.classList) {
|
||||||
el.className = curClass;
|
el.className = curClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export function removeClass(el: Element, cls: string) {
|
export function removeClass(el: Element, cls: string) {
|
||||||
if (!el || !cls) return;
|
if (!el || !cls) {
|
||||||
const classes = cls.split(' ');
|
return
|
||||||
let curClass = ' ' + el.className + ' ';
|
}
|
||||||
|
const classes = cls.split(' ')
|
||||||
|
let curClass = ` ${el.className} `
|
||||||
|
|
||||||
for (let i = 0, j = classes.length; i < j; i++) {
|
for (let i = 0, j = classes.length; i < j; i++) {
|
||||||
const clsName = classes[i];
|
const clsName = classes[i]
|
||||||
if (!clsName) continue;
|
if (!clsName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if (el.classList) {
|
if (el.classList) {
|
||||||
el.classList.remove(clsName);
|
el.classList.remove(clsName)
|
||||||
} else if (hasClass(el, clsName)) {
|
}
|
||||||
curClass = curClass.replace(' ' + clsName + ' ', ' ');
|
else if (hasClass(el, clsName)) {
|
||||||
|
curClass = curClass.replace(` ${clsName} `, ' ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!el.classList) {
|
if (!el.classList) {
|
||||||
el.className = trim(curClass);
|
el.className = trim(curClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -85,61 +101,61 @@ export function removeClass(el: Element, cls: string) {
|
|||||||
* @description:
|
* @description:
|
||||||
*/
|
*/
|
||||||
export function getViewportOffset(element: Element): ViewportOffsetResult {
|
export function getViewportOffset(element: Element): ViewportOffsetResult {
|
||||||
const doc = document.documentElement;
|
const doc = document.documentElement
|
||||||
|
|
||||||
const docScrollLeft = doc.scrollLeft;
|
const docScrollLeft = doc.scrollLeft
|
||||||
const docScrollTop = doc.scrollTop;
|
const docScrollTop = doc.scrollTop
|
||||||
const docClientLeft = doc.clientLeft;
|
const docClientLeft = doc.clientLeft
|
||||||
const docClientTop = doc.clientTop;
|
const docClientTop = doc.clientTop
|
||||||
|
|
||||||
const pageXOffset = window.pageXOffset;
|
const pageXOffset = window.pageXOffset
|
||||||
const pageYOffset = window.pageYOffset;
|
const pageYOffset = window.pageYOffset
|
||||||
|
|
||||||
const box = getBoundingClientRect(element);
|
const box = getBoundingClientRect(element)
|
||||||
|
|
||||||
const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect;
|
const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect
|
||||||
|
|
||||||
const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0);
|
const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0)
|
||||||
const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0);
|
const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0)
|
||||||
const offsetLeft = retLeft + pageXOffset;
|
const offsetLeft = retLeft + pageXOffset
|
||||||
const offsetTop = rectTop + pageYOffset;
|
const offsetTop = rectTop + pageYOffset
|
||||||
|
|
||||||
const left = offsetLeft - scrollLeft;
|
const left = offsetLeft - scrollLeft
|
||||||
const top = offsetTop - scrollTop;
|
const top = offsetTop - scrollTop
|
||||||
|
|
||||||
const clientWidth = window.document.documentElement.clientWidth;
|
const clientWidth = window.document.documentElement.clientWidth
|
||||||
const clientHeight = window.document.documentElement.clientHeight;
|
const clientHeight = window.document.documentElement.clientHeight
|
||||||
return {
|
return {
|
||||||
left: left,
|
left,
|
||||||
top: top,
|
top,
|
||||||
right: clientWidth - rectWidth - left,
|
right: clientWidth - rectWidth - left,
|
||||||
bottom: clientHeight - rectHeight - top,
|
bottom: clientHeight - rectHeight - top,
|
||||||
rightIncludeBody: clientWidth - left,
|
rightIncludeBody: clientWidth - left,
|
||||||
bottomIncludeBody: clientHeight - top,
|
bottomIncludeBody: clientHeight - top,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hackCss(attr: string, value: string) {
|
export function hackCss(attr: string, value: string) {
|
||||||
const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'];
|
const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT']
|
||||||
|
|
||||||
const styleObj: any = {};
|
const styleObj: any = {}
|
||||||
prefix.forEach((item) => {
|
prefix.forEach((item) => {
|
||||||
styleObj[`${item}${upperFirst(attr)}`] = value;
|
styleObj[`${item}${upperFirst(attr)}`] = value
|
||||||
});
|
})
|
||||||
return {
|
return {
|
||||||
...styleObj,
|
...styleObj,
|
||||||
[attr]: value,
|
[attr]: value,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export function on(
|
export function on(
|
||||||
element: Element | HTMLElement | Document | Window,
|
element: Element | HTMLElement | Document | Window,
|
||||||
event: string,
|
event: string,
|
||||||
handler: EventListenerOrEventListenerObject
|
handler: EventListenerOrEventListenerObject,
|
||||||
): void {
|
): void {
|
||||||
if (element && event && handler) {
|
if (element && event && handler) {
|
||||||
element.addEventListener(event, handler, false);
|
element.addEventListener(event, handler, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,10 +163,10 @@ export function on(
|
|||||||
export function off(
|
export function off(
|
||||||
element: Element | HTMLElement | Document | Window,
|
element: Element | HTMLElement | Document | Window,
|
||||||
event: string,
|
event: string,
|
||||||
handler: Fn
|
handler: Fn,
|
||||||
): void {
|
): void {
|
||||||
if (element && event && handler) {
|
if (element && event && handler) {
|
||||||
element.removeEventListener(event, handler, false);
|
element.removeEventListener(event, handler, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,24 +174,26 @@ export function off(
|
|||||||
export function once(el: HTMLElement, event: string, fn: EventListener): void {
|
export function once(el: HTMLElement, event: string, fn: EventListener): void {
|
||||||
const listener = function (this: any, ...args: unknown[]) {
|
const listener = function (this: any, ...args: unknown[]) {
|
||||||
if (fn) {
|
if (fn) {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
fn.apply(this, args);
|
fn.apply(this, args)
|
||||||
}
|
}
|
||||||
off(el, event, listener);
|
off(el, event, listener)
|
||||||
};
|
}
|
||||||
on(el, event, listener);
|
on(el, event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useRafThrottle<T extends FunctionArgs>(fn: T): T {
|
export function useRafThrottle<T extends FunctionArgs>(fn: T): T {
|
||||||
let locked = false;
|
let locked = false
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
return function (...args: any[]) {
|
return function (...args: any[]) {
|
||||||
if (locked) return;
|
if (locked) {
|
||||||
locked = true;
|
return
|
||||||
|
}
|
||||||
|
locked = true
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
fn.apply(this, args);
|
fn.apply(this, args)
|
||||||
locked = false;
|
locked = false
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import type { GlobEnvConfig } from '#/config';
|
import pkg from '../../package.json'
|
||||||
|
import { getConfigFileName } from '../../build/getConfigFileName'
|
||||||
|
import type { GlobEnvConfig } from '#/config'
|
||||||
|
|
||||||
import { warn } from '@/utils/log';
|
import { warn } from '@/utils/log'
|
||||||
import pkg from '../../package.json';
|
|
||||||
import { getConfigFileName } from '../../build/getConfigFileName';
|
|
||||||
|
|
||||||
export function getCommonStoragePrefix() {
|
export function getCommonStoragePrefix() {
|
||||||
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
|
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig()
|
||||||
return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase();
|
return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate cache key according to version
|
// Generate cache key according to version
|
||||||
export function getStorageShortName() {
|
export function getStorageShortName() {
|
||||||
return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase();
|
return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAppEnvConfig() {
|
export function getAppEnvConfig() {
|
||||||
const ENV_NAME = getConfigFileName(import.meta.env);
|
const ENV_NAME = getConfigFileName(import.meta.env)
|
||||||
|
|
||||||
|
// Get the global configuration (the configuration will be extracted independently when packaging)
|
||||||
const ENV = (import.meta.env.DEV
|
const ENV = (import.meta.env.DEV
|
||||||
? // Get the global configuration (the configuration will be extracted independently when packaging)
|
? (import.meta.env as unknown as GlobEnvConfig)
|
||||||
(import.meta.env as unknown as GlobEnvConfig)
|
: window[ENV_NAME as any]) as unknown as GlobEnvConfig
|
||||||
: window[ENV_NAME as any]) as unknown as GlobEnvConfig;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
VITE_GLOB_APP_TITLE,
|
VITE_GLOB_APP_TITLE,
|
||||||
@ -31,12 +31,12 @@ export function getAppEnvConfig() {
|
|||||||
VITE_GLOB_UPLOAD_URL,
|
VITE_GLOB_UPLOAD_URL,
|
||||||
VITE_GLOB_PROD_MOCK,
|
VITE_GLOB_PROD_MOCK,
|
||||||
VITE_GLOB_IMG_URL,
|
VITE_GLOB_IMG_URL,
|
||||||
} = ENV;
|
} = ENV
|
||||||
|
|
||||||
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||||
warn(
|
warn(
|
||||||
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
|
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -48,18 +48,18 @@ export function getAppEnvConfig() {
|
|||||||
VITE_GLOB_UPLOAD_URL,
|
VITE_GLOB_UPLOAD_URL,
|
||||||
VITE_GLOB_PROD_MOCK,
|
VITE_GLOB_PROD_MOCK,
|
||||||
VITE_GLOB_IMG_URL,
|
VITE_GLOB_IMG_URL,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: Development model
|
* @description: Development model
|
||||||
*/
|
*/
|
||||||
export const devMode = 'development';
|
export const devMode = 'development'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: Production mode
|
* @description: Production mode
|
||||||
*/
|
*/
|
||||||
export const prodMode = 'production';
|
export const prodMode = 'production'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: Get environment variables
|
* @description: Get environment variables
|
||||||
@ -67,7 +67,7 @@ export const prodMode = 'production';
|
|||||||
* @example:
|
* @example:
|
||||||
*/
|
*/
|
||||||
export function getEnv(): string {
|
export function getEnv(): string {
|
||||||
return import.meta.env.MODE;
|
return import.meta.env.MODE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +76,7 @@ export function getEnv(): string {
|
|||||||
* @example:
|
* @example:
|
||||||
*/
|
*/
|
||||||
export function isDevMode(): boolean {
|
export function isDevMode(): boolean {
|
||||||
return import.meta.env.DEV;
|
return import.meta.env.DEV
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,5 +85,5 @@ export function isDevMode(): boolean {
|
|||||||
* @example:
|
* @example:
|
||||||
*/
|
*/
|
||||||
export function isProdMode(): boolean {
|
export function isProdMode(): boolean {
|
||||||
return import.meta.env.PROD;
|
return import.meta.env.PROD
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,32 @@
|
|||||||
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
|
/* eslint-disable ts/ban-ts-comment */
|
||||||
|
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios'
|
||||||
import qs from 'qs';
|
import qs from 'qs'
|
||||||
import { AxiosCanceler } from './axiosCancel';
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { isFunction } from '@/utils/is';
|
import { AxiosCanceler } from './axiosCancel'
|
||||||
import { cloneDeep } from 'lodash-es';
|
import type { CreateAxiosOptions, RequestOptions, Result, UploadFileParams } from './types'
|
||||||
|
import { isFunction } from '@/utils/is'
|
||||||
|
|
||||||
import type { RequestOptions, CreateAxiosOptions, Result, UploadFileParams } from './types';
|
import { ContentTypeEnum, RequestEnum } from '@/enums/httpEnum'
|
||||||
import { ContentTypeEnum, RequestEnum } from '@/enums/httpEnum';
|
|
||||||
|
|
||||||
export * from './axiosTransform';
|
export * from './axiosTransform'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: axios模块
|
* @description: axios模块
|
||||||
*/
|
*/
|
||||||
export class VAxios {
|
export class VAxios {
|
||||||
private axiosInstance: AxiosInstance;
|
private axiosInstance: AxiosInstance
|
||||||
private options: CreateAxiosOptions;
|
private options: CreateAxiosOptions
|
||||||
|
|
||||||
constructor(options: CreateAxiosOptions) {
|
constructor(options: CreateAxiosOptions) {
|
||||||
this.options = options;
|
this.options = options
|
||||||
this.axiosInstance = axios.create(options);
|
this.axiosInstance = axios.create(options)
|
||||||
this.setupInterceptors();
|
this.setupInterceptors()
|
||||||
}
|
}
|
||||||
|
|
||||||
getAxios(): AxiosInstance {
|
getAxios(): AxiosInstance {
|
||||||
return this.axiosInstance;
|
return this.axiosInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,9 +34,9 @@ export class VAxios {
|
|||||||
*/
|
*/
|
||||||
configAxios(config: CreateAxiosOptions) {
|
configAxios(config: CreateAxiosOptions) {
|
||||||
if (!this.axiosInstance) {
|
if (!this.axiosInstance) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
this.createAxios(config);
|
this.createAxios(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,97 +44,99 @@ export class VAxios {
|
|||||||
*/
|
*/
|
||||||
setHeader(headers: any): void {
|
setHeader(headers: any): void {
|
||||||
if (!this.axiosInstance) {
|
if (!this.axiosInstance) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
Object.assign(this.axiosInstance.defaults.headers, headers);
|
Object.assign(this.axiosInstance.defaults.headers, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求方法
|
* @description: 请求方法
|
||||||
*/
|
*/
|
||||||
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
||||||
let conf: AxiosRequestConfig = cloneDeep(config);
|
let conf: AxiosRequestConfig = cloneDeep(config)
|
||||||
const transform = this.getTransform();
|
const transform = this.getTransform()
|
||||||
|
|
||||||
const { requestOptions } = this.options;
|
const { requestOptions } = this.options
|
||||||
|
|
||||||
const opt: RequestOptions = Object.assign({}, requestOptions, options);
|
const opt: RequestOptions = { ...requestOptions, ...options }
|
||||||
|
|
||||||
const { beforeRequestHook, requestCatch, transformRequestData } = transform || {};
|
const { beforeRequestHook, requestCatch, transformRequestData } = transform || {}
|
||||||
if (beforeRequestHook && isFunction(beforeRequestHook)) {
|
if (beforeRequestHook && isFunction(beforeRequestHook)) {
|
||||||
conf = beforeRequestHook(conf, opt);
|
conf = beforeRequestHook(conf, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
//这里重新 赋值成最新的配置
|
// 这里重新 赋值成最新的配置
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
conf.requestOptions = opt;
|
conf.requestOptions = opt
|
||||||
// 支持 FormData
|
// 支持 FormData
|
||||||
conf = this.supportFormData(conf);
|
conf = this.supportFormData(conf)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axiosInstance
|
this.axiosInstance
|
||||||
.request<any, AxiosResponse<Result>>(conf)
|
.request<any, AxiosResponse<Result>>(conf)
|
||||||
.then((res: AxiosResponse<Result>) => {
|
.then((res: AxiosResponse<Result>) => {
|
||||||
// 请求是否被取消
|
// 请求是否被取消
|
||||||
const isCancel = axios.isCancel(res);
|
const isCancel = axios.isCancel(res)
|
||||||
if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
|
if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
|
||||||
try {
|
try {
|
||||||
const ret = transformRequestData(res, opt);
|
const ret = transformRequestData(res, opt)
|
||||||
resolve(ret);
|
resolve(ret)
|
||||||
} catch (err) {
|
|
||||||
reject(err || new Error('request error!'));
|
|
||||||
}
|
}
|
||||||
return;
|
catch (err) {
|
||||||
|
reject(err || new Error('request error!'))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
resolve(res as unknown as Promise<T>);
|
resolve(res as unknown as Promise<T>)
|
||||||
})
|
})
|
||||||
.catch((e: Error) => {
|
.catch((e: Error) => {
|
||||||
if (requestCatch && isFunction(requestCatch)) {
|
if (requestCatch && isFunction(requestCatch)) {
|
||||||
reject(requestCatch(e));
|
reject(requestCatch(e))
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
reject(e);
|
reject(e)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 创建axios实例
|
* @description: 创建axios实例
|
||||||
*/
|
*/
|
||||||
private createAxios(config: CreateAxiosOptions): void {
|
private createAxios(config: CreateAxiosOptions): void {
|
||||||
this.axiosInstance = axios.create(config);
|
this.axiosInstance = axios.create(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTransform() {
|
private getTransform() {
|
||||||
const { transform } = this.options;
|
const { transform } = this.options
|
||||||
return transform;
|
return transform
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 文件上传
|
* @description: 文件上传
|
||||||
*/
|
*/
|
||||||
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
|
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
|
||||||
const formData = new window.FormData();
|
const formData = new window.FormData()
|
||||||
const customFilename = params.name || 'file';
|
const customFilename = params.name || 'file'
|
||||||
|
|
||||||
if (params.filename) {
|
if (params.filename) {
|
||||||
formData.append(customFilename, params.file, params.filename);
|
formData.append(customFilename, params.file, params.filename)
|
||||||
} else {
|
}
|
||||||
formData.append(customFilename, params.file);
|
else {
|
||||||
|
formData.append(customFilename, params.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.data) {
|
if (params.data) {
|
||||||
Object.keys(params.data).forEach((key) => {
|
Object.keys(params.data).forEach((key) => {
|
||||||
const value = params.data![key];
|
const value = params.data![key]
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value.forEach((item) => {
|
value.forEach((item) => {
|
||||||
formData.append(`${key}[]`, item);
|
formData.append(`${key}[]`, item)
|
||||||
});
|
})
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
formData.append(key, params.data![key]);
|
formData.append(key, params.data![key])
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axiosInstance.request<T>({
|
return this.axiosInstance.request<T>({
|
||||||
@ -141,80 +144,81 @@ export class VAxios {
|
|||||||
data: formData,
|
data: formData,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type': ContentTypeEnum.FORM_DATA,
|
'Content-type': ContentTypeEnum.FORM_DATA,
|
||||||
ignoreCancelToken: true,
|
'ignoreCancelToken': true,
|
||||||
},
|
},
|
||||||
...config,
|
...config,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// support form-data
|
// support form-data
|
||||||
supportFormData(config: AxiosRequestConfig) {
|
supportFormData(config: AxiosRequestConfig) {
|
||||||
const headers = config.headers || this.options.headers;
|
const headers = config.headers || this.options.headers
|
||||||
const contentType = headers?.['Content-Type'] || headers?.['content-type'];
|
const contentType = headers?.['Content-Type'] || headers?.['content-type']
|
||||||
|
|
||||||
if (
|
if (
|
||||||
contentType !== ContentTypeEnum.FORM_URLENCODED ||
|
contentType !== ContentTypeEnum.FORM_URLENCODED
|
||||||
!Reflect.has(config, 'data') ||
|
|| !Reflect.has(config, 'data')
|
||||||
config.method?.toUpperCase() === RequestEnum.GET
|
|| config.method?.toUpperCase() === RequestEnum.GET
|
||||||
) {
|
) {
|
||||||
return config;
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
|
data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 拦截器配置
|
* @description: 拦截器配置
|
||||||
*/
|
*/
|
||||||
private setupInterceptors() {
|
private setupInterceptors() {
|
||||||
const transform = this.getTransform();
|
const transform = this.getTransform()
|
||||||
if (!transform) {
|
if (!transform) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
requestInterceptors,
|
requestInterceptors,
|
||||||
requestInterceptorsCatch,
|
requestInterceptorsCatch,
|
||||||
responseInterceptors,
|
responseInterceptors,
|
||||||
responseInterceptorsCatch,
|
responseInterceptorsCatch,
|
||||||
} = transform;
|
} = transform
|
||||||
|
|
||||||
const axiosCanceler = new AxiosCanceler();
|
const axiosCanceler = new AxiosCanceler()
|
||||||
|
|
||||||
// 请求拦截器配置处理
|
// 请求拦截器配置处理
|
||||||
|
// @ts-expect-error
|
||||||
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
|
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
|
||||||
const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config;
|
const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config
|
||||||
const ignoreCancel =
|
const ignoreCancel
|
||||||
ignoreCancelToken !== undefined
|
= ignoreCancelToken !== undefined
|
||||||
? ignoreCancelToken
|
? ignoreCancelToken
|
||||||
: this.options.requestOptions?.ignoreCancelToken;
|
: this.options.requestOptions?.ignoreCancelToken
|
||||||
|
|
||||||
!ignoreCancel && axiosCanceler.addPending(config);
|
!ignoreCancel && axiosCanceler.addPending(config)
|
||||||
if (requestInterceptors && isFunction(requestInterceptors)) {
|
if (requestInterceptors && isFunction(requestInterceptors)) {
|
||||||
config = requestInterceptors(config, this.options);
|
config = requestInterceptors(config, this.options)
|
||||||
}
|
}
|
||||||
return config;
|
return config
|
||||||
}, undefined);
|
}, undefined)
|
||||||
|
|
||||||
// 请求拦截器错误捕获
|
// 请求拦截器错误捕获
|
||||||
requestInterceptorsCatch &&
|
requestInterceptorsCatch
|
||||||
isFunction(requestInterceptorsCatch) &&
|
&& isFunction(requestInterceptorsCatch)
|
||||||
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
|
&& this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch)
|
||||||
|
|
||||||
// 响应结果拦截器处理
|
// 响应结果拦截器处理
|
||||||
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
|
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
|
||||||
res && axiosCanceler.removePending(res.config);
|
res && axiosCanceler.removePending(res.config)
|
||||||
if (responseInterceptors && isFunction(responseInterceptors)) {
|
if (responseInterceptors && isFunction(responseInterceptors)) {
|
||||||
res = responseInterceptors(res);
|
res = responseInterceptors(res)
|
||||||
}
|
}
|
||||||
return res;
|
return res
|
||||||
}, undefined);
|
}, undefined)
|
||||||
|
|
||||||
// 响应结果拦截器错误捕获
|
// 响应结果拦截器错误捕获
|
||||||
responseInterceptorsCatch &&
|
responseInterceptorsCatch
|
||||||
isFunction(responseInterceptorsCatch) &&
|
&& isFunction(responseInterceptorsCatch)
|
||||||
this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
|
&& this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,33 @@
|
|||||||
import axios, { AxiosRequestConfig, Canceler } from 'axios';
|
import type { AxiosRequestConfig, Canceler } from 'axios'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
import qs from 'qs';
|
import qs from 'qs'
|
||||||
|
|
||||||
import { isFunction } from '@/utils/is/index';
|
import { isFunction } from '@/utils/is/index'
|
||||||
|
|
||||||
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
|
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
|
||||||
let pendingMap = new Map<string, Canceler>();
|
let pendingMap = new Map<string, Canceler>()
|
||||||
|
|
||||||
export const getPendingUrl = (config: AxiosRequestConfig) =>
|
export function getPendingUrl(config: AxiosRequestConfig) {
|
||||||
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&');
|
return [config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&')
|
||||||
|
}
|
||||||
|
|
||||||
export class AxiosCanceler {
|
export class AxiosCanceler {
|
||||||
/**
|
/**
|
||||||
* 添加请求
|
* 添加请求
|
||||||
* @param {Object} config
|
* @param {object} config
|
||||||
*/
|
*/
|
||||||
addPending(config: AxiosRequestConfig) {
|
addPending(config: AxiosRequestConfig) {
|
||||||
this.removePending(config);
|
this.removePending(config)
|
||||||
const url = getPendingUrl(config);
|
const url = getPendingUrl(config)
|
||||||
config.cancelToken =
|
config.cancelToken
|
||||||
config.cancelToken ||
|
= config.cancelToken
|
||||||
new axios.CancelToken((cancel) => {
|
|| new axios.CancelToken((cancel) => {
|
||||||
if (!pendingMap.has(url)) {
|
if (!pendingMap.has(url)) {
|
||||||
// 如果 pending 中不存在当前请求,则添加进去
|
// 如果 pending 中不存在当前请求,则添加进去
|
||||||
pendingMap.set(url, cancel);
|
pendingMap.set(url, cancel)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,23 +35,23 @@ export class AxiosCanceler {
|
|||||||
*/
|
*/
|
||||||
removeAllPending() {
|
removeAllPending() {
|
||||||
pendingMap.forEach((cancel) => {
|
pendingMap.forEach((cancel) => {
|
||||||
cancel && isFunction(cancel) && cancel();
|
cancel && isFunction(cancel) && cancel()
|
||||||
});
|
})
|
||||||
pendingMap.clear();
|
pendingMap.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除请求
|
* 移除请求
|
||||||
* @param {Object} config
|
* @param {object} config
|
||||||
*/
|
*/
|
||||||
removePending(config: AxiosRequestConfig) {
|
removePending(config: AxiosRequestConfig) {
|
||||||
const url = getPendingUrl(config);
|
const url = getPendingUrl(config)
|
||||||
|
|
||||||
if (pendingMap.has(url)) {
|
if (pendingMap.has(url)) {
|
||||||
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
|
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
|
||||||
const cancel = pendingMap.get(url);
|
const cancel = pendingMap.get(url)
|
||||||
cancel && cancel(url);
|
cancel && cancel(url)
|
||||||
pendingMap.delete(url);
|
pendingMap.delete(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +59,6 @@ export class AxiosCanceler {
|
|||||||
* @description: 重置
|
* @description: 重置
|
||||||
*/
|
*/
|
||||||
reset(): void {
|
reset(): void {
|
||||||
pendingMap = new Map<string, Canceler>();
|
pendingMap = new Map<string, Canceler>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* 数据处理类,可以根据项目自行配置
|
* 数据处理类,可以根据项目自行配置
|
||||||
*/
|
*/
|
||||||
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
import type { RequestOptions, Result } from './types';
|
import type { RequestOptions, Result } from './types'
|
||||||
|
|
||||||
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
||||||
authenticationScheme?: string;
|
authenticationScheme?: string
|
||||||
transform?: AxiosTransform;
|
transform?: AxiosTransform
|
||||||
requestOptions?: RequestOptions;
|
requestOptions?: RequestOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AxiosTransform {
|
export abstract class AxiosTransform {
|
||||||
@ -15,17 +15,17 @@ export abstract class AxiosTransform {
|
|||||||
* @description: 请求之前处理配置
|
* @description: 请求之前处理配置
|
||||||
* @description: Process configuration before request
|
* @description: Process configuration before request
|
||||||
*/
|
*/
|
||||||
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
|
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求成功处理
|
* @description: 请求成功处理
|
||||||
*/
|
*/
|
||||||
transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
|
transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求失败处理
|
* @description: 请求失败处理
|
||||||
*/
|
*/
|
||||||
requestCatch?: (e: Error) => Promise<any>;
|
requestCatch?: (e: Error) => Promise<any>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求之前的拦截器
|
* @description: 请求之前的拦截器
|
||||||
@ -33,20 +33,20 @@ export abstract class AxiosTransform {
|
|||||||
requestInterceptors?: (
|
requestInterceptors?: (
|
||||||
config: AxiosRequestConfig,
|
config: AxiosRequestConfig,
|
||||||
options: CreateAxiosOptions
|
options: CreateAxiosOptions
|
||||||
) => AxiosRequestConfig;
|
) => AxiosRequestConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求之后的拦截器
|
* @description: 请求之后的拦截器
|
||||||
*/
|
*/
|
||||||
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
|
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求之前的拦截器错误处理
|
* @description: 请求之前的拦截器错误处理
|
||||||
*/
|
*/
|
||||||
requestInterceptorsCatch?: (error: Error) => void;
|
requestInterceptorsCatch?: (error: Error) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求之后的拦截器错误处理
|
* @description: 请求之后的拦截器错误处理
|
||||||
*/
|
*/
|
||||||
responseInterceptorsCatch?: (error: Error) => void;
|
responseInterceptorsCatch?: (error: Error) => void
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
import { showFailToast } from 'vant';
|
import { showFailToast } from 'vant'
|
||||||
|
|
||||||
export function checkStatus(status: number, msg: string): void {
|
export function checkStatus(status: number, msg: string): void {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 400:
|
case 400:
|
||||||
showFailToast(msg);
|
showFailToast(msg)
|
||||||
break;
|
break
|
||||||
// 401: 未登录
|
// 401: 未登录
|
||||||
// 未登录则跳转登录页面,并携带当前页面的路径
|
// 未登录则跳转登录页面,并携带当前页面的路径
|
||||||
// 在登录成功后返回当前页面,这一步需要在登录页操作。
|
// 在登录成功后返回当前页面,这一步需要在登录页操作。
|
||||||
case 401:
|
case 401:
|
||||||
showFailToast('用户没有权限(令牌、用户名、密码错误)!');
|
showFailToast('用户没有权限(令牌、用户名、密码错误)!')
|
||||||
break;
|
break
|
||||||
case 403:
|
case 403:
|
||||||
showFailToast('用户得到授权,但是访问是被禁止的。!');
|
showFailToast('用户得到授权,但是访问是被禁止的。!')
|
||||||
break;
|
break
|
||||||
// 404请求不存在
|
// 404请求不存在
|
||||||
case 404:
|
case 404:
|
||||||
showFailToast('网络请求错误,未找到该资源!');
|
showFailToast('网络请求错误,未找到该资源!')
|
||||||
break;
|
break
|
||||||
case 405:
|
case 405:
|
||||||
showFailToast('网络请求错误,请求方法未允许!');
|
showFailToast('网络请求错误,请求方法未允许!')
|
||||||
break;
|
break
|
||||||
case 408:
|
case 408:
|
||||||
showFailToast('网络请求超时');
|
showFailToast('网络请求超时')
|
||||||
break;
|
break
|
||||||
case 500:
|
case 500:
|
||||||
showFailToast('服务器错误,请联系管理员!');
|
showFailToast('服务器错误,请联系管理员!')
|
||||||
break;
|
break
|
||||||
case 501:
|
case 501:
|
||||||
showFailToast('网络未实现');
|
showFailToast('网络未实现')
|
||||||
break;
|
break
|
||||||
case 502:
|
case 502:
|
||||||
showFailToast('网络错误');
|
showFailToast('网络错误')
|
||||||
break;
|
break
|
||||||
case 503:
|
case 503:
|
||||||
showFailToast('服务不可用,服务器暂时过载或维护!');
|
showFailToast('服务不可用,服务器暂时过载或维护!')
|
||||||
break;
|
break
|
||||||
case 504:
|
case 504:
|
||||||
showFailToast('网络超时');
|
showFailToast('网络超时')
|
||||||
break;
|
break
|
||||||
case 505:
|
case 505:
|
||||||
showFailToast('http版本不支持该请求!');
|
showFailToast('http版本不支持该请求!')
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
showFailToast(msg);
|
showFailToast(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { isObject, isString } from '@/utils/is';
|
import { isObject, isString } from '@/utils/is'
|
||||||
|
|
||||||
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
|
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm'
|
||||||
|
|
||||||
export function joinTimestamp<T extends boolean>(
|
export function joinTimestamp<T extends boolean>(
|
||||||
join: boolean,
|
join: boolean,
|
||||||
restful: T
|
restful: T
|
||||||
): T extends true ? string : object;
|
): T extends true ? string : object
|
||||||
|
|
||||||
export function joinTimestamp(join: boolean, restful = false): string | object {
|
export function joinTimestamp(join: boolean, restful = false): string | object {
|
||||||
if (!join) {
|
if (!join) {
|
||||||
return restful ? '' : {};
|
return restful ? '' : {}
|
||||||
}
|
}
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime()
|
||||||
if (restful) {
|
if (restful) {
|
||||||
return `?_t=${now}`;
|
return `?_t=${now}`
|
||||||
}
|
}
|
||||||
return { _t: now };
|
return { _t: now }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,25 +23,26 @@ export function joinTimestamp(join: boolean, restful = false): string | object {
|
|||||||
*/
|
*/
|
||||||
export function formatRequestDate(params: Recordable) {
|
export function formatRequestDate(params: Recordable) {
|
||||||
if (Object.prototype.toString.call(params) !== '[object Object]') {
|
if (Object.prototype.toString.call(params) !== '[object Object]') {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
if (params[key] && params[key]._isAMomentObject) {
|
if (params[key] && params[key]._isAMomentObject) {
|
||||||
params[key] = params[key].format(DATE_TIME_FORMAT);
|
params[key] = params[key].format(DATE_TIME_FORMAT)
|
||||||
}
|
}
|
||||||
if (isString(key)) {
|
if (isString(key)) {
|
||||||
const value = params[key];
|
const value = params[key]
|
||||||
if (value) {
|
if (value) {
|
||||||
try {
|
try {
|
||||||
params[key] = isString(value) ? value.trim() : value;
|
params[key] = isString(value) ? value.trim() : value
|
||||||
} catch (error) {
|
}
|
||||||
throw new Error(error as any);
|
catch (error) {
|
||||||
|
throw new Error(error as any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isObject(params[key])) {
|
if (isObject(params[key])) {
|
||||||
formatRequestDate(params[key]);
|
formatRequestDate(params[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
|
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
|
||||||
import { VAxios } from './Axios';
|
import type { AxiosResponse } from 'axios'
|
||||||
import { AxiosTransform } from './axiosTransform';
|
import axios from 'axios'
|
||||||
import axios, { AxiosResponse } from 'axios';
|
import { showDialog, showFailToast } from 'vant'
|
||||||
import { checkStatus } from './checkStatus';
|
import { VAxios } from './Axios'
|
||||||
import { joinTimestamp, formatRequestDate } from './helper';
|
import type { AxiosTransform } from './axiosTransform'
|
||||||
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';
|
import { checkStatus } from './checkStatus'
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { formatRequestDate, joinTimestamp } from './helper'
|
||||||
import { useGlobSetting } from '@/hooks/setting';
|
import type { CreateAxiosOptions, RequestOptions, Result } from './types'
|
||||||
|
import { ContentTypeEnum, RequestEnum, ResultEnum } from '@/enums/httpEnum'
|
||||||
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
|
||||||
import { isString } from '@/utils/is/';
|
import { isString } from '@/utils/is/'
|
||||||
import { deepMerge, isUrl } from '@/utils';
|
import { deepMerge, isUrl } from '@/utils'
|
||||||
import { setObjToUrlParams } from '@/utils/urlUtils';
|
import { setObjToUrlParams } from '@/utils/urlUtils'
|
||||||
|
|
||||||
import { RequestOptions, Result, CreateAxiosOptions } from './types';
|
import { useUserStoreWidthOut } from '@/store/modules/user'
|
||||||
|
|
||||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
import router from '@/router'
|
||||||
|
import { storage } from '@/utils/Storage'
|
||||||
|
|
||||||
const globSetting = useGlobSetting();
|
const globSetting = useGlobSetting()
|
||||||
const urlPrefix = globSetting.urlPrefix || '';
|
const urlPrefix = globSetting.urlPrefix || ''
|
||||||
|
|
||||||
import router from '@/router';
|
|
||||||
import { storage } from '@/utils/Storage';
|
|
||||||
import { showFailToast, showDialog } from 'vant';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 数据处理,方便区分多种处理方式
|
* @description: 数据处理,方便区分多种处理方式
|
||||||
@ -39,29 +39,29 @@ const transform: AxiosTransform = {
|
|||||||
errorMessageText,
|
errorMessageText,
|
||||||
isTransformResponse,
|
isTransformResponse,
|
||||||
isReturnNativeResponse,
|
isReturnNativeResponse,
|
||||||
} = options;
|
} = options
|
||||||
|
|
||||||
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||||
if (isReturnNativeResponse) {
|
if (isReturnNativeResponse) {
|
||||||
return res;
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不进行任何处理,直接返回
|
// 不进行任何处理,直接返回
|
||||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||||
if (!isTransformResponse) {
|
if (!isTransformResponse) {
|
||||||
return res.data;
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = res;
|
const { data } = res
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// return '[HTTP] Request has no return value';
|
// return '[HTTP] Request has no return value';
|
||||||
throw new Error('请求出错,请稍候重试');
|
throw new Error('请求出错,请稍候重试')
|
||||||
}
|
}
|
||||||
// 这里 code,result,message为 后台统一的字段,需要修改为项目自己的接口返回格式
|
// 这里 code,result,message为 后台统一的字段,需要修改为项目自己的接口返回格式
|
||||||
const { code, result, message } = data;
|
const { code, result, message } = data
|
||||||
// 请求成功
|
// 请求成功
|
||||||
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
|
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS
|
||||||
// 是否显示提示信息
|
// 是否显示提示信息
|
||||||
if (isShowMessage) {
|
if (isShowMessage) {
|
||||||
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
||||||
@ -69,109 +69,116 @@ const transform: AxiosTransform = {
|
|||||||
message: successMessageText || message || '操作成功!',
|
message: successMessageText || message || '操作成功!',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// on close
|
// on close
|
||||||
});
|
})
|
||||||
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
}
|
||||||
|
else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
||||||
// 是否显示自定义信息提示
|
// 是否显示自定义信息提示
|
||||||
showFailToast(message || errorMessageText || '操作失败!');
|
showFailToast(message || errorMessageText || '操作失败!')
|
||||||
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
}
|
||||||
|
else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
||||||
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||||
showDialog({
|
showDialog({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
message: message,
|
message,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// on close
|
// on close
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接口请求成功,直接返回结果
|
// 接口请求成功,直接返回结果
|
||||||
if (code === ResultEnum.SUCCESS) {
|
if (code === ResultEnum.SUCCESS) {
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
||||||
let errorMsg = message;
|
let errorMsg = message
|
||||||
|
const LoginName = PageEnum.BASE_LOGIN_NAME
|
||||||
|
const LoginPath = PageEnum.BASE_LOGIN
|
||||||
switch (code) {
|
switch (code) {
|
||||||
// 请求失败
|
// 请求失败
|
||||||
case ResultEnum.ERROR:
|
case ResultEnum.ERROR:
|
||||||
showFailToast(errorMsg);
|
showFailToast(errorMsg)
|
||||||
break;
|
break
|
||||||
// token 过期
|
// token 过期
|
||||||
case ResultEnum.TOKEN_EXPIRED:
|
case ResultEnum.TOKEN_EXPIRED:
|
||||||
const LoginName = PageEnum.BASE_LOGIN_NAME;
|
if (router.currentRoute.value?.name === LoginName) {
|
||||||
const LoginPath = PageEnum.BASE_LOGIN;
|
return
|
||||||
if (router.currentRoute.value?.name === LoginName) return;
|
}
|
||||||
// 到登录页
|
// 到登录页
|
||||||
errorMsg = '登录超时,请重新登录!';
|
errorMsg = '登录超时,请重新登录!'
|
||||||
showDialog({
|
showDialog({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
message: '登录身份已失效,请重新登录!',
|
message: '登录身份已失效,请重新登录!',
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
storage.clear();
|
storage.clear()
|
||||||
window.location.href = LoginPath;
|
window.location.href = LoginPath
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// on cancel
|
// on cancel
|
||||||
});
|
})
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 请求之前处理config
|
// 请求之前处理config
|
||||||
beforeRequestHook: (config, options) => {
|
beforeRequestHook: (config, options) => {
|
||||||
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
|
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options
|
||||||
|
|
||||||
const isUrlStr = isUrl(config.url as string);
|
const isUrlStr = isUrl(config.url as string)
|
||||||
|
|
||||||
if (!isUrlStr && joinPrefix) {
|
if (!isUrlStr && joinPrefix) {
|
||||||
config.url = `${urlPrefix}${config.url}`;
|
config.url = `${urlPrefix}${config.url}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUrlStr && apiUrl && isString(apiUrl)) {
|
if (!isUrlStr && apiUrl && isString(apiUrl)) {
|
||||||
config.url = `${apiUrl}${config.url}`;
|
config.url = `${apiUrl}${config.url}`
|
||||||
}
|
}
|
||||||
const params = config.params || {};
|
const params = config.params || {}
|
||||||
const data = config.data || false;
|
const data = config.data || false
|
||||||
if (config.method?.toUpperCase() === RequestEnum.GET) {
|
if (config.method?.toUpperCase() === RequestEnum.GET) {
|
||||||
if (!isString(params)) {
|
if (!isString(params)) {
|
||||||
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
|
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
|
||||||
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
|
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false))
|
||||||
} else {
|
|
||||||
// 兼容restful风格
|
|
||||||
config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
|
|
||||||
config.params = undefined;
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
|
// 兼容restful风格
|
||||||
|
config.url = `${config.url + params}${joinTimestamp(joinTime, true)}`
|
||||||
|
config.params = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (!isString(params)) {
|
if (!isString(params)) {
|
||||||
formatDate && formatRequestDate(params);
|
formatDate && formatRequestDate(params)
|
||||||
if (
|
if (
|
||||||
Reflect.has(config, 'data') &&
|
Reflect.has(config, 'data')
|
||||||
config.data &&
|
&& config.data
|
||||||
(Object.keys(config.data).length > 0 || config.data instanceof FormData)
|
&& (Object.keys(config.data).length > 0 || config.data instanceof FormData)
|
||||||
) {
|
) {
|
||||||
config.data = data;
|
config.data = data
|
||||||
config.params = params;
|
config.params = params
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// params 是添加到 url 的请求字符串中的,用于 get 请求
|
// params 是添加到 url 的请求字符串中的,用于 get 请求
|
||||||
// 非GET请求如果没有提供 data,则将 params 视为 data
|
// 非GET请求如果没有提供 data,则将 params 视为 data
|
||||||
config.data = params;
|
config.data = params
|
||||||
config.params = undefined;
|
config.params = undefined
|
||||||
}
|
}
|
||||||
if (joinParamsToUrl) {
|
if (joinParamsToUrl) {
|
||||||
config.url = setObjToUrlParams(
|
config.url = setObjToUrlParams(
|
||||||
config.url as string,
|
config.url as string,
|
||||||
Object.assign({}, config.params, config.data)
|
{ ...config.params, ...config.data },
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// 兼容restful风格
|
// 兼容restful风格
|
||||||
config.url = config.url + params;
|
config.url = config.url + params
|
||||||
config.params = undefined;
|
config.params = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return config;
|
return config
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,30 +186,30 @@ const transform: AxiosTransform = {
|
|||||||
*/
|
*/
|
||||||
requestInterceptors: (config, options) => {
|
requestInterceptors: (config, options) => {
|
||||||
// 请求之前处理config
|
// 请求之前处理config
|
||||||
const userStore = useUserStoreWidthOut();
|
const userStore = useUserStoreWidthOut()
|
||||||
const token = userStore.getToken;
|
const token = userStore.getToken
|
||||||
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
||||||
// jwt token
|
// jwt token
|
||||||
(config as Recordable).headers.Authorization = options.authenticationScheme
|
(config as Recordable).headers.Authorization = options.authenticationScheme
|
||||||
? `${options.authenticationScheme} ${token}`
|
? `${options.authenticationScheme} ${token}`
|
||||||
: token;
|
: token
|
||||||
}
|
}
|
||||||
return config;
|
return config
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 响应错误处理
|
* @description: 响应错误处理
|
||||||
*/
|
*/
|
||||||
responseInterceptorsCatch: (error: any) => {
|
responseInterceptorsCatch: (error: any) => {
|
||||||
const { response, code, message } = error || {};
|
const { response, code, message } = error || {}
|
||||||
// TODO 此处要根据后端接口返回格式修改
|
// TODO 此处要根据后端接口返回格式修改
|
||||||
const msg: string =
|
const msg: string
|
||||||
response && response.data && response.data.message ? response.data.message : '';
|
= response && response.data && response.data.message ? response.data.message : ''
|
||||||
const err: string = error.toString();
|
const err: string = error.toString()
|
||||||
try {
|
try {
|
||||||
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
|
if (code === 'ECONNABORTED' && message.includes('timeout')) {
|
||||||
showFailToast('接口请求超时,请刷新页面重试!');
|
showFailToast('接口请求超时,请刷新页面重试!')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (err && err.includes('Network Error')) {
|
if (err && err.includes('Network Error')) {
|
||||||
showDialog({
|
showDialog({
|
||||||
@ -210,23 +217,25 @@ const transform: AxiosTransform = {
|
|||||||
message: '请检查您的网络连接是否正常',
|
message: '请检查您的网络连接是否正常',
|
||||||
})
|
})
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch(() => {});
|
.catch(() => {})
|
||||||
return Promise.reject(error);
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
throw new Error(error as any);
|
catch (error) {
|
||||||
|
throw new Error(error as any)
|
||||||
}
|
}
|
||||||
// 请求是否被取消
|
// 请求是否被取消
|
||||||
const isCancel = axios.isCancel(error);
|
const isCancel = axios.isCancel(error)
|
||||||
if (!isCancel) {
|
if (!isCancel) {
|
||||||
checkStatus(error.response && error.response.status, msg);
|
checkStatus(error.response && error.response.status, msg)
|
||||||
} else {
|
|
||||||
console.warn(error, '请求被取消!');
|
|
||||||
}
|
}
|
||||||
//return Promise.reject(error);
|
else {
|
||||||
return Promise.reject(response?.data);
|
console.warn(error, '请求被取消!')
|
||||||
|
}
|
||||||
|
// return Promise.reject(error);
|
||||||
|
return Promise.reject(response?.data)
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||||
return new VAxios(
|
return new VAxios(
|
||||||
@ -260,7 +269,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
|||||||
// 接口地址
|
// 接口地址
|
||||||
apiUrl: globSetting.apiUrl,
|
apiUrl: globSetting.apiUrl,
|
||||||
// 接口拼接地址
|
// 接口拼接地址
|
||||||
urlPrefix: urlPrefix,
|
urlPrefix,
|
||||||
// 是否加入时间戳
|
// 是否加入时间戳
|
||||||
joinTime: true,
|
joinTime: true,
|
||||||
// 忽略重复请求
|
// 忽略重复请求
|
||||||
@ -270,12 +279,12 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
|||||||
},
|
},
|
||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
},
|
},
|
||||||
opt || {}
|
opt || {},
|
||||||
)
|
),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const http = createAxios();
|
export const http = createAxios()
|
||||||
|
|
||||||
// 项目,多个不同 api 地址,直接在这里导出多个
|
// 项目,多个不同 api 地址,直接在这里导出多个
|
||||||
// src/api ts 里面接口,就可以单独使用这个请求,
|
// src/api ts 里面接口,就可以单独使用这个请求,
|
||||||
|
@ -1,65 +1,65 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import type { AxiosRequestConfig } from 'axios'
|
||||||
import { AxiosTransform } from './axiosTransform';
|
import type { AxiosTransform } from './axiosTransform'
|
||||||
|
|
||||||
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
||||||
transform?: AxiosTransform;
|
transform?: AxiosTransform
|
||||||
requestOptions?: RequestOptions;
|
requestOptions?: RequestOptions
|
||||||
authenticationScheme?: string;
|
authenticationScheme?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传文件
|
// 上传文件
|
||||||
export interface UploadFileParams {
|
export interface UploadFileParams {
|
||||||
// 其他参数
|
// 其他参数
|
||||||
data?: Recordable;
|
data?: Recordable
|
||||||
// 文件参数接口字段名
|
// 文件参数接口字段名
|
||||||
name?: string;
|
name?: string
|
||||||
// 文件
|
// 文件
|
||||||
file: File | Blob;
|
file: File | Blob
|
||||||
// 文件名称
|
// 文件名称
|
||||||
filename?: string;
|
filename?: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestOptions {
|
export interface RequestOptions {
|
||||||
// 请求参数拼接到url
|
// 请求参数拼接到url
|
||||||
joinParamsToUrl?: boolean;
|
joinParamsToUrl?: boolean
|
||||||
// 格式化请求参数时间
|
// 格式化请求参数时间
|
||||||
formatDate?: boolean;
|
formatDate?: boolean
|
||||||
// 是否显示提示信息
|
// 是否显示提示信息
|
||||||
isShowMessage?: boolean;
|
isShowMessage?: boolean
|
||||||
// 是否解析成JSON
|
// 是否解析成JSON
|
||||||
isParseToJson?: boolean;
|
isParseToJson?: boolean
|
||||||
// 成功的文本信息
|
// 成功的文本信息
|
||||||
successMessageText?: string;
|
successMessageText?: string
|
||||||
// 是否显示成功信息
|
// 是否显示成功信息
|
||||||
isShowSuccessMessage?: boolean;
|
isShowSuccessMessage?: boolean
|
||||||
// 是否显示失败信息
|
// 是否显示失败信息
|
||||||
isShowErrorMessage?: boolean;
|
isShowErrorMessage?: boolean
|
||||||
// 错误的文本信息
|
// 错误的文本信息
|
||||||
errorMessageText?: string;
|
errorMessageText?: string
|
||||||
// 是否加入url
|
// 是否加入url
|
||||||
joinPrefix?: boolean;
|
joinPrefix?: boolean
|
||||||
// 接口地址, 不填则使用默认apiUrl
|
// 接口地址, 不填则使用默认apiUrl
|
||||||
apiUrl?: string;
|
apiUrl?: string
|
||||||
// 请求拼接路径
|
// 请求拼接路径
|
||||||
urlPrefix?: string;
|
urlPrefix?: string
|
||||||
// 错误消息提示类型
|
// 错误消息提示类型
|
||||||
errorMessageMode?: 'none' | 'modal';
|
errorMessageMode?: 'none' | 'modal'
|
||||||
// 是否添加时间戳
|
// 是否添加时间戳
|
||||||
joinTime?: boolean;
|
joinTime?: boolean
|
||||||
// 不进行任何处理,直接返回
|
// 不进行任何处理,直接返回
|
||||||
isTransformResponse?: boolean;
|
isTransformResponse?: boolean
|
||||||
// 是否返回原生响应头
|
// 是否返回原生响应头
|
||||||
isReturnNativeResponse?: boolean;
|
isReturnNativeResponse?: boolean
|
||||||
//忽略重复请求
|
// 忽略重复请求
|
||||||
ignoreCancelToken?: boolean;
|
ignoreCancelToken?: boolean
|
||||||
// 是否携带token
|
// 是否携带token
|
||||||
withToken?: boolean;
|
withToken?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Result<T = any> {
|
export interface Result<T = any> {
|
||||||
code: number;
|
code: number
|
||||||
type?: 'success' | 'error' | 'warning';
|
type?: 'success' | 'error' | 'warning'
|
||||||
message: string;
|
message: string
|
||||||
result?: T;
|
result?: T
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { isObject } from './is/index';
|
import { isObject } from './is/index'
|
||||||
|
|
||||||
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
|
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
|
||||||
let key: string;
|
let key: string
|
||||||
for (key in target) {
|
for (key in target) {
|
||||||
src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);
|
src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key])
|
||||||
}
|
}
|
||||||
return src;
|
return src
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,9 +15,9 @@ export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
|
|||||||
* @returns {string} The processed part of the color
|
* @returns {string} The processed part of the color
|
||||||
*/
|
*/
|
||||||
function addLight(color: string, amount: number) {
|
function addLight(color: string, amount: number) {
|
||||||
const cc = parseInt(color, 16) + amount;
|
const cc = Number.parseInt(color, 16) + amount
|
||||||
const c = cc > 255 ? 255 : cc;
|
const c = cc > 255 ? 255 : cc
|
||||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
|
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,12 +27,12 @@ function addLight(color: string, amount: number) {
|
|||||||
* @returns {string} The HEX representation of the processed color
|
* @returns {string} The HEX representation of the processed color
|
||||||
*/
|
*/
|
||||||
export function darken(color: string, amount: number) {
|
export function darken(color: string, amount: number) {
|
||||||
color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
|
color = color.includes('#') ? color.substring(1, color.length) : color
|
||||||
amount = Math.trunc((255 * amount) / 100);
|
amount = Math.trunc((255 * amount) / 100)
|
||||||
return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(
|
return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(
|
||||||
color.substring(2, 4),
|
color.substring(2, 4),
|
||||||
amount
|
amount,
|
||||||
)}${subtractLight(color.substring(4, 6), amount)}`;
|
)}${subtractLight(color.substring(4, 6), amount)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,31 +42,31 @@ export function darken(color: string, amount: number) {
|
|||||||
* @returns {string} The processed color represented as HEX
|
* @returns {string} The processed color represented as HEX
|
||||||
*/
|
*/
|
||||||
export function lighten(color: string, amount: number) {
|
export function lighten(color: string, amount: number) {
|
||||||
color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
|
color = color.includes('#') ? color.substring(1, color.length) : color
|
||||||
amount = Math.trunc((255 * amount) / 100);
|
amount = Math.trunc((255 * amount) / 100)
|
||||||
return `#${addLight(color.substring(0, 2), amount)}${addLight(
|
return `#${addLight(color.substring(0, 2), amount)}${addLight(
|
||||||
color.substring(2, 4),
|
color.substring(2, 4),
|
||||||
amount
|
amount,
|
||||||
)}${addLight(color.substring(4, 6), amount)}`;
|
)}${addLight(color.substring(4, 6), amount)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否 url
|
* 判断是否 url
|
||||||
* */
|
*/
|
||||||
const RegExp = /^http(s)?:\/\//iu;
|
const RegExp = /^http(s)?:\/\//iu
|
||||||
export function isUrl(url: string) {
|
export function isUrl(url: string) {
|
||||||
return RegExp.test(url);
|
return RegExp.test(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一维数组转二维数组
|
* 一维数组转二维数组
|
||||||
* */
|
*/
|
||||||
export function arrayTrans(arr: number[]): number[][] {
|
export function arrayTrans(arr: number[]): number[][] {
|
||||||
const newArr: number[][] = [];
|
const newArr: number[][] = []
|
||||||
while (arr.length > 0) {
|
while (arr.length > 0) {
|
||||||
newArr.push(arr.splice(0, 2));
|
newArr.push(arr.splice(0, 2))
|
||||||
}
|
}
|
||||||
return newArr;
|
return newArr
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,24 +76,24 @@ export function arrayTrans(arr: number[]): number[][] {
|
|||||||
* @returns {string} The processed part of the color
|
* @returns {string} The processed part of the color
|
||||||
*/
|
*/
|
||||||
function subtractLight(color: string, amount: number) {
|
function subtractLight(color: string, amount: number) {
|
||||||
const cc = parseInt(color, 16) - amount;
|
const cc = Number.parseInt(color, 16) - amount
|
||||||
const c = cc < 0 ? 0 : cc;
|
const c = cc < 0 ? 0 : cc
|
||||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
|
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hexToRgba(hex: string, opacity: number) {
|
export function hexToRgba(hex: string, opacity: number) {
|
||||||
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
|
||||||
hex = hex.replace(shorthandRegex, function (m, r, g, b) {
|
hex = hex.replace(shorthandRegex, (m, r, g, b) => {
|
||||||
return r + r + g + g + b + b;
|
return r + r + g + g + b + b
|
||||||
});
|
})
|
||||||
|
|
||||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||||
opacity = opacity >= 0 && opacity <= 1 ? Number(opacity) : 1;
|
opacity = opacity >= 0 && opacity <= 1 ? Number(opacity) : 1
|
||||||
return result
|
return result
|
||||||
? 'rgba(' +
|
? `rgba(${
|
||||||
[parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16), opacity].join(
|
[Number.parseInt(result[1], 16), Number.parseInt(result[2], 16), Number.parseInt(result[3], 16), opacity].join(
|
||||||
','
|
',',
|
||||||
) +
|
)
|
||||||
')'
|
})`
|
||||||
: hex;
|
: hex
|
||||||
}
|
}
|
||||||
|
@ -1,125 +1,125 @@
|
|||||||
const toString = Object.prototype.toString;
|
const toString = Object.prototype.toString
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 判断值是否未某个类型
|
* @description: 判断值是否未某个类型
|
||||||
*/
|
*/
|
||||||
export function is(val: unknown, type: string) {
|
export function is(val: unknown, type: string) {
|
||||||
return toString.call(val) === `[object ${type}]`;
|
return toString.call(val) === `[object ${type}]`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为函数
|
* @description: 是否为函数
|
||||||
*/
|
*/
|
||||||
export function isFunction<T = Function>(val: unknown): val is T {
|
export function isFunction<T = Function>(val: unknown): val is T {
|
||||||
return is(val, 'Function');
|
return is(val, 'Function')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否已定义
|
* @description: 是否已定义
|
||||||
*/
|
*/
|
||||||
export const isDef = <T = unknown>(val?: T): val is T => {
|
export function isDef<T = unknown>(val?: T): val is T {
|
||||||
return typeof val !== 'undefined';
|
return typeof val !== 'undefined'
|
||||||
};
|
}
|
||||||
|
|
||||||
export const isUnDef = <T = unknown>(val?: T): val is T => {
|
export function isUnDef<T = unknown>(val?: T): val is T {
|
||||||
return !isDef(val);
|
return !isDef(val)
|
||||||
};
|
}
|
||||||
/**
|
/**
|
||||||
* @description: 是否为对象
|
* @description: 是否为对象
|
||||||
*/
|
*/
|
||||||
export const isObject = (val: any): val is Record<any, any> => {
|
export function isObject(val: any): val is Record<any, any> {
|
||||||
return val !== null && is(val, 'Object');
|
return val !== null && is(val, 'Object')
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为时间
|
* @description: 是否为时间
|
||||||
*/
|
*/
|
||||||
export function isDate(val: unknown): val is Date {
|
export function isDate(val: unknown): val is Date {
|
||||||
return is(val, 'Date');
|
return is(val, 'Date')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为数值
|
* @description: 是否为数值
|
||||||
*/
|
*/
|
||||||
export function isNumber(val: unknown): val is number {
|
export function isNumber(val: unknown): val is number {
|
||||||
return is(val, 'Number');
|
return is(val, 'Number')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为AsyncFunction
|
* @description: 是否为AsyncFunction
|
||||||
*/
|
*/
|
||||||
export function isAsyncFunction<T = any>(val: unknown): val is () => Promise<T> {
|
export function isAsyncFunction<T = any>(val: unknown): val is () => Promise<T> {
|
||||||
return is(val, 'AsyncFunction');
|
return is(val, 'AsyncFunction')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为promise
|
* @description: 是否为promise
|
||||||
*/
|
*/
|
||||||
export function isPromise<T = any>(val: unknown): val is Promise<T> {
|
export function isPromise<T = any>(val: unknown): val is Promise<T> {
|
||||||
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch);
|
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为字符串
|
* @description: 是否为字符串
|
||||||
*/
|
*/
|
||||||
export function isString(val: unknown): val is string {
|
export function isString(val: unknown): val is string {
|
||||||
return is(val, 'String');
|
return is(val, 'String')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为boolean类型
|
* @description: 是否为boolean类型
|
||||||
*/
|
*/
|
||||||
export function isBoolean(val: unknown): val is boolean {
|
export function isBoolean(val: unknown): val is boolean {
|
||||||
return is(val, 'Boolean');
|
return is(val, 'Boolean')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为数组
|
* @description: 是否为数组
|
||||||
*/
|
*/
|
||||||
export function isArray(val: any): val is Array<any> {
|
export function isArray(val: any): val is Array<any> {
|
||||||
return val && Array.isArray(val);
|
return val && Array.isArray(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否客户端
|
* @description: 是否客户端
|
||||||
*/
|
*/
|
||||||
export const isClient = () => {
|
export function isClient() {
|
||||||
return typeof window !== 'undefined';
|
return typeof window !== 'undefined'
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否为浏览器
|
* @description: 是否为浏览器
|
||||||
*/
|
*/
|
||||||
export const isWindow = (val: any): val is Window => {
|
export function isWindow(val: any): val is Window {
|
||||||
return typeof window !== 'undefined' && is(val, 'Window');
|
return typeof window !== 'undefined' && is(val, 'Window')
|
||||||
};
|
}
|
||||||
|
|
||||||
export const isElement = (val: unknown): val is Element => {
|
export function isElement(val: unknown): val is Element {
|
||||||
return isObject(val) && !!val.tagName;
|
return isObject(val) && !!val.tagName
|
||||||
};
|
}
|
||||||
|
|
||||||
export const isServer = typeof window === 'undefined';
|
export const isServer = typeof window === 'undefined'
|
||||||
|
|
||||||
// 是否为图片节点
|
// 是否为图片节点
|
||||||
export function isImageDom(o: Element) {
|
export function isImageDom(o: Element) {
|
||||||
return o && ['IMAGE', 'IMG'].includes(o.tagName);
|
return o && ['IMAGE', 'IMG'].includes(o.tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNull(val: unknown): val is null {
|
export function isNull(val: unknown): val is null {
|
||||||
return val === null;
|
return val === null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNullAndUnDef(val: unknown): val is null | undefined {
|
export function isNullAndUnDef(val: unknown): val is null | undefined {
|
||||||
return isUnDef(val) && isNull(val);
|
return isUnDef(val) && isNull(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNullOrUnDef(val: unknown): val is null | undefined {
|
export function isNullOrUnDef(val: unknown): val is null | undefined {
|
||||||
return isUnDef(val) || isNull(val);
|
return isUnDef(val) || isNull(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 是否是 https
|
* @description: 是否是 https
|
||||||
*/
|
*/
|
||||||
export function isHttps(val: string): boolean {
|
export function isHttps(val: string): boolean {
|
||||||
return /^https?:\/\//.test(val);
|
return /^https?:\/\//.test(val)
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
import * as echarts from 'echarts/core';
|
import * as echarts from 'echarts/core'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BarChart,
|
BarChart,
|
||||||
|
GaugeChart,
|
||||||
LineChart,
|
LineChart,
|
||||||
PieChart,
|
|
||||||
MapChart,
|
MapChart,
|
||||||
PictorialBarChart,
|
PictorialBarChart,
|
||||||
|
PieChart,
|
||||||
RadarChart,
|
RadarChart,
|
||||||
GaugeChart,
|
} from 'echarts/charts'
|
||||||
} from 'echarts/charts';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
GridComponent,
|
|
||||||
PolarComponent,
|
|
||||||
AriaComponent,
|
AriaComponent,
|
||||||
ParallelComponent,
|
|
||||||
LegendComponent,
|
|
||||||
RadarComponent,
|
|
||||||
ToolboxComponent,
|
|
||||||
DataZoomComponent,
|
|
||||||
VisualMapComponent,
|
|
||||||
TimelineComponent,
|
|
||||||
CalendarComponent,
|
CalendarComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
MarkLineComponent,
|
MarkLineComponent,
|
||||||
} from 'echarts/components';
|
ParallelComponent,
|
||||||
|
PolarComponent,
|
||||||
|
RadarComponent,
|
||||||
|
TimelineComponent,
|
||||||
|
TitleComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
VisualMapComponent,
|
||||||
|
} from 'echarts/components'
|
||||||
|
|
||||||
import { SVGRenderer } from 'echarts/renderers';
|
import { SVGRenderer } from 'echarts/renderers'
|
||||||
|
|
||||||
echarts.use([
|
echarts.use([
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
@ -52,6 +52,6 @@ echarts.use([
|
|||||||
TimelineComponent,
|
TimelineComponent,
|
||||||
CalendarComponent,
|
CalendarComponent,
|
||||||
MarkLineComponent,
|
MarkLineComponent,
|
||||||
]);
|
])
|
||||||
|
|
||||||
export default echarts;
|
export default echarts
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
const projectName = import.meta.env.VITE_GLOB_APP_TITLE;
|
const projectName = import.meta.env.VITE_GLOB_APP_TITLE
|
||||||
|
|
||||||
export function warn(message: string) {
|
export function warn(message: string) {
|
||||||
console.warn(`[${projectName} warn]:${message}`);
|
console.warn(`[${projectName} warn]:${message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function error(message: string) {
|
export function error(message: string) {
|
||||||
throw new Error(`[${projectName} error]:${message}`);
|
throw new Error(`[${projectName} error]:${message}`)
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,17 @@
|
|||||||
* ==>www.baidu.com?a=3&b=4
|
* ==>www.baidu.com?a=3&b=4
|
||||||
*/
|
*/
|
||||||
export function setObjToUrlParams(baseUrl: string, obj: object): string {
|
export function setObjToUrlParams(baseUrl: string, obj: object): string {
|
||||||
let parameters = '';
|
let parameters = ''
|
||||||
let url = '';
|
let url = ''
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
|
parameters += `${key}=${encodeURIComponent(obj[key])}&`
|
||||||
}
|
}
|
||||||
parameters = parameters.replace(/&$/, '');
|
parameters = parameters.replace(/&$/, '')
|
||||||
if (/\?$/.test(baseUrl)) {
|
if (/\?$/.test(baseUrl)) {
|
||||||
url = baseUrl + parameters;
|
url = baseUrl + parameters
|
||||||
} else {
|
|
||||||
url = baseUrl.replace(/\/?$/, '?') + parameters;
|
|
||||||
}
|
}
|
||||||
return url;
|
else {
|
||||||
|
url = baseUrl.replace(/\/?$/, '?') + parameters
|
||||||
|
}
|
||||||
|
return url
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col justify-center items-center h-screen p-60px">
|
<div class="h-screen flex flex-col items-center justify-center p-60px">
|
||||||
<div class="wel-box flex flex-col items-center justify-between w-full">
|
<div class="wel-box w-full flex flex-col items-center justify-between">
|
||||||
<SvgIcon class="logo" :size="130" name="logo" />
|
<SvgIcon class="logo" :size="130" name="logo" />
|
||||||
<div class="text-darkBlue dark:text-garyWhite text-2xl font-black mt-12 mb-4 text-center"
|
<div class="text-darkBlue dark:text-garyWhite mb-4 mt-12 text-center text-2xl font-black">
|
||||||
>欢迎来到 {{ title }}</div
|
{{ title }}
|
||||||
>
|
</div>
|
||||||
<div class="w-full mt-4 mb-6">
|
<div class="mb-6 mt-4 w-full">
|
||||||
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
||||||
<van-swipe-item
|
<van-swipe-item
|
||||||
class="text-gray-700 dark:text-gray-400 leading-relaxed text-center"
|
|
||||||
v-for="(text, index) in getSwipeText"
|
v-for="(text, index) in getSwipeText"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
class="text-center text-gray-700 leading-relaxed dark:text-gray-400"
|
||||||
>
|
>
|
||||||
<p class="text-lg">{{ text.title }}</p>
|
<p class="text-lg">
|
||||||
<p class="text-sm">{{ text.details }}</p>
|
{{ text.title }}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm">
|
||||||
|
{{ text.details }}
|
||||||
|
</p>
|
||||||
</van-swipe-item>
|
</van-swipe-item>
|
||||||
</van-swipe>
|
</van-swipe>
|
||||||
</div>
|
</div>
|
||||||
@ -22,48 +26,48 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
import SvgIcon from '@/components/SvgIcon.vue';
|
import SvgIcon from '@/components/SvgIcon.vue'
|
||||||
import { useGlobSetting } from '@/hooks/setting';
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DashboardPage',
|
name: 'DashboardPage',
|
||||||
});
|
})
|
||||||
|
|
||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore()
|
||||||
const globSetting = useGlobSetting();
|
const globSetting = useGlobSetting()
|
||||||
|
|
||||||
const { title } = globSetting;
|
const { title } = globSetting
|
||||||
|
|
||||||
const getSwipeText = computed(() => {
|
const getSwipeText = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: '💡 最新技术栈',
|
title: '💡 最新技术栈',
|
||||||
details: '基于Vue3、Vant4、Vite、TypeScript、UnoCSS等最新技术栈开发',
|
details: '基于Vue3、Vant4、Vite、TypeScript、UnoCSS等最新技术栈开发',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '⚡️ 轻量快速的热重载',
|
title: '⚡️ 轻量快速的热重载',
|
||||||
details: '无论应用程序大小如何,都始终极快的模块热重载(HMR)',
|
details: '无论应用程序大小如何,都始终极快的模块热重载(HMR)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '🔩 主题配置',
|
title: '🔩 主题配置',
|
||||||
details: '具备主题配置及黑暗主题适配,且持久化保存',
|
details: '具备主题配置及黑暗主题适配,且持久化保存',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '🛠️ 丰富的 Vite 插件',
|
title: '🛠️ 丰富的 Vite 插件',
|
||||||
details: '集成大部分 Vite 插件,无需繁琐配置,开箱即用',
|
details: '集成大部分 Vite 插件,无需繁琐配置,开箱即用',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '📊 内置 useEcharts hooks',
|
title: '📊 内置 useEcharts hooks',
|
||||||
details: '满足大部分图表展示,只需要写你的 options',
|
details: '满足大部分图表展示,只需要写你的 options',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '🥳 完善的登录系统、路由、Axios配置',
|
title: '🥳 完善的登录系统、路由、Axios配置',
|
||||||
details: '所有架构已搭建完毕,你可以直接开发你的业务需求',
|
details: '所有架构已搭建完毕,你可以直接开发你的业务需求',
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -1,40 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col justify-center page-container">
|
<div class="page-container flex flex-col justify-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img src="~@/assets/icons/exception/403.svg" alt="" />
|
<img src="~@/assets/icons/exception/403.svg" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="text-base text-gray-500">抱歉,你无权访问该页面</h1>
|
<h1 class="text-base text-gray-500">
|
||||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
抱歉,你无权访问该页面
|
||||||
|
</h1>
|
||||||
|
<n-button type="info" @click="goHome">
|
||||||
|
回到首页
|
||||||
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter();
|
|
||||||
function goHome() {
|
const router = useRouter()
|
||||||
router.push('/');
|
function goHome() {
|
||||||
}
|
router.push('/')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.page-container {
|
.page-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 50px 0;
|
padding: 50px 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
.text-center {
|
.text-center {
|
||||||
h1 {
|
h1 {
|
||||||
color: #666;
|
color: #666;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 350px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,37 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col justify-center page-container">
|
<div class="page-container flex flex-col justify-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img src="~@/assets/icons/exception/404.svg" alt="" />
|
<img src="~@/assets/icons/exception/404.svg" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="text-base text-gray-500">抱歉,你访问的页面不存在</h1>
|
<h1 class="text-base text-gray-500">
|
||||||
<van-button type="primary" @click="goHome">回到首页</van-button>
|
抱歉,你访问的页面不存在
|
||||||
|
</h1>
|
||||||
|
<van-button type="primary" @click="goHome">
|
||||||
|
回到首页
|
||||||
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter();
|
|
||||||
function goHome() {
|
const router = useRouter()
|
||||||
router.push('/');
|
function goHome() {
|
||||||
}
|
router.push('/')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.page-container {
|
.page-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.text-center {
|
.text-center {
|
||||||
h1 {
|
h1 {
|
||||||
color: #666;
|
color: #666;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 350px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,40 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col justify-center page-container">
|
<div class="page-container flex flex-col justify-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img src="~@/assets/icons/exception/500.svg" alt="" />
|
<img src="~@/assets/icons/exception/500.svg" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="text-base text-gray-500">抱歉,服务器出错了</h1>
|
<h1 class="text-base text-gray-500">
|
||||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
抱歉,服务器出错了
|
||||||
|
</h1>
|
||||||
|
<n-button type="info" @click="goHome">
|
||||||
|
回到首页
|
||||||
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter();
|
|
||||||
function goHome() {
|
const router = useRouter()
|
||||||
router.push('/');
|
function goHome() {
|
||||||
}
|
router.push('/')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.page-container {
|
.page-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 50px 0;
|
padding: 50px 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
.text-center {
|
.text-center {
|
||||||
h1 {
|
h1 {
|
||||||
color: #666;
|
color: #666;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 350px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<van-form ref="formRef" v-if="getShow" class="flex flex-col items-center" @submit="handleReset">
|
<van-form v-if="getShow" ref="formRef" class="flex flex-col items-center" @submit="handleReset">
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center mb-25px !rounded-md"
|
|
||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
|
class="enter-y mb-25px items-center !rounded-md"
|
||||||
name="username"
|
name="username"
|
||||||
placeholder="用户名"
|
placeholder="用户名"
|
||||||
:rules="getFormRules.username"
|
:rules="getFormRules.username"
|
||||||
@ -15,8 +15,8 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center mb-25px !rounded-md"
|
|
||||||
v-model="formData.mobile"
|
v-model="formData.mobile"
|
||||||
|
class="enter-y mb-25px items-center !rounded-md"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="手机号码"
|
placeholder="手机号码"
|
||||||
:rules="getFormRules.mobile"
|
:rules="getFormRules.mobile"
|
||||||
@ -29,8 +29,8 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center mb-70px !rounded-md"
|
|
||||||
v-model="formData.sms"
|
v-model="formData.sms"
|
||||||
|
class="enter-y mb-70px items-center !rounded-md"
|
||||||
center
|
center
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入短信验证码"
|
placeholder="请输入短信验证码"
|
||||||
@ -42,7 +42,9 @@
|
|||||||
</Icon>
|
</Icon>
|
||||||
</template>
|
</template>
|
||||||
<template #button>
|
<template #button>
|
||||||
<van-button size="small" type="primary">发送验证码</van-button>
|
<van-button size="small" type="primary">
|
||||||
|
发送验证码
|
||||||
|
</van-button>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
@ -69,39 +71,40 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref, unref } from 'vue';
|
import { computed, reactive, ref, unref } from 'vue'
|
||||||
import type { FormInstance } from 'vant';
|
import type { FormInstance } from 'vant'
|
||||||
import { Icon } from '@vicons/utils';
|
import { Icon } from '@vicons/utils'
|
||||||
import { UserOutlined, MobileOutlined, EditOutlined } from '@vicons/antd';
|
import { EditOutlined, MobileOutlined, UserOutlined } from '@vicons/antd'
|
||||||
import { LoginStateEnum, useLoginState, useFormRules } from './useLogin';
|
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||||
|
|
||||||
const { handleBackLogin, getLoginState } = useLoginState();
|
const { handleBackLogin, getLoginState } = useLoginState()
|
||||||
const { getFormRules } = useFormRules();
|
const { getFormRules } = useFormRules()
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD);
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false)
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>()
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
mobile: '',
|
mobile: '',
|
||||||
sms: '',
|
sms: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
function handleReset() {
|
function handleReset() {
|
||||||
formRef.value
|
formRef.value
|
||||||
?.validate()
|
?.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true
|
||||||
// do something
|
// do something
|
||||||
} finally {
|
}
|
||||||
loading.value = false;
|
finally {
|
||||||
}
|
loading.value = false
|
||||||
})
|
}
|
||||||
.catch(() => {
|
})
|
||||||
console.error('验证失败');
|
.catch(() => {
|
||||||
});
|
console.error('验证失败')
|
||||||
}
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-center h-screen p-8">
|
<div class="h-screen flex justify-center p-8">
|
||||||
<div class="flex flex-col w-full">
|
<div class="w-full flex flex-col">
|
||||||
<LoginTitle />
|
<LoginTitle />
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
<ForgetPasswordForm />
|
<ForgetPasswordForm />
|
||||||
@ -13,18 +13,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LoginTitle from './LoginTitle.vue';
|
import LoginTitle from './LoginTitle.vue'
|
||||||
import LoginForm from './LoginForm.vue';
|
import LoginForm from './LoginForm.vue'
|
||||||
import ForgetPasswordForm from './ForgetPasswordForm.vue';
|
import ForgetPasswordForm from './ForgetPasswordForm.vue'
|
||||||
import RegisterForm from './RegisterForm.vue';
|
import RegisterForm from './RegisterForm.vue'
|
||||||
import LoginWave from './LoginWave.vue';
|
import LoginWave from './LoginWave.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
:deep(.van-field__left-icon) {
|
:deep(.van-field__left-icon) {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
:deep(.van-field__right-icon) {
|
:deep(.van-field__right-icon) {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<van-form ref="formRef" v-if="getShow" class="flex flex-col items-center" @submit="handleSubmit">
|
<van-form v-if="getShow" ref="formRef" class="flex flex-col items-center" @submit="handleSubmit">
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center mb-25px !rounded-md"
|
|
||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
|
class="enter-y mb-25px items-center !rounded-md"
|
||||||
name="username"
|
name="username"
|
||||||
placeholder="用户名"
|
placeholder="用户名"
|
||||||
:rules="getFormRules.username"
|
:rules="getFormRules.username"
|
||||||
@ -14,8 +14,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center mb-25px !rounded-md"
|
|
||||||
v-model="formData.password"
|
v-model="formData.password"
|
||||||
|
class="enter-y mb-25px items-center !rounded-md"
|
||||||
:type="switchPassType ? 'password' : 'text'"
|
:type="switchPassType ? 'password' : 'text'"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="密码"
|
placeholder="密码"
|
||||||
@ -37,16 +37,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<div class="enter-y w-full px-5px flex justify-between mb-100px">
|
<div class="enter-y mb-100px w-full flex justify-between px-5px">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<van-switch class="mr-8px !text-30px" v-model="rememberMe" />
|
<van-switch v-model="rememberMe" class="mr-8px !text-30px" />
|
||||||
<span class="!text-25px">记住我</span>
|
<span class="!text-25px">记住我</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="!text-25px" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">忘记密码?</a>
|
<a class="!text-25px" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">忘记密码?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-button
|
<van-button
|
||||||
class="enter-y !rounded-md !mb-25px"
|
class="enter-y !mb-25px !rounded-md"
|
||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
native-type="submit"
|
native-type="submit"
|
||||||
@ -67,64 +67,69 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref, unref } from 'vue';
|
import { computed, onMounted, reactive, ref, unref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { showFailToast, showLoadingToast, showSuccessToast } from 'vant';
|
import { showFailToast, showLoadingToast, showSuccessToast } from 'vant'
|
||||||
import type { FormInstance } from 'vant';
|
import type { FormInstance } from 'vant'
|
||||||
import { Icon } from '@vicons/utils';
|
import { Icon } from '@vicons/utils'
|
||||||
import { UserOutlined, LockOutlined, EyeOutlined, EyeInvisibleOutlined } from '@vicons/antd';
|
import { EyeInvisibleOutlined, EyeOutlined, LockOutlined, UserOutlined } from '@vicons/antd'
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||||
import { ResultEnum } from '@/enums/httpEnum';
|
import { useUserStore } from '@/store/modules/user'
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
import { LoginStateEnum, useLoginState, useFormRules } from './useLogin';
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
|
|
||||||
const { setLoginState, getLoginState } = useLoginState();
|
const { setLoginState, getLoginState } = useLoginState()
|
||||||
const { getFormRules } = useFormRules();
|
const { getFormRules } = useFormRules()
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>()
|
||||||
const loading = ref(false);
|
const loading = ref(false)
|
||||||
const rememberMe = ref(false);
|
const rememberMe = ref(false)
|
||||||
const switchPassType = ref(true);
|
const switchPassType = ref(true)
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: '123456',
|
password: '123456',
|
||||||
});
|
})
|
||||||
|
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
formRef.value
|
formRef.value
|
||||||
?.validate()
|
?.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true
|
||||||
showLoadingToast('登录中...');
|
showLoadingToast('登录中...')
|
||||||
const { code, message: msg } = await userStore.Login({
|
const { code, message: msg } = await userStore.Login({
|
||||||
username: formData.username,
|
username: formData.username,
|
||||||
password: formData.password,
|
password: formData.password,
|
||||||
});
|
})
|
||||||
if (code == ResultEnum.SUCCESS) {
|
if (code === ResultEnum.SUCCESS) {
|
||||||
const toPath = decodeURIComponent((route.query?.redirect || '/') as string);
|
const toPath = decodeURIComponent((route.query?.redirect || '/') as string)
|
||||||
showSuccessToast('登录成功,即将进入系统');
|
showSuccessToast('登录成功,即将进入系统')
|
||||||
if (route.name === PageEnum.BASE_LOGIN_NAME) {
|
if (route.name === PageEnum.BASE_LOGIN_NAME) {
|
||||||
router.replace('/');
|
router.replace('/')
|
||||||
} else router.replace(toPath);
|
}
|
||||||
} else {
|
else {
|
||||||
showFailToast(msg || '登录失败');
|
router.replace(toPath)
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
}
|
||||||
})
|
else {
|
||||||
.catch(() => {
|
showFailToast(msg || '登录失败')
|
||||||
console.error('验证失败');
|
}
|
||||||
});
|
}
|
||||||
}
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.error('验证失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {});
|
onMounted(() => {})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center justify-center">
|
<div class="flex flex-col items-center justify-center">
|
||||||
<div class="logo my-35px enter-y">
|
<div class="logo enter-y my-35px">
|
||||||
<SvgIcon class="!h-250px !w-250px" name="logo" />
|
<SvgIcon class="!h-250px !w-250px" name="logo" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-80px text-darkBlue dark:text-garyWhite text-45px font-black enter-y">
|
<div class="text-darkBlue dark:text-garyWhite enter-y mb-80px text-45px font-black">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useGlobSetting } from '@/hooks/setting';
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
|
||||||
const globSetting = useGlobSetting();
|
const globSetting = useGlobSetting()
|
||||||
|
|
||||||
const { title } = globSetting;
|
const { title } = globSetting
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="enter-y fixed bottom-0 w-full !-z-5 wave-wrapper">
|
<div class="enter-y wave-wrapper fixed bottom-0 w-full !-z-5">
|
||||||
<svg
|
<svg
|
||||||
class="ignore-waves"
|
class="ignore-waves"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -45,61 +45,61 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
import { hexToRgba } from '@/utils/index';
|
import { hexToRgba } from '@/utils/index'
|
||||||
|
|
||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.wave-wrapper {
|
.wave-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.ignore-waves {
|
.ignore-waves {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
max-height: 80px;
|
max-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation */
|
||||||
|
.parallax > use {
|
||||||
|
animation: move-forever 12s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;
|
||||||
|
animation: move-forever 12s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parallax > use:nth-child(1) {
|
||||||
|
animation-delay: -2s;
|
||||||
|
animation-duration: 7s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parallax > use:nth-child(2) {
|
||||||
|
animation-delay: -3s;
|
||||||
|
animation-duration: 10s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parallax > use:nth-child(3) {
|
||||||
|
animation-delay: -4s;
|
||||||
|
animation-duration: 13s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parallax > use:nth-child(4) {
|
||||||
|
animation-delay: -5s;
|
||||||
|
animation-duration: 16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move-forever {
|
||||||
|
0% {
|
||||||
|
transform: translate3d(-90px, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animation */
|
100% {
|
||||||
.parallax > use {
|
transform: translate3d(85px, 0, 0);
|
||||||
animation: move-forever 12s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;
|
|
||||||
animation: move-forever 12s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parallax > use:nth-child(1) {
|
|
||||||
animation-delay: -2s;
|
|
||||||
animation-duration: 7s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parallax > use:nth-child(2) {
|
|
||||||
animation-delay: -3s;
|
|
||||||
animation-duration: 10s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parallax > use:nth-child(3) {
|
|
||||||
animation-delay: -4s;
|
|
||||||
animation-duration: 13s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parallax > use:nth-child(4) {
|
|
||||||
animation-delay: -5s;
|
|
||||||
animation-duration: 16s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes move-forever {
|
|
||||||
0% {
|
|
||||||
transform: translate3d(-90px, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: translate3d(85px, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<van-form ref="formRef" v-if="getShow" class="flex flex-col" @submit="handleRegister">
|
<van-form v-if="getShow" ref="formRef" class="flex flex-col" @submit="handleRegister">
|
||||||
<van-cell-group inset class="enter-y !mx-0 !mb-60px">
|
<van-cell-group inset class="enter-y !mx-0 !mb-60px">
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center !rounded-md"
|
|
||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
|
class="enter-y items-center !rounded-md"
|
||||||
name="username"
|
name="username"
|
||||||
placeholder="用户名"
|
placeholder="用户名"
|
||||||
:rules="getFormRules.username"
|
:rules="getFormRules.username"
|
||||||
@ -16,8 +16,8 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center !rounded-md"
|
|
||||||
v-model="formData.mobile"
|
v-model="formData.mobile"
|
||||||
|
class="enter-y items-center !rounded-md"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="手机号码"
|
placeholder="手机号码"
|
||||||
:rules="getFormRules.mobile"
|
:rules="getFormRules.mobile"
|
||||||
@ -30,8 +30,8 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center !rounded-md"
|
|
||||||
v-model="formData.sms"
|
v-model="formData.sms"
|
||||||
|
class="enter-y items-center !rounded-md"
|
||||||
center
|
center
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入短信验证码"
|
placeholder="请输入短信验证码"
|
||||||
@ -43,13 +43,15 @@
|
|||||||
</Icon>
|
</Icon>
|
||||||
</template>
|
</template>
|
||||||
<template #button>
|
<template #button>
|
||||||
<van-button size="small" type="primary">发送验证码</van-button>
|
<van-button size="small" type="primary">
|
||||||
|
发送验证码
|
||||||
|
</van-button>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center !rounded-md"
|
|
||||||
v-model="formData.password"
|
v-model="formData.password"
|
||||||
|
class="enter-y items-center !rounded-md"
|
||||||
:type="switchPassType ? 'password' : 'text'"
|
:type="switchPassType ? 'password' : 'text'"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="密码"
|
placeholder="密码"
|
||||||
@ -72,8 +74,8 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
class="enter-y items-center !rounded-md"
|
|
||||||
v-model="formData.confirmPassword"
|
v-model="formData.confirmPassword"
|
||||||
|
class="enter-y items-center !rounded-md"
|
||||||
:type="switchConfirmPassType ? 'password' : 'text'"
|
:type="switchConfirmPassType ? 'password' : 'text'"
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
placeholder="确认密码"
|
placeholder="确认密码"
|
||||||
@ -131,58 +133,59 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref, unref } from 'vue';
|
import { computed, reactive, ref, unref } from 'vue'
|
||||||
import type { FormInstance } from 'vant';
|
import type { FormInstance } from 'vant'
|
||||||
import { Icon } from '@vicons/utils';
|
import { Icon } from '@vicons/utils'
|
||||||
import {
|
import {
|
||||||
UserOutlined,
|
EditOutlined,
|
||||||
MobileOutlined,
|
EyeInvisibleOutlined,
|
||||||
EditOutlined,
|
EyeOutlined,
|
||||||
LockOutlined,
|
LockOutlined,
|
||||||
EyeOutlined,
|
MobileOutlined,
|
||||||
EyeInvisibleOutlined,
|
UserOutlined,
|
||||||
} from '@vicons/antd';
|
} from '@vicons/antd'
|
||||||
import { LoginStateEnum, useLoginState, useFormRules } from './useLogin';
|
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||||
|
|
||||||
const { handleBackLogin, getLoginState } = useLoginState();
|
const { handleBackLogin, getLoginState } = useLoginState()
|
||||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER);
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false)
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
mobile: '',
|
mobile: '',
|
||||||
sms: '',
|
sms: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
policy: false,
|
policy: false,
|
||||||
});
|
})
|
||||||
|
|
||||||
const { getFormRules } = useFormRules(formData);
|
const { getFormRules } = useFormRules(formData)
|
||||||
|
|
||||||
const switchPassType = ref(true);
|
const switchPassType = ref(true)
|
||||||
const switchConfirmPassType = ref(true);
|
const switchConfirmPassType = ref(true)
|
||||||
|
|
||||||
function handleRegister() {
|
function handleRegister() {
|
||||||
formRef.value
|
formRef.value
|
||||||
?.validate()
|
?.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true
|
||||||
// do something
|
// do something
|
||||||
|
|
||||||
console.log('%c [ ]-167', 'font-size:13px; background:pink; color:#bf2c9f;');
|
console.log('%c [ ]-167', 'font-size:13px; background:pink; color:#bf2c9f;')
|
||||||
} finally {
|
}
|
||||||
loading.value = false;
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
console.log('%c [ ]-171', 'font-size:13px; background:pink; color:#bf2c9f;');
|
console.log('%c [ ]-171', 'font-size:13px; background:pink; color:#bf2c9f;')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
console.error('验证失败');
|
console.error('验证失败')
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { FieldRule } from 'vant';
|
import type { FieldRule } from 'vant'
|
||||||
import { computed, ref, unref } from 'vue';
|
import { computed, ref, unref } from 'vue'
|
||||||
|
|
||||||
export enum LoginStateEnum {
|
export enum LoginStateEnum {
|
||||||
LOGIN,
|
LOGIN,
|
||||||
@ -7,54 +7,54 @@ export enum LoginStateEnum {
|
|||||||
RESET_PASSWORD,
|
RESET_PASSWORD,
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentState = ref(LoginStateEnum.LOGIN);
|
const currentState = ref(LoginStateEnum.LOGIN)
|
||||||
|
|
||||||
export function useLoginState() {
|
export function useLoginState() {
|
||||||
function setLoginState(state: LoginStateEnum) {
|
function setLoginState(state: LoginStateEnum) {
|
||||||
currentState.value = state;
|
currentState.value = state
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLoginState = computed(() => currentState.value);
|
const getLoginState = computed(() => currentState.value)
|
||||||
|
|
||||||
function handleBackLogin() {
|
function handleBackLogin() {
|
||||||
setLoginState(LoginStateEnum.LOGIN);
|
setLoginState(LoginStateEnum.LOGIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { setLoginState, getLoginState, handleBackLogin };
|
return { setLoginState, getLoginState, handleBackLogin }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFormRules(formData?: Recordable) {
|
export function useFormRules(formData?: Recordable) {
|
||||||
const getUsernameFormRule = computed(() => createRule('请输入用户名'));
|
const getUsernameFormRule = computed(() => createRule('请输入用户名'))
|
||||||
const getPasswordFormRule = computed(() => createRule('请输入密码'));
|
const getPasswordFormRule = computed(() => createRule('请输入密码'))
|
||||||
const getSmsFormRule = computed(() => createRule('请输入短信验证码'));
|
const getSmsFormRule = computed(() => createRule('请输入短信验证码'))
|
||||||
const getMobileFormRule = computed(() => createRule('请输入手机号码'));
|
const getMobileFormRule = computed(() => createRule('请输入手机号码'))
|
||||||
|
|
||||||
const validatePolicy = async (value: any, _: FieldRule) => {
|
const validatePolicy = async (value: any, _: FieldRule) => {
|
||||||
return !value ? Promise.resolve('勾选后才能注册') : Promise.resolve(true);
|
return !value ? Promise.resolve('勾选后才能注册') : Promise.resolve(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const validateConfirmPassword = (password: string) => {
|
const validateConfirmPassword = (password: string) => {
|
||||||
return async (value: string) => {
|
return async (value: string) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return Promise.resolve('请输入确认密码');
|
return Promise.resolve('请输入确认密码')
|
||||||
}
|
}
|
||||||
if (value !== password) {
|
if (value !== password) {
|
||||||
return Promise.resolve('两次输入密码不一致');
|
return Promise.resolve('两次输入密码不一致')
|
||||||
}
|
}
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true)
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const getFormRules = computed((): { [k: string]: FieldRule[] } => {
|
const getFormRules = computed((): { [k: string]: FieldRule[] } => {
|
||||||
const usernameFormRule = unref(getUsernameFormRule);
|
const usernameFormRule = unref(getUsernameFormRule)
|
||||||
const passwordFormRule = unref(getPasswordFormRule);
|
const passwordFormRule = unref(getPasswordFormRule)
|
||||||
const smsFormRule = unref(getSmsFormRule);
|
const smsFormRule = unref(getSmsFormRule)
|
||||||
const mobileFormRule = unref(getMobileFormRule);
|
const mobileFormRule = unref(getMobileFormRule)
|
||||||
|
|
||||||
const mobileRule = {
|
const mobileRule = {
|
||||||
sms: smsFormRule,
|
sms: smsFormRule,
|
||||||
mobile: mobileFormRule,
|
mobile: mobileFormRule,
|
||||||
};
|
}
|
||||||
switch (unref(currentState)) {
|
switch (unref(currentState)) {
|
||||||
// register form rules
|
// register form rules
|
||||||
case LoginStateEnum.REGISTER:
|
case LoginStateEnum.REGISTER:
|
||||||
@ -66,24 +66,24 @@ export function useFormRules(formData?: Recordable) {
|
|||||||
],
|
],
|
||||||
policy: [{ validator: validatePolicy, trigger: 'onBlur' }],
|
policy: [{ validator: validatePolicy, trigger: 'onBlur' }],
|
||||||
...mobileRule,
|
...mobileRule,
|
||||||
};
|
}
|
||||||
|
|
||||||
// reset password form rules
|
// reset password form rules
|
||||||
case LoginStateEnum.RESET_PASSWORD:
|
case LoginStateEnum.RESET_PASSWORD:
|
||||||
return {
|
return {
|
||||||
username: usernameFormRule,
|
username: usernameFormRule,
|
||||||
...mobileRule,
|
...mobileRule,
|
||||||
};
|
}
|
||||||
|
|
||||||
// login form rules
|
// login form rules
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
username: usernameFormRule,
|
username: usernameFormRule,
|
||||||
password: passwordFormRule,
|
password: passwordFormRule,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return { getFormRules };
|
return { getFormRules }
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRule(message: string): FieldRule[] {
|
function createRule(message: string): FieldRule[] {
|
||||||
@ -93,5 +93,5 @@ function createRule(message: string): FieldRule[] {
|
|||||||
message,
|
message,
|
||||||
trigger: 'onBlur',
|
trigger: 'onBlur',
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,106 +1,107 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-card m-40px p-30px rounded-2xl shadow-xl">
|
<div class="my-card m-40px rounded-2xl p-30px shadow-xl">
|
||||||
<div ref="chartRef" :style="{ height: '350px' }"></div>
|
<div ref="chartRef" :style="{ height: '350px' }" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useECharts } from '@/hooks/web/useECharts';
|
import type { Ref } from 'vue'
|
||||||
import { onMounted, ref, Ref } from 'vue';
|
import { onMounted, ref } from 'vue'
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
|
||||||
const chartRef = ref<HTMLDivElement | null>(null);
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
const chartOptions: EChartsOption = {
|
const chartOptions: EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
// Use axis to trigger tooltip
|
// Use axis to trigger tooltip
|
||||||
type: 'shadow', // 'shadow' as default; can also be 'line' or 'shadow'
|
type: 'shadow', // 'shadow' as default; can also be 'line' or 'shadow'
|
||||||
},
|
|
||||||
},
|
},
|
||||||
legend: {},
|
},
|
||||||
grid: {
|
legend: {},
|
||||||
left: '1%',
|
grid: {
|
||||||
right: '7%',
|
left: '1%',
|
||||||
bottom: '3%',
|
right: '7%',
|
||||||
containLabel: true,
|
bottom: '3%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Direct',
|
||||||
|
type: 'bar',
|
||||||
|
stack: 'total',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [320, 302, 301, 334, 390, 330, 320],
|
||||||
},
|
},
|
||||||
xAxis: {
|
{
|
||||||
type: 'value',
|
name: 'Mail Ad',
|
||||||
|
type: 'bar',
|
||||||
|
stack: 'total',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [120, 132, 101, 134, 90, 230, 210],
|
||||||
},
|
},
|
||||||
yAxis: {
|
{
|
||||||
type: 'category',
|
name: 'Affiliate Ad',
|
||||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
type: 'bar',
|
||||||
|
stack: 'total',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [220, 182, 191, 234, 290, 330, 310],
|
||||||
},
|
},
|
||||||
series: [
|
{
|
||||||
{
|
name: 'Video Ad',
|
||||||
name: 'Direct',
|
type: 'bar',
|
||||||
type: 'bar',
|
stack: 'total',
|
||||||
stack: 'total',
|
label: {
|
||||||
label: {
|
show: true,
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [320, 302, 301, 334, 390, 330, 320],
|
|
||||||
},
|
},
|
||||||
{
|
emphasis: {
|
||||||
name: 'Mail Ad',
|
focus: 'series',
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [120, 132, 101, 134, 90, 230, 210],
|
|
||||||
},
|
},
|
||||||
{
|
data: [150, 212, 201, 154, 190, 330, 410],
|
||||||
name: 'Affiliate Ad',
|
},
|
||||||
type: 'bar',
|
{
|
||||||
stack: 'total',
|
name: 'Search Engine',
|
||||||
label: {
|
type: 'bar',
|
||||||
show: true,
|
stack: 'total',
|
||||||
},
|
label: {
|
||||||
emphasis: {
|
show: true,
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [220, 182, 191, 234, 290, 330, 310],
|
|
||||||
},
|
},
|
||||||
{
|
emphasis: {
|
||||||
name: 'Video Ad',
|
focus: 'series',
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [150, 212, 201, 154, 190, 330, 410],
|
|
||||||
},
|
},
|
||||||
{
|
data: [820, 832, 901, 934, 1290, 1330, 1320],
|
||||||
name: 'Search Engine',
|
},
|
||||||
type: 'bar',
|
],
|
||||||
stack: 'total',
|
}
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [820, 832, 901, 934, 1290, 1330, 1320],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setOptions(chartOptions);
|
setOptions(chartOptions)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import lineChart from './lineChart.vue';
|
import lineChart from './lineChart.vue'
|
||||||
import barChart from './barChart.vue';
|
import barChart from './barChart.vue'
|
||||||
import pieChart from './pieChart.vue';
|
import pieChart from './pieChart.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -1,119 +1,120 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-card m-40px p-30px rounded-2xl shadow-xl">
|
<div class="my-card m-40px rounded-2xl p-30px shadow-xl">
|
||||||
<div ref="chartRef" :style="{ height: '350px' }"></div>
|
<div ref="chartRef" :style="{ height: '350px' }" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useECharts } from '@/hooks/web/useECharts';
|
import type { Ref } from 'vue'
|
||||||
import { onMounted, ref, Ref } from 'vue';
|
import { onMounted, ref } from 'vue'
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
|
||||||
const chartRef = ref<HTMLDivElement | null>(null);
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
const chartOptions: EChartsOption = {
|
const chartOptions: EChartsOption = {
|
||||||
title: {
|
title: {
|
||||||
text: 'Stacked Area Chart',
|
text: 'Stacked Area Chart',
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
type: 'cross',
|
type: 'cross',
|
||||||
label: {
|
label: {
|
||||||
backgroundColor: '#6a7985',
|
backgroundColor: '#6a7985',
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
},
|
||||||
data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine'],
|
legend: {
|
||||||
top: '10%',
|
data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine'],
|
||||||
|
top: '10%',
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
saveAsImage: {},
|
||||||
},
|
},
|
||||||
toolbox: {
|
},
|
||||||
feature: {
|
grid: {
|
||||||
saveAsImage: {},
|
top: '30%',
|
||||||
},
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
},
|
},
|
||||||
grid: {
|
],
|
||||||
top: '30%',
|
yAxis: [
|
||||||
left: '3%',
|
{
|
||||||
right: '4%',
|
type: 'value',
|
||||||
bottom: '3%',
|
|
||||||
containLabel: true,
|
|
||||||
},
|
},
|
||||||
xAxis: [
|
],
|
||||||
{
|
series: [
|
||||||
type: 'category',
|
{
|
||||||
boundaryGap: false,
|
name: 'Email',
|
||||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
},
|
},
|
||||||
],
|
data: [120, 132, 101, 134, 90, 230, 210],
|
||||||
yAxis: [
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
name: 'Union Ads',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
areaStyle: {},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
},
|
},
|
||||||
],
|
data: [220, 182, 191, 234, 290, 330, 310],
|
||||||
series: [
|
},
|
||||||
{
|
{
|
||||||
name: 'Email',
|
name: 'Video Ads',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
stack: 'Total',
|
stack: 'Total',
|
||||||
areaStyle: {},
|
areaStyle: {},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
focus: 'series',
|
focus: 'series',
|
||||||
},
|
|
||||||
data: [120, 132, 101, 134, 90, 230, 210],
|
|
||||||
},
|
},
|
||||||
{
|
data: [150, 232, 201, 154, 190, 330, 410],
|
||||||
name: 'Union Ads',
|
},
|
||||||
type: 'line',
|
{
|
||||||
stack: 'Total',
|
name: 'Direct',
|
||||||
areaStyle: {},
|
type: 'line',
|
||||||
emphasis: {
|
stack: 'Total',
|
||||||
focus: 'series',
|
areaStyle: {},
|
||||||
},
|
emphasis: {
|
||||||
data: [220, 182, 191, 234, 290, 330, 310],
|
focus: 'series',
|
||||||
},
|
},
|
||||||
{
|
data: [320, 332, 301, 334, 390, 330, 320],
|
||||||
name: 'Video Ads',
|
},
|
||||||
type: 'line',
|
{
|
||||||
stack: 'Total',
|
name: 'Search Engine',
|
||||||
areaStyle: {},
|
type: 'line',
|
||||||
emphasis: {
|
stack: 'Total',
|
||||||
focus: 'series',
|
label: {
|
||||||
},
|
show: true,
|
||||||
data: [150, 232, 201, 154, 190, 330, 410],
|
position: 'top',
|
||||||
},
|
},
|
||||||
{
|
areaStyle: {},
|
||||||
name: 'Direct',
|
emphasis: {
|
||||||
type: 'line',
|
focus: 'series',
|
||||||
stack: 'Total',
|
|
||||||
areaStyle: {},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [320, 332, 301, 334, 390, 330, 320],
|
|
||||||
},
|
},
|
||||||
{
|
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||||
name: 'Search Engine',
|
},
|
||||||
type: 'line',
|
],
|
||||||
stack: 'Total',
|
}
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
},
|
|
||||||
areaStyle: {},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setOptions(chartOptions);
|
setOptions(chartOptions)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -1,65 +1,66 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-card m-40px p-30px rounded-2xl shadow-xl">
|
<div class="my-card m-40px rounded-2xl p-30px shadow-xl">
|
||||||
<div ref="chartRef" :style="{ height: '350px' }"></div>
|
<div ref="chartRef" :style="{ height: '350px' }" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useECharts } from '@/hooks/web/useECharts';
|
import type { Ref } from 'vue'
|
||||||
import { onMounted, ref, Ref } from 'vue';
|
import { onMounted, ref } from 'vue'
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
|
||||||
const chartRef = ref<HTMLDivElement | null>(null);
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
const chartOptions: EChartsOption = {
|
const chartOptions: EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
top: '5%',
|
top: '5%',
|
||||||
left: 'center',
|
left: 'center',
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: 'Access From',
|
name: 'Access From',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['40%', '70%'],
|
radius: ['40%', '70%'],
|
||||||
center: ['50%', '60%'],
|
center: ['50%', '60%'],
|
||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
borderColor: '#fff',
|
borderColor: '#fff',
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: false,
|
|
||||||
position: 'center',
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
fontSize: '40',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
labelLine: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
{ value: 1048, name: 'Search Engine' },
|
|
||||||
{ value: 735, name: 'Direct' },
|
|
||||||
{ value: 580, name: 'Email' },
|
|
||||||
{ value: 484, name: 'Union Ads' },
|
|
||||||
{ value: 300, name: 'Video Ads' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
label: {
|
||||||
};
|
show: false,
|
||||||
|
position: 'center',
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: '40',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ value: 1048, name: 'Search Engine' },
|
||||||
|
{ value: 735, name: 'Direct' },
|
||||||
|
{ value: 580, name: 'Email' },
|
||||||
|
{ value: 484, name: 'Union Ads' },
|
||||||
|
{ value: 300, name: 'Video Ads' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setOptions(chartOptions);
|
setOptions(chartOptions)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
<div>
|
<div>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="username"
|
||||||
label="用户名"
|
label="用户名"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="username"
|
|
||||||
/>
|
/>
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="afterPhone"
|
||||||
label="手机号"
|
label="手机号"
|
||||||
readonly
|
readonly
|
||||||
is-link
|
is-link
|
||||||
@ -18,7 +19,6 @@
|
|||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="afterPhone"
|
|
||||||
/>
|
/>
|
||||||
<van-field
|
<van-field
|
||||||
label="修改登录密码"
|
label="修改登录密码"
|
||||||
@ -34,23 +34,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NavBar from './components/NavBar.vue';
|
import { computed } from 'vue'
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import NavBar from './components/NavBar.vue'
|
||||||
import { computed } from 'vue';
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
const { username, phone } = userStore.getUserInfo;
|
const { username, phone } = userStore.getUserInfo
|
||||||
|
|
||||||
const phoneDesensitize = (phone: string) => {
|
function phoneDesensitize(phone: string) {
|
||||||
const reg = /(\d{3})\d{4}(\d{4})/;
|
const reg = /(\d{3})\d{4}(\d{4})/
|
||||||
return phone.replace(reg, '$1****$2');
|
return phone.replace(reg, '$1****$2')
|
||||||
};
|
}
|
||||||
|
|
||||||
const afterPhone = computed(() => phoneDesensitize(phone));
|
const afterPhone = computed(() => phoneDesensitize(phone))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
:deep(.van-field__control) {
|
:deep(.van-field__control) {
|
||||||
color: var(--van-text-color-2);
|
color: var(--van-text-color-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
修改登录密码页面
|
<p>修改登录密码页面</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NavBar from './components/NavBar.vue';
|
import NavBar from './components/NavBar.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NavBar>
|
<NavBar>
|
||||||
<template #right><span class="text-32px" @click="handleNickname">保存</span></template>
|
<template #right>
|
||||||
|
<span class="text-32px" @click="handleNickname">保存</span>
|
||||||
|
</template>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<van-form ref="formRef">
|
<van-form ref="formRef">
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="formValue.nickname"
|
||||||
class="mt-20px"
|
class="mt-20px"
|
||||||
name="nickname"
|
name="nickname"
|
||||||
v-model="formValue.nickname"
|
|
||||||
placeholder="请输入昵称(2-12字)"
|
placeholder="请输入昵称(2-12字)"
|
||||||
:rules="[
|
:rules="[
|
||||||
{
|
{
|
||||||
@ -26,62 +28,63 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NavBar from './components/NavBar.vue';
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import type { FormInstance } from 'vant'
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { showToast } from 'vant'
|
||||||
import type { FormInstance } from 'vant';
|
import NavBar from './components/NavBar.vue'
|
||||||
import { showToast } from 'vant';
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const { nickname } = userStore.getUserInfo;
|
const { nickname } = userStore.getUserInfo
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
const formValue = reactive({
|
const formValue = reactive({
|
||||||
nickname: '',
|
nickname: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const validateNickname = () => {
|
function validateNickname() {
|
||||||
return async (value: string) => {
|
return async (value: string) => {
|
||||||
const pattern = /^[\u4E00-\u9FA5A-Za-z0-9-_.·]+$/;
|
const pattern = /^[\u4E00-\u9FA5A-Za-z0-9-_.·]+$/
|
||||||
if (!pattern.test(value)) {
|
if (!pattern.test(value)) {
|
||||||
return Promise.resolve('请输入正确内容');
|
return Promise.resolve('请输入正确内容')
|
||||||
}
|
}
|
||||||
if (value.length < 2 || value.length > 12) {
|
if (value.length < 2 || value.length > 12) {
|
||||||
return Promise.resolve('长度不符合');
|
return Promise.resolve('长度不符合')
|
||||||
}
|
}
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true)
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleNickname() {
|
|
||||||
formRef.value
|
|
||||||
?.validate()
|
|
||||||
.then(async () => {
|
|
||||||
try {
|
|
||||||
const formValue = formRef.value?.getValues();
|
|
||||||
showToast({
|
|
||||||
message: `当前表单值:${JSON.stringify(formValue)}`,
|
|
||||||
});
|
|
||||||
// do something
|
|
||||||
} finally {
|
|
||||||
// after successful
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.error('验证失败');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
function handleNickname() {
|
||||||
formValue.nickname = nickname;
|
formRef.value
|
||||||
});
|
?.validate()
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const formValue = formRef.value?.getValues()
|
||||||
|
showToast({
|
||||||
|
message: `当前表单值:${JSON.stringify(formValue)}`,
|
||||||
|
})
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// after successful
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.error('验证失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
formValue.nickname = nickname
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.note {
|
.note {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
color: var(--van-text-color-2);
|
color: var(--van-text-color-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NavBar>
|
<NavBar>
|
||||||
<template #right><span class="text-32px" @click="handleNickname">保存</span></template>
|
<template #right>
|
||||||
|
<span class="text-32px" @click="handleNickname">保存</span>
|
||||||
|
</template>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<van-form ref="formRef">
|
<van-form ref="formRef">
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="formValue.sign"
|
||||||
class="mt-20px"
|
class="mt-20px"
|
||||||
name="sign"
|
name="sign"
|
||||||
clearable
|
clearable
|
||||||
v-model="formValue.sign"
|
|
||||||
rows="4"
|
rows="4"
|
||||||
autosize
|
autosize
|
||||||
label="签名"
|
label="签名"
|
||||||
@ -22,49 +24,50 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NavBar from './components/NavBar.vue';
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import type { FormInstance } from 'vant'
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { showToast } from 'vant'
|
||||||
import type { FormInstance } from 'vant';
|
import NavBar from './components/NavBar.vue'
|
||||||
import { showToast } from 'vant';
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const { sign } = userStore.getUserInfo;
|
const { sign } = userStore.getUserInfo
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
const formValue = reactive({
|
const formValue = reactive({
|
||||||
sign: '',
|
sign: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
function handleNickname() {
|
function handleNickname() {
|
||||||
formRef.value
|
formRef.value
|
||||||
?.validate()
|
?.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
try {
|
try {
|
||||||
const formValue = formRef.value?.getValues();
|
const formValue = formRef.value?.getValues()
|
||||||
showToast({
|
showToast({
|
||||||
message: `当前表单值:${JSON.stringify(formValue)}`,
|
message: `当前表单值:${JSON.stringify(formValue)}`,
|
||||||
});
|
})
|
||||||
// do something
|
// do something
|
||||||
} finally {
|
}
|
||||||
// after successful
|
finally {
|
||||||
}
|
// after successful
|
||||||
})
|
}
|
||||||
.catch(() => {
|
})
|
||||||
console.error('验证失败');
|
.catch(() => {
|
||||||
});
|
console.error('验证失败')
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
formValue.sign = sign ?? '';
|
formValue.sign = sign ?? ''
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.note {
|
.note {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
color: var(--van-text-color-2);
|
color: var(--van-text-color-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -19,37 +19,37 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="state.nickname"
|
||||||
label="昵称"
|
label="昵称"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="state.nickname"
|
|
||||||
is-link
|
is-link
|
||||||
to="/editNickname"
|
to="/editNickname"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="state.genderText"
|
||||||
label="性别"
|
label="性别"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="state.genderText"
|
|
||||||
is-link
|
is-link
|
||||||
@click="showGenderPicker = true"
|
@click="showGenderPicker = true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="state.sign"
|
||||||
label="签名"
|
label="签名"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="state.sign"
|
|
||||||
is-link
|
is-link
|
||||||
to="/editSign"
|
to="/editSign"
|
||||||
/>
|
/>
|
||||||
@ -71,21 +71,21 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="state.industryText"
|
||||||
label="行业"
|
label="行业"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="state.industryText"
|
|
||||||
is-link
|
is-link
|
||||||
@click="showIndustryPicker = true"
|
@click="showIndustryPicker = true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<van-popup v-model:show="showGenderPicker" position="bottom" round>
|
<van-popup v-model:show="showGenderPicker" position="bottom" round>
|
||||||
<van-picker
|
<van-picker
|
||||||
visible-option-num="3"
|
|
||||||
v-model="state.genderValues"
|
v-model="state.genderValues"
|
||||||
|
visible-option-num="3"
|
||||||
:columns="genderColumns"
|
:columns="genderColumns"
|
||||||
@confirm="handleGender"
|
@confirm="handleGender"
|
||||||
@cancel="showGenderPicker = false"
|
@cancel="showGenderPicker = false"
|
||||||
@ -104,78 +104,80 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import NavBar from './components/NavBar.vue';
|
import { showToast } from 'vant'
|
||||||
import UploaderImage from './components/UploaderImage.vue';
|
import NavBar from './components/NavBar.vue'
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import UploaderImage from './components/UploaderImage.vue'
|
||||||
import { FormColumns, genderColumns, industryColumns } from './pickColumns';
|
import type { FormColumns } from './pickColumns'
|
||||||
import { showToast } from 'vant';
|
import { genderColumns, industryColumns } from './pickColumns'
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
const { avatar, gender, industry, cover } = userStore.getUserInfo;
|
const { avatar, gender, industry, cover } = userStore.getUserInfo
|
||||||
|
|
||||||
const showGenderPicker = ref(false);
|
const showGenderPicker = ref(false)
|
||||||
const showIndustryPicker = ref(false);
|
const showIndustryPicker = ref(false)
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
nickname: '',
|
nickname: '',
|
||||||
sign: '',
|
sign: '',
|
||||||
// the field v-model
|
// the field v-model
|
||||||
genderText: '',
|
genderText: '',
|
||||||
industryText: '',
|
industryText: '',
|
||||||
// the pick v-model
|
// the pick v-model
|
||||||
industryValues: [0],
|
industryValues: [0],
|
||||||
genderValues: [0],
|
genderValues: [0],
|
||||||
});
|
})
|
||||||
|
|
||||||
const handleGender = ({ selectedOptions }) => {
|
function handleGender({ selectedOptions }) {
|
||||||
state.genderText = selectedOptions[0].text;
|
state.genderText = selectedOptions[0].text
|
||||||
showToast(JSON.stringify(selectedOptions));
|
showToast(JSON.stringify(selectedOptions))
|
||||||
// do something
|
// do something
|
||||||
showGenderPicker.value = false;
|
showGenderPicker.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleIndustry = ({ selectedOptions }) => {
|
function handleIndustry({ selectedOptions }) {
|
||||||
state.industryText = selectedOptions[0].text;
|
state.industryText = selectedOptions[0].text
|
||||||
showToast(JSON.stringify(selectedOptions));
|
showToast(JSON.stringify(selectedOptions))
|
||||||
// do something
|
// do something
|
||||||
showIndustryPicker.value = false;
|
showIndustryPicker.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
const getFromText = (columns: FormColumns[], value = 0) =>
|
function getFromText(columns: FormColumns[], value = 0) {
|
||||||
columns.find((item) => item.value === value)?.text;
|
return columns.find(item => item.value === value)?.text
|
||||||
|
}
|
||||||
|
|
||||||
function initState() {
|
function initState() {
|
||||||
Object.keys(state).forEach((key) => {
|
Object.keys(state).forEach((key) => {
|
||||||
state[key] = userStore.getUserInfo[key];
|
state[key] = userStore.getUserInfo[key]
|
||||||
});
|
})
|
||||||
// set field text value.
|
// set field text value.
|
||||||
state.genderText = getFromText(genderColumns, gender) ?? '';
|
state.genderText = getFromText(genderColumns, gender) ?? ''
|
||||||
state.industryText = getFromText(industryColumns, industry) ?? '';
|
state.industryText = getFromText(industryColumns, industry) ?? ''
|
||||||
// set the pick selected value.
|
// set the pick selected value.
|
||||||
state.industryValues = [industry ?? 0];
|
state.industryValues = [industry ?? 0]
|
||||||
state.genderValues = [gender];
|
state.genderValues = [gender]
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initState();
|
initState()
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
height: 140px;
|
height: 140px;
|
||||||
}
|
}
|
||||||
.cover {
|
.cover {
|
||||||
width: 170px;
|
width: 170px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
:deep(.van-image__img) {
|
:deep(.van-image__img) {
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.van-field__control) {
|
:deep(.van-field__control) {
|
||||||
color: var(--van-text-color-2);
|
color: var(--van-text-color-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
<div flex="~" justify="center">
|
<div flex="~" justify="center">
|
||||||
<div grid="~ cols-8 gap-2">
|
<div grid="~ cols-8 gap-2">
|
||||||
<span
|
<span
|
||||||
|
v-for="(item, index) in designStore.appThemeList"
|
||||||
|
:key="index"
|
||||||
h="70px"
|
h="70px"
|
||||||
w="70px"
|
w="70px"
|
||||||
items-center
|
items-center
|
||||||
border="2 rounded-md border-white"
|
border="2 rounded-md border-white"
|
||||||
flex="~"
|
flex="~"
|
||||||
justify="center"
|
justify="center"
|
||||||
v-for="(item, index) in designStore.appThemeList"
|
|
||||||
:key="index"
|
|
||||||
:style="{ 'background-color': item }"
|
:style="{ 'background-color': item }"
|
||||||
@click="togTheme(item)"
|
@click="togTheme(item)"
|
||||||
>
|
>
|
||||||
@ -38,6 +38,7 @@
|
|||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
|
v-model="animateState.text"
|
||||||
label="动画类型"
|
label="动画类型"
|
||||||
readonly
|
readonly
|
||||||
:disabled="!designStore.isPageAnimate"
|
:disabled="!designStore.isPageAnimate"
|
||||||
@ -46,7 +47,6 @@
|
|||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
:border="false"
|
:border="false"
|
||||||
v-model="animateState.text"
|
|
||||||
@click="openAnimatePick"
|
@click="openAnimatePick"
|
||||||
/>
|
/>
|
||||||
<van-popup v-model:show="animateState.showPicker" position="bottom" round>
|
<van-popup v-model:show="animateState.showPicker" position="bottom" round>
|
||||||
@ -61,48 +61,50 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive } from 'vue';
|
import { computed, reactive } from 'vue'
|
||||||
import { Icon } from '@vicons/utils';
|
import { Icon } from '@vicons/utils'
|
||||||
import { updateDarkSign } from '@/theme';
|
import { CheckOutlined } from '@vicons/antd'
|
||||||
import { CheckOutlined } from '@vicons/antd';
|
import NavBar from './components/NavBar.vue'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { updateDarkSign } from '@/theme'
|
||||||
import { animates as animateOptions } from '@/settings/animateSetting';
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
import NavBar from './components/NavBar.vue';
|
import { animates as animateOptions } from '@/settings/animateSetting'
|
||||||
|
|
||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore()
|
||||||
|
|
||||||
const getDarkMode = computed({
|
const getDarkMode = computed({
|
||||||
get: () => designStore.getDarkMode === 'dark',
|
get: () => designStore.getDarkMode === 'dark',
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
const darkMode = value ? 'dark' : 'light';
|
const darkMode = value ? 'dark' : 'light'
|
||||||
updateDarkSign(darkMode);
|
updateDarkSign(darkMode)
|
||||||
designStore.setDarkMode(darkMode);
|
designStore.setDarkMode(darkMode)
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
function togTheme(color: string) {
|
function togTheme(color: string) {
|
||||||
designStore.appTheme = color;
|
designStore.appTheme = color
|
||||||
|
}
|
||||||
|
|
||||||
|
const findCurrentAnimateType = animateOptions.find(
|
||||||
|
item => item.value === designStore.pageAnimateType,
|
||||||
|
)
|
||||||
|
|
||||||
|
const animateState = reactive({
|
||||||
|
text: findCurrentAnimateType?.text,
|
||||||
|
value: [designStore.pageAnimateType],
|
||||||
|
showPicker: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
function openAnimatePick() {
|
||||||
|
if (designStore.isPageAnimate) {
|
||||||
|
animateState.showPicker = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const findCurrentAnimateType = animateOptions.find(
|
function handleSaveAnimateType({ selectedOptions }) {
|
||||||
(item) => item.value === designStore.pageAnimateType
|
animateState.text = selectedOptions[0].text
|
||||||
);
|
designStore.setPageAnimateType(selectedOptions[0].value)
|
||||||
|
animateState.showPicker = false
|
||||||
const animateState = reactive({
|
}
|
||||||
text: findCurrentAnimateType?.text,
|
|
||||||
value: [designStore.pageAnimateType],
|
|
||||||
showPicker: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const openAnimatePick = () => {
|
|
||||||
if (designStore.isPageAnimate) animateState.showPicker = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveAnimateType = ({ selectedOptions }) => {
|
|
||||||
animateState.text = selectedOptions[0].text;
|
|
||||||
designStore.setPageAnimateType(selectedOptions[0].value);
|
|
||||||
animateState.showPicker = false;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -9,21 +9,21 @@
|
|||||||
</Icon>
|
</Icon>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<slot name="right"></slot>
|
<slot name="right" />
|
||||||
</template>
|
</template>
|
||||||
</van-nav-bar>
|
</van-nav-bar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Icon } from '@vicons/utils';
|
import { Icon } from '@vicons/utils'
|
||||||
import { ChevronBack } from '@vicons/ionicons5';
|
import { ChevronBack } from '@vicons/ionicons5'
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const currentRoute = useRoute();
|
const currentRoute = useRoute()
|
||||||
|
|
||||||
const getTitle = computed(() => currentRoute.meta.title as string);
|
const getTitle = computed(() => currentRoute.meta.title as string)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -7,26 +7,26 @@
|
|||||||
accept="image/*"
|
accept="image/*"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<slot name="default"></slot>
|
<slot name="default" />
|
||||||
</template>
|
</template>
|
||||||
</van-uploader>
|
</van-uploader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { showFailToast } from 'vant';
|
import { showFailToast } from 'vant'
|
||||||
|
|
||||||
function beforeRead(file) {
|
function beforeRead(file) {
|
||||||
if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg') {
|
if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg') {
|
||||||
return true;
|
return true
|
||||||
}
|
|
||||||
showFailToast('请上传正确格式的图片');
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
showFailToast('请上传正确格式的图片')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function afterRead(file) {
|
function afterRead(file) {
|
||||||
console.log('%c [ file ]-43', 'font-size:13px; background:pink; color:#bf2c9f;', file);
|
console.log('%c [ file ]-43', 'font-size:13px; background:pink; color:#bf2c9f;', file)
|
||||||
// 这里写上传逻辑
|
// 这里写上传逻辑
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div :style="getUserCoverBg" class="my-bg h-550px -z-19"> </div>
|
<div :style="getUserCoverBg" class="my-bg h-550px -z-19" />
|
||||||
<div
|
<div
|
||||||
class="my-card shadow-xl relative -top-150px mx-40px rounded-2xl flex flex-col items-center pb-20px"
|
class="my-card relative mx-40px flex flex-col items-center rounded-2xl pb-20px shadow-xl -top-150px"
|
||||||
>
|
>
|
||||||
<van-image
|
<van-image
|
||||||
class="border-4 border-solid !absolute -top-90px h-170px w-170px"
|
class="h-170px w-170px border-4 border-solid !absolute -top-90px"
|
||||||
round
|
round
|
||||||
fit="cover"
|
fit="cover"
|
||||||
:src="avatar"
|
:src="avatar"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col items-center mt-90px">
|
<div class="mt-90px flex flex-col items-center">
|
||||||
<p class="font-black text-40px mb-20px">{{ nickname }}</p>
|
<p class="mb-20px text-40px font-black">
|
||||||
<p class="text-30px px-36px">{{ sign }}</p>
|
{{ nickname }}
|
||||||
|
</p>
|
||||||
|
<p class="px-36px text-30px">
|
||||||
|
{{ sign }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<van-divider class="w-full" />
|
<van-divider class="w-full" />
|
||||||
|
|
||||||
@ -57,8 +61,8 @@
|
|||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-action-sheet
|
<van-action-sheet
|
||||||
teleport="body"
|
|
||||||
v-model:show="showLogoutAction"
|
v-model:show="showLogoutAction"
|
||||||
|
teleport="body"
|
||||||
:actions="logoutActions"
|
:actions="logoutActions"
|
||||||
cancel-text="取消"
|
cancel-text="取消"
|
||||||
description="确认退出登录吗"
|
description="确认退出登录吗"
|
||||||
@ -69,60 +73,61 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue'
|
||||||
import { Icon } from '@vicons/utils';
|
import { Icon } from '@vicons/utils'
|
||||||
import { IdcardFilled } from '@vicons/antd';
|
import { IdcardFilled } from '@vicons/antd'
|
||||||
import { Person, ColorPalette, DocumentText, LogOut } from '@vicons/ionicons5';
|
import { ColorPalette, DocumentText, LogOut, Person } from '@vicons/ionicons5'
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import { showToast } from 'vant'
|
||||||
import { showToast } from 'vant';
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
const showLogoutAction = ref(false);
|
const showLogoutAction = ref(false)
|
||||||
|
|
||||||
const { nickname, avatar, cover, sign } = userStore.getUserInfo;
|
const { nickname, avatar, cover, sign } = userStore.getUserInfo
|
||||||
|
|
||||||
const logoutActions = [
|
const logoutActions = [
|
||||||
{
|
{
|
||||||
name: '退出登录',
|
name: '退出登录',
|
||||||
color: '#ee0a24',
|
color: '#ee0a24',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
userStore.Logout();
|
userStore.Logout()
|
||||||
showToast('退出成功');
|
showToast('退出成功')
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const getUserCoverBg = computed(() => {
|
const getUserCoverBg = computed(() => {
|
||||||
return { backgroundImage: `url(${cover ? cover : avatar})` };
|
return { backgroundImage: `url(${cover || avatar})` }
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.my-bg {
|
.my-bg {
|
||||||
clip-path: inset(0 -55% 0 -55% round 0 0 100% 100%);
|
clip-path: inset(0 -55% 0 -55% round 0 0 100% 100%);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0), #000);
|
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0), #000);
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-cell {
|
||||||
|
align-items: center;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: var(--van-cell-active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.van-cell {
|
.xicon {
|
||||||
align-items: center;
|
margin-right: 10px;
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: var(--van-cell-active-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.xicon {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
export interface FormColumns {
|
export interface FormColumns {
|
||||||
text: string;
|
text: string
|
||||||
value: number;
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const genderColumns: FormColumns[] = [
|
export const genderColumns: FormColumns[] = [
|
||||||
{ text: '男', value: 0 },
|
{ text: '男', value: 0 },
|
||||||
{ text: '女', value: 1 },
|
{ text: '女', value: 1 },
|
||||||
];
|
]
|
||||||
|
|
||||||
export const industryColumns: FormColumns[] = [
|
export const industryColumns: FormColumns[] = [
|
||||||
{ text: '不展示', value: 0 },
|
{ text: '不展示', value: 0 },
|
||||||
@ -27,4 +27,4 @@ export const industryColumns: FormColumns[] = [
|
|||||||
{ text: '媒体/广告/公关', value: 15 },
|
{ text: '媒体/广告/公关', value: 15 },
|
||||||
{ text: '体育/健身', value: 16 },
|
{ text: '体育/健身', value: 16 },
|
||||||
{ text: '企事业单位', value: 17 },
|
{ text: '企事业单位', value: 17 },
|
||||||
];
|
]
|
||||||
|
@ -1,28 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center justify-center h-screen p-8">
|
<div class="h-screen flex flex-col items-center justify-center p-8">
|
||||||
<!-- <van-cell center title="🌗 暗黑模式">
|
<!-- <van-cell center title="🌗 暗黑模式">
|
||||||
<template #right-icon>
|
<template #right-icon>
|
||||||
<van-switch v-model="getDarkMode" size="18px" />
|
<van-switch v-model="getDarkMode" size="18px" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell> -->
|
</van-cell> -->
|
||||||
<div class="wel-box flex flex-col justify-between w-full">
|
<div class="wel-box w-full flex flex-col justify-between">
|
||||||
<div class="wel-top">
|
<div class="wel-top">
|
||||||
<div class="logo enter-y">
|
<div class="logo enter-y">
|
||||||
<SvgIcon :size="130" name="logo" />
|
<SvgIcon :size="130" name="logo" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-darkBlue dark:text-garyWhite text-2xl font-black mt-12 mb-4 text-center enter-y"
|
class="text-darkBlue dark:text-garyWhite enter-y mb-4 mt-12 text-center text-2xl font-black"
|
||||||
>欢迎来到 {{ title }}</div
|
|
||||||
>
|
>
|
||||||
<div class="w-full mt-4 mb-6 enter-y">
|
欢迎来到 {{ title }}
|
||||||
|
</div>
|
||||||
|
<div class="enter-y mb-6 mt-4 w-full">
|
||||||
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
||||||
<van-swipe-item
|
<van-swipe-item
|
||||||
class="text-gray-700 dark:text-gray-400 leading-relaxed text-center"
|
|
||||||
v-for="(text, index) in getSwipeText"
|
v-for="(text, index) in getSwipeText"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
class="text-center text-gray-700 leading-relaxed dark:text-gray-400"
|
||||||
>
|
>
|
||||||
<p class="text-lg">{{ text.title }}</p>
|
<p class="text-lg">
|
||||||
<p class="text-sm">{{ text.details }}</p>
|
{{ text.title }}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm">
|
||||||
|
{{ text.details }}
|
||||||
|
</p>
|
||||||
</van-swipe-item>
|
</van-swipe-item>
|
||||||
</van-swipe>
|
</van-swipe>
|
||||||
</div>
|
</div>
|
||||||
@ -33,74 +38,75 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
@click="router.push({ name: 'Login' })"
|
@click="router.push({ name: 'Login' })"
|
||||||
>Let's Get Started</van-button
|
|
||||||
>
|
>
|
||||||
<a class="enter-y text-sm mt-6">创建账户?</a>
|
Let's Get Started
|
||||||
|
</van-button>
|
||||||
|
<a class="enter-y mt-6 text-sm">创建账户?</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useRouter } from 'vue-router'
|
||||||
import SvgIcon from '@/components/SvgIcon.vue';
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
import { useGlobSetting } from '@/hooks/setting';
|
import SvgIcon from '@/components/SvgIcon.vue'
|
||||||
import { useRouter } from 'vue-router';
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
|
||||||
import { updateDarkSign } from '@/theme';
|
// import { updateDarkSign } from '@/theme'
|
||||||
|
|
||||||
const getDarkMode = computed({
|
// const getDarkMode = computed({
|
||||||
get: () => designStore.getDarkMode === 'dark',
|
// get: () => designStore.getDarkMode === 'dark',
|
||||||
set: (value) => {
|
// set: (value) => {
|
||||||
const darkMode = value ? 'dark' : 'light';
|
// const darkMode = value ? 'dark' : 'light'
|
||||||
updateDarkSign(darkMode);
|
// updateDarkSign(darkMode)
|
||||||
designStore.setDarkMode(darkMode);
|
// designStore.setDarkMode(darkMode)
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
|
||||||
|
const designStore = useDesignSettingStore()
|
||||||
|
const globSetting = useGlobSetting()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const { title } = globSetting
|
||||||
|
|
||||||
|
const getSwipeText = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: '💡 最新技术栈',
|
||||||
|
details: '基于Vue3、Vant4、Vite、TypeScript等最新技术栈开发',
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
title: '⚡️ 轻量快速的热重载',
|
||||||
const designStore = useDesignSettingStore();
|
details: '无论应用程序大小如何,都始终极快的模块热重载(HMR)',
|
||||||
const globSetting = useGlobSetting();
|
},
|
||||||
const router = useRouter();
|
{
|
||||||
|
title: '🔩 主题配置',
|
||||||
const { title } = globSetting;
|
details: '具备主题配置及黑暗主题适配,且持久化保存',
|
||||||
|
},
|
||||||
const getSwipeText = computed(() => {
|
{
|
||||||
return [
|
title: '🛠️ 丰富的 Vite 插件',
|
||||||
{
|
details: '集成大部分 Vite 插件,无需繁琐配置,开箱即用',
|
||||||
title: '💡 最新技术栈',
|
},
|
||||||
details: '基于Vue3、Vant4、Vite、TypeScript等最新技术栈开发',
|
]
|
||||||
},
|
})
|
||||||
{
|
|
||||||
title: '⚡️ 轻量快速的热重载',
|
|
||||||
details: '无论应用程序大小如何,都始终极快的模块热重载(HMR)',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '🔩 主题配置',
|
|
||||||
details: '具备主题配置及黑暗主题适配,且持久化保存',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '🛠️ 丰富的 Vite 插件',
|
|
||||||
details: '集成大部分 Vite 插件,无需繁琐配置,开箱即用',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.wel-box {
|
.wel-box {
|
||||||
min-height: 50vh;
|
min-height: 50vh;
|
||||||
max-width: 45vh;
|
max-width: 45vh;
|
||||||
min-width: 30vh;
|
min-width: 30vh;
|
||||||
.wel-top {
|
.wel-top {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
|
||||||
.wel-bottom {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.wel-bottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"baseUrl": ".",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"strict": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"baseUrl": ".",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"lib": ["ESNext", "DOM"],
|
|
||||||
"types": ["vite/client"],
|
|
||||||
"typeRoots": ["./node_modules/@types/", "./types"],
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"],
|
"@/*": ["src/*"],
|
||||||
"#/*": ["types/*"]
|
"#/*": ["types/*"]
|
||||||
}
|
},
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"typeRoots": ["./node_modules/@types/", "./types"],
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
|
32
types/config.d.ts
vendored
32
types/config.d.ts
vendored
@ -1,29 +1,29 @@
|
|||||||
export interface GlobConfig {
|
export interface GlobConfig {
|
||||||
title: string;
|
title: string
|
||||||
titleCN: string;
|
titleCN: string
|
||||||
apiUrl: string;
|
apiUrl: string
|
||||||
shortName: string;
|
shortName: string
|
||||||
urlPrefix?: string;
|
urlPrefix?: string
|
||||||
uploadUrl?: string;
|
uploadUrl?: string
|
||||||
prodMock: boolean;
|
prodMock: boolean
|
||||||
imgUrl?: string;
|
imgUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GlobEnvConfig {
|
export interface GlobEnvConfig {
|
||||||
// 标题
|
// 标题
|
||||||
VITE_GLOB_APP_TITLE: string;
|
VITE_GLOB_APP_TITLE: string
|
||||||
// 中文标题
|
// 中文标题
|
||||||
VITE_GLOB_APP_TITLE_CN: string;
|
VITE_GLOB_APP_TITLE_CN: string
|
||||||
// 接口地址
|
// 接口地址
|
||||||
VITE_GLOB_API_URL: string;
|
VITE_GLOB_API_URL: string
|
||||||
// 接口前缀
|
// 接口前缀
|
||||||
VITE_GLOB_API_URL_PREFIX?: string;
|
VITE_GLOB_API_URL_PREFIX?: string
|
||||||
// Project abbreviation
|
// Project abbreviation
|
||||||
VITE_GLOB_APP_SHORT_NAME: string;
|
VITE_GLOB_APP_SHORT_NAME: string
|
||||||
// 图片上传地址
|
// 图片上传地址
|
||||||
VITE_GLOB_UPLOAD_URL?: string;
|
VITE_GLOB_UPLOAD_URL?: string
|
||||||
// 图片前缀地址
|
// 图片前缀地址
|
||||||
VITE_GLOB_IMG_URL?: string;
|
VITE_GLOB_IMG_URL?: string
|
||||||
// 生产环境开启mock
|
// 生产环境开启mock
|
||||||
VITE_GLOB_PROD_MOCK: boolean;
|
VITE_GLOB_PROD_MOCK: boolean
|
||||||
}
|
}
|
||||||
|
78
types/global.d.ts
vendored
78
types/global.d.ts
vendored
@ -1,9 +1,9 @@
|
|||||||
import type {
|
import type {
|
||||||
VNodeChild,
|
|
||||||
ComponentPublicInstance,
|
ComponentPublicInstance,
|
||||||
FunctionalComponent,
|
FunctionalComponent,
|
||||||
|
VNodeChild,
|
||||||
PropType as VuePropType,
|
PropType as VuePropType,
|
||||||
} from 'vue';
|
} from 'vue'
|
||||||
|
|
||||||
// declare global 在具有 import 或 export 声明全局范围内的事物的文件中使用。
|
// declare global 在具有 import 或 export 声明全局范围内的事物的文件中使用。
|
||||||
// 这在包含 import 或 export 因为此类文件被视为模块的文件中是必需的,并且在模块中声明的任何内容都在模块范围内。
|
// 这在包含 import 或 export 因为此类文件被视为模块的文件中是必需的,并且在模块中声明的任何内容都在模块范围内。
|
||||||
@ -12,66 +12,66 @@ import type {
|
|||||||
declare global {
|
declare global {
|
||||||
const __APP_INFO__: {
|
const __APP_INFO__: {
|
||||||
pkg: {
|
pkg: {
|
||||||
name: string;
|
name: string
|
||||||
version: string;
|
version: string
|
||||||
dependencies: Recordable<string>;
|
dependencies: Recordable<string>
|
||||||
devDependencies: Recordable<string>;
|
devDependencies: Recordable<string>
|
||||||
};
|
}
|
||||||
lastBuildTime: string;
|
lastBuildTime: string
|
||||||
};
|
}
|
||||||
|
|
||||||
// vue
|
// vue
|
||||||
type PropType<T> = VuePropType<T>;
|
type PropType<T> = VuePropType<T>
|
||||||
type VueNode = VNodeChild | JSX.Element;
|
type VueNode = VNodeChild | JSX.Element
|
||||||
|
|
||||||
export type Writable<T> = {
|
export type Writable<T> = {
|
||||||
-readonly [P in keyof T]: T[P];
|
-readonly [P in keyof T]: T[P];
|
||||||
};
|
}
|
||||||
|
|
||||||
type Nullable<T> = T | null;
|
type Nullable<T> = T | null
|
||||||
type NonNullable<T> = T extends null | undefined ? never : T;
|
type NonNullable<T> = T extends null | undefined ? never : T
|
||||||
type Recordable<T = any> = Record<string, T>;
|
type Recordable<T = any> = Record<string, T>
|
||||||
type ReadonlyRecordable<T = any> = {
|
interface ReadonlyRecordable<T = any> {
|
||||||
readonly [key: string]: T;
|
readonly [key: string]: T
|
||||||
};
|
}
|
||||||
type Indexable<T = any> = {
|
interface Indexable<T = any> {
|
||||||
[key: string]: T;
|
[key: string]: T
|
||||||
};
|
}
|
||||||
type DeepPartial<T> = {
|
type DeepPartial<T> = {
|
||||||
[P in keyof T]?: DeepPartial<T[P]>;
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
};
|
}
|
||||||
type TimeoutHandle = ReturnType<typeof setTimeout>;
|
type TimeoutHandle = ReturnType<typeof setTimeout>
|
||||||
type IntervalHandle = ReturnType<typeof setInterval>;
|
type IntervalHandle = ReturnType<typeof setInterval>
|
||||||
|
|
||||||
interface ChangeEvent extends Event {
|
interface ChangeEvent extends Event {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WheelEvent {
|
interface WheelEvent {
|
||||||
path?: EventTarget[];
|
path?: EventTarget[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMetaEnv extends ViteEnv {
|
interface ImportMetaEnv extends ViteEnv {
|
||||||
__: unknown;
|
__: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ViteEnv {
|
interface ViteEnv {
|
||||||
VITE_PORT: number;
|
VITE_PORT: number
|
||||||
VITE_USE_MOCK: boolean;
|
VITE_USE_MOCK: boolean
|
||||||
VITE_PUBLIC_PATH: string;
|
VITE_PUBLIC_PATH: string
|
||||||
VITE_GLOB_APP_TITLE: string;
|
VITE_GLOB_APP_TITLE: string
|
||||||
VITE_GLOB_APP_SHORT_NAME: string;
|
VITE_GLOB_APP_SHORT_NAME: string
|
||||||
VITE_DROP_CONSOLE: boolean;
|
VITE_DROP_CONSOLE: boolean
|
||||||
VITE_GLOB_PROD_MOCK: boolean;
|
VITE_GLOB_PROD_MOCK: boolean
|
||||||
VITE_GLOB_IMG_URL: string;
|
VITE_GLOB_IMG_URL: string
|
||||||
VITE_PROXY: [string, string][];
|
VITE_PROXY: [string, string][]
|
||||||
VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
|
VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none'
|
||||||
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
|
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export type JSXComponent<Props = any> =
|
export type JSXComponent<Props = any> =
|
||||||
| { new (): ComponentPublicInstance<Props> }
|
| { new (): ComponentPublicInstance<Props> }
|
||||||
| FunctionalComponent<Props>;
|
| FunctionalComponent<Props>
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user