feat: 更新前端和后端配置及依赖项

refactor: 重构代码结构和优化配置

docs: 更新文档和注释内容

build: 更新构建配置和依赖项

ci: 更新CI配置和工作流

chore: 更新杂项配置和脚本

fix: 修复已知问题和错误

style: 优化代码格式和样式

perf: 优化性能和提升执行效率

test: 更新测试用例和配置
This commit is contained in:
zhangtao
2025-08-10 05:45:18 +08:00
parent 190ff7cc90
commit 130038cb11
27 changed files with 2537 additions and 68 deletions
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
}
+4 -5
View File
@@ -1,15 +1,14 @@
# http://editorconfig.org
root = true
# 表示所有文件适用
[*]
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = space # 缩进风格(tab | space
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
# 表示仅 md 文件适用以下规则
[*.md]
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
+8 -1
View File
@@ -1 +1,8 @@
shamefully-hoist=true
# registry = https://registry.npmjs.org
registry = https://registry.npmmirror.com
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=true
ignore-workspace-root-check=true
install-workspace-root=true
+22 -21
View File
@@ -4,7 +4,7 @@
uni-mini-router是一个轻量级的路由管理库,专为uni-app设计,解决了uni-app原生路由系统中没有路由拦截等关键功能的问题。它提供了类似Vue Router的API体验,使得在uni-app项目中实现更加灵活和强大的路由管理成为可能。
### 主要特点
### 主要特点
1. **Vue Router风格API**:提供与Vue Router相似的API,降低学习成本
2. **路由拦截功能**:支持全局导航守卫,可以在路由跳转前后执行逻辑
@@ -38,7 +38,7 @@ function generateRoutes() {
const newPath = `/${page.path}`
return { ...page, path: newPath }
})
// 处理分包路由
if (subPackages && subPackages.length > 0) {
subPackages.forEach((subPackage) => {
@@ -49,7 +49,7 @@ function generateRoutes() {
routes.push(...subRoutes)
})
}
return routes
}
@@ -71,10 +71,10 @@ import router from './router'
export function createApp() {
const app = createSSRApp(App)
// 使用路由
app.use(router)
return {
app
}
@@ -93,7 +93,7 @@ export default defineConfig({
plugins: [
AutoImport({
imports: [
'vue',
'vue',
{
from: 'uni-mini-router',
imports: ['createRouter', 'useRouter', 'useRoute']
@@ -124,15 +124,15 @@ router.push({ path: '/pages/index/index' })
router.push({ name: 'index' })
// 携带参数
router.push({
path: '/pages/detail/index',
query: { id: 10 }
router.push({
path: '/pages/detail/index',
query: { id: 10 }
})
// 通过名称 + 参数
router.push({
name: 'detail',
params: { id: 10 }
router.push({
name: 'detail',
params: { id: 10 }
})
// Tab页面导航
@@ -195,12 +195,12 @@ uni-mini-router提供了全局导航守卫功能,可以在路由跳转前后
// src/router/index.ts
router.beforeEach((to, from, next) => {
console.log('路由跳转:', from.path, '->', to.path)
// 检查是否需要登录
if (to.meta && to.meta.requireAuth) {
// 检查登录状态
const isLoggedIn = uni.getStorageSync('token')
if (!isLoggedIn) {
// 未登录,跳转到登录页
uni.showToast({ title: '请先登录', icon: 'none' })
@@ -208,7 +208,7 @@ router.beforeEach((to, from, next) => {
return
}
}
// 继续导航
next()
})
@@ -220,7 +220,7 @@ router.beforeEach((to, from, next) => {
// src/router/index.ts
router.afterEach((to, from) => {
console.log('路由跳转完成:', to.path)
// 可以在这里做一些统计或记录
})
```
@@ -266,7 +266,7 @@ router.beforeEach((to, from, next) => {
// 检查页面是否需要登录
if (to.meta && to.meta.requireAuth) {
const token = uni.getStorageSync('token')
if (!token) {
// 显示登录提示
uni.showModal({
@@ -288,7 +288,7 @@ router.beforeEach((to, from, next) => {
return
}
}
// 继续导航
next()
})
@@ -308,11 +308,11 @@ function handleLogin() {
setTimeout(() => {
// 登录成功,存储token
uni.setStorageSync('token', 'user_token_example')
// 获取之前要去的页面
const redirect = uni.getStorageSync('redirect') || '/pages/index/index'
uni.removeStorageSync('redirect')
// 跳转回原来的页面
router.replaceAll(redirect)
}, 1000)
@@ -421,6 +421,7 @@ uni-mini-router为uni-app提供了Vue Router风格的路由解决方案,特别
使用uni-mini-router可以让你的uni-app项目路由管理更加规范化和工程化,提升开发效率和代码质量。
参考资料:
- [uni-mini-router GitHub仓库](https://github.com/Moonofweisheng/uni-mini-router)
- [uni-mini-router官方文档](https://moonofweisheng.github.io/uni-mini-router/)
- [uni-app官方路由文档](https://uniapp.dcloud.net.cn/tutorial/page.html)
- [uni-app官方路由文档](https://uniapp.dcloud.net.cn/tutorial/page.html)
+4
View File
@@ -22,13 +22,17 @@ export default [
ignores: [
"node_modules/**",
"dist/**",
"auto-imports.d.ts",
"unpackage/**",
"public/**",
"static/**",
"**/u-charts/**",
"**/qiun-**/**",
"**/auto-imports.d.ts",
// unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
"src/types/auto-imports.d.ts",
// vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
"uni-pages.d.ts",
],
},
// 检查文件的配置
+3 -2
View File
@@ -1,7 +1,8 @@
<!doctype html>
<html>
<html build-time="%BUILD_TIME%">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="/favicon.png" type="image/x-icon" />
<script>
var coverSupport =
"CSS" in window &&
@@ -13,7 +14,7 @@
'" />'
);
</script>
<title></title>
<title>FastApp</title>
<!--preload-links-->
<!--app-context-->
</head>
+148
View File
@@ -0,0 +1,148 @@
import path from 'node:path'
import process from 'node:process'
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
import { loadEnv } from 'vite'
// 手动解析命令行参数获取 mode
function getMode() {
const args = process.argv.slice(2)
const modeFlagIndex = args.findIndex(arg => arg === '--mode')
return modeFlagIndex !== -1 ? args[modeFlagIndex + 1] : args[0] === 'build' ? 'production' : 'development' // 默认 development
}
// 获取环境变量的范例
const env = loadEnv(getMode(), path.resolve(process.cwd(), 'env'))
const {
VITE_APP_TITLE,
VITE_UNI_APPID,
VITE_WX_APPID,
VITE_APP_PUBLIC_BASE,
VITE_FALLBACK_LOCALE,
} = env
export default defineManifestConfig({
'name': VITE_APP_TITLE,
'appid': VITE_UNI_APPID,
'description': '',
'versionName': '1.0.0',
'versionCode': '100',
'transformPx': false,
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
'h5': {
router: {
// base: VITE_APP_PUBLIC_BASE,
},
},
/* 5+App特有相关 */
'app-plus': {
usingComponents: true,
nvueStyleCompiler: 'uni-app',
compilerVersion: 3,
compatible: {
ignoreVersion: true,
},
splashscreen: {
alwaysShowBeforeRender: true,
waiting: true,
autoclose: true,
delay: 0,
},
/* 模块配置 */
modules: {},
/* 应用发布信息 */
distribute: {
/* android打包配置 */
android: {
minSdkVersion: 30,
targetSdkVersion: 30,
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
permissions: [
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
'<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>',
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
],
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {},
/* 图标配置 */
icons: {
android: {
hdpi: 'static/app/icons/72x72.png',
xhdpi: 'static/app/icons/96x96.png',
xxhdpi: 'static/app/icons/144x144.png',
xxxhdpi: 'static/app/icons/192x192.png',
},
ios: {
appstore: 'static/app/icons/1024x1024.png',
ipad: {
'app': 'static/app/icons/76x76.png',
'app@2x': 'static/app/icons/152x152.png',
'notification': 'static/app/icons/20x20.png',
'notification@2x': 'static/app/icons/40x40.png',
'proapp@2x': 'static/app/icons/167x167.png',
'settings': 'static/app/icons/29x29.png',
'settings@2x': 'static/app/icons/58x58.png',
'spotlight': 'static/app/icons/40x40.png',
'spotlight@2x': 'static/app/icons/80x80.png',
},
iphone: {
'app@2x': 'static/app/icons/120x120.png',
'app@3x': 'static/app/icons/180x180.png',
'notification@2x': 'static/app/icons/40x40.png',
'notification@3x': 'static/app/icons/60x60.png',
'settings@2x': 'static/app/icons/58x58.png',
'settings@3x': 'static/app/icons/87x87.png',
'spotlight@2x': 'static/app/icons/80x80.png',
'spotlight@3x': 'static/app/icons/120x120.png',
},
},
},
},
},
/* 快应用特有相关 */
'quickapp': {},
/* 小程序特有相关 */
'mp-weixin': {
appid: VITE_WX_APPID,
setting: {
urlCheck: false,
// 是否启用 ES6 转 ES5
es6: true,
minified: true,
},
optimization: {
subPackages: true,
},
// styleIsolation: 'shared',
usingComponents: true,
// __usePrivacyCheck__: true,
},
'mp-alipay': {
usingComponents: true,
styleIsolation: 'shared',
},
'mp-baidu': {
usingComponents: true,
},
'mp-toutiao': {
usingComponents: true,
},
'uniStatistics': {
enable: false,
},
'vueVersion': '3',
})
+61 -22
View File
@@ -1,14 +1,42 @@
{
"name": "fastapp",
"type": "module",
"version": "2.0.0",
"unibest-version": "3.8.0",
"packageManager": "pnpm@10.10.0",
"description": "unibest - 最好的 uniapp 开发模板",
"generate-time": "用户创建项目时生成",
"author": {
"name": "1014TaoTao",
"zhName": "1014TaoTao",
"email": "948080782@qq.com",
"github": "https://github.com/1014TaoTao/fastapi_vue3_admin",
"gitee": "https://gitee.com/tao__tao/fastapi_vue3_admin"
},
"license": "MIT",
"homepage": "https://unibest.tech",
"repository": "https://github.com/1014TaoTao/fastapi_vue3_admin",
"bugs": {
"url": "https://github.com/1014TaoTao/fastapi_vue3_admin/issues"
},
"engines": {
"node": ">=18",
"pnpm": ">=7.30"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
"uvm": "npx @dcloudio/uvm@latest",
"uvm-rm": "node ./scripts/postupgrade.js",
"postuvm": "echo upgrade uni-app success!",
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:app-harmony": "uni -p app-harmony",
"dev:custom": "uni -p",
"dev": "uni",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp": "uni -p mp-weixin",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
@@ -26,6 +54,7 @@
"build:app-ios": "uni build -p app-ios",
"build:app-harmony": "uni build -p app-harmony",
"build:custom": "uni build -p",
"build": "uni build",
"build:h5": "uni build --mode production",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
@@ -41,11 +70,13 @@
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit",
"lint": "eslint",
"lint:fix": "eslint --fix",
"lint:eslint": "eslint \"src/**/*.{vue,ts}\" --fix",
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,css,scss,vue,html,md}\"",
"lint:stylelint": "stylelint \"**/*.{css,scss,vue,html}\" --fix",
"lint:lint-staged": "lint-staged",
"prepare": "husky",
"prepare": "git init && husky",
"commit": "git-cz"
},
"lint-staged": {
@@ -75,36 +106,38 @@
}
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4020420240722002",
"@dcloudio/uni-app-harmony": "3.0.0-4020420240722002",
"@dcloudio/uni-app-plus": "3.0.0-4020420240722002",
"@dcloudio/uni-components": "3.0.0-4020420240722002",
"@dcloudio/uni-h5": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-alipay": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-baidu": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-jd": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-lark": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-qq": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-toutiao": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-weixin": "3.0.0-4020420240722002",
"@dcloudio/uni-mp-xhs": "3.0.0-4020420240722002",
"@dcloudio/uni-quickapp-webview": "3.0.0-4020420240722002",
"@dcloudio/uni-app": "3.0.0-4070520250711001",
"@dcloudio/uni-app-harmony": "3.0.0-4070520250711001",
"@dcloudio/uni-app-plus": "3.0.0-4070520250711001",
"@dcloudio/uni-components": "3.0.0-4070520250711001",
"@dcloudio/uni-h5": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-alipay": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-baidu": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-harmony": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-jd": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-lark": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-qq": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-weixin": "3.0.0-4070520250711001",
"@dcloudio/uni-mp-xhs": "3.0.0-4070520250711001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4070520250711001",
"@stomp/stompjs": "^7.1.1",
"@uni-helper/uni-use": "^0.19.14",
"@vueuse/core": "9.13.0",
"pinia": "^2.2.2",
"vue": "^3.5.13",
"vue": "^3.4.21",
"vue-i18n": "^9.14.5",
"wot-design-uni": "^1.9.1"
},
"devDependencies": {
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-4020420240722002",
"@dcloudio/uni-cli-shared": "3.0.0-4020420240722002",
"@dcloudio/uni-stacktracey": "3.0.0-4020420240722002",
"@dcloudio/vite-plugin-uni": "3.0.0-4020420240722002",
"@dcloudio/uni-automator": "3.0.0-4070520250711001",
"@dcloudio/uni-cli-shared": "3.0.0-4070520250711001",
"@dcloudio/uni-stacktracey": "3.0.0-4070520250711001",
"@dcloudio/vite-plugin-uni": "3.0.0-4070520250711001",
"@eslint/js": "^9.10.0",
"@uni-helper/uni-types": "1.0.0-alpha.6",
"@uni-helper/vite-plugin-uni-components": "^0.2.0",
@@ -139,8 +172,14 @@
"unocss": "^0.62.4",
"unocss-preset-weapp": "^0.62.2",
"unplugin-auto-import": "^0.18.3",
"vite": "6.3.2",
"vite": "5.2.8",
"vue-eslint-parser": "^9.4.3",
"vue-tsc": "^1.0.24"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
},
"lint-staged": {
"*": "eslint --fix"
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ export default defineUniPages({
// 导航栏配置
navigationBarBackgroundColor: "@navBgColor",
navigationBarTextStyle: "@navTxtStyle",
navigationBarTitleText: "Wot-Demo",
navigationBarTitleText: "FastApp",
// 页面背景配置
backgroundColor: "@bgColor",
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "vue-uniapp-template",
"appid": "",
"description": "有来移动端跨端解决方案开发模板",
"versionName": "1.0.0",
"description": "FastApp",
"versionName": "2.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
+8 -2
View File
@@ -1,4 +1,10 @@
{
"easycom": {
"autoscan": true,
"custom": {
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue"
}
},
"pages": [
{
"path": "pages/index/index",
@@ -77,7 +83,7 @@
"globalStyle": {
"navigationBarBackgroundColor": "@navBgColor",
"navigationBarTextStyle": "@navTxtStyle",
"navigationBarTitleText": "Wot-Demo",
"navigationBarTitleText": "FastApp",
"backgroundColor": "@bgColor",
"backgroundTextStyle": "@bgTxtStyle",
"backgroundColorTop": "@bgColorTop",
@@ -107,4 +113,4 @@
},
"__esModule": true,
"subPackages": []
}
}
+15
View File
@@ -0,0 +1,15 @@
# http://editorconfig.org
root = true
# 表示所有文件适用
[*]
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = space # 缩进风格(tab | space
indent_size = 2 # 缩进大小
insert_final_newline = true # 始终在文件末尾插入一个新行
# 表示仅 md 文件适用以下规则
[*.md]
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
+316
View File
@@ -0,0 +1,316 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"EffectScope": true,
"ElMessage": true,
"ElMessageBox": true,
"ElNotification": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"asyncComputed": true,
"autoResetRef": true,
"computed": true,
"computedAsync": true,
"computedEager": true,
"computedInject": true,
"computedWithControl": true,
"controlledComputed": true,
"controlledRef": true,
"createApp": true,
"createEventHook": true,
"createGlobalState": true,
"createInjectionState": true,
"createReactiveFn": true,
"createReusableTemplate": true,
"createSharedComposable": true,
"createTemplatePromise": true,
"createUnrefFn": true,
"customRef": true,
"debouncedRef": true,
"debouncedWatch": true,
"defineAsyncComponent": true,
"defineComponent": true,
"eagerComputed": true,
"effectScope": true,
"extendRef": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"ignorableWatch": true,
"inject": true,
"isDefined": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"makeDestructurable": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onClickOutside": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onKeyStroke": true,
"onLongPress": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onStartTyping": true,
"onUnmounted": true,
"onUpdated": true,
"pausableWatch": true,
"provide": true,
"reactify": true,
"reactifyObject": true,
"reactive": true,
"reactiveComputed": true,
"reactiveOmit": true,
"reactivePick": true,
"readonly": true,
"ref": true,
"refAutoReset": true,
"refDebounced": true,
"refDefault": true,
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveRef": true,
"resolveUnref": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"syncRef": true,
"syncRefs": true,
"templateRef": true,
"throttledRef": true,
"throttledWatch": true,
"toRaw": true,
"toReactive": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
"tryOnMounted": true,
"tryOnScopeDispose": true,
"tryOnUnmounted": true,
"unref": true,
"unrefElement": true,
"until": true,
"useActiveElement": true,
"useAnimate": true,
"useArrayDifference": true,
"useArrayEvery": true,
"useArrayFilter": true,
"useArrayFind": true,
"useArrayFindIndex": true,
"useArrayFindLast": true,
"useArrayIncludes": true,
"useArrayJoin": true,
"useArrayMap": true,
"useArrayReduce": true,
"useArraySome": true,
"useArrayUnique": true,
"useAsyncQueue": true,
"useAsyncState": true,
"useAttrs": true,
"useBase64": true,
"useBattery": true,
"useBluetooth": true,
"useBreakpoints": true,
"useBroadcastChannel": true,
"useBrowserLocation": true,
"useCached": true,
"useClipboard": true,
"useCloned": true,
"useColorMode": true,
"useConfirmDialog": true,
"useCounter": true,
"useCssModule": true,
"useCssVar": true,
"useCssVars": true,
"useCurrentElement": true,
"useCycleList": true,
"useDark": true,
"useDateFormat": true,
"useDebounce": true,
"useDebounceFn": true,
"useDebouncedRefHistory": true,
"useDeviceMotion": true,
"useDeviceOrientation": true,
"useDevicePixelRatio": true,
"useDevicesList": true,
"useDisplayMedia": true,
"useDocumentVisibility": true,
"useDraggable": true,
"useDropZone": true,
"useElementBounding": true,
"useElementByPoint": true,
"useElementHover": true,
"useElementSize": true,
"useElementVisibility": true,
"useEventBus": true,
"useEventListener": true,
"useEventSource": true,
"useEyeDropper": true,
"useFavicon": true,
"useFetch": true,
"useFileDialog": true,
"useFileSystemAccess": true,
"useFocus": true,
"useFocusWithin": true,
"useFps": true,
"useFullscreen": true,
"useGamepad": true,
"useGeolocation": true,
"useIdle": true,
"useImage": true,
"useInfiniteScroll": true,
"useIntersectionObserver": true,
"useInterval": true,
"useIntervalFn": true,
"useKeyModifier": true,
"useLastChanged": true,
"useLocalStorage": true,
"useMagicKeys": true,
"useManualRefHistory": true,
"useMediaControls": true,
"useMediaQuery": true,
"useMemoize": true,
"useMemory": true,
"useMounted": true,
"useMouse": true,
"useMouseInElement": true,
"useMousePressed": true,
"useMutationObserver": true,
"useNavigatorLanguage": true,
"useNetwork": true,
"useNow": true,
"useObjectUrl": true,
"useOffsetPagination": true,
"useOnline": true,
"usePageLeave": true,
"useParallax": true,
"useParentElement": true,
"usePerformanceObserver": true,
"usePermission": true,
"usePointer": true,
"usePointerLock": true,
"usePointerSwipe": true,
"usePreferredColorScheme": true,
"usePreferredContrast": true,
"usePreferredDark": true,
"usePreferredLanguages": true,
"usePreferredReducedMotion": true,
"usePrevious": true,
"useRafFn": true,
"useRefHistory": true,
"useResizeObserver": true,
"useScreenOrientation": true,
"useScreenSafeArea": true,
"useScriptTag": true,
"useScroll": true,
"useScrollLock": true,
"useSessionStorage": true,
"useShare": true,
"useSlots": true,
"useSorted": true,
"useSpeechRecognition": true,
"useSpeechSynthesis": true,
"useStepper": true,
"useStorage": true,
"useStorageAsync": true,
"useStyleTag": true,
"useSupported": true,
"useSwipe": true,
"useTemplateRefsList": true,
"useTextDirection": true,
"useTextSelection": true,
"useTextareaAutosize": true,
"useThrottle": true,
"useThrottleFn": true,
"useThrottledRefHistory": true,
"useTimeAgo": true,
"useTimeout": true,
"useTimeoutFn": true,
"useTimeoutPoll": true,
"useTimestamp": true,
"useTitle": true,
"useToNumber": true,
"useToString": true,
"useToggle": true,
"useTransition": true,
"useUrlSearchParams": true,
"useUserMedia": true,
"useVModel": true,
"useVModels": true,
"useVibrate": true,
"useVirtualList": true,
"useWakeLock": true,
"useWebNotification": true,
"useWebSocket": true,
"useWebWorker": true,
"useWebWorkerFn": true,
"useWindowFocus": true,
"useWindowScroll": true,
"useWindowSize": true,
"watch": true,
"watchArray": true,
"watchAtMost": true,
"watchDebounced": true,
"watchDeep": true,
"watchEffect": true,
"watchIgnorable": true,
"watchImmediate": true,
"watchOnce": true,
"watchPausable": true,
"watchPostEffect": true,
"watchSyncEffect": true,
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"useRoute": true,
"useRouter": true,
"storeToRefs": true,
"whenever": true,
"DirectiveBinding": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"WritableComputedRef": true,
"acceptHMRUpdate": true,
"createPinia": true,
"defineStore": true,
"getActivePinia": true,
"injectLocal": true,
"mapActions": true,
"mapGetters": true,
"mapState": true,
"mapStores": true,
"mapWritableState": true,
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true,
"onWatcherCleanup": true,
"provideLocal": true,
"setActivePinia": true,
"setMapStoreSuffix": true,
"useClipboardItems": true,
"useI18n": true,
"useId": true,
"useLink": true,
"useModel": true,
"useTemplateRef": true
}
}
+12
View File
@@ -0,0 +1,12 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html
pnpm-lock.yaml
+41
View File
@@ -0,0 +1,41 @@
# 在单参数箭头函数中始终添加括号
arrowParens: "always"
# JSX 多行元素的闭合标签另起一行
bracketSameLine: false
# 对象字面量中的括号之间添加空格
bracketSpacing: true
# 自动格式化嵌入的代码(如 Markdown 和 HTML 内的代码)
embeddedLanguageFormatting: "auto"
# 忽略 HTML 空白敏感度,将空白视为非重要内容
htmlWhitespaceSensitivity: "ignore"
# 不插入 @prettier 的 pragma 注释
insertPragma: false
# 在 JSX 中使用双引号
jsxSingleQuote: false
# 每行代码的最大长度限制为 100 字符
printWidth: 100
# 在 Markdown 中保留原有的换行格式
proseWrap: "preserve"
# 仅在必要时添加对象属性的引号
quoteProps: "as-needed"
# 不要求文件开头插入 @prettier 的 pragma 注释
requirePragma: false
# 在语句末尾添加分号
semi: true
# 使用双引号而不是单引号
singleQuote: false
# 缩进使用 2 个空格
tabWidth: 2
# 在多行元素的末尾添加逗号(ES5 支持的对象、数组等)
trailingComma: "es5"
# 使用空格而不是制表符缩进
useTabs: false
# Vue 文件中的 <script> 和 <style> 不增加额外的缩进
vueIndentScriptAndStyle: false
# 根据系统自动检测换行符
endOfLine: "auto"
# 对 HTML 文件应用特定格式化规则
overrides:
- files: "*.html"
options:
parser: "html"
+11
View File
@@ -0,0 +1,11 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html
+38
View File
@@ -0,0 +1,38 @@
module.exports = {
extends: [
"stylelint-config-recommended",
"stylelint-config-recommended-scss",
"stylelint-config-recommended-vue/scss",
"stylelint-config-html/vue",
"stylelint-config-recess-order",
],
plugins: [
"stylelint-prettier", // 统一代码风格,格式冲突时以 Prettier 规则为准
],
overrides: [
{
files: ["**/*.{vue,html}"],
customSyntax: "postcss-html",
},
{
files: ["**/*.{css,scss}"],
customSyntax: "postcss-scss",
},
],
rules: {
"prettier/prettier": true, // 强制执行 Prettier 格式化规则(需配合 .prettierrc 配置文件)
"no-empty-source": null, // 允许空的样式文件
"declaration-property-value-no-unknown": null, // 允许非常规数值格式 ,如 height: calc(100% - 50)
// 允许使用未知伪类
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["global", "export", "deep"],
},
],
// 允许使用未知伪元素
"at-rule-no-unknown": null, // 禁用默认的未知 at-rule 检查
"scss/at-rule-no-unknown": true, // 启用 SCSS 特定的 at-rule 检查
},
};
+386
View File
@@ -0,0 +1,386 @@
# 2.11.5 (2024/6/18)
## ✨ feat
- 支持后端文件导入([#142](https://github.com/youlaitech/vue3-element-admin/pull/142)) [@cshaptx4869](https://github.com/cshaptx4869)
## 🐛 fix
- vue-dev-tools 插件导致菜单路由切换卡死,暂时关闭 ([28349e](https://github.com/youlaitech/vue3-element-admin/commit/28349efe147afab36531ba148eaac3a448fe6c71)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.4 (2024/6/16)
## ✨ feat
- 操作栏增加render配置参数([#138](https://github.com/youlaitech/vue3-element-admin/pull/140)) [@cshaptx4869](https://github.com/cshaptx4869)
- 左侧工具栏增加type配置参数([#141](https://github.com/youlaitech/vue3-element-admin/pull/141)) [@diamont1001](https://github.com/diamont1001)
## ♻️ refactor
- 更换权限分配弹窗类型为 drawer 并添加父子联动开关([2d9193](https://github.com/youlaitech/vue3-element-admin/commit/2d9193c47fd224f01f82b9c0b2bbeb5e7cb33584)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.3 (2024/6/11)
## ✨ feat
- 支持默认工具栏的导入([#138](https://github.com/youlaitech/vue3-element-admin/pull/138)) [@cshaptx4869](https://github.com/cshaptx4869)
- 添加CURD导入示例([19e7bb](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869)
## ♻️ refactor
- 修改导出全量数据选项文本([904fec](https://github.com/youlaitech/vue3-element-admin/commit/904fecad65217650482fcdbb10ffb7f3d27eb9ea)) [@cshaptx4869](https://github.com/cshaptx4869)
## 🐛 fix
- 菜单列表未适配el-icon导致图标不显示问题修复([e72b68](https://github.com/youlaitech/vue3-element-admin/commit/e72b68337562b5a7ea24ad55bbe00023e1266b40)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.2 (2024/6/8)
## ✨ feat
- 支持表格远程筛选([#131](https://github.com/youlaitech/vue3-element-admin/pull/131)) [@cshaptx4869](https://github.com/cshaptx4869)
- 支持标签输入框([#132](https://github.com/youlaitech/vue3-element-admin/pull/132)) [@cshaptx4869](https://github.com/cshaptx4869)
- 表单项支持tips配置([#133](https://github.com/youlaitech/vue3-element-admin/pull/133)) [@cshaptx4869](https://github.com/cshaptx4869)
- 前端导出支持全量数据([#134](https://github.com/youlaitech/vue3-element-admin/pull/134)) [@cshaptx4869](https://github.com/cshaptx4869)
- 支持选中数据导出([#135](https://github.com/youlaitech/vue3-element-admin/pull/135)) [@cshaptx4869](https://github.com/cshaptx4869)
- 表格默认工具栏的导出、搜索按钮增加权限点控制([883128](https://github.com/youlaitech/vue3-element-admin/commit/8831289b655f2cc086ecdababaa89f8d8a087c42)) [@cshaptx4869](https://github.com/cshaptx4869)
- 页签title支持动态设置([23876a](https://github.com/youlaitech/vue3-element-admin/commit/23876aa396143bf77cb5c86af8d6023d9ff6555a)) [@haoxianrui](https://github.com/haoxianrui)
## ♻️ refactor
- 默认工具栏支持自定义([#136](https://github.com/youlaitech/vue3-element-admin/pull/136)) [@cshaptx4869](https://github.com/cshaptx4869)
- 未配置全量导出接口时选项隐藏([eab91ef](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869)
## 🐛 fix
- 修复注销登出后redirect跳转路由参数丢失([5626017](https://github.com/youlaitech/vue3-element-admin/commit/562601736731afd20bb1a5140d856f6515720159)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.1 (2024/6/6)
## ✨ feat
- 增加pagination、request、parseData配置参数([#119](https://github.com/youlaitech/vue3-element-admin/pull/119)) [@cshaptx4869](https://github.com/cshaptx4869)
- 增加返回顶部功能([#120](https://github.com/youlaitech/vue3-element-admin/pull/120)) [@cshaptx4869](https://github.com/cshaptx4869)
- 支持前端导出([#126](https://github.com/youlaitech/vue3-element-admin/pull/126)) [@cshaptx4869](https://github.com/cshaptx4869)
## ♻️ refactor
- 重构布局样式(解决页面抖动问题)([#116](https://github.com/youlaitech/vue3-element-admin/pull/116)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修改CURD示例编辑弹窗尺寸([#121](https://github.com/youlaitech/vue3-element-admin/pull/121)) [@cshaptx4869](https://github.com/cshaptx4869)
- 统一注册vue插件([#122](https://github.com/youlaitech/vue3-element-admin/pull/122)) [@cshaptx4869](https://github.com/cshaptx4869)
- 默认主题跟随系统([#128](https://github.com/youlaitech/vue3-element-admin/pull/128)) [@cshaptx4869](https://github.com/cshaptx4869)
- 增加"scss.lint.unknownAtRules": "ignore"代码,解决style中使用@apply提示unknow at rules@apply提示问题([Gitee#22](https://gitee.com/youlaiorg/vue3-element-admin/pulls/22)) [@zjsy521](https://gitee.com/zjsy521)
## 🐛 fix
- 修复左侧布局移动端菜单弹出样式 ([#117](https://github.com/youlaitech/vue3-element-admin/pull/117)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修复编辑后未清空id再新增菜单覆盖的问题([0e78eeb](https://github.com/youlaitech/vue3-element-admin/commit/0e78eeb75008fa8e9732b1b4e7d7a1ea345c7a1b)) [@haoxianrui](https://github.com/haoxianrui)
- 修复水印层级问题([#123](https://github.com/youlaitech/vue3-element-admin/pull/123)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修复混合布局样式问题([#124](https://github.com/youlaitech/vue3-element-admin/pull/124)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修复关闭弹窗时没有clearValidate问题([#125](https://github.com/youlaitech/vue3-element-admin/pull/125)) [@andm31](https://github.com/andm31)
# 2.11.0 (2024/5/27)
## ✨ feat
- 菜单添加路由参数设置(author by [haoxianrui](https://github.com/haoxianrui)
- 增加列表选择组件(author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加列表选择组件使用示例(author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加defaultToolbar配置参数(author by [cshaptx4869](https://github.com/cshaptx4869)
- 表单弹窗支持drawer模式(author by [cshaptx4869](https://github.com/cshaptx4869)
- 表单项增加computed和watchEffect配置(author by [cshaptx4869](https://github.com/cshaptx4869)
- 支持switch属性修改(author by [cshaptx4869](https://github.com/cshaptx4869)
- 表单项增加文本类型支持(author by [cshaptx4869](https://github.com/cshaptx4869)
- 列表列增加show配置项(author by [cshaptx4869](https://github.com/cshaptx4869)
- 支持搜索表单显隐控制(author by [cshaptx4869](https://github.com/cshaptx4869)
- 支持input属性修改(author by [cshaptx4869](https://github.com/cshaptx4869)
- search配置新增函数能力拓展(author by [xiudaozhe](https://github.com/xiudaozhe)
- 表格新增列设置控制(author by [haoxianrui](https://github.com/haoxianrui)
- 搜索添加展开和收缩(author by [haoxianrui](https://github.com/haoxianrui)
- watch函数增加配置项参数返回(author by [cshaptx4869](https://github.com/cshaptx4869)
## ♻️ refactor
- 重构图标选择组件(author by [cshaptx4869](https://github.com/cshaptx4869)
- 重构列表选择组件默认样式 (author by [cshaptx4869](https://github.com/cshaptx4869)
- 加强对话框表单组件和列表选择组件(author by [cshaptx4869](https://github.com/cshaptx4869)
- routeMeta增加alwaysShow字段声明(author by [cshaptx4869](https://github.com/cshaptx4869)
- 分页组件增加溢出滚动效果(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修正登录表单的Ref类型(author by [cshaptx4869](https://github.com/cshaptx4869)
- 点击表格刷新按钮不重置页码(author by [cshaptx4869](https://github.com/cshaptx4869)
- 筛选列超出一定高度滚动(author by [cshaptx4869](https://github.com/cshaptx4869)
- 优化加强initFn函数,表单项增加initFn函数(author by [cshaptx4869](https://github.com/cshaptx4869)
- 重构watch、computed、watchEffect调用(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改操作成功提示(author by [cshaptx4869](https://github.com/cshaptx4869)
- PageSearch 改用card作为容器,样式改用unocss写法(author by [cshaptx4869](https://github.com/cshaptx4869)
- 优化首页 loading 动画效果author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- 路由是否始终显示不限制只有顶级目录才有的配置,开放至菜单 (author by [haoxianrui](https://github.com/haoxianrui)
- sockjs-client 报错 global is not defined 导致开发环境无法打开 WebSocket 页面问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 发送用户重启密码功能,最少为6位字符(小于6位登陆时不允许的问题) (author by [dreamnyj](https://gitee.com/dreamnyj)
- 修复系统设置面板滚动条问题(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修复表单插槽失效问题(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改tagsview刷新丢失query问题(author by [xiudaozhe](https://github.com/xiudaozhe)
## 📦️ build
- 升级 NPM 包版本至最新 author by [haoxianrui](https://github.com/haoxianrui)
## ⚙️ ci
- 规整脚本执行命令(author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.10.1 (2024/5/4)
## ♻️ refactor
- 抽离CURD的使用部分代码为Hooks实现(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改CURD导入权限点标识名(author by [cshaptx4869](https://github.com/cshaptx4869)
- cURD表单字段支持watch监听(author by [cshaptx4869](https://github.com/cshaptx4869)
- cURD表单input支持number修饰(author by [cshaptx4869](https://github.com/cshaptx4869)
- cURD表单组件支持checkbox多选框(author by [cshaptx4869](https://github.com/cshaptx4869)
- 优化axios响应数据TS类型提示(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改CURD表单组件自定义类型的attrs传值(author by [cshaptx4869](https://github.com/cshaptx4869)
- 同步重置密码按钮权限标识重命名(author by [haoxianrui](https://github.com/haoxianrui)
- 重构API为静态方法实现模块化管理,并将types.ts重命名为model.ts用于存放接口模型定义(author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- sockjs-client 报错 global is not defined 导致开发环境无法打开 WebSocket 页面问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 主题颜色设置覆盖暗黑模式下el-table行激活的背景色问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 修复因API接口调整而影响的调用页面的问题 (author by [haoxianrui](https://github.com/haoxianrui)
## 📦️ build
- 升级 NPM 包版本至最新 author by [haoxianrui](https://github.com/haoxianrui)
# 2.10.0 (2024/4/26)
## ✨ feat
- 封装增删改查组件(author by [cshaptx4869](https://github.com/cshaptx4869)
- 集成 vite-plugin-vue-devtools 插件(author by [Tricker39](https://github.com/Tricker39)
- 增加CURD配置化实现(author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.9.3 (2024/04/14)
## ✨ feat
- 增加vue文件代码片段(author by [cshaptx4869](https://github.com/cshaptx4869)
- 菜单 hover 背景色添加值全局SCSS变量进行控制(author by [haoxianrui](https://github.com/haoxianrui)
## ♻️ refactor
- 加强基础国际化(author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加语言和布局大小枚举类型(author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加侧边栏状态枚举类型(author by [cshaptx4869](https://github.com/cshaptx4869)
- 使用布局枚举替换字面量(author by [haoxianrui](https://github.com/haoxianrui)
- 控制台使用静态数据循环渲染(author by [april](mailto:april@zen-game.cn)
- 本地缓存的 token 变量重命名(author by [haoxianrui](https://github.com/haoxianrui)
- 完善 Vite 环境变量类型声明(author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- 修复构建时提示iconComponent.name可能为undefined的报错 author by [wangji1042](https://github.com/wangji1042)
- 修复浏览器密码自动填充时可能存在的报错 (author by [cshaptx4869](https://github.com/cshaptx4869)
- 修复eslint报错(author by [cshaptx4869](https://github.com/cshaptx4869)
- 移动端下点击左侧菜单节点后关闭侧边栏(author by [haoxianrui](https://github.com/haoxianrui)
- 添加 size 类型断言修复类型报错(author by [haoxianrui](https://github.com/haoxianrui)
## 📦️ build
- husky9.x版本适配 author by [cshaptx4869](https://github.com/cshaptx4869)
- 升级 npm 包版本至最新(author by [haoxianrui](https://github.com/haoxianrui)
# 2.9.2 (2024/03/05)
## ✨ feat
- vscode开发扩展推荐(author by [cshaptx4869](https://github.com/cshaptx4869)
- 完善基础增删改查Mock接口(author by [haoxianrui](https://github.com/haoxianrui)
## ♻️ refactor
- 修改login密码框功能实现(author by [cshaptx4869](https://github.com/cshaptx4869)
- 弱化页面进入动画效果(author by [cshaptx4869](https://github.com/cshaptx4869)
- 取消推荐TypeScript Vue Plugin author by [cshaptx4869](https://github.com/cshaptx4869)
- 网站加载动画替换 (author by [haoxianrui](https://github.com/haoxianrui)
- 优化主题和主题色监听,避免多个页面重复初始化 (author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- AppMain 高度在非固定头部不正确导致出现滚动条问题修复 (author by [haoxianrui](https://github.com/haoxianrui)
- 修复混合模式开启固定Head时的样式问题 (author by [cshaptx4869](https://github.com/cshaptx4869)
- 设置面板统一字体大小 (author by [cshaptx4869](https://github.com/cshaptx4869)
## 📦️build
- 通过env配置控制mock服务 author by [cshaptx4869](https://github.com/cshaptx4869)
- 升级依赖包至最新版本 (author by [haoxianrui](https://github.com/haoxianrui)
- 定义vite全局常量替换项目标题和版本 (author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.9.1 (2024/02/28)
## ♻️ refactor
- 项目配置按钮移入navbarauthor by [cshaptx4869](https://github.com/cshaptx4869)
- 优化user数据定义(author by [cshaptx4869](https://github.com/cshaptx4869)
- 统一设置栏的 SVG 图标风格
## 🐛 fix
- 规整一些开发依赖(author by [cshaptx4869](https://github.com/cshaptx4869)
- 修复登录页主题切换问题 (author by [cshaptx4869](https://github.com/cshaptx4869)
## 🚀 pref
- 压缩图片资源 author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.9.0 (2024/02/25)
## ✨ feat
- 引入 animate.css 动画库
- 新增水印和配置
- 动态路由菜单支持 element plus 的图标
## ♻️ refactor
- Layout 布局重构和相关问题修复
- sass 使用 @use 替代 @import 引入外部文件指令
## 🐛 fix
- 修复管理页面部分弹窗无法打开问题
- 主题颜色设置按钮 hover 等未变化问题修复
# 2.8.1 (2024/01/10)
## ✨ feat
- 替换 Mock 解决方案 vite-plugin-mock 为 vite-plugin-mock-dev-server 适配 Vite5
# 2.8.0 (2023/12/27)
## ⬆️ chore
- 升级 Vite4 至 Vite5
# 2.7.1 (2023/12/12)
## ♻️ refactor
- 将打包后的文件进行分类 (author by [ityangzhiwen](https://gitee.com/ityangzhiwen)
# 2.7.0 (2023/11/19)
## ♻️ refactor
- 代码重构优化
- 修改自动导入组件类型声明文件路径
- 完善 typescript 类型
## 🐛 fix
- 修复管理页面部分弹窗无法打开问题
# 2.7.0 (2023/11/19)
## ♻️ refactor
- 代码重构
- 修改自动导入组件类型声明文件路径
- 完善 typescript 类型
## 🐛 fix
- 修复管理页面部分弹窗无法打开问题
# 2.6.3 (2023/10/22)
## ✨ feat
- 菜单管理新增目录只有一级子路由是否始终显示(alwaysShow)和路由页面是否缓存(keepAlive)的配置
- 接口文档新增 swagger、knife4j
- 引入和支持 tsx
## ♻️ refactor
- 代码瘦身,整理并删除未使用的 svg
- 控制台样式优化
## 🐛 fix
- 菜单栏折叠和展开的图标暗黑模式显示问题修复
# 2.6.2 (2023/10/11)
## 🐛 fix
- 主题设置未持久化问题
- UnoCSS 插件无智能提示
## ♻️ refactor
- WebSocket 演示样式和代码优化
- 用户管理代码重构
# 2.6.1 (2023/9/4)
## 🐛 fix
- 导航顶部模式、混合模式样式在固定 Header 出现的样式问题修复
- 固定 Header 没有持久化问题修复
- 字典回显兼容 String 和 Number 类型
# 2.6.0 (2023/8/24)💥💥💥
## ✨ feat
- 导航顶部模式、混合模式支持(author by [april-tong](https://april-tong.com/)
- 平台文档(内嵌)author by [april-tong](https://april-tong.com/)
# 2.5.0 (2023/8/8)
## ✨ feat
- 新增 Mockauthor by [ygcaicn](https://github.com/ygcaicn)
- 图标 DEMOauthor by [ygcaicn](https://github.com/ygcaicn)
## 🐛 fix
- 字典支持 Number 类型
# 2.4.1 (2023/7/20)
## ✨ feat
- 整合 vite-plugin-compression 插件打包优化(3.66MB → 1.58MB) author by [april-tong](https://april-tong.com/)
- 字典组件封装(author by [haoxr](https://juejin.cn/user/4187394044331261/posts)
## 🐛 fix
- 分页组件hidden无效
- 签名无法保存至后端
- Git 提交 stylelint 校验部分机器报错
# 2.4.0 (2023/6/17)
## ✨ feat
- 新增组件标签输入框(author by [april-tong](https://april-tong.com/)
- 新增组件签名(author by [april-tong](https://april-tong.com/)
- 新增组件表格(author by [april-tong](https://april-tong.com/)
- Echarts 图表添加下载功能 author by [april-tong](https://april-tong.com/)
## ♻️ refactor
- 限制包管理器为 pnpm 和 node 版本16+
- 自定义组件自动导入配置
- 搜索框样式写法优化
## 🐛 fix
- 用户导入的部门回显成数字问题修复
## ⬆️ chore
- element-plus 版本升级 2.3.5 → 2.3.6
# 2.3.1 (2023/5/21)
## 🔄 refactor
- 组件示例文件名称优化
# 2.2.2 (2023/5/11)
## ✨ feat
- 组件封装示例添加源码地址
- 角色、菜单、部门、字段按钮添加权限控制
# 2.3.0 (2023/5/12)
## ⬆️ chore
- vue 版本升级 3.2.45 → 3.3.1 ([CHANGELOG](https://github.com/vuejs/core/blob/main/CHANGELOG.md))
- vite 版本升级 4.3.1 → 4.3.5
## ♻️ refactor
- 使用 vue 3.3 版本新特性 `defineOptions``setup` 定义组件名称,移除重复的 `script` 标签
# 2.2.2 (2023/5/11)
## ✨ feat
- 用户新增提交添加 `vueUse``useDebounceFn` 函数实现按钮防抖节流
# 2.2.1 (2023/4/25)
## 🐛 fix
- 图标选择器组件使用 `onClickOutside` 未排除下拉弹出框元素导致无法输入搜索。
+93
View File
@@ -0,0 +1,93 @@
module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// 自定义规则
rules: {
// @see https://commitlint.js.org/#/reference-rules
// 提交类型枚举,git提交type必须是以下类型
"type-enum": [
2,
"always",
[
"feat", // 新增功能
"fix", // 修复缺陷
"docs", // 文档变更
"style", // 代码格式(不影响功能,例如空格、分号等格式修正)
"refactor", // 代码重构(不包括 bug 修复、功能新增)
"perf", // 性能优化
"test", // 添加疏漏测试或已有测试改动
"build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
"ci", // 修改 CI 配置、脚本
"revert", // 回滚 commit
"chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
"wip", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
],
],
"subject-case": [0], // subject大小写不做校验
},
prompt: {
messages: {
type: "选择你要提交的类型 :",
scope: "选择一个提交范围(可选):",
customScope: "请输入自定义的提交范围 :",
subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: "选择关联issue前缀(可选):",
customFooterPrefix: "输入自定义issue前缀 :",
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
generatingByAI: "正在通过 AI 生成你的提交简短描述...",
generatedSelectByAI: "选择一个 AI 生成的简短描述:",
confirmCommit: "是否提交或修改commit ?",
},
// prettier-ignore
types: [
{ value: "feat", name: "特性: ✨ 新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "文档: 📝 文档变更(更新README文件,或者注释)", emoji: ":memo:" },
{ value: "style", name: "格式: 🌈 代码格式(空格、格式化、缺失的分号等)", emoji: ":lipstick:" },
{ value: "refactor", name: "重构: 🔄 代码重构(不修复错误也不添加特性的代码更改)", emoji: ":recycle:" },
{ value: "perf", name: "性能: 🚀 性能优化", emoji: ":zap:" },
{ value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "集成: ⚙️ 修改 CI 配置、脚本", emoji: ":ferris_wheel:"},
{ value: "revert", name: "回退: ↩️ 回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
{ value: "wip", name: "开发中: 🚧 开发阶段临时提交", emoji: ":construction:"},
],
useEmoji: true,
emojiAlign: "center",
useAI: false,
aiNumber: 1,
themeColorCode: "",
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixes: [{ value: "closed", name: "closed: ISSUES has been processed" }],
customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: "",
},
};
+216
View File
@@ -0,0 +1,216 @@
// https://eslint.org/docs/latest/use/configure/configuration-files-new
import eslint from "@eslint/js";
import pluginVue from "eslint-plugin-vue";
import * as typescriptEslint from "typescript-eslint";
import vueParser from "vue-eslint-parser";
import globals from "globals";
import configPrettier from "eslint-config-prettier";
// 解析自动导入配置
import fs from "node:fs";
let autoImportGlobals = {};
try {
autoImportGlobals =
JSON.parse(fs.readFileSync("./.eslintrc-auto-import.json", "utf-8")).globals || {};
} catch (error) {
// 文件不存在或解析错误时使用空对象
console.warn("Could not load auto-import globals", error);
}
// Element Plus组件
const elementPlusComponents = {
// Element Plus 组件添加为全局变量,避免 no-undef 报错
ElInput: "readonly",
ElSelect: "readonly",
ElSwitch: "readonly",
ElCascader: "readonly",
ElInputNumber: "readonly",
ElTimePicker: "readonly",
ElTimeSelect: "readonly",
ElDatePicker: "readonly",
ElTreeSelect: "readonly",
ElText: "readonly",
ElRadioGroup: "readonly",
ElCheckboxGroup: "readonly",
ElOption: "readonly",
ElRadio: "readonly",
ElCheckbox: "readonly",
ElInputTag: "readonly",
ElForm: "readonly",
ElFormItem: "readonly",
ElTable: "readonly",
ElTableColumn: "readonly",
ElButton: "readonly",
ElDialog: "readonly",
ElPagination: "readonly",
ElMessage: "readonly",
ElMessageBox: "readonly",
ElNotification: "readonly",
ElTree: "readonly",
};
export default [
// 忽略文件配置
{
ignores: [
"**/node_modules/**",
"**/dist/**",
"**/*.min.*",
"**/auto-imports.d.ts",
"**/components.d.ts",
],
},
// 基础 JavaScript 配置
eslint.configs.recommended,
// Vue 推荐配置
...pluginVue.configs["flat/recommended"],
// TypeScript 推荐配置
...typescriptEslint.configs.recommended,
// 全局配置
{
// 指定要检查的文件
files: ["**/*.{js,mjs,cjs,ts,mts,cts,vue}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser, // 浏览器环境全局变量
...globals.node, // Node.js 环境全局变量
...globals.es2022, // ES2022 全局对象
...autoImportGlobals, // 自动导入的 API 函数
...elementPlusComponents, // Element Plus 组件
// 全局类型定义,解决 TypeScript 中定义但 ESLint 不识别的问题
PageQuery: "readonly",
PageResult: "readonly",
OptionType: "readonly",
ApiResponse: "readonly",
ExcelResult: "readonly",
TagView: "readonly",
AppSettings: "readonly",
__APP_INFO__: "readonly",
},
},
plugins: {
vue: pluginVue,
"@typescript-eslint": typescriptEslint.plugin,
},
rules: {
// 基础规则
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
// ES6+ 规则
"prefer-const": "error",
"no-var": "error",
"object-shorthand": "error",
// 最佳实践
eqeqeq: "off",
"no-multi-spaces": "error",
"no-multiple-empty-lines": ["error", { max: 1, maxBOF: 0, maxEOF: 0 }],
// 禁用与 TypeScript 冲突的规则
"no-unused-vars": "off",
"no-undef": "off",
"no-redeclare": "off",
"@typescript-eslint/ban-ts-comment": "off",
},
},
// Vue 文件特定配置
{
files: ["**/*.vue"],
languageOptions: {
parser: vueParser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: typescriptEslint.parser,
extraFileExtensions: [".vue"],
},
},
rules: {
// Vue 规则
"vue/multi-word-component-names": "off",
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "error",
"vue/no-unused-vars": "error",
"vue/no-mutating-props": "off",
"vue/valid-v-for": "warn",
"vue/no-template-shadow": "warn",
"vue/return-in-computed-property": "warn",
"vue/block-order": [
"error",
{
order: ["template", "script", "style"],
},
],
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "never",
component: "always",
},
svg: "always",
math: "always",
},
],
"vue/component-name-in-template-casing": ["error", "PascalCase"],
"@typescript-eslint/no-explicit-any": "off",
},
},
// TypeScript 文件特定配置
{
files: ["**/*.{ts,tsx,mts,cts}"],
languageOptions: {
parser: typescriptEslint.parser,
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// TypeScript 规则
"@typescript-eslint/no-explicit-any": "off", // 允许使用any类型,方便开发
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": "warn", // 降级为警告
"@typescript-eslint/no-unused-expressions": "warn", // 降级为警告
"@typescript-eslint/consistent-type-imports": "off", // 关闭强制使用type import
"@typescript-eslint/no-import-type-side-effects": "error",
},
},
// .d.ts 文件配置
{
files: ["**/*.d.ts"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
},
},
// CURD 组件配置
{
files: ["**/components/CURD/**/*.{ts,vue}"],
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "off",
},
},
// Prettier 集成(必须放在最后)
configPrettier,
];
+85 -9
View File
@@ -5,18 +5,69 @@
"private": true,
"type": "module",
"scripts": {
"i": "pnpm install",
"dev": "vite",
"prod": "vite --mode prod",
"build": "vite build",
"preview": "vite preview",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
"docs:preview": "vitepress preview docs",
"build:pro": "pnpm vite build --mode pro",
"build:gitee": "pnpm vite build --mode gitee",
"build:dev": "pnpm vite build --mode dev",
"build:test": "pnpm vite build --mode test",
"serve:pro": "pnpm vite preview --mode pro",
"serve:dev": "pnpm vite preview --mode dev",
"serve:test": "pnpm vite preview --mode test",
"clean": "pnpx rimraf node_modules",
"ts:check": "pnpm vue-tsc --noEmit --skipLibCheck",
"npm:check": "pnpx npm-check-updates -u",
"clean:cache": "pnpx rimraf node_modules/.cache",
"prepare": "husky install",
"p": "plop",
"icon": "esno ./scripts/icon.ts",
"preview": "vite preview",
"type-check": "vue-tsc --noEmit",
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"",
"lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.cjs",
"lint:eslint": "eslint --cache \"src/**/*.{vue,ts,js}\" --fix",
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,css,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache \"**/*.{css,scss,vue}\" --fix",
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:stylelint",
"preinstall": "npx only-allow pnpm",
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
},
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
],
"*.{cjs,json}": [
"prettier --write"
],
"*.{vue,html}": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,css}": [
"stylelint --fix",
"prettier --write"
],
"*.md": [
"prettier --write"
]
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^13.5.0",
"@wangeditor-next/editor": "^5.6.38",
"@wangeditor-next/editor": "^5.6.42",
"@wangeditor-next/editor-for-vue": "^5.1.14",
"animate.css": "^4.1.1",
"axios": "^1.10.0",
@@ -39,26 +90,51 @@
"vue3-cron-plus": "^0.1.9"
},
"devDependencies": {
"@eslint/js": "^9.32.0",
"@iconify/utils": "^2.3.0",
"@types/codemirror": "^5.60.16",
"@types/node": "^22.16.5",
"@types/nprogress": "^0.2.3",
"@types/path-browserify": "^1.0.3",
"@types/qs": "^6.14.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
"@vitejs/plugin-vue": "^5.2.4",
"autoprefixer": "^10.4.21",
"commitizen": "^4.3.1",
"cz-git": "^1.12.0",
"eslint": "^9.32.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.3",
"eslint-plugin-vue": "^10.4.0",
"fs-extra": "^11.2.0",
"husky": "^9.1.7",
"sass": "^1.89.2",
"terser": "^5.43.1",
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0",
"unocss": "66.2.3",
"unplugin-auto-import": "^19.3.0",
"unplugin-vue-components": "^28.8.0",
"vite": "^6.3.5",
"vitepress": "^1.6.4"
"vitepress": "^1.6.4",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^2.2.12"
},
"packageManager": "pnpm@9.15.3",
"engines": {
"node": ">=18.0.0",
"npm": ">=10.0.0"
"npm": ">=10.0.0",
"pnpm": ">=8.1.0"
},
"repository": "https://gitee.com/tao__tao/fastapi_vue3_admin.git",
"author": "948080782@qq.com",
"license": "MIT"
}
"repository": {
"type": "git",
"url": "https://gitee.com/tao__tao/fastapi_vue3_admin.git"
},
"bugs": {
"url": "https://gitee.com/tao__tao/fastapi_vue3_admin/issues"
},
"author": "fastapiadmin <502431556@qq.com>",
"license": "MIT",
"homepage": "https://gitee.com/tao__tao/fastapi_vue3_admin"
}
+191
View File
@@ -0,0 +1,191 @@
import request from "@/utils/request";
const GENERATOR_BASE_URL = "/api/v1/codegen";
const GeneratorAPI = {
/** 获取数据表分页列表 */
getTablePage(params: TablePageQuery) {
return request<any, PageResult<TablePageVO[]>>({
url: `${GENERATOR_BASE_URL}/table/page`,
method: "get",
params,
});
},
/** 获取代码生成配置 */
getGenConfig(tableName: string) {
return request<any, GenConfigForm>({
url: `${GENERATOR_BASE_URL}/${tableName}/config`,
method: "get",
});
},
/** 获取代码生成配置 */
saveGenConfig(tableName: string, data: GenConfigForm) {
return request({
url: `${GENERATOR_BASE_URL}/${tableName}/config`,
method: "post",
data,
});
},
/** 获取代码生成预览数据 */
getPreviewData(tableName: string) {
return request<any, GeneratorPreviewVO[]>({
url: `${GENERATOR_BASE_URL}/${tableName}/preview`,
method: "get",
});
},
/** 重置代码生成配置 */
resetGenConfig(tableName: string) {
return request({
url: `${GENERATOR_BASE_URL}/${tableName}/config`,
method: "delete",
});
},
/**
* 下载 ZIP 文件
* @param url
* @param fileName
*/
download(tableName: string) {
return request({
url: `${GENERATOR_BASE_URL}/${tableName}/download`,
method: "get",
responseType: "blob",
}).then((response) => {
const fileName = decodeURI(
response.headers["content-disposition"].split(";")[1].split("=")[1]
);
const blob = new Blob([response.data], { type: "application/zip" });
const a = document.createElement("a");
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
});
},
};
export default GeneratorAPI;
/** 代码生成预览对象 */
export interface GeneratorPreviewVO {
/** 文件生成路径 */
path: string;
/** 文件名称 */
fileName: string;
/** 文件内容 */
content: string;
}
/** 数据表分页查询参数 */
export interface TablePageQuery extends PageQuery {
/** 关键字(表名) */
keywords?: string;
}
/** 数据表分页对象 */
export interface TablePageVO {
/** 表名称 */
tableName: string;
/** 表描述 */
tableComment: string;
/** 存储引擎 */
engine: string;
/** 字符集排序规则 */
tableCollation: string;
/** 创建时间 */
createTime: string;
}
/** 代码生成配置表单 */
export interface GenConfigForm {
/** 主键 */
id?: string;
/** 表名 */
tableName?: string;
/** 业务名 */
businessName?: string;
/** 模块名 */
moduleName?: string;
/** 包名 */
packageName?: string;
/** 实体名 */
entityName?: string;
/** 作者 */
author?: string;
/** 上级菜单 */
parentMenuId?: string;
/** 后端应用名 */
backendAppName?: string;
/** 前端应用名 */
frontendAppName?: string;
/** 字段配置列表 */
fieldConfigs?: FieldConfig[];
}
/** 字段配置 */
export interface FieldConfig {
/** 主键 */
id?: string;
/** 列名 */
columnName?: string;
/** 列类型 */
columnType?: string;
/** 字段名 */
fieldName?: string;
/** 字段类型 */
fieldType?: string;
/** 字段描述 */
fieldComment?: string;
/** 是否在列表显示 */
isShowInList?: number;
/** 是否在表单显示 */
isShowInForm?: number;
/** 是否在查询条件显示 */
isShowInQuery?: number;
/** 是否必填 */
isRequired?: number;
/** 表单类型 */
formType?: number;
/** 查询类型 */
queryType?: number;
/** 字段长度 */
maxLength?: number;
/** 字段排序 */
fieldSort?: number;
/** 字典类型 */
dictType?: string;
}
+866
View File
@@ -0,0 +1,866 @@
<template>
<div class="app-container">
<!-- 搜索区域 -->
<div class="search-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item prop="keywords" label="关键字">
<el-input
v-model="queryParams.keywords"
placeholder="表名"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item class="search-buttons">
<el-button type="primary" @click="handleQuery">
<template #icon>
<Search />
</template>
搜索
</el-button>
<el-button @click="handleResetQuery">
<template #icon>
<Refresh />
</template>
重置
</el-button>
</el-form-item>
</el-form>
</div>
<el-card shadow="hover" class="table-card">
<el-table
ref="dataTableRef"
v-loading="loading"
:data="pageData"
highlight-current-row
border
class="data-table__content"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="表名" prop="tableName" min-width="100" />
<el-table-column label="描述" prop="tableComment" width="150" />
<el-table-column label="存储引擎" align="center" prop="engine" />
<el-table-column label="排序规则" align="center" prop="tableCollation" />
<el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column fixed="right" label="操作" width="200">
<template #default="scope">
<el-button
type="primary"
size="small"
link
@click="handleOpenDialog(scope.row.tableName)"
>
<template #icon>
<MagicStick />
</template>
生成代码
</el-button>
<el-button
v-if="scope.row.isConfigured === 1"
type="danger"
size="small"
link
@click="handleResetConfig(scope.row.tableName)"
>
<template #icon>
<RefreshLeft />
</template>
重置配置
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
</el-card>
<el-drawer
v-model="dialog.visible"
:title="dialog.title"
size="80%"
@close="dialog.visible = false"
>
<el-steps :active="active" align-center finish-status="success" simple>
<el-step title="基础配置" />
<el-step title="字段配置" />
<el-step title="预览生成" />
</el-steps>
<div class="mt-5">
<el-form
v-show="active == 0"
:model="genConfigFormData"
:label-width="100"
:rules="genConfigFormRules"
>
<el-row>
<el-col :span="12">
<el-form-item label="表名" prop="tableName">
<el-input v-model="genConfigFormData.tableName" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务名" prop="businessName">
<el-input v-model="genConfigFormData.businessName" placeholder="用户" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="主包名" prop="packageName">
<el-input v-model="genConfigFormData.packageName" placeholder="com.youlai.boot" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="模块名" prop="moduleName">
<el-input v-model="genConfigFormData.moduleName" placeholder="system" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="实体名" prop="entityName">
<el-input v-model="genConfigFormData.entityName" placeholder="User" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="作者">
<el-input v-model="genConfigFormData.author" placeholder="youlai" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item>
<template #label>
<div class="flex-y-between">
<span>上级菜单</span>
<el-tooltip effect="dark">
<template #content>
选择上级菜单,生成代码后会自动创建对应菜单。
<br />
注意1:生成菜单后需分配权限给角色,否则菜单将无法显示。
<br />
注意2:演示环境默认不生成菜单,如需生成,请在本地部署数据库。
</template>
<el-icon class="cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-tree-select
v-model="genConfigFormData.parentMenuId"
placeholder="选择上级菜单"
:data="menuOptions"
check-strictly
:render-after-expand="false"
filterable
clearable
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div v-show="active == 1" class="elTableCustom">
<el-table
v-loading="loading"
row-key="id"
:element-loading-text="loadingText"
highlight--currentrow
:data="genConfigFormData.fieldConfigs"
>
<el-table-column width="55" align="center">
<el-icon class="cursor-move sortable-handle">
<Rank />
</el-icon>
</el-table-column>
<el-table-column label="列名" width="110">
<template #default="scope">
{{ scope.row.columnName }}
</template>
</el-table-column>
<el-table-column label="列类型" width="80">
<template #default="scope">
{{ scope.row.columnType }}
</template>
</el-table-column>
<el-table-column label="字段名" width="120">
<template #default="scope">
<el-input v-model="scope.row.fieldName" />
</template>
</el-table-column>
<el-table-column label="字段类型" width="80">
<template #default="scope">
{{ scope.row.fieldType }}
</template>
</el-table-column>
<el-table-column label="字段注释" min-width="100">
<template #default="scope">
<el-input v-model="scope.row.fieldComment" />
</template>
</el-table-column>
<el-table-column label="最大长度" width="80">
<template #default="scope">
<el-input v-model="scope.row.maxLength" />
</template>
</el-table-column>
<el-table-column width="70">
<template #header>
<div class="flex-y-center">
<span>查询</span>
<el-checkbox
v-model="isCheckAllQuery"
class="ml-1"
@change="toggleCheckAll('isShowInQuery', isCheckAllQuery)"
/>
</div>
</template>
<template #default="scope">
<el-checkbox v-model="scope.row.isShowInQuery" :true-value="1" :false-value="0" />
</template>
</el-table-column>
<el-table-column width="70">
<template #header>
<div class="flex-y-center">
<span>列表</span>
<el-checkbox
v-model="isCheckAllList"
class="ml-1"
@change="toggleCheckAll('isShowInList', isCheckAllList)"
/>
</div>
</template>
<template #default="scope">
<el-checkbox v-model="scope.row.isShowInList" :true-value="1" :false-value="0" />
</template>
</el-table-column>
<el-table-column width="70">
<template #header>
<div class="flex-y-center">
<span>表单</span>
<el-checkbox
v-model="isCheckAllForm"
class="ml-1"
@change="toggleCheckAll('isShowInForm', isCheckAllForm)"
/>
</div>
</template>
<template #default="scope">
<el-checkbox v-model="scope.row.isShowInForm" :true-value="1" :false-value="0" />
</template>
</el-table-column>
<el-table-column label="必填" width="70">
<template #default="scope">
<el-checkbox
v-if="scope.row.isShowInForm == 1"
v-model="scope.row.isRequired"
:true-value="1"
:false-value="0"
/>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="查询方式" min-width="120">
<template #default="scope">
<el-select
v-if="scope.row.isShowInQuery === 1"
v-model="scope.row.queryType"
placeholder="请选择"
>
<el-option
v-for="(item, key) in queryTypeOptions"
:key="key"
:label="item.label"
:value="item.value"
/>
</el-select>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="表单类型" min-width="120">
<template #default="scope">
<el-select
v-if="scope.row.isShowInQuery === 1 || scope.row.isShowInForm === 1"
v-model="scope.row.formType"
placeholder="请选择"
>
<el-option
v-for="(item, key) in formTypeOptions"
:key="key"
:label="item.label"
:value="item.value"
/>
</el-select>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="字典类型" min-width="100">
<template #default="scope">
<el-select
v-if="scope.row.formType === FormTypeEnum.SELECT.value"
v-model="scope.row.dictType"
placeholder="请选择"
clearable
>
<el-option
v-for="item in dictOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</div>
<el-row v-show="active == 2">
<el-col :span="6">
<el-scrollbar max-height="72vh">
<el-tree
:data="treeData"
default-expand-all
highlight-current
@node-click="handleFileTreeNodeClick"
>
<template #default="{ data }">
<div :class="`i-svg:${getFileTreeNodeIcon(data.label)}`" />
<span class="ml-1">{{ data.label }}</span>
</template>
</el-tree>
</el-scrollbar>
</el-col>
<el-col :span="18">
<el-scrollbar max-height="72vh">
<div class="absolute z-36 right-5 top-2">
<el-link type="primary" @click="handleCopyCode">
<el-icon>
<CopyDocument />
</el-icon>
一键复制
</el-link>
</div>
<Codemirror
ref="cmRef"
v-model:value="code"
:options="cmOptions"
border
:readonly="true"
height="100%"
width="100%"
/>
</el-scrollbar>
</el-col>
</el-row>
</div>
<template #footer>
<el-button v-if="active !== 0" type="success" @click="handlePrevClick">
<el-icon>
<Back />
</el-icon>
{{ prevBtnText }}
</el-button>
<el-button type="primary" @click="handleNextClick">
{{ nextBtnText }}
<el-icon v-if="active !== 2">
<Right />
</el-icon>
<el-icon v-else>
<Download />
</el-icon>
</el-button>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "Codegen",
});
import Sortable from "sortablejs";
import "codemirror/mode/javascript/javascript.js";
import Codemirror from "codemirror-editor-vue3";
import type { CmComponentRef } from "codemirror-editor-vue3";
import type { EditorConfiguration } from "codemirror";
import { FormTypeEnum } from "@/enums/codegen/form.enum";
import { QueryTypeEnum } from "@/enums/codegen/query.enum";
import GeneratorAPI, {
TablePageVO,
GenConfigForm,
TablePageQuery,
FieldConfig,
} from "@/api/codegen.api";
import DictAPI from "@/api/system/dict.api";
import MenuAPI from "@/api/system/menu.api";
interface TreeNode {
label: string;
content?: string;
children?: TreeNode[];
}
const treeData = ref<TreeNode[]>([]);
const queryFormRef = ref();
const queryParams = reactive<TablePageQuery>({
pageNum: 1,
pageSize: 10,
});
const loading = ref(false);
const loadingText = ref("loading...");
const pageData = ref<TablePageVO[]>([]);
const total = ref(0);
const formTypeOptions: Record<string, OptionType> = FormTypeEnum;
const queryTypeOptions: Record<string, OptionType> = QueryTypeEnum;
const dictOptions = ref<OptionType[]>();
const menuOptions = ref<OptionType[]>([]);
const genConfigFormData = ref<GenConfigForm>({
fieldConfigs: [],
});
const genConfigFormRules = {
tableName: [{ required: true, message: "请输入表名", trigger: "blur" }],
businessName: [{ required: true, message: "请输入业务名", trigger: "blur" }],
packageName: [{ required: true, message: "请输入主包名", trigger: "blur" }],
moduleName: [{ required: true, message: "请输入模块名", trigger: "blur" }],
entityName: [{ required: true, message: "请输入实体名", trigger: "blur" }],
};
const dialog = reactive({
visible: false,
title: "",
});
const { copy, copied } = useClipboard();
const code = ref();
const cmRef = ref<CmComponentRef>();
const cmOptions: EditorConfiguration = {
mode: "text/javascript",
};
const prevBtnText = ref("");
const nextBtnText = ref("下一步,字段配置");
const active = ref(0);
const currentTableName = ref("");
const sortFlag = ref<object>();
// 查询是否全选
const isCheckAllQuery = ref(false);
// 列表是否全选
const isCheckAllList = ref(false);
// 表单是否全选
const isCheckAllForm = ref(false);
watch(active, (val) => {
if (val === 0) {
nextBtnText.value = "下一步,字段配置";
} else if (val === 1) {
prevBtnText.value = "上一步,基础配置";
nextBtnText.value = "下一步,确认生成";
} else if (val === 2) {
prevBtnText.value = "上一步,字段配置";
nextBtnText.value = "下载代码";
}
});
watch(copied, () => {
if (copied.value) {
ElMessage.success("复制成功");
}
});
watch(
() => genConfigFormData.value.fieldConfigs as FieldConfig[],
(newVal: FieldConfig[]) => {
newVal.forEach((fieldConfig) => {
if (
fieldConfig.fieldType &&
fieldConfig.fieldType.includes("Date") &&
fieldConfig.isShowInQuery === 1
) {
fieldConfig.queryType = QueryTypeEnum.BETWEEN.value as number;
}
});
},
{ deep: true, immediate: true }
);
const initSort = () => {
if (sortFlag.value) {
return;
}
const table = document.querySelector(".elTableCustom .el-table__body-wrapper tbody");
sortFlag.value = Sortable.create(<HTMLElement>table, {
group: "shared",
animation: 150,
ghostClass: "sortable-ghost", //拖拽样式
handle: ".sortable-handle", //拖拽区域
easing: "cubic-bezier(1, 0, 0, 1)",
// 结束拖动事件
onEnd: (item: any) => {
setNodeSort(item.oldIndex, item.newIndex);
},
});
};
const setNodeSort = (oldIndex: number, newIndex: number) => {
// 使用arr复制一份表格数组数据
const arr = Object.assign([], genConfigFormData.value.fieldConfigs);
const currentRow = arr.splice(oldIndex, 1)[0];
arr.splice(newIndex, 0, currentRow);
arr.forEach((item: FieldConfig, index) => {
item.fieldSort = index + 1;
});
genConfigFormData.value.fieldConfigs = [];
nextTick(async () => {
genConfigFormData.value.fieldConfigs = arr;
});
};
/** 上一步 */
function handlePrevClick() {
if (active.value === 2) {
//这里需要重新获取一次数据,如果第一次生成代码后,再次点击上一步,数据不重新获取,再次点击下一步,会再次插入数据,导致索引重复报错
genConfigFormData.value = {
fieldConfigs: [],
};
nextTick(() => {
loading.value = true;
GeneratorAPI.getGenConfig(currentTableName.value)
.then((data) => {
genConfigFormData.value = data;
})
.finally(() => {
loading.value = false;
});
});
initSort();
}
if (active.value-- <= 0) active.value = 0;
}
/** 下一步 */
function handleNextClick() {
if (active.value === 0) {
//这里需要校验基础配置
const { tableName, packageName, businessName, moduleName, entityName } =
genConfigFormData.value;
if (!tableName || !packageName || !businessName || !moduleName || !entityName) {
ElMessage.error("表名、业务名、包名、模块名、实体名不能为空");
return;
}
initSort();
}
if (active.value === 1) {
// 保存生成配置
const tableName = genConfigFormData.value.tableName;
if (!tableName) {
ElMessage.error("表名不能为空");
return;
}
loading.value = true;
loadingText.value = "代码生成中,请稍后...";
GeneratorAPI.saveGenConfig(tableName, genConfigFormData.value)
.then(() => {
handlePreview(tableName);
})
.then(() => {
if (active.value++ >= 2) active.value = 2;
})
.finally(() => {
loading.value = false;
loadingText.value = "loading...";
});
} else {
if (active.value++ >= 2) {
active.value = 2;
}
if (active.value === 2) {
const tableName = genConfigFormData.value.tableName;
if (!tableName) {
ElMessage.error("表名不能为空");
return;
}
GeneratorAPI.download(tableName);
}
}
}
/** 查询 */
function handleQuery() {
loading.value = true;
GeneratorAPI.getTablePage(queryParams)
.then((data) => {
pageData.value = data.list;
total.value = data.total;
})
.finally(() => {
loading.value = false;
});
}
/** 重置查询 */
function handleResetQuery() {
queryFormRef.value.resetFields();
queryParams.pageNum = 1;
handleQuery();
}
/** 打开弹窗 */
async function handleOpenDialog(tableName: string) {
dialog.visible = true;
active.value = 0;
menuOptions.value = await MenuAPI.getOptions(true);
currentTableName.value = tableName;
// 获取字典数据
DictAPI.getList().then((data) => {
dictOptions.value = data;
loading.value = true;
GeneratorAPI.getGenConfig(tableName)
.then((data) => {
dialog.title = `${tableName} 代码生成`;
genConfigFormData.value = data;
checkAllSelected("isShowInQuery", isCheckAllQuery);
checkAllSelected("isShowInList", isCheckAllList);
checkAllSelected("isShowInForm", isCheckAllForm);
// 如果已经配置过,直接跳转到预览页面
if (genConfigFormData.value.id) {
active.value = 2;
handlePreview(tableName);
} else {
// 如果没有配置过,跳转到基础配置页面
active.value = 0;
}
})
.finally(() => {
loading.value = false;
});
});
}
/** 重置配置 */
function handleResetConfig(tableName: string) {
ElMessageBox.confirm("确定要重置配置吗?", "提示", {
type: "warning",
}).then(() => {
GeneratorAPI.resetGenConfig(tableName).then(() => {
ElMessage.success("重置成功");
handleQuery();
});
});
}
type FieldConfigKey = "isShowInQuery" | "isShowInList" | "isShowInForm";
/** 全选 */
const toggleCheckAll = (key: FieldConfigKey, value: boolean) => {
const fieldConfigs = genConfigFormData.value?.fieldConfigs;
if (fieldConfigs) {
fieldConfigs.forEach((row: FieldConfig) => {
row[key] = value ? 1 : 0;
});
}
};
const checkAllSelected = (key: keyof FieldConfig, isCheckAllRef: any) => {
const fieldConfigs = genConfigFormData.value?.fieldConfigs || [];
isCheckAllRef.value = fieldConfigs.every((row: FieldConfig) => row[key] === 1);
};
/** 获取生成预览 */
function handlePreview(tableName: string) {
treeData.value = [];
GeneratorAPI.getPreviewData(tableName)
.then((data) => {
dialog.title = `代码生成 ${tableName}`;
// 组装树形结构完善代码
const tree = buildTree(data);
treeData.value = [tree];
// 默认选中第一个叶子节点并设置 code 值
const firstLeafNode = findFirstLeafNode(tree);
if (firstLeafNode) {
code.value = firstLeafNode.content || "";
}
})
.catch(() => {
active.value = 0;
});
}
/**
* 递归构建树形结构
*
* @param data - 数据数组
* @returns 树形结构根节点
*/
function buildTree(data: { path: string; fileName: string; content: string }[]): TreeNode {
// 动态获取根节点
const root: TreeNode = { label: "前后端代码", children: [] };
data.forEach((item) => {
// 将路径分成数组
const separator = item.path.includes("/") ? "/" : "\\";
const parts = item.path.split(separator);
// 定义特殊路径
const specialPaths = [
"src" + separator + "main",
"java",
genConfigFormData.value.backendAppName,
genConfigFormData.value.frontendAppName,
(genConfigFormData.value.packageName + "." + genConfigFormData.value.moduleName).replace(
/\./g,
separator
),
];
// 检查路径中的特殊部分并合并它们
const mergedParts: string[] = [];
let buffer: string[] = [];
parts.forEach((part) => {
buffer.push(part);
const currentPath = buffer.join(separator);
if (specialPaths.includes(currentPath)) {
mergedParts.push(currentPath);
buffer = [];
}
});
// 将 mergedParts 路径中的分隔符\替换为/
mergedParts.forEach((part, index) => {
mergedParts[index] = part.replace(/\\/g, "/");
});
if (buffer.length > 0) {
mergedParts.push(...buffer);
}
let currentNode = root;
mergedParts.forEach((part) => {
// 查找或创建当前部分的子节点
let node = currentNode.children?.find((child) => child.label === part);
if (!node) {
node = { label: part, children: [] };
currentNode.children?.push(node);
}
currentNode = node;
});
// 添加文件节点
currentNode.children?.push({
label: item.fileName,
content: item?.content,
});
});
return root;
}
/**
* 递归查找第一个叶子节点
* @param node - 树形节点
* @returns 第一个叶子节点
*/
function findFirstLeafNode(node: TreeNode): TreeNode | null {
if (!node.children || node.children.length === 0) {
return node;
}
for (const child of node.children) {
const leafNode = findFirstLeafNode(child);
if (leafNode) {
return leafNode;
}
}
return null;
}
/** 文件树节点 Click */
function handleFileTreeNodeClick(data: TreeNode) {
if (!data.children || data.children.length === 0) {
code.value = data.content || "";
}
}
/** 获取文件树节点图标 */
function getFileTreeNodeIcon(label: string) {
if (label.endsWith(".java")) {
return "java";
}
if (label.endsWith(".html")) {
return "html";
}
if (label.endsWith(".vue")) {
return "vue";
}
if (label.endsWith(".ts")) {
return "typescript";
}
if (label.endsWith(".xml")) {
return "xml";
}
return "file";
}
/** 一键复制 */
const handleCopyCode = () => {
if (code.value) {
copy(code.value);
}
};
/** 组件挂载后执行 */
onMounted(() => {
handleQuery();
cmRef.value?.destroy();
});
</script>
+1 -1
View File
@@ -28,6 +28,6 @@
"types": ["node", "vite/client", "element-plus/global"]
},
"include": ["mock/**/*.ts", "src/**/*.ts", "src/**/*.vue", "vite.config.ts"],
"include": ["mock/**/*.ts", "src/**/*.ts", "src/**/*.vue", "vite.config.ts", "eslint.config.ts"],
"exclude": ["node_modules", "dist"]
}
+2 -2
View File
@@ -1,5 +1,5 @@
import vue from "@vitejs/plugin-vue";
import { type ConfigEnv, loadEnv, defineConfig } from "vite";
import { type ConfigEnv, type UserConfig, loadEnv, defineConfig, PluginOption } from "vite";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
@@ -47,7 +47,7 @@ export default defineConfig(({ mode }: ConfigEnv) => {
target: env.VITE_API_BASE_URL, // 代理目标地址:https://后端地址
secure: false, // 请求是否https
changeOrigin: true, // 是否跨域
rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
rewrite: (path: string) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
},
},
},