feat: the production environment can be dynamically configured

This commit is contained in:
nebv 2020-10-13 01:40:21 +08:00
parent e83cb06bb9
commit bb3b8f817d
29 changed files with 563 additions and 118 deletions

View File

@ -9,3 +9,7 @@ VITE_GLOB_API_URL=/api
# Interface prefix # Interface prefix
VITE_GLOB_API_URL_PREFIX= VITE_GLOB_API_URL_PREFIX=
# TODO use Cdn
VITE_USE_CDN = true

View File

@ -218,6 +218,7 @@ yarn clean:lib # 删除node_modules兼容window系统
- [x] 图表库 - [x] 图表库
- [x] 数字动画 - [x] 数字动画
- [x] 首屏加载等待动画 - [x] 首屏加载等待动画
- [x] 抽取生产环境配置文件
## 正在开发的功能 ## 正在开发的功能
@ -228,7 +229,7 @@ yarn clean:lib # 删除node_modules兼容window系统
- [ ] 主题配置 - [ ] 主题配置
- [ ] 黑暗主题 - [ ] 黑暗主题
- [ ] 打包 Gzip - [ ] 打包 Gzip
- [ ] 抽取生产环境配置文件 - [ ] 打包 CDN
- [ ] 系统性能优化 - [ ] 系统性能优化
更多组件/功能/建议/bug/欢迎提交 pr 或者 issue 更多组件/功能/建议/bug/欢迎提交 pr 或者 issue

21
build/config/vite/cdn.ts Normal file
View File

@ -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,
};

View File

@ -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');
}

1
build/constant.ts Normal file
View File

@ -0,0 +1 @@
export const GLOB_CONFIG_FILE_NAME = '_app.config.js';

5
build/getShortName.ts Normal file
View File

@ -0,0 +1,5 @@
export const getShortName = (env: any) => {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
.toUpperCase()
.replace(/\s/g, '');
};

36
build/jsc.js Normal file
View File

@ -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,
}
);
}

28
build/script/build.ts Normal file
View File

@ -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);
}
};

44
build/script/buildConf.ts Normal file
View File

@ -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 });
}

View File

@ -1,14 +1,11 @@
// #!/usr/bin/env node // #!/usr/bin/env node
import { sh } from 'tasksfile'; import { sh } from 'tasksfile';
import chalk from 'chalk'; import { errorConsole, successConsole } from '../utils';
const createChangeLog = async () => { export const runChangeLog = async () => {
try { try {
let cmd = `conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 `; 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, { await sh(cmd, {
async: true, async: true,
nopipe: true, nopipe: true,
@ -18,21 +15,10 @@ const createChangeLog = async () => {
async: true, async: true,
nopipe: true, nopipe: true,
}); });
console.log( successConsole('CHANGE_LOG.md generated successfully');
chalk.blue.bold('**************** ') +
chalk.green.bold('CHANGE_LOG generated successfully') +
chalk.blue.bold(' ****************')
);
} catch (error) { } catch (error) {
console.log( errorConsole('CHANGE_LOG.md generated error\n' + error);
chalk.blue.red('**************** ') +
chalk.green.red('CHANGE_LOG generated error\n' + error) +
chalk.blue.red(' ****************')
);
process.exit(1); process.exit(1);
} }
}; };
createChangeLog();
module.exports = {
createChangeLog,
};

45
build/script/cli.ts Normal file
View File

@ -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 {};

12
build/script/hm.ts Normal file
View File

@ -0,0 +1,12 @@
// 百度统计代码 用于站点部署
// 只在build:site开启
export const hmScript = `<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?384d6046e02f6ac4ea075357bd0e9b43";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
`;

View File

@ -2,9 +2,14 @@ import { exec, which } from 'shelljs';
function ignoreCaseGit() { function ignoreCaseGit() {
try { try {
if (which('git')) { if (which('git').code === 0) {
exec('git config core.ignorecase false '); exec('git config core.ignorecase false ');
} }
} catch (error) {} } catch (error) {
console.log(error);
}
}
export function runPostInstall() {
ignoreCaseGit();
} }
ignoreCaseGit();

View File

@ -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 path from 'path';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import chalk from 'chalk';
import { sh } from 'tasksfile'; import { sh } from 'tasksfile';
import { successConsole, errorConsole } from '../utils';
const resolve = (dir: string) => { const resolve = (dir: string) => {
return path.resolve(process.cwd(), dir); return path.resolve(process.cwd(), dir);
}; };
const reg = /[\u4E00-\u9FA5\uF900-\uFA2D]/;
let NEED_INSTALL = false; let NEED_INSTALL = false;
fs.mkdirp(resolve('build/.cache')); export async function runPreserve() {
function checkPkgUpdate() { const cwdPath = process.cwd();
const pkg = require('../../package.json'); if (reg.test(cwdPath)) {
const { dependencies, devDependencies } = pkg; errorConsole(
const depsFile = resolve('build/.cache/deps.json'); 'Do not include Chinese, Japanese or Korean in the full path of the project directory, please modify the directory name and run again!'
if (!fs.pathExistsSync(depsFile)) { );
NEED_INSTALL = true; errorConsole('项目目录全路径请勿包含中文、日文、韩文,请修改目录名后再次重新运行!');
return; process.exit(1);
} }
const depsJson = require('../.cache/deps.json');
if (!isEqual(depsJson, { dependencies, devDependencies })) { fs.mkdirp(resolve('build/.cache'));
NEED_INSTALL = true; 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');
if (!isEqual(depsJson, { dependencies, devDependencies })) {
NEED_INSTALL = true;
}
} }
} checkPkgUpdate();
checkPkgUpdate();
(async () => {
if (NEED_INSTALL) { if (NEED_INSTALL) {
console.log( // no error
chalk.blue.bold('**************** ') + successConsole(
chalk.red.bold('检测到依赖变化,正在安装依赖(Tip: 项目首次运行也会执行)') + '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)'
chalk.blue.bold(' ****************')
); );
try { try {
// 从代码执行貌似不会自动读取.npmrc 所以手动加上源地址 await sh('npm run bootstrap ', {
// await run('yarn install --registry=https://registry.npm.taobao.org ', {
await sh('yarn install ', {
async: true, async: true,
nopipe: true, nopipe: true,
}); });
console.log(
chalk.blue.bold('**************** ') + successConsole('Dependency installation is successful, start running the project');
chalk.green.bold('依赖安装成功,正在运行!') +
chalk.blue.bold(' ****************')
);
const pkg = require('../../package.json'); const pkg = require('../../package.json');
const { dependencies, devDependencies } = pkg; const { dependencies, devDependencies } = pkg;
@ -64,4 +68,4 @@ checkPkgUpdate();
} }
} catch (error) {} } catch (error) {}
} }
})(); }

View File

@ -11,7 +11,7 @@ import { getIPAddress } from '../utils';
const BUILD = 1; const BUILD = 1;
const NO_BUILD = 2; const NO_BUILD = 2;
// 启动服务器 // start server
const startApp = () => { const startApp = () => {
const port = 9680; const port = 9680;
portfinder.basePort = port; portfinder.basePort = port;
@ -23,7 +23,6 @@ const startApp = () => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
// const publicPath = process.env.BASE_URL;
app.listen(port, function () { app.listen(port, function () {
const empty = ' '; const empty = ' ';
const common = `The preview program is already running: 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({ const prompt = inquirer.prompt({
type: 'list', type: 'list',
message: 'Please select a preview method', message: 'Please select a preview method',
@ -61,7 +60,3 @@ const preview = async () => {
} }
startApp(); startApp();
}; };
(() => {
preview();
})();

View File

@ -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}</title>`);
}
return html;
}
function injectConfigScript(html: string) {
const tag = `\t\t<script src='${viteConfig.base || './'}${GLOB_CONFIG_FILE_NAME}?v=${
pkg.version
}-${new Date().getTime()}'></script>`;
if (/<\/head>/.test(html)) {
return html.replace(/<\/head>/, `${tag}\n\t\t</head>`);
}
return html;
}
function injectHmScript(html: string) {
if (/<head>/.test(html)) {
return html.replace(/<head>/, `<head>\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 += `<link rel="stylesheet" href="${cssLink}">`;
}
if (/<\/head>/.test(html)) {
return html.replace(/<\/head>/, `${cdnCssTag}\n\t\t</head>`);
}
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
// <script type="importmap">
// { "imports": {
// "vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.esm-browser.js",
// "vue-router": "https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.0-alpha.13/vue-router.esm.js",
// "vuex": "https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.0-beta.2/vuex.esm-browser.js"
// } }
// </script>
cdnJsTag += `\t<script type="text/javascript" src="${src}"></script>\n`;
}
if (/<\/body>/.test(html)) {
return html.replace(/<\/body>/, `${cdnJsTag}\n</body>`);
}
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);
}
}

View File

@ -1,6 +1,9 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path';
import { networkInterfaces } from 'os'; import { networkInterfaces } from 'os';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import chalk from 'chalk';
export const isFunction = (arg: unknown): arg is (...args: any[]) => any => export const isFunction = (arg: unknown): arg is (...args: any[]) => any =>
typeof arg === 'function'; typeof arg === 'function';
export const isRegExp = (arg: unknown): arg is RegExp => export const isRegExp = (arg: unknown): arg is RegExp =>
@ -72,6 +75,8 @@ export interface ViteEnv {
VITE_USE_MOCK: boolean; VITE_USE_MOCK: boolean;
VITE_PUBLIC_PATH: string; VITE_PUBLIC_PATH: string;
VITE_PROXY: [string, string][]; VITE_PROXY: [string, string][];
VITE_GLOB_APP_TITLE: string;
VITE_USE_CDN: boolean;
} }
export function loadEnv(): ViteEnv { export function loadEnv(): ViteEnv {
@ -100,3 +105,49 @@ export function loadEnv(): ViteEnv {
} }
return ret; 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);
}

View File

@ -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;
};

View File

@ -2,9 +2,14 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<title></title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue Vben admin 2.0</title>
</head> </head>
<body> <body>
<div id="app"> <div id="app">
@ -43,7 +48,7 @@
.app-loading { .app-loading {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(255, 255, 255, 0, 3); background: rgba(255, 255, 255, 0, 1);
} }
.app-loading .app-loading-wrap { .app-loading .app-loading-wrap {

View File

@ -3,21 +3,22 @@
"version": "2.0.0-beta.6", "version": "2.0.0-beta.6",
"scripts": { "scripts": {
"bootstrap": "yarn install", "bootstrap": "yarn install",
"serve": "ts-node --project ./build/tsconfig.json ./build/script/preserve && cross-env NODE_ENV=development vite", "serve": "node ./build/jsc.js preserve && cross-env NODE_ENV=development vite",
"build": "cross-env NODE_ENV=production vite build ", "build": "node ./build/jsc.js build",
"report": "cross-env REPORT=true yarn build ", "build:site": "cross-env SITE=true npm run build ",
"build:no-cache": "yarn clean:cache && yarn build", "build:no-cache": "yarn clean:cache && npm run build",
"preview": "ts-node --project ./build/tsconfig.json ./build/script/preview", "report": "cross-env REPORT=true npm run build ",
"log": "ts-node --project ./build/tsconfig.json ./build/script/changelog", "preview": "node ./build/jsc.js preview",
"gen:gz": "ts-node --project build/tsconfig.build.json ./build/gzip/index.ts ", "log": "node ./build/jsc.js log",
"clean:cache": "npx rimraf node_modules/.cache/ && npx rimraf node_modules/.vite_opt_cache", "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", "clean:lib": "npx rimraf node_modules",
"ls-lint": "npx ls-lint", "ls-lint": "npx ls-lint",
"lint:eslint": "eslint --fix --ext \"src/**/*.{vue,less,css,scss}\"", "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: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/", "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", "reinstall": "rimraf node_modules && rimraf yarn.lock && rimraf package.lock.json && npm run bootstrap",
"postinstall": "ts-node --project ./build/tsconfig.json ./build/script/postinstall" "postinstall": "node ./build/jsc.js postinstall"
}, },
"dependencies": { "dependencies": {
"@iconify/iconify": "^2.0.0-rc.1", "@iconify/iconify": "^2.0.0-rc.1",
@ -53,6 +54,7 @@
"@types/qrcode": "^1.3.5", "@types/qrcode": "^1.3.5",
"@types/rollup-plugin-visualizer": "^2.6.0", "@types/rollup-plugin-visualizer": "^2.6.0",
"@types/shelljs": "^0.8.8", "@types/shelljs": "^0.8.8",
"@types/yargs": "^15.0.8",
"@types/zxcvbn": "^4.4.0", "@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.4.0", "@typescript-eslint/eslint-plugin": "^4.4.0",
"@typescript-eslint/parser": "^4.4.0", "@typescript-eslint/parser": "^4.4.0",
@ -90,7 +92,8 @@
"vite": "^1.0.0-rc.4", "vite": "^1.0.0-rc.4",
"vite-plugin-mock": "^1.0.2", "vite-plugin-mock": "^1.0.2",
"vite-plugin-purge-icons": "^0.4.1", "vite-plugin-purge-icons": "^0.4.1",
"vue-eslint-parser": "^7.1.0" "vue-eslint-parser": "^7.1.0",
"yargs": "^16.0.3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -283,7 +283,19 @@
const element = values[key]; const element = values[key];
if (fields.includes(key) && element !== undefined && element !== null) { 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) { if (formEl) {
formEl.validateFields([key]); formEl.validateFields([key]);
} }

View File

@ -209,7 +209,7 @@ export default defineComponent({
: {}; : {};
return ( return (
<Menu <Menu
forceSubMenuRender={props.isAppMenu} // forceSubMenuRender={props.isAppMenu}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
defaultSelectedKeys={defaultSelectedKeys} defaultSelectedKeys={defaultSelectedKeys}
mode={mode} mode={mode}

View File

@ -77,7 +77,7 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
if (el) { if (el) {
headerHeight = (el as HTMLElement).offsetHeight; headerHeight = (el as HTMLElement).offsetHeight;
} }
tableHeightRef.value = const tHeight =
bottomIncludeBody - bottomIncludeBody -
(resizeHeightOffset || 0) - (resizeHeightOffset || 0) -
paddingHeight - paddingHeight -
@ -86,8 +86,7 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
footerHeight - footerHeight -
headerHeight; headerHeight;
useTimeout(() => { useTimeout(() => {
tableHeightRef.value = tableHeightRef.value = tHeight > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
cb && cb(); cb && cb();
}, 0); }, 0);
} }

View File

@ -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 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 { const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL, VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME, VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL_PREFIX, VITE_GLOB_API_URL_PREFIX,
} = getGlobEnvConfig(); } = ENV;
export const useSetting = (): SettingWrap => { export const useSetting = (): SettingWrap => {
// Take global configuration // Take global configuration
@ -19,7 +25,9 @@ export const useSetting = (): SettingWrap => {
urlPrefix: VITE_GLOB_API_URL_PREFIX, urlPrefix: VITE_GLOB_API_URL_PREFIX,
}; };
const projectSetting: Readonly<ProjectConfig> = getProjectSetting; const projectSetting: Readonly<ProjectConfig> = getProjectSetting;
console.log('======================');
console.log(glob);
console.log('======================');
return { return {
globSetting: glob as Readonly<GlobConfig>, globSetting: glob as Readonly<GlobConfig>,
projectSetting, projectSetting,

View File

@ -59,19 +59,17 @@ export function useECharts(
function resize() { function resize() {
const chartInstance = unref(chartInstanceRef); const chartInstance = unref(chartInstanceRef);
if (!chartInstance) { if (!chartInstance) return;
return;
}
chartInstance.resize(); chartInstance.resize();
} }
tryOnUnmounted(() => { tryOnUnmounted(() => {
const chartInstance = unref(chartInstanceRef); const chartInstance = unref(chartInstanceRef);
if (!chartInstance) { if (!chartInstance) return;
return;
}
chartInstance.dispose(); chartInstance.dispose();
chartInstanceRef.value = null; chartInstanceRef.value = null;
}); });
return { return {
setOptions, setOptions,
echarts, echarts,

View File

@ -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: * @description:
*/ */

View File

@ -1,12 +1,13 @@
import { isDevMode, getEnv } from '/@/utils/env'; import { isDevMode, getEnv } from '/@/utils/env';
import { useSetting } from '/@/hooks/core/useSetting'; import { useSetting } from '/@/hooks/core/useSetting';
import moment from 'moment';
import pkg from '../../../package.json'; import pkg from '../../../package.json';
const { globSetting } = useSetting(); const { globSetting } = useSetting();
// Generate cache key according to version // Generate cache key according to version
export const getStorageShortName = () => { export const getStorageShortName = () => {
const shortTime = moment().format('MMDDHHmmss');
return `${globSetting.shortName}__${getEnv()}${ return `${globSetting.shortName}__${getEnv()}${
isDevMode() ? `__${(pkg as any).version}` : '__' + process.env.VITE_BUILD_SHORT_TIME `__${pkg.version}` + (isDevMode() ? '' : `__${shortTime}`)
}__`.toUpperCase(); }__`.toUpperCase();
}; };

View File

@ -4,14 +4,26 @@ import type { UserConfig, Plugin as VitePlugin } from 'vite';
import visualizer from 'rollup-plugin-visualizer'; import visualizer from 'rollup-plugin-visualizer';
import { modifyVars } from './build/config/glob/lessModifyVars'; 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 { createProxy } from './build/config/vite/proxy';
import { createMockServer } from 'vite-plugin-mock'; import { createMockServer } from 'vite-plugin-mock';
import PurgeIcons from 'vite-plugin-purge-icons'; import PurgeIcons from 'vite-plugin-purge-icons';
import { isDevFn, isReportMode, isProdFn, loadEnv } from './build/utils';
setupBasicEnv(); import { isDevFn, isReportMode, isProdFn, loadEnv } from './build/utils';
const { VITE_USE_MOCK, VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = loadEnv(); const pkg = require('./package.json');
const {
VITE_USE_MOCK,
VITE_PORT,
VITE_PUBLIC_PATH,
VITE_PROXY,
VITE_GLOB_APP_TITLE,
// VITE_USE_CDN,
} = loadEnv();
function pathResolve(dir: string) { function pathResolve(dir: string) {
return resolve(__dirname, '.', dir); return resolve(__dirname, '.', dir);
@ -95,9 +107,9 @@ const viteConfig: UserConfig = {
alias: { alias: {
'/@/': pathResolve('src'), '/@/': pathResolve('src'),
}, },
// define: { define: {
// __ENV__: 'value', __VERSION__: pkg.version,
// }, },
// css预处理 // css预处理
cssPreprocessOptions: { cssPreprocessOptions: {
less: { less: {
@ -122,8 +134,35 @@ const viteConfig: UserConfig = {
plugins: [PurgeIcons(), ...vitePlugins], plugins: [PurgeIcons(), ...vitePlugins],
rollupOutputOptions: {}, rollupOutputOptions: {},
rollupInputOptions: { rollupInputOptions: {
// TODO
// external: VITE_USE_CDN ? externals : [],
plugins: rollupPlugins, 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; export default viteConfig;

View File

@ -801,6 +801,18 @@
resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== 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@*": "@types/zrender@*":
version "4.0.0" version "4.0.0"
resolved "https://registry.npmjs.org/@types/zrender/-/zrender-4.0.0.tgz#a6806f12ec4eccaaebd9b0d816f049aca6188fbd" 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" strip-ansi "^6.0.0"
wrap-ansi "^6.2.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: clone-regexp@^2.1.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" 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" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.14.tgz#9de555e75669187c2315317fbf489b229b1a4cbb"
integrity sha512-w2CEVeRcUhCGYMHnNNwb8q+9w42scL7RcNzJm85gZVzNBE3AF0sLq5YP/IdaTBJIFBphIKG3bGbwRH+zsgH/ig== integrity sha512-w2CEVeRcUhCGYMHnNNwb8q+9w42scL7RcNzJm85gZVzNBE3AF0sLq5YP/IdaTBJIFBphIKG3bGbwRH+zsgH/ig==
escalade@^3.1.0: escalade@^3.0.2, escalade@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e"
integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig== 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" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== 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" version "2.0.5"
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
@ -7004,6 +7025,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0" string-width "^4.1.0"
strip-ansi "^6.0.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: wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 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" resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== 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: yallist@^3.0.2:
version "3.1.1" version "3.1.1"
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 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" camelcase "^5.0.0"
decamelize "^1.2.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: yargs@^13.2.4:
version "13.3.2" version "13.3.2"
resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" 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" y18n "^4.0.0"
yargs-parser "^18.1.2" 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: ylru@^1.2.0:
version "1.2.1" version "1.2.1"
resolved "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" resolved "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"