diff --git a/.env.production b/.env.production index 57c73d26d46a6c79b25ea76aeed20641fc515c67..d0a2a6b300a6d0e0d908668a8e2cad8344b13e10 100644 --- a/.env.production +++ b/.env.production @@ -9,3 +9,7 @@ VITE_GLOB_API_URL=/api # Interface prefix VITE_GLOB_API_URL_PREFIX= + + +# TODO use Cdn +VITE_USE_CDN = true diff --git a/README.md b/README.md index 19bc2ff938b4274ab92f9b3b2097ce63d34b3cb3..ba477d48d1d2533371845827be156f6c3de72a49 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ yarn clean:lib # 删除node_modules,兼容window系统 - [x] 图表库 - [x] 数字动画 - [x] 首屏加载等待动画 +- [x] 抽取生产环境配置文件 ## 正在开发的功能 @@ -228,7 +229,7 @@ yarn clean:lib # 删除node_modules,兼容window系统 - [ ] 主题配置 - [ ] 黑暗主题 - [ ] 打包 Gzip -- [ ] 抽取生产环境配置文件 +- [ ] 打包 CDN - [ ] 系统性能优化 更多组件/功能/建议/bug/欢迎提交 pr 或者 issue diff --git a/build/config/vite/cdn.ts b/build/config/vite/cdn.ts new file mode 100644 index 0000000000000000000000000000000000000000..0538e1a4e286646d73c023c0500bca4b0e23e0ef --- /dev/null +++ b/build/config/vite/cdn.ts @@ -0,0 +1,21 @@ +const css = ['//cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css']; + +// TODO use esm? +const js = [ + '//cdn.bootcdn.net/ajax/libs/vue/3.0.0/vue.global.prod.js', + '//cdn.bootcdn.net/ajax/libs/vue-router/4.0.0-beta.13/vue-router.global.min.js', + '//cdn.bootcdn.net/ajax/libs/vuex/4.0.0-beta.4/vuex.global.prod.js', + '//cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js', + '//cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js', + '//cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js', + // '//cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js', + // '//cdn.bootcdn.net/ajax/libs/crypto-js/3.3.0/crypto-js.min.js', + // '//cdn.bootcdn.net/ajax/libs/vue-i18n/8.18.1/vue-i18n.min.js', +]; + +export const externals = ['vue', 'vuex', 'vue-router', 'axios', 'qs', 'nprogress']; + +export const cdnConf = { + css, + js, +}; diff --git a/build/config/vite/env.ts b/build/config/vite/env.ts deleted file mode 100644 index 313f446c92dc59801326ab40ade156e8d69ddc08..0000000000000000000000000000000000000000 --- a/build/config/vite/env.ts +++ /dev/null @@ -1,10 +0,0 @@ -import moment from 'moment'; -// @ts-ignore -import pkg from '../../../package.json'; -export function setupBasicEnv() { - // version - process.env.VITE_VERSION = (pkg as any).version; - // build time - process.env.VITE_APP_BUILD_TIME = moment().format('YYYY-MM-DD HH:mm:ss'); - process.env.VITE_BUILD_SHORT_TIME = moment().format('MMDDHHmmss'); -} diff --git a/build/constant.ts b/build/constant.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e02f35c69cc9122cf2c0475e2ae826eb6eea3fe --- /dev/null +++ b/build/constant.ts @@ -0,0 +1 @@ +export const GLOB_CONFIG_FILE_NAME = '_app.config.js'; diff --git a/build/getShortName.ts b/build/getShortName.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a52fc34b91e359052cefcb465a064a7c66c0524 --- /dev/null +++ b/build/getShortName.ts @@ -0,0 +1,5 @@ +export const getShortName = (env: any) => { + return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__` + .toUpperCase() + .replace(/\s/g, ''); +}; diff --git a/build/jsc.js b/build/jsc.js new file mode 100644 index 0000000000000000000000000000000000000000..d39ce932c461382758184edc017cdbbd33dfca10 --- /dev/null +++ b/build/jsc.js @@ -0,0 +1,36 @@ +// js调用cli 兼容调用ts + +const { sh } = require('tasksfile'); +const { argv } = require('yargs'); + +let command = ``; + +Object.keys(argv).forEach((key) => { + if (!/^\$/.test(key) && key !== '_') { + // @ts-ignore + if (argv[key]) { + command += `--${key} `; + } + } +}); + +// 执行任务名称 +let taskList = argv._; + +let NODE_ENV = process.env.NODE_ENV || 'development'; + +if (taskList.includes('build') || taskList.includes('report') || taskList.includes('preview')) { + NODE_ENV = 'production'; +} + +if (taskList && Array.isArray(taskList) && taskList.length) { + sh( + `cross-env NODE_ENV=${NODE_ENV} ts-node --project ./build/tsconfig.json ./build/script/cli.ts ${taskList.join( + ' ' + )} ${command}`, + { + async: true, + nopipe: true, + } + ); +} diff --git a/build/script/build.ts b/build/script/build.ts new file mode 100644 index 0000000000000000000000000000000000000000..a87373a1db210d97a12388e2df5acc3ac09dfe6f --- /dev/null +++ b/build/script/build.ts @@ -0,0 +1,28 @@ +// #!/usr/bin/env node + +import { sh } from 'tasksfile'; +import { argv } from 'yargs'; +import { runBuildConfig } from './buildConf'; +import { runUpdateHtml } from './updateHtml'; +import { errorConsole, successConsole } from '../utils'; + +export const runBuild = async () => { + try { + const argvList = argv._; + let cmd = `cross-env NODE_ENV=production vite build`; + await sh(cmd, { + async: true, + nopipe: true, + }); + + // Generate configuration file + if (!argvList.includes('no-conf')) { + await runBuildConfig(); + } + await runUpdateHtml(); + successConsole('Vite Build successfully!'); + } catch (error) { + errorConsole('Vite Build Error\n' + error); + process.exit(1); + } +}; diff --git a/build/script/buildConf.ts b/build/script/buildConf.ts new file mode 100644 index 0000000000000000000000000000000000000000..b80cafc0f49440bf731bfda0bd6db6e9bddd57cf --- /dev/null +++ b/build/script/buildConf.ts @@ -0,0 +1,44 @@ +import { GLOB_CONFIG_FILE_NAME } from '../constant'; +import fs, { writeFileSync } from 'fs-extra'; + +import viteConfig from '../../vite.config'; +import { errorConsole, successConsole, getCwdPath, getEnvConfig } from '../utils'; + +const getShortName = (env: any) => { + return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__` + .toUpperCase() + .replace(/\s/g, ''); +}; + +function createConfig( + { + configName, + config, + configFileName = GLOB_CONFIG_FILE_NAME, + }: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} } +) { + try { + const windowConf = `window.${configName}`; + const outDir = viteConfig.outDir || 'dist'; + const configStr = `${windowConf}=${JSON.stringify(config)}; + + Object.freeze(${windowConf}); + Object.defineProperty(window, "${configName}", { + configurable: false, + writable: false, + }); + `; + fs.mkdirp(getCwdPath(outDir)); + writeFileSync(getCwdPath(`${outDir}/${configFileName}`), configStr); + + successConsole('The configuration file is build successfully!'); + } catch (error) { + errorConsole('Configuration file configuration file failed to package\n' + error); + } +} + +export function runBuildConfig() { + const config = getEnvConfig(); + const configFileName = getShortName(config); + createConfig({ config, configName: configFileName }); +} diff --git a/build/script/changelog.ts b/build/script/changelog.ts index 19d6d448c8cac12273bfd56ecfccf84874fe8c1f..65f3b0c1b461e94da76cb10f09da1c77ee29fbbf 100644 --- a/build/script/changelog.ts +++ b/build/script/changelog.ts @@ -1,14 +1,11 @@ // #!/usr/bin/env node import { sh } from 'tasksfile'; -import chalk from 'chalk'; +import { errorConsole, successConsole } from '../utils'; -const createChangeLog = async () => { +export const runChangeLog = async () => { try { let cmd = `conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 `; - // if (shell.which('git')) { - // cmd += '&& git add CHANGELOG.md'; - // } await sh(cmd, { async: true, nopipe: true, @@ -18,21 +15,10 @@ const createChangeLog = async () => { async: true, nopipe: true, }); - console.log( - chalk.blue.bold('**************** ') + - chalk.green.bold('CHANGE_LOG generated successfully!') + - chalk.blue.bold(' ****************') - ); + successConsole('CHANGE_LOG.md generated successfully!'); } catch (error) { - console.log( - chalk.blue.red('**************** ') + - chalk.green.red('CHANGE_LOG generated error\n' + error) + - chalk.blue.red(' ****************') - ); + errorConsole('CHANGE_LOG.md generated error\n' + error); + process.exit(1); } }; -createChangeLog(); -module.exports = { - createChangeLog, -}; diff --git a/build/script/cli.ts b/build/script/cli.ts new file mode 100644 index 0000000000000000000000000000000000000000..17f5b84bbeb264c888edaaaef7fb405a4e62f334 --- /dev/null +++ b/build/script/cli.ts @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +import chalk from 'chalk'; +import { argv } from 'yargs'; + +import { runChangeLog } from './changelog'; +import { runPostInstall } from './postinstall'; +import { runPreview } from './preview'; +import { runPreserve } from './preserve'; +import { runBuild } from './build'; + +const task = (argv._ || [])[0]; + +console.log('Run Task: ' + chalk.cyan(task)); + +switch (task) { + // change log + case 'log': + runChangeLog(); + break; + + case 'build': + runBuild(); + break; + + case 'preserve': + runPreserve(); + break; + + case 'postinstall': + runPostInstall(); + break; + + case 'preview': + runPreview(); + break; + + // TODO + case 'gzip': + break; + default: + break; +} + +export default {}; diff --git a/build/script/hm.ts b/build/script/hm.ts new file mode 100644 index 0000000000000000000000000000000000000000..2c3f3b6b166c5017c6a812447984f32f1ef1d9a3 --- /dev/null +++ b/build/script/hm.ts @@ -0,0 +1,12 @@ +// 百度统计代码 用于站点部署 +// 只在build:site开启 +export const hmScript = ` +`; diff --git a/build/script/postinstall.ts b/build/script/postinstall.ts index 242489f338d3668e8df3fc41bbe36420f3add765..dd02de404571220dcc9b38ebbca383d7ab52413b 100644 --- a/build/script/postinstall.ts +++ b/build/script/postinstall.ts @@ -2,9 +2,14 @@ import { exec, which } from 'shelljs'; function ignoreCaseGit() { try { - if (which('git')) { + if (which('git').code === 0) { exec('git config core.ignorecase false '); } - } catch (error) {} + } catch (error) { + console.log(error); + } +} + +export function runPostInstall() { + ignoreCaseGit(); } -ignoreCaseGit(); diff --git a/build/script/preserve.ts b/build/script/preserve.ts index 4f19e503a6d93f14bb903db340bb3a78310a5654..cd4beaef87747ceeb27212087ed8604ee398f251 100644 --- a/build/script/preserve.ts +++ b/build/script/preserve.ts @@ -1,53 +1,57 @@ -// 是否需要更新依赖,防止package.json更新了依赖,其他人获取代码后没有install +// Do you need to update the dependencies to prevent package.json from updating the dependencies, and no install after others get the code import path from 'path'; import fs from 'fs-extra'; import { isEqual } from 'lodash'; -import chalk from 'chalk'; import { sh } from 'tasksfile'; +import { successConsole, errorConsole } from '../utils'; const resolve = (dir: string) => { return path.resolve(process.cwd(), dir); }; +const reg = /[\u4E00-\u9FA5\uF900-\uFA2D]/; + let NEED_INSTALL = false; -fs.mkdirp(resolve('build/.cache')); -function checkPkgUpdate() { - const pkg = require('../../package.json'); - const { dependencies, devDependencies } = pkg; - const depsFile = resolve('build/.cache/deps.json'); - if (!fs.pathExistsSync(depsFile)) { - NEED_INSTALL = true; - return; +export async function runPreserve() { + const cwdPath = process.cwd(); + if (reg.test(cwdPath)) { + errorConsole( + 'Do not include Chinese, Japanese or Korean in the full path of the project directory, please modify the directory name and run again!' + ); + errorConsole('项目目录全路径请勿包含中文、日文、韩文,请修改目录名后再次重新运行!'); + process.exit(1); } - const depsJson = require('../.cache/deps.json'); - if (!isEqual(depsJson, { dependencies, devDependencies })) { - NEED_INSTALL = true; - } -} -checkPkgUpdate(); + fs.mkdirp(resolve('build/.cache')); + function checkPkgUpdate() { + const pkg = require('../../package.json'); + const { dependencies, devDependencies } = pkg; + const depsFile = resolve('build/.cache/deps.json'); + if (!fs.pathExistsSync(depsFile)) { + NEED_INSTALL = true; + return; + } + const depsJson = require('../.cache/deps.json'); -(async () => { + if (!isEqual(depsJson, { dependencies, devDependencies })) { + NEED_INSTALL = true; + } + } + checkPkgUpdate(); if (NEED_INSTALL) { - console.log( - chalk.blue.bold('**************** ') + - chalk.red.bold('检测到依赖变化,正在安装依赖(Tip: 项目首次运行也会执行)!') + - chalk.blue.bold(' ****************') + // no error + successConsole( + 'A dependency change is detected, and the dependency is being installed to ensure that the dependency is consistent! (Tip: The project will be executed for the first time)!' ); try { - // 从代码执行貌似不会自动读取.npmrc 所以手动加上源地址 - // await run('yarn install --registry=https://registry.npm.taobao.org ', { - await sh('yarn install ', { + await sh('npm run bootstrap ', { async: true, nopipe: true, }); - console.log( - chalk.blue.bold('**************** ') + - chalk.green.bold('依赖安装成功,正在运行!') + - chalk.blue.bold(' ****************') - ); + + successConsole('Dependency installation is successful, start running the project!'); const pkg = require('../../package.json'); const { dependencies, devDependencies } = pkg; @@ -64,4 +68,4 @@ checkPkgUpdate(); } } catch (error) {} } -})(); +} diff --git a/build/script/preview.ts b/build/script/preview.ts index 468b6faded61cbcd46931bd115fe38400253cdb1..2ca2ca53384ad27367642751e481e00f395229dc 100644 --- a/build/script/preview.ts +++ b/build/script/preview.ts @@ -11,7 +11,7 @@ import { getIPAddress } from '../utils'; const BUILD = 1; const NO_BUILD = 2; -// 启动服务器 +// start server const startApp = () => { const port = 9680; portfinder.basePort = port; @@ -23,7 +23,6 @@ const startApp = () => { if (err) { throw err; } else { - // const publicPath = process.env.BASE_URL; app.listen(port, function () { const empty = ' '; const common = `The preview program is already running: @@ -36,7 +35,7 @@ const startApp = () => { }); }; -const preview = async () => { +export const runPreview = async () => { const prompt = inquirer.prompt({ type: 'list', message: 'Please select a preview method', @@ -61,7 +60,3 @@ const preview = async () => { } startApp(); }; - -(() => { - preview(); -})(); diff --git a/build/script/updateHtml.ts b/build/script/updateHtml.ts new file mode 100644 index 0000000000000000000000000000000000000000..38286e29590f5d18041bccdbad953f99a0a1c555 --- /dev/null +++ b/build/script/updateHtml.ts @@ -0,0 +1,98 @@ +import { readFileSync, writeFileSync, existsSync } from 'fs-extra'; +import viteConfig, { htmlConfig } from '../../vite.config'; +import { getCwdPath, successConsole, errorConsole } from '../utils'; +import { GLOB_CONFIG_FILE_NAME } from '../constant'; +import { hmScript } from './hm'; +const pkg = require('../../package.json'); + +const { title, addHm, cdnConf, useCdn } = htmlConfig; + +function injectTitle(html: string, htmlTitle: string) { + if (/<\/title>/.test(html)) { + return html.replace(/<\/title>/, `${htmlTitle}`); + } + return html; +} + +function injectConfigScript(html: string) { + const tag = `\t\t`; + + if (/<\/head>/.test(html)) { + return html.replace(/<\/head>/, `${tag}\n\t\t`); + } + return html; +} + +function injectHmScript(html: string) { + if (//.test(html)) { + return html.replace(//, `\n${hmScript}`); + } + return html; +} + +function injectCdnCss(html: string) { + if (!cdnConf) return html; + const { css } = cdnConf; + if (!css || css.length === 0) return html; + + let cdnCssTag = ''; + for (const cssLink of css) { + cdnCssTag += ``; + } + if (/<\/head>/.test(html)) { + return html.replace(/<\/head>/, `${cdnCssTag}\n\t\t`); + } + return html; +} + +function injectCdnjs(html: string) { + if (!cdnConf) return html; + const { js } = cdnConf; + if (!js || js.length === 0) return html; + + let cdnJsTag = ''; + for (const src of js) { + // TODO + // + cdnJsTag += `\t\n`; + } + if (/<\/body>/.test(html)) { + return html.replace(/<\/body>/, `${cdnJsTag}\n`); + } + return html; +} + +export async function runUpdateHtml() { + const outDir = viteConfig.outDir || 'dist'; + const indexPath = getCwdPath(outDir, 'index.html'); + if (!existsSync(`${indexPath}`)) { + return; + } + try { + let processedHtml = ''; + const rawHtml = readFileSync(indexPath, 'utf-8'); + processedHtml = rawHtml; + processedHtml = injectTitle(processedHtml, title); + processedHtml = injectConfigScript(processedHtml); + if (addHm) { + processedHtml = injectHmScript(processedHtml); + } + if (useCdn) { + processedHtml = injectCdnCss(processedHtml); + processedHtml = injectCdnjs(processedHtml); + } + + writeFileSync(indexPath, processedHtml); + successConsole('Update Html Successfully!'); + } catch (error) { + errorConsole('Update Html Error\n' + error); + } +} diff --git a/build/utils.ts b/build/utils.ts index a210dc04cc2c2870f83e63c76e5d874fa4ad58df..9c44a0e4a204ec3a7662add11e8c501f023ad0bc 100644 --- a/build/utils.ts +++ b/build/utils.ts @@ -1,6 +1,9 @@ import fs from 'fs'; +import path from 'path'; import { networkInterfaces } from 'os'; import dotenv from 'dotenv'; +import chalk from 'chalk'; + export const isFunction = (arg: unknown): arg is (...args: any[]) => any => typeof arg === 'function'; export const isRegExp = (arg: unknown): arg is RegExp => @@ -72,6 +75,8 @@ export interface ViteEnv { VITE_USE_MOCK: boolean; VITE_PUBLIC_PATH: string; VITE_PROXY: [string, string][]; + VITE_GLOB_APP_TITLE: string; + VITE_USE_CDN: boolean; } export function loadEnv(): ViteEnv { @@ -100,3 +105,49 @@ export function loadEnv(): ViteEnv { } return ret; } + +export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) { + let envConfig = {}; + confFiles.forEach((item) => { + try { + const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item))); + + envConfig = { ...envConfig, ...env }; + } catch (error) {} + }); + Object.keys(envConfig).forEach((key) => { + const reg = new RegExp(`^(${match})`); + if (!reg.test(key)) { + Reflect.deleteProperty(envConfig, key); + } + }); + return envConfig; +} + +export function successConsole(message: any) { + console.log( + chalk.blue.bold('**************** ') + + chalk.green.bold('✨ ' + message) + + chalk.blue.bold(' ****************') + ); +} + +export function errorConsole(message: any) { + console.log( + chalk.blue.bold('**************** ') + + chalk.red.bold('✨ ' + message) + + chalk.blue.bold(' ****************') + ); +} + +export function warnConsole(message: any) { + console.log( + chalk.blue.bold('**************** ') + + chalk.yellow.bold('✨ ' + message) + + chalk.blue.bold(' ****************') + ); +} + +export function getCwdPath(...dir: string[]) { + return path.resolve(process.cwd(), ...dir); +} diff --git a/getEnvConfig.ts b/getEnvConfig.ts deleted file mode 100644 index 5da9a57d878c819119f91f34525e8b808a13fa4e..0000000000000000000000000000000000000000 --- a/getEnvConfig.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { GlobEnvConfig } from './src/types/config'; - -export const getGlobEnvConfig = (): GlobEnvConfig => { - const env = import.meta.env; - return (env as unknown) as GlobEnvConfig; -}; diff --git a/index.html b/index.html index 84ba684a6c329a3190e0561c81e8854e102ace47..6c1daa941a547f90d4c2dd39688545b563232ab1 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,14 @@ + + + + - - Vue Vben admin 2.0
@@ -43,7 +48,7 @@ .app-loading { width: 100%; height: 100%; - background: rgba(255, 255, 255, 0, 3); + background: rgba(255, 255, 255, 0, 1); } .app-loading .app-loading-wrap { diff --git a/package.json b/package.json index 8713543c2ce8590d0bde7fb132a2e776e1a447e8..73a4ef28f595ed404341202aaefb137f768ecc3e 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,22 @@ "version": "2.0.0-beta.6", "scripts": { "bootstrap": "yarn install", - "serve": "ts-node --project ./build/tsconfig.json ./build/script/preserve && cross-env NODE_ENV=development vite", - "build": "cross-env NODE_ENV=production vite build ", - "report": "cross-env REPORT=true yarn build ", - "build:no-cache": "yarn clean:cache && yarn build", - "preview": "ts-node --project ./build/tsconfig.json ./build/script/preview", - "log": "ts-node --project ./build/tsconfig.json ./build/script/changelog", - "gen:gz": "ts-node --project build/tsconfig.build.json ./build/gzip/index.ts ", - "clean:cache": "npx rimraf node_modules/.cache/ && npx rimraf node_modules/.vite_opt_cache", + "serve": "node ./build/jsc.js preserve && cross-env NODE_ENV=development vite", + "build": "node ./build/jsc.js build", + "build:site": "cross-env SITE=true npm run build ", + "build:no-cache": "yarn clean:cache && npm run build", + "report": "cross-env REPORT=true npm run build ", + "preview": "node ./build/jsc.js preview", + "log": "node ./build/jsc.js log", + "gen:gz": "node ./build/jsc.js gzip", + "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite_opt_cache", "clean:lib": "npx rimraf node_modules", "ls-lint": "npx ls-lint", "lint:eslint": "eslint --fix --ext \"src/**/*.{vue,less,css,scss}\"", "lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"", - "lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", - "reinstall": "npx rimraf node_modules && npx rimraf yarn.lock && npx rimraf package.lock.json && yarn run bootstrap", - "postinstall": "ts-node --project ./build/tsconfig.json ./build/script/postinstall" + "lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", + "reinstall": "rimraf node_modules && rimraf yarn.lock && rimraf package.lock.json && npm run bootstrap", + "postinstall": "node ./build/jsc.js postinstall" }, "dependencies": { "@iconify/iconify": "^2.0.0-rc.1", @@ -53,6 +54,7 @@ "@types/qrcode": "^1.3.5", "@types/rollup-plugin-visualizer": "^2.6.0", "@types/shelljs": "^0.8.8", + "@types/yargs": "^15.0.8", "@types/zxcvbn": "^4.4.0", "@typescript-eslint/eslint-plugin": "^4.4.0", "@typescript-eslint/parser": "^4.4.0", @@ -90,7 +92,8 @@ "vite": "^1.0.0-rc.4", "vite-plugin-mock": "^1.0.2", "vite-plugin-purge-icons": "^0.4.1", - "vue-eslint-parser": "^7.1.0" + "vue-eslint-parser": "^7.1.0", + "yargs": "^16.0.3" }, "repository": { "type": "git", diff --git a/src/components/Form/src/BasicForm.vue b/src/components/Form/src/BasicForm.vue index bd153ecabaabce4cc48395ddb6e8884ff3c408fc..15382631f2dc3a90acfbde441bad537b6bd14d2d 100644 --- a/src/components/Form/src/BasicForm.vue +++ b/src/components/Form/src/BasicForm.vue @@ -283,7 +283,19 @@ const element = values[key]; if (fields.includes(key) && element !== undefined && element !== null) { // 时间 - (formModel as any)[key] = itemIsDateType(key) ? moment(element) : element; + if (itemIsDateType(key)) { + if (Array.isArray(element)) { + const arr: any[] = []; + for (const ele of element) { + arr.push(moment(ele)); + } + (formModel as any)[key] = arr; + } else { + (formModel as any)[key] = moment(element); + } + } else { + (formModel as any)[key] = element; + } if (formEl) { formEl.validateFields([key]); } diff --git a/src/components/Menu/src/BasicMenu.tsx b/src/components/Menu/src/BasicMenu.tsx index dd4ef27b9ed6b82c66e5dcba73a862616e5c0c2a..bac61a1026b07cfd81a283c93ea431589d960f06 100644 --- a/src/components/Menu/src/BasicMenu.tsx +++ b/src/components/Menu/src/BasicMenu.tsx @@ -209,7 +209,7 @@ export default defineComponent({ : {}; return ( , tableElRe if (el) { headerHeight = (el as HTMLElement).offsetHeight; } - tableHeightRef.value = + const tHeight = bottomIncludeBody - (resizeHeightOffset || 0) - paddingHeight - @@ -86,8 +86,7 @@ export function useTableScroll(refProps: ComputedRef, tableElRe footerHeight - headerHeight; useTimeout(() => { - tableHeightRef.value = - tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value; + tableHeightRef.value = tHeight > maxHeight! ? (maxHeight as number) : tableHeightRef.value; cb && cb(); }, 0); } diff --git a/src/hooks/core/useSetting.ts b/src/hooks/core/useSetting.ts index 845e100241b0edf673843309ec2f803e6451b5b9..d4dbdef14d96d42656892ecfc3398c0961a8e43a 100644 --- a/src/hooks/core/useSetting.ts +++ b/src/hooks/core/useSetting.ts @@ -1,14 +1,20 @@ -import type { ProjectConfig, GlobConfig, SettingWrap } from '/@/types/config'; +import type { ProjectConfig, GlobConfig, SettingWrap, GlobEnvConfig } from '/@/types/config'; import getProjectSetting from '/@/settings/projectSetting'; -import { getGlobEnvConfig } from '../../../getEnvConfig'; +import { getGlobEnvConfig, isDevMode } from '/@/utils/env'; +import { getShortName } from '../../../build/getShortName'; + +const ENV_NAME = getShortName(import.meta.env); +const ENV = ((isDevMode() + ? getGlobEnvConfig() + : window[ENV_NAME as any]) as unknown) as GlobEnvConfig; const { + VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_APP_SHORT_NAME, - VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL_PREFIX, -} = getGlobEnvConfig(); +} = ENV; export const useSetting = (): SettingWrap => { // Take global configuration @@ -19,7 +25,9 @@ export const useSetting = (): SettingWrap => { urlPrefix: VITE_GLOB_API_URL_PREFIX, }; const projectSetting: Readonly = getProjectSetting; - + console.log('======================'); + console.log(glob); + console.log('======================'); return { globSetting: glob as Readonly, projectSetting, diff --git a/src/hooks/web/useECharts.ts b/src/hooks/web/useECharts.ts index c739a686161c717a9f895e1a984f57748a4f41ac..6cdf1f13b4c3182c794b38d061fae963af9850bf 100644 --- a/src/hooks/web/useECharts.ts +++ b/src/hooks/web/useECharts.ts @@ -59,19 +59,17 @@ export function useECharts( function resize() { const chartInstance = unref(chartInstanceRef); - if (!chartInstance) { - return; - } + if (!chartInstance) return; chartInstance.resize(); } + tryOnUnmounted(() => { const chartInstance = unref(chartInstanceRef); - if (!chartInstance) { - return; - } + if (!chartInstance) return; chartInstance.dispose(); chartInstanceRef.value = null; }); + return { setOptions, echarts, diff --git a/src/utils/env.ts b/src/utils/env.ts index e06d5bb1f16b91bca430049458826ab35a865a5d..58818a72c805547b2753c6dba5f160b576292355 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -1,3 +1,10 @@ +import type { GlobEnvConfig } from '/@/types/config'; + +export const getGlobEnvConfig = (): GlobEnvConfig => { + const env = import.meta.env; + return (env as unknown) as GlobEnvConfig; +}; + /** * @description: 开发模式 */ diff --git a/src/utils/helper/envHelper.ts b/src/utils/helper/envHelper.ts index 978b4a1d643a5b50583c98a39d08db0933ba1e80..63020e6ef9e362d0e2cc97651b3501d0743d33b0 100644 --- a/src/utils/helper/envHelper.ts +++ b/src/utils/helper/envHelper.ts @@ -1,12 +1,13 @@ import { isDevMode, getEnv } from '/@/utils/env'; import { useSetting } from '/@/hooks/core/useSetting'; - +import moment from 'moment'; import pkg from '../../../package.json'; const { globSetting } = useSetting(); // Generate cache key according to version export const getStorageShortName = () => { + const shortTime = moment().format('MMDDHHmmss'); return `${globSetting.shortName}__${getEnv()}${ - isDevMode() ? `__${(pkg as any).version}` : '__' + process.env.VITE_BUILD_SHORT_TIME + `__${pkg.version}` + (isDevMode() ? '' : `__${shortTime}`) }__`.toUpperCase(); }; diff --git a/vite.config.ts b/vite.config.ts index 76726296445ff02f409f43b090afa9bb91530a61..2c9228344c2e049d5355ed0defc2e65aeb2f65eb 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,14 +4,26 @@ import type { UserConfig, Plugin as VitePlugin } from 'vite'; import visualizer from 'rollup-plugin-visualizer'; import { modifyVars } from './build/config/glob/lessModifyVars'; -import { setupBasicEnv } from './build/config/vite/env'; +import { + // externals, + cdnConf, +} from './build/config/vite/cdn'; + import { createProxy } from './build/config/vite/proxy'; import { createMockServer } from 'vite-plugin-mock'; import PurgeIcons from 'vite-plugin-purge-icons'; + import { isDevFn, isReportMode, isProdFn, loadEnv } from './build/utils'; +const pkg = require('./package.json'); -setupBasicEnv(); -const { VITE_USE_MOCK, VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = loadEnv(); +const { + VITE_USE_MOCK, + VITE_PORT, + VITE_PUBLIC_PATH, + VITE_PROXY, + VITE_GLOB_APP_TITLE, + // VITE_USE_CDN, +} = loadEnv(); function pathResolve(dir: string) { return resolve(__dirname, '.', dir); @@ -95,9 +107,9 @@ const viteConfig: UserConfig = { alias: { '/@/': pathResolve('src'), }, - // define: { - // __ENV__: 'value', - // }, + define: { + __VERSION__: pkg.version, + }, // css预处理 cssPreprocessOptions: { less: { @@ -122,8 +134,35 @@ const viteConfig: UserConfig = { plugins: [PurgeIcons(), ...vitePlugins], rollupOutputOptions: {}, rollupInputOptions: { + // TODO + // external: VITE_USE_CDN ? externals : [], plugins: rollupPlugins, }, }; +// 用于打包部署站点使用。实际项目可以删除 +const isSite = process.env.SITE === 'true'; +// 扩展配置, 往打包后的html注入内容 +// 只针对生产环境 +// TODO 目前只是简单手动注入实现,后续vite应该会提供配置项 +export const htmlConfig: { + title: string; + addHm?: boolean; + cdnConf?: { + css?: string[]; + js?: string[]; + }; + useCdn: boolean; +} = { + // html title + title: VITE_GLOB_APP_TITLE, + // 百度统计,不需要可以删除 + addHm: isSite, + // 使用cdn打包 + // TODO Cdn esm使用方式需要只能支持google,暂时关闭,后续查询更好的方式 + useCdn: false, + // useCdn: VITE_USE_CDN, + // cdn列表 + cdnConf, +}; export default viteConfig; diff --git a/yarn.lock b/yarn.lock index 6143a08bf314de166f7da0ce137ee62695d122a5..b74ad507f90d138d95db9d01a41977be38b081ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -801,6 +801,18 @@ resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== +"@types/yargs-parser@*": + version "15.0.0" + resolved "https://registry.npm.taobao.org/@types/yargs-parser/download/@types/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" + integrity sha1-yz+fdBhp4gzOMw/765JxWQSDiC0= + +"@types/yargs@^15.0.8": + version "15.0.8" + resolved "https://registry.npm.taobao.org/@types/yargs/download/@types/yargs-15.0.8.tgz?cache=0&sync_timestamp=1602182032636&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fyargs%2Fdownload%2F%40types%2Fyargs-15.0.8.tgz#7644904cad7427eb704331ea9bf1ee5499b82e23" + integrity sha1-dkSQTK10J+twQzHqm/HuVJm4LiM= + dependencies: + "@types/yargs-parser" "*" + "@types/zrender@*": version "4.0.0" resolved "https://registry.npmjs.org/@types/zrender/-/zrender-4.0.0.tgz#a6806f12ec4eccaaebd9b0d816f049aca6188fbd" @@ -1634,6 +1646,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.0: + version "7.0.1" + resolved "https://registry.npm.taobao.org/cliui/download/cliui-7.0.1.tgz#a4cb67aad45cd83d8d05128fc9f4d8fbb887e6b3" + integrity sha1-pMtnqtRc2D2NBRKPyfTY+7iH5rM= + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-regexp@^2.1.0: version "2.2.0" resolved "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" @@ -2406,7 +2427,7 @@ esbuild@^0.7.1: resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.14.tgz#9de555e75669187c2315317fbf489b229b1a4cbb" integrity sha512-w2CEVeRcUhCGYMHnNNwb8q+9w42scL7RcNzJm85gZVzNBE3AF0sLq5YP/IdaTBJIFBphIKG3bGbwRH+zsgH/ig== -escalade@^3.1.0: +escalade@^3.0.2, escalade@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e" integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig== @@ -2912,7 +2933,7 @@ gensync@^1.0.0-beta.1: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7004,6 +7025,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-7.0.0.tgz?cache=0&sync_timestamp=1587574502741&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwrap-ansi%2Fdownload%2Fwrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM= + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -7041,6 +7071,11 @@ y18n@^4.0.0: resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.1: + version "5.0.2" + resolved "https://registry.npm.taobao.org/y18n/download/y18n-5.0.2.tgz?cache=0&sync_timestamp=1601576683926&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-5.0.2.tgz#48218df5da2731b4403115c39a1af709c873f829" + integrity sha1-SCGN9donMbRAMRXDmhr3Cchz+Ck= + yallist@^3.0.2: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -7067,6 +7102,11 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.0.0: + version "20.2.1" + resolved "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-20.2.1.tgz?cache=0&sync_timestamp=1601576684570&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs-parser%2Fdownload%2Fyargs-parser-20.2.1.tgz#28f3773c546cdd8a69ddae68116b48a5da328e77" + integrity sha1-KPN3PFRs3Ypp3a5oEWtIpdoyjnc= + yargs@^13.2.4: version "13.3.2" resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -7100,6 +7140,19 @@ yargs@^15.0.0, yargs@^15.1.0: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^16.0.3: + version "16.0.3" + resolved "https://registry.npm.taobao.org/yargs/download/yargs-16.0.3.tgz?cache=0&sync_timestamp=1600660006050&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-16.0.3.tgz#7a919b9e43c90f80d4a142a89795e85399a7e54c" + integrity sha1-epGbnkPJD4DUoUKol5XoU5mn5Uw= + dependencies: + cliui "^7.0.0" + escalade "^3.0.2" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.1" + yargs-parser "^20.0.0" + ylru@^1.2.0: version "1.2.1" resolved "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"