Compare commits
34 Commits
ec4379abe4
...
cd9c3b4dd5
Author | SHA1 | Date |
---|---|---|
|
cd9c3b4dd5 | |
|
71f2aac2ad | |
|
deffcb8fb1 | |
|
c47a65b5de | |
|
7633593bfc | |
|
0cd9129efa | |
|
f8467610f6 | |
|
16991d5b95 | |
|
42f1a7edfe | |
|
f95dc4d947 | |
|
11b20ba6f5 | |
|
9356d39347 | |
|
8f3ea1e87f | |
|
16a8170e86 | |
|
d38844eea5 | |
|
e832b5ffab | |
|
fd722d2515 | |
|
b6e263d114 | |
|
ff2f3144dc | |
|
4f0007ee01 | |
|
faaca67cc6 | |
|
3d3540e3e9 | |
|
2325754ede | |
|
617ff87736 | |
|
474f3ab89b | |
|
e2b6d49518 | |
|
5c241728f0 | |
|
be1b09dc70 | |
|
ea18ded70c | |
|
dfdf9546b0 | |
|
f95b65c1e0 | |
|
679f418387 | |
|
74de80bf68 | |
|
e4288b00e3 |
|
@ -0,0 +1,21 @@
|
|||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.eslintcache
|
||||
report.html
|
||||
|
||||
yarn.lock
|
||||
npm-debug.log*
|
||||
.pnpm-error.log*
|
||||
.pnpm-debug.log
|
||||
tests/**/coverage/
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
tsconfig.tsbuildinfo
|
|
@ -0,0 +1,29 @@
|
|||
# 应用名称
|
||||
VITE_APP_TITLE="代码生成器"
|
||||
|
||||
# 平台本地运行端口号
|
||||
VITE_PORT=7000
|
||||
|
||||
# 开发环境读取配置文件路径
|
||||
VITE_PUBLIC_PATH=/
|
||||
|
||||
# 跨域代理地址
|
||||
VITE_APP_URL=http://localhost:9999
|
||||
|
||||
# 如果端口被占用会直接退出,而不是尝试下一个端口
|
||||
VITE_STRICT_PORT=false
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN=false
|
||||
|
||||
# 是否使用Mock
|
||||
VITE_MOCK_DEV_SERVER=true
|
||||
|
||||
# mock地址
|
||||
VITE_MOCK_BASE_API=/mock
|
||||
|
||||
# 基础请求路径
|
||||
VITE_APP_BASE_API=/api
|
||||
|
||||
# 是否启用gzip压缩
|
||||
VITE_COMPRESSION=gzip
|
|
@ -0,0 +1,26 @@
|
|||
# 平台本地运行端口号
|
||||
VITE_PORT=7000
|
||||
|
||||
# 开发环境读取配置文件路径
|
||||
VITE_PUBLIC_PATH=/
|
||||
|
||||
# 跨域代理地址
|
||||
VITE_APP_URL=http://localhost:8800
|
||||
|
||||
# 基础请求路径
|
||||
VITE_APP_BASE_API=/api
|
||||
|
||||
# mock地址
|
||||
VITE_MOCK_BASE_API=/mock
|
||||
|
||||
# 是否使用Mock
|
||||
VITE_MOCK_DEV_SERVER=true
|
||||
|
||||
# 如果端口被占用会直接退出,而不是尝试下一个端口
|
||||
VITE_STRICT_PORT=false
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN=false
|
||||
|
||||
# 是否启用gzip压缩
|
||||
VITE_COMPRESSION=gzip
|
|
@ -0,0 +1,26 @@
|
|||
# 平台本地运行端口号
|
||||
VITE_PORT=7000
|
||||
|
||||
# 开发环境读取配置文件路径
|
||||
VITE_PUBLIC_PATH=/
|
||||
|
||||
# 跨域代理地址
|
||||
VITE_APP_URL=http://localhost:8800
|
||||
|
||||
# 基础请求路径
|
||||
VITE_APP_BASE_API=/api
|
||||
|
||||
# 是否使用Mock
|
||||
VITE_MOCK_DEV_SERVER=true
|
||||
|
||||
# mock地址
|
||||
VITE_MOCK_BASE_API=/mock
|
||||
|
||||
# 如果端口被占用会直接退出,而不是尝试下一个端口
|
||||
VITE_STRICT_PORT=false
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN=false
|
||||
|
||||
# 是否启用gzip压缩
|
||||
VITE_COMPRESSION=gzip
|
|
@ -0,0 +1,11 @@
|
|||
dist
|
||||
node_modules
|
||||
public
|
||||
.husky
|
||||
.vscode
|
||||
.idea
|
||||
*.sh
|
||||
*.md
|
||||
|
||||
src/assets
|
||||
stats.html
|
|
@ -0,0 +1,46 @@
|
|||
export default {
|
||||
// (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
|
||||
arrowParens: "always",
|
||||
// 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false
|
||||
bracketSameLine: false,
|
||||
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
|
||||
bracketSpacing: true,
|
||||
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
|
||||
embeddedLanguageFormatting: "auto",
|
||||
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
|
||||
htmlWhitespaceSensitivity: "ignore",
|
||||
// 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false
|
||||
insertPragma: false,
|
||||
// 在 JSX 中使用单引号替代双引号,默认false
|
||||
jsxSingleQuote: false,
|
||||
// 每行最多字符数量,超出换行(默认100)
|
||||
printWidth: 100,
|
||||
// 超出打印宽度 (always | never | preserve )
|
||||
proseWrap: "preserve",
|
||||
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
|
||||
quoteProps: "as-needed",
|
||||
// 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false
|
||||
requirePragma: false,
|
||||
// 结尾添加分号
|
||||
semi: true,
|
||||
// 使用单引号 (true:单引号;false:双引号)
|
||||
singleQuote: true,
|
||||
// 缩进空格数,默认2个空格
|
||||
tabWidth: 2,
|
||||
// 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号
|
||||
trailingComma: "es5",
|
||||
// 指定缩进方式,空格或tab,默认false,即使用空格
|
||||
useTabs: false,
|
||||
// vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
|
||||
vueIndentScriptAndStyle: false,
|
||||
|
||||
endOfLine: "auto",
|
||||
overrides: [
|
||||
{
|
||||
files: "*.html",
|
||||
options: {
|
||||
parser: "html",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
dist
|
||||
node_modules
|
||||
public
|
||||
.husky
|
||||
.vscode
|
||||
.idea
|
||||
*.sh
|
||||
*.md
|
||||
|
||||
src/assets
|
||||
stats.html
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"hash": "727e0168",
|
||||
"configHash": "10198a12",
|
||||
"lockfileHash": "530f8857",
|
||||
"browserHash": "41b39cb9",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "module"
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import type { BuildOptions } from 'vite';
|
||||
|
||||
import { pathResolve } from './utils';
|
||||
|
||||
export const buildEnv = (): BuildOptions => {
|
||||
return {
|
||||
target: 'es2015',
|
||||
assetsInlineLimit: 20000,
|
||||
// 构建输出的目录,默认值为"dist"
|
||||
outDir: 'docker/dist',
|
||||
// 用于指定使用的代码压缩工具。在这里,minify 被设置为 'terser',表示使用 Terser 进行代码压缩。默认值terser
|
||||
// esbuild 打包更快,但是不能去除 console.log,terser打包慢,但能去除 console.log
|
||||
minify: 'terser', // "esbuild"
|
||||
// 用于配置 Terser 的选项
|
||||
terserOptions: {
|
||||
// 用于配置压缩选项
|
||||
compress: {
|
||||
drop_console: true, // 是否删除代码中的 console 语句, 默认值false
|
||||
drop_debugger: true, // 是否删除代码中的 debugger 语句, 默认值false
|
||||
},
|
||||
},
|
||||
// 禁用 gzip 压缩大小报告,可略微减少打包时间
|
||||
reportCompressedSize: false,
|
||||
// 用于指定是否生成源映射文件。源映射文件可以帮助调试和定位源代码中的错误。当设置为false时,构建过程不会生成源映射文件
|
||||
sourcemap: false,
|
||||
// 用于配置 CommonJS 模块的选项
|
||||
commonjsOptions: {
|
||||
// 用于指定是否忽略 CommonJS 模块中的 try-catch 语句。当设置为false时,构建过程会保留 CommonJS 模块中的 try-catch 语句
|
||||
ignoreTryCatch: false,
|
||||
},
|
||||
// 规定触发警告的 chunk 大小, 当某个代码分块的大小超过该限制时,Vite 会发出警告
|
||||
chunkSizeWarningLimit: 2000,
|
||||
rollupOptions: {
|
||||
external: ['md-editor-v3', 'echarts'],
|
||||
input: {
|
||||
// @ts-ignore
|
||||
index: pathResolve('../index.html', import.meta.url),
|
||||
},
|
||||
// 静态资源分类打包
|
||||
output: {
|
||||
chunkFileNames: 'static/js/[name]-[hash].js',
|
||||
entryFileNames: 'static/js/[name]-[hash].js',
|
||||
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
|
||||
manualChunks: (id) => {
|
||||
// 如果是包含在包中则打包成 vendor
|
||||
if (id.includes('node_modules')) {
|
||||
return `vendor`;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
import { Plugin as importToCDN } from 'vite-plugin-cdn-import';
|
||||
|
||||
import { wrapperEnv } from './utils';
|
||||
|
||||
/**
|
||||
* @description 打包时采用`cdn`模式,仅限外网使用(默认不采用,如果需要采用cdn模式,请在 .env.production 文件,将 VITE_CDN 设置成true)
|
||||
* 平台采用国内cdn:https://www.bootcdn.cn,当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
|
||||
* 注意:上面提到的仅限外网使用也不是完全肯定的,如果你们公司内网部署的有相关js、css文件,也可以将下面配置对应改一下,整一套内网版cdn
|
||||
*/
|
||||
export const cdn = importToCDN({
|
||||
//(prodUrl解释: name: 对应下面modules的name,version: 自动读取本地package.json中dependencies依赖中对应包的版本号,path: 对应下面modules的path,当然也可写完整路径,会替换prodUrl)
|
||||
// prodUrl: 'https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}',
|
||||
prodUrl: 'https://unpkg.com/{name}@{version}/{path}',
|
||||
modules: [
|
||||
{
|
||||
name: 'vue',
|
||||
var: 'Vue',
|
||||
path: 'dist/vue.global.prod.js',
|
||||
},
|
||||
{
|
||||
name: 'vue-router',
|
||||
var: 'VueRouter',
|
||||
path: 'dist/vue-router.global.js',
|
||||
},
|
||||
{
|
||||
name: 'pinia',
|
||||
var: 'Pinia',
|
||||
path: 'dist/pinia.iife.js',
|
||||
},
|
||||
{
|
||||
name: 'axios',
|
||||
var: 'axios',
|
||||
path: 'dist/axios.min.js',
|
||||
},
|
||||
{
|
||||
name: 'dayjs',
|
||||
var: 'dayjs',
|
||||
path: 'dayjs.min.js',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
/* 是否使用CDN加速 */
|
||||
export const useCDN = (mode) => {
|
||||
const env = wrapperEnv(mode, 'VITE');
|
||||
return env.VITE_CDN ? cdn : null;
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
import type { CSSOptions } from 'vite';
|
||||
|
||||
|
||||
export const css = (mode: string): CSSOptions => {
|
||||
return {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@use "@/assets/styles/minix/sidebar" as *;`,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
import { dependencies, devDependencies, engines, name, version } from '../package.json';
|
||||
|
||||
const __APP_INFO__ = {
|
||||
pkg: { name, version, engines, dependencies, devDependencies },
|
||||
lastBuildTime: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
|
||||
export const define = () => {
|
||||
return {
|
||||
__APP_INFO__: JSON.stringify(__APP_INFO__),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
import boxen, { type Options as BoxenOptions } from 'boxen';
|
||||
import dayjs, { type Dayjs } from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import gradientString from 'gradient-string';
|
||||
|
||||
import { logOutputSize, wrapperEnv } from './utils';
|
||||
|
||||
dayjs.extend(duration);
|
||||
|
||||
const boxenOptions: BoxenOptions = {
|
||||
padding: 0.94,
|
||||
borderColor: 'cyan',
|
||||
borderStyle: 'round',
|
||||
textAlignment: 'left',
|
||||
};
|
||||
|
||||
/* 输出日志信息 */
|
||||
const printLogMessage = (VITE_PORT: number) => {
|
||||
return gradientString('cyan', 'magenta').multiline(
|
||||
`欢迎使用此项目,项目访问地址如下:
|
||||
http://localhost:${VITE_PORT}`
|
||||
);
|
||||
};
|
||||
|
||||
export const viteConsoleLog = (mode: string) => {
|
||||
const { VITE_PORT } = wrapperEnv(mode);
|
||||
|
||||
let config: { command: string };
|
||||
let startTime: Dayjs;
|
||||
let endTime: Dayjs;
|
||||
return {
|
||||
name: 'vite:buildInfo',
|
||||
configResolved(resolvedConfig) {
|
||||
config = resolvedConfig;
|
||||
},
|
||||
buildStart() {
|
||||
console.log(boxen(printLogMessage(VITE_PORT), boxenOptions));
|
||||
if (config.command === 'build') {
|
||||
startTime = dayjs(new Date());
|
||||
}
|
||||
},
|
||||
closeBundle() {
|
||||
if (config.command === 'build') {
|
||||
endTime = dayjs(new Date());
|
||||
const format = dayjs.duration(endTime.diff(startTime)).format('mm分ss秒');
|
||||
|
||||
console.log(
|
||||
boxen(
|
||||
gradientString('cyan', 'magenta').multiline(
|
||||
`🎉 恭喜打包完成(总用时${format})打包大小(${logOutputSize()})`
|
||||
),
|
||||
boxenOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* 此文件作用于 `vite.config.ts` 的 `optimizeDeps.include` 依赖预构建配置项
|
||||
* 依赖预构建,`vite` 启动时会将下面 include 里的模块,编译成 esm 格式并缓存到 node_modules/.vite 文件夹,页面加载到对应模块时如果浏览器有缓存就读取浏览器缓存,如果没有会读取本地缓存并按需加载
|
||||
* 尤其当您禁用浏览器缓存时(这种情况只应该发生在调试阶段)必须将对应模块加入到 include里,否则会遇到开发环境切换页面卡顿的问题(vite 会认为它是一个新的依赖包会重新加载并强制刷新页面),因为它既无法使用浏览器缓存,又没有在本地 node_modules/.vite 里缓存
|
||||
* 温馨提示:如果您使用的第三方库是全局引入,也就是引入到 src/main.ts 文件里,就不需要再添加到 include 里了,因为 vite 会自动将它们缓存到 node_modules/.vite
|
||||
*/
|
||||
const include = ['vue', 'vue-router', 'dayjs', 'axios', 'pinia', 'vue-types', 'js-cookie'];
|
||||
|
||||
/**
|
||||
* 在预构建中强制排除的依赖项
|
||||
*/
|
||||
const exclude: string[] = [];
|
||||
|
||||
export { exclude, include };
|
|
@ -0,0 +1,45 @@
|
|||
import UnoCssIcons from '@unocss/preset-icons';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import { presetIcons } from 'unocss';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import type { PluginOption } from 'vite';
|
||||
import removeConsole from 'vite-plugin-remove-console';
|
||||
import Inspector from 'vite-plugin-vue-inspector';
|
||||
|
||||
import { useCDN } from './cdn';
|
||||
import { viteConsoleLog } from './info';
|
||||
import { compressPack, report } from './utils';
|
||||
|
||||
export const plugins = (mode: string): PluginOption[] => {
|
||||
return [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
Inspector(),
|
||||
report(),
|
||||
removeConsole(),
|
||||
useCDN(mode),
|
||||
viteConsoleLog(mode),
|
||||
UnoCSS({
|
||||
hmrTopLevelAwait: false,
|
||||
inspector: true, // 控制台是否打印 UnoCSS inspector
|
||||
presets: [
|
||||
presetIcons({
|
||||
prefix: '',
|
||||
extraProperties: {
|
||||
display: 'inline-block',
|
||||
'vertical-align': 'middle',
|
||||
},
|
||||
}),
|
||||
UnoCssIcons({
|
||||
prefix: '',
|
||||
extraProperties: {
|
||||
display: 'inline-block',
|
||||
'vertical-align': 'middle',
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
compressPack(mode),
|
||||
];
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { pathResolve } from './utils';
|
||||
|
||||
export const resolve = () => {
|
||||
return {
|
||||
alias: {
|
||||
'@': pathResolve('../src'),
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
import type { ServerOptions } from 'vite';
|
||||
|
||||
import { wrapperEnv } from './utils';
|
||||
|
||||
/* 开发服务配置 */
|
||||
export const server = (mode: string) => {
|
||||
const { VITE_PORT, VITE_APP_URL, VITE_STRICT_PORT } = wrapperEnv(mode);
|
||||
|
||||
const options: ServerOptions = {
|
||||
strictPort: VITE_STRICT_PORT,
|
||||
port: VITE_PORT,
|
||||
host: '0.0.0.0',
|
||||
open: true,
|
||||
cors: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: VITE_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '/api'),
|
||||
},
|
||||
'/mock': {
|
||||
target: VITE_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/mock/, '/mock'),
|
||||
},
|
||||
},
|
||||
// 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
|
||||
warmup: {
|
||||
clientFiles: ['./index.html', './src/{views,components}/*'],
|
||||
},
|
||||
};
|
||||
|
||||
return options;
|
||||
};
|
|
@ -0,0 +1,118 @@
|
|||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { loadEnv } from 'vite';
|
||||
import viteCompression from 'vite-plugin-compression';
|
||||
|
||||
import { buildEnv } from './buildEnv';
|
||||
|
||||
export const root: string = process.cwd();
|
||||
|
||||
/**
|
||||
* @description 根据可选的路径片段生成一个新的绝对路径
|
||||
* @param dir 路径片段,默认`build`
|
||||
* @param metaUrl 模块的完整`url`,如果在`build`目录外调用必传`import.meta.url`
|
||||
*/
|
||||
// @ts-ignore
|
||||
export const pathResolve = (dir = '.', metaUrl = import.meta.url) => {
|
||||
// 当前文件目录的绝对路径
|
||||
const currentFileDir = dirname(fileURLToPath(metaUrl));
|
||||
// build 目录的绝对路径
|
||||
const buildDir = resolve(currentFileDir, 'build');
|
||||
// 解析的绝对路径
|
||||
const resolvedPath = resolve(currentFileDir, dir);
|
||||
// 检查解析的绝对路径是否在 build 目录内
|
||||
if (resolvedPath.startsWith(buildDir)) {
|
||||
// 在 build 目录内,返回当前文件路径
|
||||
return fileURLToPath(metaUrl);
|
||||
}
|
||||
// 不在 build 目录内,返回解析后的绝对路径
|
||||
return resolvedPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* 封装环境变量配置
|
||||
* @param mode 当前模式
|
||||
* @param prefix 需要过滤的前缀
|
||||
* @link 参考:https://cn.vite.dev/config/#using-environment-variables-in-config
|
||||
*/
|
||||
// @ts-ignore
|
||||
export const wrapperEnv = (mode: string, prefix: string = ''): ViteEnv => {
|
||||
const env: any = loadEnv(mode, root, prefix);
|
||||
|
||||
// 将变量转换指定类型
|
||||
for (const envName of Object.keys(env)) {
|
||||
let realName: string | boolean | number = env[envName].replace(/\\n/g, '\n');
|
||||
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
|
||||
|
||||
if (envName === 'VITE_PORT') {
|
||||
realName = Number(realName);
|
||||
}
|
||||
env[envName] = realName;
|
||||
// @ts-ignore
|
||||
process.env[envName] = realName;
|
||||
}
|
||||
return env;
|
||||
};
|
||||
|
||||
/* 打包分析 */
|
||||
export const report = () => {
|
||||
const lifecycle = process.env.npm_lifecycle_event;
|
||||
return lifecycle === 'report'
|
||||
? visualizer({ open: true, brotliSize: true, filename: 'report.html' })
|
||||
: (null as any);
|
||||
};
|
||||
|
||||
/* 启用gzip压缩 */
|
||||
export const compressPack = (mode: string) => {
|
||||
const { VITE_COMPRESSION } = wrapperEnv(mode);
|
||||
|
||||
return VITE_COMPRESSION == 'gzip' ? viteCompression({ threshold: 1024000 }) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算打包后文件夹大小
|
||||
* @returns
|
||||
*/
|
||||
export const logOutputSize = (): string => {
|
||||
const outDir = `../${buildEnv().outDir}`;
|
||||
|
||||
function convertSize(size: number) {
|
||||
const units: Array<string> = ['byte', 'KB', 'MB', 'GB'];
|
||||
|
||||
// 输入的单位是否存在
|
||||
let index = 0;
|
||||
|
||||
while (size >= 1024) {
|
||||
size /= 1024;
|
||||
index++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[index]}`;
|
||||
}
|
||||
|
||||
// 计算文件夹字节大小
|
||||
function getFolderSize(folderPath: string) {
|
||||
let size = 0;
|
||||
|
||||
fs.readdirSync(folderPath).forEach((fileName: string) => {
|
||||
const filePath = path.join(folderPath, fileName);
|
||||
const stats = fs.statSync(filePath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
size += stats.size;
|
||||
} else if (stats.isDirectory()) {
|
||||
size += getFolderSize(filePath);
|
||||
}
|
||||
});
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
const folderSize = getFolderSize(path.resolve(__dirname, outDir));
|
||||
|
||||
return convertSize(folderSize);
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
# 使用官方的 Nginx 镜像作为基础镜像
|
||||
FROM nginx:1.27.3
|
||||
|
||||
# 删除默认的 Nginx 配置文件
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
# 将自定义的 Nginx 配置文件复制到容器中
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
#COPY bunny-web.site.csr /etc/nginx/bunny-web.site.csr
|
||||
#COPY bunny-web.site.key /etc/nginx/bunny-web.site.key
|
||||
#COPY bunny-web.site_bundle.crt /etc/nginx/bunny-web.site_bundle.crt
|
||||
#COPY bunny-web.site_bundle.pem /etc/nginx/bunny-web.site_bundle.pem
|
||||
|
||||
# 设置时区,构建镜像时执行的命令
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo "Asia/Shanghai" > /etc/timezone
|
||||
|
||||
# 创建一个目录来存放前端项目文件
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
# 将前端项目打包文件复制到 Nginx 的默认静态文件目录
|
||||
COPY dist/ /usr/share/nginx/html
|
||||
# 复制到nginx目录下
|
||||
COPY dist/ /etc/nginx/html
|
||||
|
||||
# 暴露 Nginx 的默认端口
|
||||
EXPOSE 80
|
||||
#EXPOSE 443
|
||||
|
||||
# 自动启动 Nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
|
@ -0,0 +1,32 @@
|
|||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 ;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
client_max_body_size 5M; # 最大文件上传设置
|
||||
|
||||
location / {
|
||||
root /etc/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
# 后端跨域请求
|
||||
location ~/api/ {
|
||||
proxy_pass http://172.17.0.1:8000;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
error_page 404 404.html;
|
||||
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
import js from '@eslint/js';
|
||||
import pluginTypeScript from '@typescript-eslint/eslint-plugin';
|
||||
import * as parserTypeScript from '@typescript-eslint/parser';
|
||||
import configPrettier from 'eslint-config-prettier';
|
||||
import { defineFlatConfig } from 'eslint-define-config';
|
||||
import pluginPrettier from 'eslint-plugin-prettier';
|
||||
import eslintPluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import * as parserVue from 'vue-eslint-parser';
|
||||
|
||||
export default defineFlatConfig([
|
||||
{
|
||||
...js.configs.recommended,
|
||||
ignores: ['**/.*', 'dist/*', '*.d.ts', 'public/*', 'src/assets/**', 'src/**/iconfont/**'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
// index.d.ts
|
||||
RefType: 'readonly',
|
||||
EmitType: 'readonly',
|
||||
TargetContext: 'readonly',
|
||||
ComponentRef: 'readonly',
|
||||
ElRef: 'readonly',
|
||||
ForDataType: 'readonly',
|
||||
AnyFunction: 'readonly',
|
||||
PropType: 'readonly',
|
||||
Writable: 'readonly',
|
||||
Nullable: 'readonly',
|
||||
NonNullable: 'readonly',
|
||||
Recordable: 'readonly',
|
||||
ReadonlyRecordable: 'readonly',
|
||||
Indexable: 'readonly',
|
||||
DeepPartial: 'readonly',
|
||||
Without: 'readonly',
|
||||
Exclusive: 'readonly',
|
||||
TimeoutHandle: 'readonly',
|
||||
IntervalHandle: 'readonly',
|
||||
Effect: 'readonly',
|
||||
ChangeEvent: 'readonly',
|
||||
WheelEvent: 'readonly',
|
||||
ImportMetaEnv: 'readonly',
|
||||
Fn: 'readonly',
|
||||
PromiseFn: 'readonly',
|
||||
ComponentElRef: 'readonly',
|
||||
parseInt: 'readonly',
|
||||
parseFloat: 'readonly',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
prettier: pluginPrettier,
|
||||
'simple-import-sort': eslintPluginSimpleImportSort,
|
||||
},
|
||||
rules: {
|
||||
...configPrettier.rules,
|
||||
...pluginPrettier.configs.recommended.rules,
|
||||
'simple-import-sort/imports': 'error',
|
||||
'no-debugger': 'off',
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.?([cm])ts', '**/*.?([cm])tsx'],
|
||||
languageOptions: {
|
||||
parser: parserTypeScript,
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': pluginTypeScript,
|
||||
},
|
||||
rules: {
|
||||
...pluginTypeScript.configs.strict.rules,
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-as-const': 'warn',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-import-type-side-effects': 'error',
|
||||
'@typescript-eslint/prefer-literal-enum-member': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
disallowTypeAnnotations: false,
|
||||
fixStyle: 'inline-type-imports',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.d.ts'],
|
||||
rules: {
|
||||
'eslint-comments/no-unlimited-disable': 'off',
|
||||
'import/no-duplicates': 'off',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.?([cm])js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.vue'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
$: 'readonly',
|
||||
$$: 'readonly',
|
||||
$computed: 'readonly',
|
||||
$customRef: 'readonly',
|
||||
$ref: 'readonly',
|
||||
$shallowRef: 'readonly',
|
||||
$toRef: 'readonly',
|
||||
},
|
||||
parser: parserVue,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
extraFileExtensions: ['.vue'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
vue: pluginVue,
|
||||
},
|
||||
processor: pluginVue.processors['.vue'],
|
||||
rules: {
|
||||
...pluginVue.configs.base.rules,
|
||||
...pluginVue.configs['vue3-essential'].rules,
|
||||
...pluginVue.configs['vue3-recommended'].rules,
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/require-explicit-emits': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-setup-props-reactivity-loss': 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'always',
|
||||
component: 'always',
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<link href="/favicon.ico" rel="icon" type="image/svg+xml"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>%VITE_APP_TITLE%</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/src/main.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"name": "generator-code-web",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"report": "rimraf dist && vite build",
|
||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
|
||||
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@highlightjs/vue-plugin": "^2.1.0",
|
||||
"@types/node": "^22.13.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.24.1",
|
||||
"@typescript-eslint/parser": "^8.24.1",
|
||||
"@unocss/preset-icons": "^66.0.0",
|
||||
"@unocss/reset": "^66.0.0",
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.7.9",
|
||||
"boxen": "^8.0.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"esbuild": "^0.25.1",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-define-config": "^2.1.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"gradient-string": "^3.0.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"naive-ui": "^2.41.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.3.1",
|
||||
"pinia-plugin-persistedstate": "^3.2.3",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.3.3",
|
||||
"qs": "^6.14.0",
|
||||
"rimraf": "^5.0.10",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"sass": "^1.77.8",
|
||||
"stylelint": "^16.14.1",
|
||||
"stylelint-config-recess-order": "^6.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.6.0",
|
||||
"stylelint-config-standard-scss": "^14.0.0",
|
||||
"stylelint-prettier": "^5.0.3",
|
||||
"terser": "^5.39.0",
|
||||
"unocss": "^66.0.0",
|
||||
"vite-plugin-cdn-import": "^1.0.1",
|
||||
"vite-plugin-remove-console": "^2.2.0",
|
||||
"vite-plugin-vue-inspector": "^5.3.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-demi": "^0.14.10",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"vue-router": "^4.4.3",
|
||||
"vue-types": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.310",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qs": "^6.9.18",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"typescript": "~5.7.2",
|
||||
"vite": "^6.1.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vue-tsc": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0",
|
||||
"pnpm": ">=8.6.10"
|
||||
},
|
||||
"pnpm": {
|
||||
"allowedDeprecatedVersions": {
|
||||
"sourcemap-codec": "*",
|
||||
"domexception": "*",
|
||||
"w3c-hr-time": "*",
|
||||
"stable": "*",
|
||||
"abab": "*"
|
||||
},
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
"eslint": "9"
|
||||
}
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<n-config-provider>
|
||||
<n-message-provider>
|
||||
<content />
|
||||
<n-dialog-provider>
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition :name="route.meta.transition || 'fade-transform'" mode="out-in">
|
||||
<component :is="Component" :key="route.path" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { NConfigProvider, NDialogProvider, NMessageProvider } from 'naive-ui';
|
||||
|
||||
import Content from '@/views/content.vue';
|
||||
</script>
|
|
@ -0,0 +1,64 @@
|
|||
import axios, { type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
|
||||
import { TOKEN_KEY } from '@/enums/CacheEnum';
|
||||
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 50000,
|
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' },
|
||||
paramsSerializer: (params) => {
|
||||
return qs.stringify(params);
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const accessToken = localStorage.getItem(TOKEN_KEY);
|
||||
if (accessToken) {
|
||||
config.headers.Authorization = accessToken;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error: any) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
// 检查配置的响应类型是否为二进制类型('blob' 或 'arraybuffer'), 如果是,直接返回响应对象
|
||||
if (response.config.responseType === 'blob' || response.config.responseType === 'arraybuffer') {
|
||||
return response;
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
const { code, message } = response.data;
|
||||
if (code !== 200) {
|
||||
(window as any).$message.error(message);
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// 系统出错
|
||||
return Promise.reject(response.data.message || 'Error');
|
||||
},
|
||||
(error: any) => {
|
||||
// 异常处理
|
||||
if (error.response.data) {
|
||||
const { code, message } = error.response.data;
|
||||
if (code === 500) {
|
||||
(window as any).$message.error(message);
|
||||
} else {
|
||||
(window as any).$message.error(message || '系统出错');
|
||||
}
|
||||
}
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
);
|
||||
|
||||
// 导出 axios 实例
|
||||
export default service;
|
|
@ -0,0 +1,68 @@
|
|||
import axios, { type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
|
||||
import { TOKEN_KEY } from '@/enums/CacheEnum';
|
||||
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_MOCK_BASE_API,
|
||||
timeout: 50000,
|
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' },
|
||||
paramsSerializer: (params) => {
|
||||
return qs.stringify(params);
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const accessToken = localStorage.getItem(TOKEN_KEY);
|
||||
if (accessToken) {
|
||||
config.headers.Authorization = accessToken;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error: any) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
// 检查配置的响应类型是否为二进制类型('blob' 或 'arraybuffer'), 如果是,直接返回响应对象
|
||||
if (response.config.responseType === 'blob' || response.config.responseType === 'arraybuffer') {
|
||||
return response;
|
||||
}
|
||||
// const { code, data, msg } = response.data;
|
||||
// if (code === ResultEnum.SUCCESS) {
|
||||
// return data;
|
||||
// }
|
||||
|
||||
if (response.status === 200) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ElMessage.error(msg || '系统出错');
|
||||
return Promise.reject(response.data.message || 'Error');
|
||||
},
|
||||
(error: any) => {
|
||||
// 异常处理
|
||||
if (error.response.data) {
|
||||
// const { code, msg } = error.response.data;
|
||||
// if (code === ResultEnum.TOKEN_INVALID) {
|
||||
// ElNotification({
|
||||
// title: '提示',
|
||||
// message: '您的会话已过期,请重新登录',
|
||||
// type: 'info',
|
||||
// });
|
||||
// } else {
|
||||
// ElMessage.error(msg || '系统出错');
|
||||
// }
|
||||
}
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
);
|
||||
|
||||
// 导出 axios 实例
|
||||
export default service;
|
|
@ -0,0 +1,20 @@
|
|||
import request from '@/api/server/request';
|
||||
import type { BaseResult } from '@/types/request';
|
||||
|
||||
/* 当前数据库信息 */
|
||||
export const fetchTableInfo = (params: any) => {
|
||||
return request<any, BaseResult<any>>({
|
||||
url: '/sqlParser/tableInfo',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
};
|
||||
|
||||
/* 当前数据库信息 */
|
||||
export const fetchColumnMetaData = (params: any) => {
|
||||
return request<any, BaseResult<any>>({
|
||||
url: '/sqlParser/columnMetaData',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
import request from '@/api/server/request';
|
||||
import type { BaseResult } from '@/types/request';
|
||||
|
||||
/* 当前数据库信息 */
|
||||
export const getDatabaseInfoMetaData = () => {
|
||||
return request<any, BaseResult<any>>({ url: '/table/databaseInfoMetaData', method: 'GET' });
|
||||
};
|
||||
|
||||
/* 数据库所有的表 */
|
||||
export const getDatabaseTableList = (params: any) => {
|
||||
return request<any, BaseResult<any>>({ url: '/table/databaseTableList', method: 'get', params });
|
||||
};
|
||||
|
||||
/* 获取表属性 */
|
||||
export const getTableMetaData = (params: object) => {
|
||||
return request<any, BaseResult<any>>({ url: '/table/tableMetaData', method: 'get', params });
|
||||
};
|
||||
|
||||
/* 获取列属性 */
|
||||
export const getTableColumnInfo = (params: object) => {
|
||||
return request<any, BaseResult<any>>({ url: '/table/tableColumnInfo', method: 'get', params });
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
import request from '@/api/server/request';
|
||||
import type { BaseResult } from '@/types/request'; /* 获取所有数据表 */
|
||||
|
||||
/* 获取所有数据表 */
|
||||
export const generator = (data: any) => {
|
||||
return request<any, BaseResult<any>>({ url: '/vms/generator', method: 'post', data });
|
||||
};
|
||||
|
||||
/* 获取vms文件路径 */
|
||||
export const getVmsResourcePathList = () => {
|
||||
return request<any, BaseResult<any>>({ url: '/vms/vmsResourcePathList', method: 'get' });
|
||||
};
|
||||
|
||||
/* 打包成zip下载 */
|
||||
export const downloadByZip = (data: any) => {
|
||||
return request<any, any>({
|
||||
url: '/vms/downloadByZip',
|
||||
method: 'POST',
|
||||
data,
|
||||
responseType: 'blob',
|
||||
});
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,2 @@
|
|||
@use "src/rotate";
|
||||
@use "src/transition";
|
|
@ -0,0 +1,19 @@
|
|||
/* 旋转动画 */
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 反向旋转动画 */
|
||||
@keyframes rotate-reverse {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/* fade */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all 0.38s;
|
||||
}
|
||||
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
/* breadcrumb transition */
|
||||
.breadcrumb-enter-active {
|
||||
transition: all 0.4s;
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter-from,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重置el-menu的展开收起动画时长
|
||||
*/
|
||||
.outer-most .el-collapse-transition-leave-active,
|
||||
.outer-most .el-collapse-transition-enter-active {
|
||||
transition: 0.2s all ease-in-out !important;
|
||||
}
|
||||
|
||||
.horizontal-collapse-transition {
|
||||
transition: var(--pure-transition-duration) all !important;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: opacity 0.3s,
|
||||
transform 0.3s;
|
||||
}
|
||||
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-30%);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
@use "src/element";
|
||||
|
||||
/* 定义滚动条高宽及背景高宽分别对应横竖滚动条的尺寸 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
/* 定义滚动条轨道内阴影+圆角 */
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ebecef;
|
||||
border-radius: 5px;
|
||||
box-shadow: inset 0 0 6px #ebecef;
|
||||
}
|
||||
|
||||
/* 定义滑块内阴影+圆角 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #d0d2d6;
|
||||
border-radius: 5px;
|
||||
box-shadow: inset 0 0 6px #d0d2d6;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// 空心的虚线圆
|
||||
.dashed-circle {
|
||||
width: 46px; /* 圆圈的宽度 */
|
||||
height: 46px; /* 圆圈的高度 */
|
||||
line-height: 40px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
border: 2px dashed; /* 边框宽度、样式和颜色 */
|
||||
border-radius: 50%; /* 将元素变成圆形 */
|
||||
}
|
||||
|
||||
.hover {
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-hover);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
@use "animations/animations";
|
||||
@use "common/common";
|
||||
|
||||
:root {
|
||||
--colot-primary: #027AFF;
|
||||
--color-primary-secondary: #00FFFF;
|
||||
--color-info: #7CC1FF;
|
||||
--color-hover: #1C8ADF;
|
||||
--color-warning: #FFBE44;
|
||||
--color-warning-secondary: #FEDB65;
|
||||
--background-color: #051135;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
@mixin view-style-default($sidebar-width,$content-width) {
|
||||
&__sidebar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: $sidebar-width;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
|
||||
&-item {
|
||||
padding: 9px 15px;
|
||||
width: 100%;
|
||||
background: rgba(14, 95, 255, 0.2);
|
||||
}
|
||||
|
||||
&-tag {
|
||||
float: left;
|
||||
margin: 0 7px 0 0;
|
||||
width: 62px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
background: rgba(24, 69, 135, 0.55);
|
||||
color: #fff;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&-title {
|
||||
width: 172px;
|
||||
height: 42px;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: url('@/assets/images/business-supervision/bg/sidebar/bg-frame-4.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
&-title-describe {
|
||||
font-size: 12px;
|
||||
color: var(--color-info-secondary-1);
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
width: $content-width;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<script lang="ts" setup>
|
||||
import { NButton, NButtonGroup } from 'naive-ui';
|
||||
|
||||
const props = defineProps({
|
||||
// 数据源(必须包含唯一标识字段,默认是 `id`)
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
// 当前选中的数据
|
||||
selected: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 唯一标识字段名(默认 `id`)
|
||||
idKey: {
|
||||
type: String,
|
||||
default: 'name',
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['update:selected']);
|
||||
|
||||
/* 全选 */
|
||||
const selectAll = () => {
|
||||
const allNames = props.data.map((item) => item[props.idKey]);
|
||||
emit('update:selected', [...allNames]);
|
||||
};
|
||||
|
||||
/* 反选 */
|
||||
const selectInvert = () => {
|
||||
const currentSelected = props.selected;
|
||||
const allNames = props.data.map((item) => item[props.idKey]);
|
||||
const inverted = allNames.filter((name) => !currentSelected.includes(name));
|
||||
emit('update:selected', inverted);
|
||||
};
|
||||
|
||||
/* 全不选 */
|
||||
const selectCancel = () => {
|
||||
emit('update:selected', []);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-button-group size="small">
|
||||
<n-button round type="primary" @click="selectAll">全选</n-button>
|
||||
<n-button type="warning" @click="selectInvert">反选</n-button>
|
||||
<n-button round type="error" @click="selectCancel">全不选</n-button>
|
||||
</n-button-group>
|
||||
</template>
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* 令牌缓存Key
|
||||
*/
|
||||
export const TOKEN_KEY = 'accessToken';
|
|
@ -0,0 +1,9 @@
|
|||
/** 响应码枚举 */
|
||||
export const enum ResultEnum {
|
||||
// 成功
|
||||
SUCCESS = '200',
|
||||
// 错误
|
||||
ERROR = 'B0001',
|
||||
// 令牌无效或过期
|
||||
TOKEN_INVALID = '209',
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div class="container m-auto">
|
||||
<h1 class="mt-4 text-center font-bold font-size-[22px] c-primary">代码生成器</h1>
|
||||
|
||||
<main class="container mx-auto">
|
||||
<router-view />
|
||||
</main>
|
||||
|
||||
<footer class="my-4 text-center">
|
||||
<p>© 2025 Bunny.保留所有权利.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,15 @@
|
|||
import 'animate.css';
|
||||
import '@unocss/reset/tailwind-compat.css';
|
||||
import 'uno.css';
|
||||
import 'virtual:unocss-devtools';
|
||||
import '@/assets/styles/global.scss';
|
||||
|
||||
import { createApp } from 'vue';
|
||||
|
||||
import plugins from '@/plugins';
|
||||
|
||||
import App from './App.vue';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(plugins).mount('#app');
|
|
@ -0,0 +1,9 @@
|
|||
import 'highlight.js/styles/atom-one-dark.css';
|
||||
import 'highlight.js/lib/common';
|
||||
|
||||
import highlightJSPlugin from '@highlightjs/vue-plugin';
|
||||
import type { App } from 'vue';
|
||||
|
||||
export const setupHighLight = (app: App<Element>) => {
|
||||
app.use(highlightJSPlugin);
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import type { App } from 'vue';
|
||||
|
||||
import { setupHighLight } from '@/plugins/highLight';
|
||||
import { setupRouter } from '@/router';
|
||||
import { setupStore } from '@/store';
|
||||
|
||||
export default {
|
||||
install(app: App<Element>) {
|
||||
// 设置路由
|
||||
setupRouter(app);
|
||||
// 设置状态管理
|
||||
setupStore(app);
|
||||
// 设置代码高亮
|
||||
setupHighLight(app);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
import type { App } from 'vue';
|
||||
import { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import error from '@/router/modules/error';
|
||||
import home from '@/router/modules/home';
|
||||
import remaining from '@/router/modules/remaining';
|
||||
|
||||
// 静态路由
|
||||
const routes: RouteRecordRaw[] = [...remaining, ...home, ...error] as RouteRecordRaw[];
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
scrollBehavior: () => ({ left: 0, top: 0, behavior: 'smooth' }),
|
||||
});
|
||||
|
||||
/** 全局注册 router */
|
||||
export const setupRouter = (app: App<Element>) => {
|
||||
app.use(router);
|
||||
};
|
||||
|
||||
/** 重置路由 */
|
||||
export const resetRouter = () => {
|
||||
router.replace({ path: '/' }).then();
|
||||
};
|
||||
|
||||
export default router;
|
|
@ -0,0 +1,13 @@
|
|||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import type { RouteConfigsTable } from '@/types/router/Route';
|
||||
|
||||
const routes: RouteRecordRaw[] | RouteConfigsTable[] = [
|
||||
{
|
||||
path: '/error',
|
||||
component: () => import('@/views/error-page/404.vue'),
|
||||
meta: { hidden: true },
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
|
@ -0,0 +1,24 @@
|
|||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import Layout from '@/layout/index.vue';
|
||||
import type { RouteConfigsTable } from '@/types/router/Route';
|
||||
|
||||
const routes: RouteRecordRaw[] | RouteConfigsTable[] = [
|
||||
{
|
||||
path: '/',
|
||||
name: '/',
|
||||
component: Layout,
|
||||
redirect: '/home',
|
||||
meta: { transition: 'fade' },
|
||||
children: [
|
||||
{ path: '/home', name: 'home', component: () => import('@/views/home/index.vue') },
|
||||
{
|
||||
path: '/generator-code',
|
||||
name: 'generatorCode',
|
||||
component: () => import('@/views/generator-code/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
|
@ -0,0 +1,20 @@
|
|||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import Layout from '@/layout/index.vue';
|
||||
import type { RouteConfigsTable } from '@/types/router/Route';
|
||||
|
||||
const routes: RouteRecordRaw[] | RouteConfigsTable[] = [
|
||||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
meta: { hidden: true },
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
component: () => import('@/views/redirect/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
|
@ -0,0 +1,27 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
import { isCSSColor, isPath } from '@/utils/regexp/regexpBackground';
|
||||
|
||||
const useAppStore = defineStore('appStore', {
|
||||
state() {
|
||||
return {
|
||||
background: '',
|
||||
};
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
setBackground(background: string) {
|
||||
if (isCSSColor(background)) {
|
||||
this.background = background;
|
||||
} else if (isPath(background)) {
|
||||
const href = new URL(background, import.meta.url).href;
|
||||
this.background = `url(${href})`;
|
||||
} else {
|
||||
const href = new URL('@/assets/images/common/bg/bg-layout.png', import.meta.url).href;
|
||||
this.background = `url(${href})`;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { useAppStore };
|
|
@ -0,0 +1,11 @@
|
|||
import { createPinia } from 'pinia';
|
||||
import piniaPluginPersistedState from 'pinia-plugin-persistedstate';
|
||||
import type { App } from 'vue';
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
// 全局注册 store
|
||||
export function setupStore(app: App<Element>) {
|
||||
pinia.use(piniaPluginPersistedState);
|
||||
app.use(pinia);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
import {
|
||||
getDatabaseInfoMetaData,
|
||||
getDatabaseTableList,
|
||||
getTableColumnInfo,
|
||||
getTableMetaData,
|
||||
} from '@/api/table';
|
||||
|
||||
export const useTableStore = defineStore('tableStore', {
|
||||
state: () => ({
|
||||
databaseInfoMeta: undefined,
|
||||
// 数据库所有的表
|
||||
tableList: [],
|
||||
tableListLoading: false,
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
/* 当前数据库信息 */
|
||||
async loadDatabaseInfoMeta() {
|
||||
const result = await getDatabaseInfoMetaData();
|
||||
if (result.code === 200) {
|
||||
this.databaseInfoMeta = result.data;
|
||||
}
|
||||
},
|
||||
|
||||
/* 数据库所有的表 */
|
||||
async loadDatabaseTableList() {
|
||||
this.tableListLoading = true;
|
||||
const dbName = this.databaseInfoMeta?.currentDatabase;
|
||||
const result = await getDatabaseTableList({ dbName });
|
||||
|
||||
this.tableList = result.data;
|
||||
this.tableListLoading = false;
|
||||
},
|
||||
|
||||
/* 获取表属性 */
|
||||
async getTableMetaData(tableName: string) {
|
||||
const result = await getTableMetaData({ tableName });
|
||||
if (result.code !== 200) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return result.data;
|
||||
},
|
||||
|
||||
/* 获取表属性 */
|
||||
async getTableColumnInfo(tableName: string) {
|
||||
const result = await getTableColumnInfo({ tableName });
|
||||
if (result.code !== 200) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return result.data;
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
import { generator, getVmsResourcePathList } from '@/api/vms';
|
||||
|
||||
export const useVmsStore = defineStore('vmsStore', {
|
||||
// 开启持久化
|
||||
// persist: true,
|
||||
// persist: {
|
||||
// paths: ['formValue', 'formOption'],
|
||||
// },
|
||||
state: () => ({
|
||||
generators: [],
|
||||
|
||||
// 生成服务端内容
|
||||
serverOptions: [],
|
||||
|
||||
// 生成前端内容
|
||||
webOptions: [],
|
||||
|
||||
// 查询的表单
|
||||
formValue: {
|
||||
author: 'Bunny',
|
||||
packageName: 'cn.bunny.services',
|
||||
requestMapping: '/api',
|
||||
className: '',
|
||||
tableName: '',
|
||||
simpleDateFormat: 'yyyy-MM-dd HH:mm:ss',
|
||||
tablePrefixes: 't_,sys_,qrtz_,log_',
|
||||
comment: '',
|
||||
path: [],
|
||||
},
|
||||
|
||||
// 表单选择内容
|
||||
formOption: {
|
||||
generatorServer: [],
|
||||
generatorWeb: [],
|
||||
},
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
/* 获取所有数据表 */
|
||||
async generator(data: any) {
|
||||
const result = await generator(data);
|
||||
// 需要确保已经在 setup 中执行了 window.$message = message
|
||||
|
||||
this.generators = result.data.map((i: any) => ({ ...i, path: i.path.replace('.vm', '') }));
|
||||
(window as any).$message.success(`生成成功,共 ${this.generators.length} 数据`);
|
||||
},
|
||||
|
||||
/* 获取vms文件路径 */
|
||||
async getVmsResourcePathList() {
|
||||
const result = await getVmsResourcePathList();
|
||||
// 需要确保已经在 setup 中执行了 window.$message = message
|
||||
if (result.code !== 200) {
|
||||
return {};
|
||||
}
|
||||
|
||||
this.webOptions = result.data.web;
|
||||
this.serverOptions = result.data.server;
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
declare global {
|
||||
/* 环境便配置 */
|
||||
declare interface ViteEnv {
|
||||
VITE_APP_TITLE: string;
|
||||
VITE_PORT: number;
|
||||
VITE_PUBLIC_PATH: string;
|
||||
VITE_APP_URL: string;
|
||||
VITE_APP_BASE_API: string;
|
||||
VITE_STRICT_PORT: boolean;
|
||||
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN: boolean;
|
||||
VITE_MOCK_DEV_SERVER: boolean;
|
||||
VITE_MOCK_BASE_API: string;
|
||||
VITE_CDN: boolean;
|
||||
VITE_COMPRESSION: string;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
type Recordable<T = any> = Record<string, T>;
|
|
@ -0,0 +1,6 @@
|
|||
// 基础后端返回内容
|
||||
export interface BaseResult<T> {
|
||||
code: number;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import type { RouteComponent } from 'vue-router';
|
||||
|
||||
/**
|
||||
* @description 完整子路由的`meta`配置表
|
||||
*/
|
||||
interface CustomizeRouteMeta {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
transition: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 整体路由配置表(包括完整子路由)
|
||||
*/
|
||||
export interface RouteConfigsTable {
|
||||
/** 路由地址 `必填` */
|
||||
path: string;
|
||||
/** 路由名字(保持唯一)`可选` */
|
||||
name?: string;
|
||||
/** `Layout`组件 `可选` */
|
||||
component?: RouteComponent;
|
||||
/** 路由重定向 `可选` */
|
||||
redirect?: string;
|
||||
meta?: CustomizeRouteMeta;
|
||||
/** 子路由配置项 */
|
||||
children?: Array<RouteConfigsTable>;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 复制到剪切板
|
||||
* @param text 文本内容
|
||||
*/
|
||||
export const copy = (text: string) => {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
|
||||
try {
|
||||
const success = document.execCommand('copy');
|
||||
if (success) {
|
||||
(window as any as any).$message.success('复制成功!');
|
||||
} else {
|
||||
(window as any).$message.success('复制失败!');
|
||||
}
|
||||
} catch (err: any) {
|
||||
(window as any).$message.success('复制失败!请手动复制');
|
||||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
export function downloadTextAsFile(text: string, filename: string) {
|
||||
// 直接创建 File 对象(比 Blob 更高级)
|
||||
const file = new File([text], filename, { type: 'text/plain' });
|
||||
|
||||
// 创建下载链接
|
||||
const url = URL.createObjectURL(file);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
|
||||
// 触发下载
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
// 清理
|
||||
requestIdleCallback(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(a.href);
|
||||
});
|
||||
}
|
||||
|
||||
export const downloadBlob = (response: any) => {
|
||||
try {
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = 'download.zip';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename="?(.+)"/);
|
||||
if (fileNameMatch && fileNameMatch[1]) {
|
||||
fileName = fileNameMatch[1];
|
||||
}
|
||||
}
|
||||
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', fileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
import 'nprogress/nprogress.css';
|
||||
|
||||
import NProgress from 'nprogress';
|
||||
|
||||
// 进度条
|
||||
NProgress.configure({
|
||||
// 动画方式
|
||||
easing: 'ease',
|
||||
// 递增进度条的速度
|
||||
speed: 500,
|
||||
// 是否显示加载ico
|
||||
showSpinner: false,
|
||||
// 自动递增间隔
|
||||
trickleSpeed: 200,
|
||||
// 初始化时的最小百分比
|
||||
minimum: 0.3,
|
||||
});
|
||||
|
||||
export default NProgress;
|
|
@ -0,0 +1,29 @@
|
|||
/** 判断是否是CSS颜色 */
|
||||
function isCSSColor(str: string) {
|
||||
// 匹配十六进制颜色(如 #fff, #ffffff)
|
||||
const hexColor = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
||||
|
||||
// 匹配RGB/RGBA颜色(如 rgb(255, 255, 255), rgba(255, 255, 255, 0.5))
|
||||
const rgbColor = /^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(,\s*[\d\.]+)?\s*\)$/;
|
||||
|
||||
// 匹配HSL/HSLA颜色(如 hsl(120, 100%, 50%), hsla(120, 100%, 50%, 0.5))
|
||||
const hslColor = /^hsla?\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*(,\s*[\d\.]+)?\s*\)$/;
|
||||
|
||||
// 匹配预定义颜色名称(如 red, blue, green)
|
||||
const namedColor = /^[a-zA-Z]+$/;
|
||||
|
||||
return hexColor.test(str) || rgbColor.test(str) || hslColor.test(str) || namedColor.test(str);
|
||||
}
|
||||
|
||||
/** 判断是否是相对路径或绝对路径 */
|
||||
function isPath(str: string) {
|
||||
// 匹配相对路径(如 ./path, ../path, path/to/file)
|
||||
const relativePath = /^\.{0,2}\/[^\/].*$/;
|
||||
|
||||
// 匹配绝对路径(如 /path/to/file, C:\path\to\file)
|
||||
const absolutePath = /^(?:\/|[A-Za-z]:\\).*$/;
|
||||
|
||||
return relativePath.test(str) || absolutePath.test(str);
|
||||
}
|
||||
|
||||
export { isCSSColor, isPath };
|
|
@ -0,0 +1,13 @@
|
|||
<template> </template>
|
||||
|
||||
<script>
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
// naive ui 要求这样写的 https://www.naiveui.com/zh-CN/light/components/message
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
window.$message = useMessage();
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,237 @@
|
|||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="pic-404">
|
||||
<img alt="404" class="pic-404__parent" src="../../assets/images/error/404.png" />
|
||||
<img alt="404" class="pic-404__child left" src="../../assets/images/error/404_cloud.png" />
|
||||
<img alt="404" class="pic-404__child mid" src="../../assets/images/error/404_cloud.png" />
|
||||
<img alt="404" class="pic-404__child right" src="../../assets/images/error/404_cloud.png" />
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
<div class="bullshit__info">
|
||||
All rights reserved
|
||||
<a href="https://wallstreetcn.com" style="color: #20a0ff" target="_blank">wallstreetcn</a>
|
||||
</div>
|
||||
<div class="bullshit__headline">The webmaster said that you can not enter this page...</div>
|
||||
<div class="bullshit__info">
|
||||
Please check that the URL you entered is correct, or click the button below to return to the
|
||||
homepage.
|
||||
</div>
|
||||
<a class="bullshit__return-home" href="/" @click.prevent="router.replace('/')">
|
||||
Back to home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
display: flex;
|
||||
padding: 100px;
|
||||
|
||||
.pic-404 {
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__child {
|
||||
&.left {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
width: 80px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-delay: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
width: 46px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-delay: 1.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.right {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
width: 62px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-delay: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bullshit {
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
|
||||
&__oops {
|
||||
margin-bottom: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__headline {
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-bottom: 30px;
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(60px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,68 @@
|
|||
import { NTag } from 'naive-ui';
|
||||
import type { JSX } from 'vue/jsx-runtime';
|
||||
|
||||
export const columns: any = [
|
||||
{
|
||||
title: '序号',
|
||||
key: 'no',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any, index: number) {
|
||||
return index + 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '列名称',
|
||||
key: 'columnName',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag type="primary">{row.columnName}</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '字段名称',
|
||||
key: 'lowercaseName',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag>{row.lowercaseName}</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '数据库字段类型',
|
||||
key: 'jdbcType',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag>{row.jdbcType}</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Java类型',
|
||||
key: 'javaType',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag type="warning">{row.javaType}</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '是否为主键',
|
||||
key: 'isPrimaryKey',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return row.isPrimaryKey ? <NTag type="error">是</NTag> : <NTag type="success">否</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '字段注释',
|
||||
key: 'comment',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag type="info">{row.comment}</NTag>;
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts" setup>
|
||||
import { NDataTable } from 'naive-ui';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { useTableStore } from '@/store/modules/table';
|
||||
import { columns } from '@/views/generator-code/column-field/columns';
|
||||
|
||||
const route = useRoute();
|
||||
const tableStore = useTableStore();
|
||||
|
||||
// 数据库中当前表的列信息
|
||||
const datalist = ref([]);
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
/* 数据库列信息 */
|
||||
const getColumnInfo = async () => {
|
||||
loading.value = true;
|
||||
|
||||
const tableName: any = route.query.tableName;
|
||||
datalist.value = await tableStore.getTableColumnInfo(tableName);
|
||||
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getColumnInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 当前表的列字段 -->
|
||||
<n-data-table :bordered="true" :columns="columns" :data="datalist" :loading="loading" />
|
||||
</template>
|
|
@ -0,0 +1,54 @@
|
|||
<script lang="ts" setup>
|
||||
import { NGradientText } from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
import { useTableStore } from '@/store/modules/table';
|
||||
|
||||
const tableStore = useTableStore();
|
||||
const { databaseInfoMeta } = storeToRefs(tableStore);
|
||||
|
||||
onMounted(() => {
|
||||
tableStore.loadDatabaseInfoMeta();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="database-info">
|
||||
<p>
|
||||
数据库产品名称:
|
||||
<n-gradient-text type="primary">{{ databaseInfoMeta?.databaseProductName }}</n-gradient-text>
|
||||
</p>
|
||||
<p>
|
||||
数据库产品版本:
|
||||
<n-gradient-text type="primary">
|
||||
{{ databaseInfoMeta?.databaseProductVersion }}
|
||||
</n-gradient-text>
|
||||
</p>
|
||||
<p>
|
||||
驱动名称:
|
||||
<n-gradient-text type="primary">{{ databaseInfoMeta?.driverName }}</n-gradient-text>
|
||||
</p>
|
||||
<p>
|
||||
数据库驱动版本:
|
||||
<n-gradient-text type="primary">{{ databaseInfoMeta?.driverVersion }}</n-gradient-text>
|
||||
</p>
|
||||
<p>
|
||||
数据库用户:
|
||||
<n-gradient-text type="primary">{{ databaseInfoMeta?.username }}</n-gradient-text>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.database-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
|
||||
p {
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,255 @@
|
|||
<script lang="ts" setup>
|
||||
import {
|
||||
NButton,
|
||||
NButtonGroup,
|
||||
NCheckbox,
|
||||
NCheckboxGroup,
|
||||
NForm,
|
||||
NFormItemGi,
|
||||
NGrid,
|
||||
NGridItem,
|
||||
NInput,
|
||||
NSpace,
|
||||
useMessage,
|
||||
} from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { computed } from 'vue-demi';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { fetchTableInfo } from '@/api/sqlParser';
|
||||
import { downloadByZip } from '@/api/vms';
|
||||
import SelectButtonGroup from '@/components/select-button-group.vue';
|
||||
import { useVmsStore } from '@/store/modules/vms';
|
||||
import { downloadBlob, downloadTextAsFile } from '@/utils/file';
|
||||
import {
|
||||
formValueInit,
|
||||
selectAll,
|
||||
selectAllInvert,
|
||||
selectCancelAll,
|
||||
validateFormValue,
|
||||
} from '@/views/generator-code/generator/hook';
|
||||
import { rules } from '@/views/generator-code/generator/option';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const vmsStore = useVmsStore();
|
||||
const { formValue, formOption } = storeToRefs(vmsStore);
|
||||
|
||||
const message = useMessage();
|
||||
const formRef = ref();
|
||||
const hasDownloadZip = computed(
|
||||
() => !(formOption.value.generatorWeb.length > 0 || formOption.value.generatorServer.length > 0)
|
||||
);
|
||||
|
||||
// 解析 Sql 语句
|
||||
const sql = ref();
|
||||
|
||||
/* 提交表单 */
|
||||
const onSubmit = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
formRef.value?.validate(async (errors: any) => {
|
||||
if (!errors) {
|
||||
validateFormValue();
|
||||
|
||||
// 生成代码
|
||||
await vmsStore.generator(formValue.value);
|
||||
} else {
|
||||
errors.forEach((error: any) => {
|
||||
error.forEach((err: any) => {
|
||||
message.error(`${err.message}->${err.field}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 清空已经生成的代码 */
|
||||
const clearGeneratorCode = () => {
|
||||
vmsStore.generators = [];
|
||||
};
|
||||
|
||||
/* 下载全部 */
|
||||
const downloadAll = () => {
|
||||
vmsStore.generators.forEach((vms) => {
|
||||
const code = vms.code;
|
||||
const path = vms.path.split('/')[1];
|
||||
|
||||
downloadTextAsFile(code, path);
|
||||
});
|
||||
};
|
||||
|
||||
/* 下载zip文件 */
|
||||
const downloadZipFile = async () => {
|
||||
validateFormValue();
|
||||
|
||||
const result = await downloadByZip(formValue.value);
|
||||
downloadBlob(result);
|
||||
};
|
||||
|
||||
/* 解析 SQL 语句信息 */
|
||||
const sqlParser = async () => {
|
||||
if (!sql.value) {
|
||||
message.warning('SQL 为空');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = { sql: sql.value };
|
||||
const { data } = await fetchTableInfo(params);
|
||||
|
||||
// 设置要生成的sql表中的内容
|
||||
formValue.value.comment = data.comment;
|
||||
formValue.value.tableName = data.tableName;
|
||||
formValueInit(data.tableName);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化表名称
|
||||
const tableName: any = route.query.tableName;
|
||||
formValueInit(tableName);
|
||||
|
||||
vmsStore.getVmsResourcePathList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-form ref="formRef" :label-width="80" :model="formValue" :rules="rules">
|
||||
<n-grid cols="24" item-responsive responsive="screen">
|
||||
<n-form-item-gi
|
||||
label="如果有sql会生成sql中的信息,点击【解析SQL】会替换【表名称】和【注释名称】"
|
||||
path="sql"
|
||||
span="24"
|
||||
>
|
||||
<div class="flex flex-wrap flex-col w-full">
|
||||
<n-input
|
||||
v-model:value="sql"
|
||||
:autosize="{ minRows: 3 }"
|
||||
class="w-full"
|
||||
placeholder="SQL语句"
|
||||
type="textarea"
|
||||
/>
|
||||
|
||||
<n-button-group class="mt-2">
|
||||
<n-button type="primary" @click="sqlParser">解析SQL</n-button>
|
||||
<n-button type="error" @click="sql = null">清空输入框</n-button>
|
||||
</n-button-group>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
|
||||
<!-- 需要提交的生成表单 -->
|
||||
<n-grid :cols="24" :x-gap="5" item-responsive responsive="screen">
|
||||
<n-form-item-gi label="作者名称" path="author" span="12 m:8 l:6">
|
||||
<n-input v-model:value="formValue.author" placeholder="作者名称" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="requestMapping名称" path="requestMapping" span="12 m:8 l:6">
|
||||
<n-input v-model:value="formValue.requestMapping" placeholder="requestMapping名称" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="表名称" path="tableName" span="24 m:8 l:6">
|
||||
<n-input v-model:value="formValue.tableName" placeholder="表名称" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="类名称" path="className" span="24 m:8 l:6">
|
||||
<n-input v-model:value="formValue.className" placeholder="类名称" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="包名称" path="packageName" span="24 m:8 l:6">
|
||||
<n-input v-model:value="formValue.packageName" placeholder="包名称" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="时间格式" path="simpleDateFormat" span="24 m:8 l:6">
|
||||
<n-input v-model:value="formValue.simpleDateFormat" placeholder="时间格式" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="去除开头前缀" path="tablePrefixes" span="12 m:8 l:6">
|
||||
<n-input v-model:value="formValue.tablePrefixes" placeholder="去除开头前缀" />
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="注释名称" path="comment" span="12 m:8 l:6">
|
||||
<n-input v-model:value="formValue.comment" placeholder="修改注释名称" />
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
|
||||
<!-- 需要生成的模板 -->
|
||||
<n-grid :cols="24" :x-gap="5" item-responsive responsive="screen">
|
||||
<n-form-item-gi label="生成后端" path="generatorServer" span="24 m:24 l:12">
|
||||
<n-checkbox-group v-model:value="formOption.generatorServer">
|
||||
<n-space>
|
||||
<n-checkbox
|
||||
v-for="(item, index) in vmsStore.serverOptions"
|
||||
:key="index"
|
||||
:value="item.name"
|
||||
>
|
||||
{{ item.label }}
|
||||
</n-checkbox>
|
||||
|
||||
<!-- 选择按钮 -->
|
||||
<select-button-group
|
||||
v-model:selected="formOption.generatorServer"
|
||||
:data="vmsStore.serverOptions"
|
||||
id-key="name"
|
||||
/>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi label="生成前端" path="generatorWeb" span="24 m:24 l:12">
|
||||
<n-checkbox-group v-model:value="formOption.generatorWeb">
|
||||
<n-space>
|
||||
<n-checkbox
|
||||
v-for="(item, index) in vmsStore.webOptions"
|
||||
:key="index"
|
||||
v-model:value="item.name"
|
||||
>
|
||||
{{ item.label }}
|
||||
</n-checkbox>
|
||||
|
||||
<!-- 选择按钮 -->
|
||||
<select-button-group
|
||||
v-model:selected="formOption.generatorWeb"
|
||||
:data="vmsStore.webOptions"
|
||||
id-key="name"
|
||||
/>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
|
||||
<!-- 操作选项按钮 -->
|
||||
<n-grid cols="24" item-responsive responsive="screen">
|
||||
<n-grid-item class="mt-2" span="24 m:12 l:8">
|
||||
<n-button attr-type="button" type="success" @click="selectAll">全部选择</n-button>
|
||||
<n-button attr-type="button" type="warning" @click="selectAllInvert">全部反选</n-button>
|
||||
<n-button attr-type="button" type="error" @click="selectCancelAll">全选取消</n-button>
|
||||
</n-grid-item>
|
||||
|
||||
<n-grid-item class="mt-2" span="24 m:12 l:8">
|
||||
<n-button attr-type="button" type="success" @click="onSubmit">开始生成</n-button>
|
||||
<n-button attr-type="button" type="error" @click="clearGeneratorCode">清空已生成</n-button>
|
||||
<n-button
|
||||
:disabled="!(vmsStore.generators.length > 0)"
|
||||
attr-type="button"
|
||||
type="primary"
|
||||
@click="downloadAll"
|
||||
>
|
||||
下载全部 {{ vmsStore.generators.length }}
|
||||
</n-button>
|
||||
</n-grid-item>
|
||||
|
||||
<n-grid-item class="mt-2" span="24 m:12 l:8">
|
||||
<n-button
|
||||
:disabled="hasDownloadZip"
|
||||
attr-type="button"
|
||||
class="w-full"
|
||||
type="success"
|
||||
@click="downloadZipFile"
|
||||
>
|
||||
下载zip
|
||||
</n-button>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</template>
|
|
@ -0,0 +1,72 @@
|
|||
<script lang="tsx" setup>
|
||||
import hljsVuePlugin from '@highlightjs/vue-plugin';
|
||||
import { CopySharp } from '@vicons/ionicons5';
|
||||
import { NButton, NCollapse, NCollapseItem, NIcon, NInput, useDialog, useMessage } from 'naive-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVmsStore } from '@/store/modules/vms';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { downloadTextAsFile } from '@/utils/file';
|
||||
|
||||
// 使用高亮组件
|
||||
const HighlightJS = hljsVuePlugin.component;
|
||||
|
||||
const dialog = useDialog();
|
||||
const message = useMessage();
|
||||
const vmsStore = useVmsStore();
|
||||
|
||||
/* 下载文件 */
|
||||
const download = (code: string, filename: string) => {
|
||||
const filenameSplit = filename.split('/');
|
||||
|
||||
// 拿到最后一个元素
|
||||
const inputValue = ref(filenameSplit.at(-1));
|
||||
|
||||
dialog.info({
|
||||
title: '修改文件名',
|
||||
positiveText: '下载',
|
||||
negativeText: '取消',
|
||||
content: () => (
|
||||
<NInput
|
||||
placeholder="Tiny Input"
|
||||
clearable
|
||||
value={inputValue.value}
|
||||
onInput={(value: any) => (inputValue.value = value)}
|
||||
/>
|
||||
),
|
||||
onPositiveClick: () => {
|
||||
// 下载文件
|
||||
downloadTextAsFile(code, inputValue.value);
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
message.info('取消下载');
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-collapse v-show="vmsStore.generators.length > 0" class="mt-4 p-2 border">
|
||||
<n-collapse-item
|
||||
v-for="(item, index) in vmsStore.generators"
|
||||
:key="index"
|
||||
:name="item.path"
|
||||
:title="item.path"
|
||||
class="pos-relative"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-button quaternary type="info" @click="download(item.code, item.path)">下载</n-button>
|
||||
</template>
|
||||
|
||||
<n-button class="pos-absolute right-0" color="#ff69b4" quaternary @click="copy(item.code)">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<CopySharp />
|
||||
</n-icon>
|
||||
</template>
|
||||
复制
|
||||
</n-button>
|
||||
<HighlightJS :autodetect="true" :code="item.code" language="JavaScript" />
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
</template>
|
|
@ -0,0 +1,60 @@
|
|||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import { useVmsStore } from '@/store/modules/vms';
|
||||
|
||||
const vmsStore = useVmsStore();
|
||||
const { formValue, formOption } = storeToRefs(vmsStore);
|
||||
|
||||
/* 初始化表单信息 */
|
||||
export const formValueInit = (tableName: any) => {
|
||||
formValue.value.tableName = tableName.toString();
|
||||
|
||||
// 替换类名称
|
||||
let className: any = tableName as any;
|
||||
formValue.value.tablePrefixes.split(/[,,]/).forEach((item) => {
|
||||
className = className.replace(item, '');
|
||||
});
|
||||
formValue.value.className = className;
|
||||
};
|
||||
|
||||
/* 全部选择 */
|
||||
export const selectAll = () => {
|
||||
formOption.value.generatorServer = vmsStore.serverOptions.map((option: any) => option.name);
|
||||
formOption.value.generatorWeb = vmsStore.webOptions.map((option: any) => option.name);
|
||||
};
|
||||
|
||||
/* 全部反选 */
|
||||
export const selectAllInvert = () => {
|
||||
// 反选server
|
||||
const serverNames: string[] = vmsStore.serverOptions.map((item) => item['name']);
|
||||
const generatorServer = formOption.value.generatorServer;
|
||||
formOption.value.generatorServer = serverNames.filter(
|
||||
(name: string) => !generatorServer.includes(name)
|
||||
);
|
||||
|
||||
// 反选web
|
||||
const webNames: string[] = vmsStore.webOptions.map((item) => item['name']);
|
||||
const generatorWeb = formOption.value.generatorWeb;
|
||||
formOption.value.generatorWeb = webNames.filter((name) => !generatorWeb.includes(name));
|
||||
};
|
||||
|
||||
/* 取消全部选择 */
|
||||
export const selectCancelAll = () => {
|
||||
formOption.value.generatorServer = [];
|
||||
formOption.value.generatorWeb = [];
|
||||
formValue.value.path = [];
|
||||
};
|
||||
|
||||
/* 验证 formValue.path 是否为空 */
|
||||
export const validateFormValue = () => {
|
||||
// 选择要生成的模板
|
||||
const web = formOption.value.generatorWeb;
|
||||
const server = formOption.value.generatorServer;
|
||||
|
||||
// 整理好数据
|
||||
formValue.value.path = [...server, ...web];
|
||||
if (formValue.value.path.length <= 0) {
|
||||
(window as any).$message.error(`选择要生成的模板`);
|
||||
return;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
import type { FormRules } from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import { useVmsStore } from '@/store/modules/vms';
|
||||
|
||||
const vmsStore = useVmsStore();
|
||||
const { formOption } = storeToRefs(vmsStore);
|
||||
|
||||
/* 验证生成前后端内容是否合法 */
|
||||
const validatorFormOption = () => {
|
||||
return formOption.value.generatorServer.length > 0 || formOption.value.generatorWeb.length > 0;
|
||||
};
|
||||
|
||||
// 表单验证
|
||||
export const rules: FormRules = {
|
||||
author: { required: true, trigger: ['blur', 'change', 'input'], message: '作者不能为空' },
|
||||
packageName: { required: true, trigger: ['blur', 'change', 'input'], message: '包名不能为空' },
|
||||
requestMapping: {
|
||||
required: true,
|
||||
trigger: ['blur', 'change', 'input'],
|
||||
message: '请求路径不能为空',
|
||||
},
|
||||
className: { required: true, trigger: ['blur', 'change', 'input'], message: '类名不能为空' },
|
||||
tableName: { required: true, trigger: ['blur', 'change', 'input'], message: '表名不能为空' },
|
||||
simpleDateFormat: {
|
||||
required: true,
|
||||
trigger: ['blur', 'change', 'input'],
|
||||
message: '日期格式不能为空',
|
||||
},
|
||||
generatorServer: [
|
||||
{
|
||||
required: true,
|
||||
trigger: ['blur', 'change', 'input'],
|
||||
message: '选择要生成的服务端',
|
||||
validator: validatorFormOption,
|
||||
},
|
||||
],
|
||||
generatorWeb: [
|
||||
{
|
||||
required: true,
|
||||
trigger: ['blur', 'change', 'input'],
|
||||
message: '选择要生成的前端',
|
||||
validator: validatorFormOption,
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,78 @@
|
|||
<script lang="ts" setup>
|
||||
import { NCard, NTabPane, NTabs } from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useTableStore } from '@/store/modules/table';
|
||||
import { useVmsStore } from '@/store/modules/vms';
|
||||
import ColumnField from '@/views/generator-code/column-field/index.vue';
|
||||
import ConnectInfo from '@/views/generator-code/connect-info/index.vue';
|
||||
import GeneratorForm from '@/views/generator-code/generator/generator-form.vue';
|
||||
import GeneratorPreview from '@/views/generator-code/generator/generator-preview.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const tableStore = useTableStore();
|
||||
const vmsStore = useVmsStore();
|
||||
const { formValue } = storeToRefs(vmsStore);
|
||||
|
||||
// 数据库表信息
|
||||
const tableInfo = reactive({
|
||||
/* 表名称 */
|
||||
tableName: '',
|
||||
/* 注释内容 */
|
||||
comment: '',
|
||||
/* 数据库内容 */
|
||||
tableCat: '',
|
||||
/* 通常是 "TABLE" */
|
||||
tableType: '',
|
||||
});
|
||||
|
||||
/* 获取数据库表属性 */
|
||||
const getTableData = async () => {
|
||||
const tableName: any = route.query.tableName;
|
||||
const tableMetaData = await tableStore.getTableMetaData(tableName);
|
||||
Object.assign(tableInfo, tableMetaData);
|
||||
|
||||
// 设置 生成表单注释值
|
||||
formValue.value.comment = tableInfo.comment;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<n-card>
|
||||
<template #header>
|
||||
<n-card title="数据库信息">
|
||||
<span class="color-blue cursor-pointer" @click="router.push('/')">回到首页</span>
|
||||
<ul>
|
||||
<li>表名:{{ route.query.tableName }}</li>
|
||||
<li>表注释:{{ tableInfo.comment }}</li>
|
||||
<li>数据库名:{{ tableInfo.tableCat }}</li>
|
||||
<li>类型:{{ tableInfo.tableType }}</li>
|
||||
</ul>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<n-tabs animated type="line">
|
||||
<n-tab-pane name="generator-code" tab="生成">
|
||||
<!-- 生成要提交的表单 -->
|
||||
<generator-form />
|
||||
|
||||
<!-- 生成好的数据 -->
|
||||
<generator-preview />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="connect-info" tab="连接信息">
|
||||
<connect-info />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="columns-info" tab="列字段">
|
||||
<column-field />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</template>
|
|
@ -0,0 +1,80 @@
|
|||
import type { SelectOption } from 'naive-ui';
|
||||
import { NTag, NTooltip } from 'naive-ui';
|
||||
import type { TableColumns } from 'naive-ui/es/data-table/src/interface';
|
||||
import type { VNode } from 'vue';
|
||||
import { h } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import type { JSX } from 'vue/jsx-runtime';
|
||||
|
||||
/* 表格列字段 */
|
||||
export function columns(): TableColumns<any> {
|
||||
const router = useRouter();
|
||||
|
||||
const routerPush = (row: any) => {
|
||||
router.replace({ path: '/generator-code', query: { tableName: row.tableName } }).then();
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
title: '序号',
|
||||
key: 'no',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(_, index: number): number {
|
||||
return index + 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '数据库名称',
|
||||
key: 'tableCat',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag type="primary">{row.tableCat}</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '表类型',
|
||||
key: 'tableType',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return <NTag>{row.tableType}</NTag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '表名',
|
||||
key: 'tableName',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return (
|
||||
<NTag type="info" onClick={() => routerPush(row)}>
|
||||
{row.tableName}
|
||||
</NTag>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '注释内容',
|
||||
key: 'comment',
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render(row: any): JSX.Element {
|
||||
return (
|
||||
<NTag type="info" onClick={() => routerPush(row)}>
|
||||
{row.comment}
|
||||
</NTag>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/* 为select添加 提示 */
|
||||
export const renderOptions = ({ node, option }: { node: VNode; option: SelectOption }) => {
|
||||
return h(NTooltip, null, {
|
||||
trigger: () => node,
|
||||
default: () => option.comment,
|
||||
});
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
<script lang="tsx" setup>
|
||||
import { NCard, NDataTable, NInput, NSelect, NTag } from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { useTableStore } from '@/store/modules/table';
|
||||
import { columns, renderOptions } from '@/views/home/columns';
|
||||
|
||||
const tableStore = useTableStore();
|
||||
const { tableList, tableListLoading, databaseInfoMeta } = storeToRefs(tableStore);
|
||||
|
||||
// 表格数据
|
||||
const datalist = ref([]);
|
||||
|
||||
/* 数据库所有的表 */
|
||||
const onSearch = async (databaseName: string) => {
|
||||
// 加载数据库基础信息,包含当前连接的数据库名称
|
||||
await tableStore.loadDatabaseInfoMeta();
|
||||
|
||||
// 如果名称不存在就获取默认的数据库名称
|
||||
if (databaseName) {
|
||||
databaseInfoMeta.value.currentDatabase = databaseName;
|
||||
}
|
||||
|
||||
// 获取完数据库名称开始加载当前数据库所有的表
|
||||
await tableStore.loadDatabaseTableList();
|
||||
|
||||
// 将当前的数据库列表放到可以搜索的列表中
|
||||
datalist.value = tableList.value;
|
||||
};
|
||||
|
||||
/* 当查询数据表 */
|
||||
const onChangeQueryText = (val: string) => {
|
||||
if (val.trim() == '') {
|
||||
datalist.value = tableList.value;
|
||||
return;
|
||||
}
|
||||
|
||||
datalist.value = datalist.value.filter(({ tableName }) => tableName.includes(val));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch(undefined);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card class="my-2" title="提示">
|
||||
<p>
|
||||
点击
|
||||
<n-tag>表名</n-tag>
|
||||
或
|
||||
<n-tag>注释内容</n-tag>
|
||||
跳转
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
数据库共
|
||||
<n-tag type="info">{{ tableList.length }}</n-tag>
|
||||
张表
|
||||
</p>
|
||||
|
||||
<div class="flex">
|
||||
<!-- 选择数据库 -->
|
||||
<n-select
|
||||
:on-update-value="onSearch"
|
||||
:options="databaseInfoMeta?.databaseList"
|
||||
:render-option="renderOptions"
|
||||
:value="databaseInfoMeta?.currentDatabase"
|
||||
class="mt-2 w-[200px]"
|
||||
clear-filter-after-select
|
||||
clearable
|
||||
label-field="tableCat"
|
||||
placeholder="选择数据库"
|
||||
value-field="tableCat"
|
||||
/>
|
||||
|
||||
<n-input
|
||||
class="mt-2 ml-2"
|
||||
placeholder="输入数据表名称"
|
||||
style="width: 220px"
|
||||
@input="onChangeQueryText"
|
||||
/>
|
||||
</div>
|
||||
</n-card>
|
||||
|
||||
<n-data-table
|
||||
:bordered="true"
|
||||
:columns="columns()"
|
||||
:data="datalist"
|
||||
:loading="tableListLoading"
|
||||
/>
|
||||
</template>
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const { params, query } = route;
|
||||
const { path } = params;
|
||||
|
||||
router.replace({ path: '/' + path, query });
|
||||
</script>
|
|
@ -0,0 +1,51 @@
|
|||
module.exports = {
|
||||
// 继承推荐规范配置
|
||||
extends: [
|
||||
'stylelint-config-standard',
|
||||
'stylelint-config-recommended-scss',
|
||||
'stylelint-config-recommended-vue/scss',
|
||||
'stylelint-config-html/vue',
|
||||
'stylelint-config-recess-order',
|
||||
],
|
||||
// 指定不同文件对应的解析器
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.{vue,html}'],
|
||||
customSyntax: 'postcss-html',
|
||||
},
|
||||
{
|
||||
files: ['**/*.{css,scss}'],
|
||||
customSyntax: 'postcss-scss',
|
||||
},
|
||||
],
|
||||
// 自定义规则
|
||||
rules: {
|
||||
'import-notation': 'string', // 指定导入CSS文件的方式("string"|"url")
|
||||
'selector-class-pattern': null, // 选择器类名命名规则
|
||||
'custom-property-pattern': null, // 自定义属性命名规则
|
||||
'keyframes-name-pattern': null, // 动画帧节点样式命名规则
|
||||
'no-descending-specificity': null, // 允许无降序特异性
|
||||
'no-empty-source': null, // 允许空样式
|
||||
// 允许 global 、export 、deep伪类
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global', 'export', 'deep'],
|
||||
},
|
||||
],
|
||||
// 允许未知属性
|
||||
'property-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreProperties: [],
|
||||
},
|
||||
],
|
||||
// 允许未知规则
|
||||
'at-rule-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreAtRules: ['apply', 'use', 'forward'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"strict": false,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"experimentalDecorators": true,
|
||||
"strictFunctionTypes": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": false,
|
||||
"resolveJsonModule": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@build/*": ["build/*"]
|
||||
},
|
||||
"types": ["node", "vite/client"]
|
||||
},
|
||||
"include": [
|
||||
"build/**",
|
||||
"mock/*.ts",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"vite.config.ts",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetUno,
|
||||
presetWebFonts,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
shortcuts: {
|
||||
'flex-center': 'flex justify-center items-center',
|
||||
'flex-x-between': 'flex items-center justify-between',
|
||||
'flex-x-around': 'flex items-center justify-around',
|
||||
'flex-y-center': 'flex flex-col flex-wrap justify-center items-center',
|
||||
'flex-y-between': 'flex flex-col flex-wrap justify-between items-center',
|
||||
'flex-y-around': 'flex flex-col flex-wrap justify-around items-center',
|
||||
'wh-full': 'w-full h-full',
|
||||
},
|
||||
theme: {
|
||||
colors: {
|
||||
primary: '#027AFF',
|
||||
'primary-secondary': '#00FFFF',
|
||||
info: '#7CC1FF',
|
||||
warning: '#FFBE44',
|
||||
'warning-secondary': '#FEDB65',
|
||||
},
|
||||
fontSizes: {
|
||||
sm: 'font-size-[14px]',
|
||||
base: 'font-size-[16px]',
|
||||
lg: 'font-size-[18px]',
|
||||
xl: 'font-size-[22px]',
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
presetUno(),
|
||||
presetAttributify(),
|
||||
presetIcons(),
|
||||
presetTypography(),
|
||||
presetWebFonts({
|
||||
fonts: {
|
||||
// ...
|
||||
},
|
||||
}),
|
||||
],
|
||||
transformers: [transformerDirectives(), transformerVariantGroup()],
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
import { defineConfig } from 'vite';
|
||||
|
||||
import { buildEnv } from './build/buildEnv';
|
||||
import { css } from './build/css';
|
||||
import { define } from './build/define';
|
||||
import { exclude, include } from './build/optimize';
|
||||
import { plugins } from './build/plugins';
|
||||
import { resolve } from './build/resolve';
|
||||
import { server } from './build/server';
|
||||
import { root, wrapperEnv } from './build/utils';
|
||||
|
||||
// https://vite.dev/config/
|
||||
// export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => {
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = wrapperEnv(mode, 'VITE');
|
||||
|
||||
return {
|
||||
root,
|
||||
base: env.VITE_PUBLIC_PATH,
|
||||
define: define(),
|
||||
plugins: plugins(mode),
|
||||
resolve: resolve(),
|
||||
esbuild: {
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment',
|
||||
jsxInject: "import { h } from 'vue';",
|
||||
},
|
||||
logLevel: 'info',
|
||||
css: css(mode),
|
||||
// 设为 false 可以避免 Vite 清屏而错过在终端中打印某些关键信息
|
||||
clearScreen: false,
|
||||
build: buildEnv(),
|
||||
server: server(mode),
|
||||
preview: server(mode),
|
||||
optimizeDeps: { include, exclude },
|
||||
};
|
||||
});
|
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
Loading…
Reference in New Issue