🔥 删除vue脚手架项目

This commit is contained in:
bunny 2025-07-01 22:09:33 +08:00
parent 3742a7ebca
commit 4e438bea86
82 changed files with 0 additions and 8801 deletions

View File

@ -1,21 +0,0 @@
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

View File

@ -1,29 +0,0 @@
# 应用名称
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

View File

@ -1,26 +0,0 @@
# 平台本地运行端口号
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

View File

@ -1,26 +0,0 @@
# 平台本地运行端口号
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

View File

@ -1,11 +0,0 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html

View File

@ -1,46 +0,0 @@
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",
},
},
],
};

View File

@ -1,11 +0,0 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html

View File

@ -1,8 +0,0 @@
{
"hash": "727e0168",
"configHash": "10198a12",
"lockfileHash": "530f8857",
"browserHash": "41b39cb9",
"optimized": {},
"chunks": {}
}

View File

@ -1,3 +0,0 @@
{
"type": "module"
}

View File

@ -1,53 +0,0 @@
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.logterser打包慢但能去除 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`;
}
},
},
},
};
};

View File

@ -1,47 +0,0 @@
import { Plugin as importToCDN } from 'vite-plugin-cdn-import';
import { wrapperEnv } from './utils';
/**
* @description `cdn`使cdn模式 .env.production VITE_CDN true
* cdnhttps://www.bootcdn.cn当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
* 使jscss文件cdn
*/
export const cdn = importToCDN({
//prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地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;
};

View File

@ -1,12 +0,0 @@
import type { CSSOptions } from 'vite';
export const css = (mode: string): CSSOptions => {
return {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/styles/minix/sidebar" as *;`,
},
},
};
};

View File

@ -1,14 +0,0 @@
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__),
};
};

View File

@ -1,58 +0,0 @@
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
)
);
}
},
};
};

View File

@ -1,14 +0,0 @@
/**
* `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 };

View File

@ -1,45 +0,0 @@
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),
];
};

View File

@ -1,9 +0,0 @@
import { pathResolve } from './utils';
export const resolve = () => {
return {
alias: {
'@': pathResolve('../src'),
},
};
};

View File

@ -1,34 +0,0 @@
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;
};

View File

@ -1,118 +0,0 @@
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);
};

View File

@ -1,31 +0,0 @@
# 使用官方的 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;"]

View File

@ -1,32 +0,0 @@
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;
}
}

View File

@ -1,177 +0,0 @@
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',
},
],
},
},
]);

View File

@ -1,13 +0,0 @@
<!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>

View File

@ -1,93 +0,0 @@
{
"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.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,19 +0,0 @@
<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>

View File

@ -1,64 +0,0 @@
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;

View File

@ -1,68 +0,0 @@
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;

View File

@ -1,20 +0,0 @@
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,
});
};

View File

@ -1,22 +0,0 @@
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 });
};

View File

@ -1,22 +0,0 @@
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.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,2 +0,0 @@
@use "src/rotate";
@use "src/transition";

View File

@ -1,19 +0,0 @@
/* 旋转动画 */
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 反向旋转动画 */
@keyframes rotate-reverse {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}

View File

@ -1,66 +0,0 @@
/* 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%);
}

View File

@ -1,22 +0,0 @@
@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;
}

View File

@ -1,19 +0,0 @@
// 空心的虚线圆
.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);
}
}

View File

@ -1,12 +0,0 @@
@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;
}

View File

@ -1,50 +0,0 @@
@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%;
}
}

View File

@ -1,49 +0,0 @@
<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>

View File

@ -1,4 +0,0 @@
/**
* Key
*/
export const TOKEN_KEY = 'accessToken';

View File

@ -1,9 +0,0 @@
/** 响应码枚举 */
export const enum ResultEnum {
// 成功
SUCCESS = '200',
// 错误
ERROR = 'B0001',
// 令牌无效或过期
TOKEN_INVALID = '209',
}

View File

@ -1,15 +0,0 @@
<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>&copy; 2025 Bunny.保留所有权利.</p>
</footer>
</div>
</template>

View File

@ -1,15 +0,0 @@
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');

View File

@ -1,9 +0,0 @@
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);
};

View File

@ -1,16 +0,0 @@
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);
},
};

View File

@ -1,26 +0,0 @@
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;

View File

@ -1,13 +0,0 @@
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;

View File

@ -1,24 +0,0 @@
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;

View File

@ -1,20 +0,0 @@
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;

View File

@ -1,27 +0,0 @@
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 };

View File

@ -1,11 +0,0 @@
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);
}

View File

@ -1,57 +0,0 @@
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;
},
},
});

View File

@ -1,62 +0,0 @@
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;
},
},
});

View File

@ -1,16 +0,0 @@
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;
}
}

View File

@ -1 +0,0 @@
type Recordable<T = any> = Record<string, T>;

View File

@ -1,6 +0,0 @@
// 基础后端返回内容
export interface BaseResult<T> {
code: number;
data: T;
message: string;
}

View File

@ -1,27 +0,0 @@
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>;
}

View File

@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@ -1,24 +0,0 @@
/**
*
* @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);
}
};

View File

@ -1,45 +0,0 @@
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);
}
};

View File

@ -1,19 +0,0 @@
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;

View File

@ -1,29 +0,0 @@
/** 判断是否是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 };

View File

@ -1,13 +0,0 @@
<template>&nbsp;</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>

View File

@ -1,237 +0,0 @@
<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>

View File

@ -1,68 +0,0 @@
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>;
},
},
];

View File

@ -1,35 +0,0 @@
<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>

View File

@ -1,54 +0,0 @@
<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>

View File

@ -1,255 +0,0 @@
<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>

View File

@ -1,72 +0,0 @@
<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>

View File

@ -1,60 +0,0 @@
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;
}
};

View File

@ -1,46 +0,0 @@
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,
},
],
};

View File

@ -1,78 +0,0 @@
<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>

View File

@ -1,80 +0,0 @@
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,
});
};

View File

@ -1,92 +0,0 @@
<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>

View File

@ -1,15 +0,0 @@
<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>

View File

@ -1,51 +0,0 @@
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'],
},
],
},
};

View File

@ -1,37 +0,0 @@
{
"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"]
}

View File

@ -1,49 +0,0 @@
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()],
});

View File

@ -1,37 +0,0 @@
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 },
};
});