From b5bacd2cbf3d8d4cdf63e8ffc5c16694f8a5e75f Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 28 Oct 2026 08:21:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(oidc):=20=E6=8E=A5=E5=85=A5=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E8=AE=A4=E8=AF=81=E7=99=BB=E5=BD=95=E4=B8=8E=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E8=87=AA=E5=8A=A9=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 登录页接入统一认证入口并处理认证回调 - 个人中心新增认证身份区块,支持自助绑定与解绑 - 头像菜单与应用中心新增认证身份入口 - 应用中心删除经营管理分组,业务系统卡片指向统一认证授权地址 - 用户管理页移除认证身份代绑入口与弹窗 - 身份关联接口从用户域移到认证域 --- .env.development | 13 +- .env.production | 3 + .eslintrc-auto-import.json | 316 ---- .gitignore | 8 +- .husky/pre-commit | 2 +- .stylelintrc.cjs | 4 + README.md | 10 +- deploy.example.mjs | 58 - deploy.mjs | 31 - eslint.config.ts | 79 +- package.json | 38 +- pnpm-lock.yaml | 547 ++++--- public/map/china.json | 1 - src/App.vue | 6 +- src/api/auth/identity.ts | 55 + src/api/auth/index.ts | 28 +- src/api/codegen/index.ts | 4 +- src/api/codegen/types.ts | 6 +- src/api/common.ts | 23 +- src/api/file/index.ts | 3 +- src/api/form/index.ts | 33 +- src/api/form/types.ts | 12 +- src/api/realtime/index.ts | 49 + src/api/system/app/index.ts | 9 +- src/api/system/app/types.ts | 6 +- src/api/system/config/index.ts | 9 +- src/api/system/config/types.ts | 6 +- src/api/system/dept/index.ts | 9 +- src/api/system/dept/types.ts | 4 +- src/api/system/dict/index.ts | 13 +- src/api/system/dict/types.ts | 8 +- src/api/system/log/index.ts | 11 +- src/api/system/log/types.ts | 8 +- src/api/system/menu/index.ts | 9 +- src/api/system/menu/types.ts | 12 +- src/api/system/notice/index.ts | 20 +- src/api/system/notice/types.ts | 6 +- src/api/system/role/index.ts | 9 +- src/api/system/role/types.ts | 8 +- src/api/system/tenant-plan/index.ts | 9 +- src/api/system/tenant-plan/types.ts | 6 +- src/api/system/tenant/index.ts | 14 +- src/api/system/tenant/types.ts | 6 +- src/api/system/user/index.ts | 49 +- src/api/system/user/types.ts | 40 +- src/api/workflow/index.ts | 15 +- src/api/workflow/types.ts | 10 +- src/components/AppLink/index.vue | 14 +- .../CommandPalette/useCommandPalette.ts | 6 +- src/components/Crud/CrudForm.vue | 473 ++++++ src/components/Crud/CrudModal.vue | 290 ---- src/components/Crud/CrudPage.vue | 152 ++ src/components/Crud/CrudSearch.vue | 293 ++-- src/components/Crud/CrudTable.vue | 1304 ++++++++++------- src/components/Crud/types.ts | 276 ++-- src/components/Crud/useCrudPage.ts | 159 +- src/components/Crud/utils/sort.ts | 18 + src/components/Crud/utils/workbook.ts | 65 + src/components/DictSelect/index.vue | 7 +- src/components/DictTag/index.vue | 60 +- src/components/NoticeCenter/useNotice.ts | 218 +-- src/components/NoticeDetailDialog/index.vue | 13 +- src/components/SectionTitle/index.vue | 6 +- src/components/TableActions/index.vue | 157 ++ src/components/TableActions/types.ts | 14 + src/components/TableSelect/index.vue | 13 +- src/components/TextScroll/index.vue | 111 +- src/components/Upload/FileUpload.vue | 191 +-- src/components/Upload/MultiImageUpload.vue | 133 +- src/components/Upload/SingleImageUpload.vue | 6 +- src/components/Upload/useUploadQueue.ts | 161 ++ src/components/WangEditor/index.vue | 9 +- src/composables/index.ts | 1 + src/composables/usePageTable.ts | 113 +- src/composables/useRealtime.ts | 293 ++++ src/composables/useTableSelection.ts | 34 +- src/composables/useVersionCheck.ts | 16 +- src/constants/index.ts | 5 - src/directives/permission/index.ts | 38 +- src/enums/api.ts | 6 +- src/enums/business.ts | 5 +- src/enums/codegen.ts | 2 - src/enums/common.ts | 31 +- src/enums/index.ts | 1 - src/enums/settings.ts | 1 - src/enums/sse.ts | 20 - src/lang/package/en.json | 3 +- src/lang/package/zh-cn.json | 3 +- src/layouts/BaseLayout.vue | 3 + src/layouts/components/LayoutLogo.vue | 18 +- src/layouts/components/LayoutMain.vue | 2 +- src/layouts/components/LayoutMenuIcon.vue | 3 + src/layouts/components/LayoutNavbar.vue | 3 + src/layouts/components/LayoutSidebarItem.vue | 8 +- src/layouts/components/LayoutTagsView.vue | 4 +- src/layouts/components/LayoutToolbar.vue | 49 +- src/layouts/composables/useLayoutDevice.ts | 7 +- src/layouts/composables/useMixMenu.ts | 1 - src/main.ts | 7 - src/plugins/form-create.ts | 103 +- src/plugins/vxe-table.ts | 3 + src/router/guards.ts | 15 +- src/router/views.ts | 6 +- src/settings.ts | 18 +- src/stores/app.ts | 2 - src/stores/dict.ts | 154 +- src/stores/permission.ts | 5 +- src/stores/tags-view.ts | 9 +- src/stores/tenant.ts | 53 +- src/stores/user.ts | 147 +- src/utils/auth.ts | 64 +- src/utils/download.ts | 49 +- src/utils/format.ts | 16 +- src/utils/http-response.ts | 52 + src/utils/permission.ts | 16 + src/utils/realtime/client.ts | 304 ++++ src/utils/realtime/protocol.ts | 286 ++++ src/utils/request.ts | 85 +- src/utils/sse.ts | 380 ----- src/utils/storage.ts | 102 +- src/utils/token-refresh.ts | 67 +- src/utils/validate.ts | 23 +- .../codegen/components/FieldConfigStep.vue | 5 +- .../codegen/components/GeneratorDrawer.vue | 11 +- src/views/codegen/components/PreviewStep.vue | 41 +- src/views/codegen/components/TableList.vue | 4 +- .../codegen/composables/useCodePreview.ts | 18 +- .../codegen/composables/useLocalWrite.ts | 2 - .../composables/useOnlineUserCount.ts | 31 +- src/views/dashboard/index.vue | 12 +- src/views/demo/component/dict-sync.vue | 37 +- src/views/demo/component/icon-select.vue | 4 +- .../component/table-select/config/select.ts | 4 +- .../demo/component/table-select/index.vue | 1 - src/views/demo/component/text-scroll.vue | 3 +- src/views/demo/component/upload.vue | 2 +- src/views/demo/crud/index.vue | 247 ++++ .../{cephalosome.vue => DataScreenHeader.vue} | 17 +- .../data-screen/components/base-chart.vue | 19 +- .../components/electricity-usage-mode.vue | 2 +- .../demo/data-screen/components/map-chart.vue | 4 +- .../components/mileage-energy-consumption.vue | 12 - .../components/real-time-alarm.vue | 5 +- .../components/real-time-status.vue | 7 +- .../components/vehicle-charging-chart.vue | 6 +- .../components/vehicle-distribution.vue | 11 +- src/views/demo/data-screen/index.vue | 15 +- .../utils/{seriesData.ts => series-data.ts} | 3 + src/views/demo/table/crud-single.vue | 604 -------- src/views/demo/table/crud/config/mock/add.ts | 55 - .../demo/table/crud/config/mock/content.ts | 171 --- src/views/demo/table/crud/config/mock/edit.ts | 104 -- .../demo/table/crud/config/mock/search.ts | 152 -- src/views/demo/table/crud/config/user/add.ts | 135 -- .../demo/table/crud/config/user/content.ts | 141 -- src/views/demo/table/crud/config/user/edit.ts | 128 -- .../demo/table/crud/config/user/options.ts | 29 - .../demo/table/crud/config/user/search.ts | 63 - src/views/demo/table/crud/index.vue | 235 --- src/views/demo/table/vxe-table/index.vue | 19 +- .../dynamic-form/components/PublishDialog.vue | 21 +- .../composables/useFormRenderer.ts | 10 +- src/views/dynamic-form/data.vue | 42 +- src/views/dynamic-form/designer.vue | 2 +- src/views/dynamic-form/index.vue | 12 +- src/views/dynamic-form/preview.vue | 6 +- src/views/dynamic-form/render.vue | 4 +- src/views/dynamic-form/share.vue | 2 +- src/views/dynamic-form/utils/form-field.ts | 8 +- src/views/login/components/QrCodeLogin.vue | 4 +- src/views/login/components/Register.vue | 1 - src/views/login/index.vue | 19 +- src/views/login/oidc-callback.vue | 361 ++--- src/views/system/app/index.vue | 4 +- src/views/system/config/index.vue | 132 +- src/views/system/dept/index.vue | 4 +- .../system/dict/components/DictItemPanel.vue | 8 +- src/views/system/dict/index.vue | 27 +- src/views/system/log/index.vue | 6 +- src/views/system/menu/index.vue | 87 +- src/views/system/notice/index.vue | 6 +- src/views/system/role/index.vue | 275 ++-- src/views/system/tenant/index.vue | 15 +- src/views/system/tenant/plan.vue | 9 +- src/views/system/user/index.vue | 75 +- src/views/workflow/components/BpmnViewer.vue | 8 +- src/views/workflow/components/FormDetail.vue | 53 +- .../components/InstanceDetailDrawer.vue | 72 +- .../components/bpmn/flowable-moddle.ts | 5 +- src/views/workflow/designer.vue | 2 +- src/views/workflow/launch.vue | 4 +- src/views/workflow/model.vue | 14 +- .../workflow/task/components/DoneTab.vue | 4 +- .../workflow/task/components/MineTab.vue | 8 +- .../workflow/task/components/TodoTab.vue | 4 +- .../account/components/IdentityBinding.vue | 178 +++ src/views/workspace/account/index.vue | 40 +- src/views/workspace/apps/index.vue | 257 ++-- src/views/workspace/notifications/index.vue | 4 +- tsconfig.json | 3 + types/auto-imports.d.ts | 1002 +++++-------- types/axios.d.ts | 4 + types/components.d.ts | 54 +- types/env.d.ts | 3 +- vite.config.ts | 156 +- 205 files changed, 6705 insertions(+), 7079 deletions(-) delete mode 100644 .eslintrc-auto-import.json delete mode 100644 deploy.example.mjs delete mode 100644 deploy.mjs delete mode 100644 public/map/china.json create mode 100644 src/api/auth/identity.ts create mode 100644 src/api/realtime/index.ts create mode 100644 src/components/Crud/CrudForm.vue delete mode 100644 src/components/Crud/CrudModal.vue create mode 100644 src/components/Crud/CrudPage.vue create mode 100644 src/components/Crud/utils/sort.ts create mode 100644 src/components/Crud/utils/workbook.ts create mode 100644 src/components/TableActions/index.vue create mode 100644 src/components/TableActions/types.ts create mode 100644 src/components/Upload/useUploadQueue.ts create mode 100644 src/composables/useRealtime.ts delete mode 100644 src/enums/sse.ts create mode 100644 src/utils/http-response.ts create mode 100644 src/utils/permission.ts create mode 100644 src/utils/realtime/client.ts create mode 100644 src/utils/realtime/protocol.ts delete mode 100644 src/utils/sse.ts create mode 100644 src/views/demo/crud/index.vue rename src/views/demo/data-screen/components/{cephalosome.vue => DataScreenHeader.vue} (79%) rename src/views/demo/data-screen/utils/{seriesData.ts => series-data.ts} (99%) delete mode 100644 src/views/demo/table/crud-single.vue delete mode 100644 src/views/demo/table/crud/config/mock/add.ts delete mode 100644 src/views/demo/table/crud/config/mock/content.ts delete mode 100644 src/views/demo/table/crud/config/mock/edit.ts delete mode 100644 src/views/demo/table/crud/config/mock/search.ts delete mode 100644 src/views/demo/table/crud/config/user/add.ts delete mode 100644 src/views/demo/table/crud/config/user/content.ts delete mode 100644 src/views/demo/table/crud/config/user/edit.ts delete mode 100644 src/views/demo/table/crud/config/user/options.ts delete mode 100644 src/views/demo/table/crud/config/user/search.ts delete mode 100644 src/views/demo/table/crud/index.vue create mode 100644 src/views/workspace/account/components/IdentityBinding.vue diff --git a/.env.development b/.env.development index c026485a..34c56c6d 100644 --- a/.env.development +++ b/.env.development @@ -1,5 +1,5 @@ # 应用端口 -VITE_APP_PORT=3000 +APP_PORT=3000 # 项目名称 VITE_APP_TITLE=vue3-element-admin @@ -8,9 +8,9 @@ VITE_APP_TITLE=vue3-element-admin VITE_API_BASE=/dev-api # 接口地址(开发代理转发目标,默认走线上) -VITE_PROXY_TARGET=https://api.youlai.tech # 线上 -# VITE_PROXY_TARGET=http://localhost:8000 # 本地 -# VITE_PROXY_TARGET=https://api.youlai.tech/v2 # 多租户线上 +# API_PROXY_TARGET=https://www.youlai.tech # 线上 +API_PROXY_TARGET=http://localhost:8000 # 本地 +# API_PROXY_TARGET=https://api.youlai.tech/v2 # 多租户线上 # Mock 开关 VITE_MOCK_ENABLED=false @@ -19,4 +19,7 @@ VITE_MOCK_ENABLED=false VITE_TENANT_ENABLED=false # AI 开关 -VITE_AI_ENABLED=true +VITE_AI_ENABLED=false + +# OIDC 登录入口,后端需启用相应配置 +VITE_OIDC_ENABLED=false diff --git a/.env.production b/.env.production index 648bf26b..3998e74d 100644 --- a/.env.production +++ b/.env.production @@ -9,3 +9,6 @@ VITE_TENANT_ENABLED=false # AI 辅助功能开关(true:开启 false:关闭,缺省关闭) VITE_AI_ENABLED=true + +# OIDC 登录入口,后端需启用相应配置 +VITE_OIDC_ENABLED=false diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json deleted file mode 100644 index 0e8546f8..00000000 --- a/.eslintrc-auto-import.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "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 - } -} diff --git a/.gitignore b/.gitignore index 494f1617..1b28f8ee 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ stats.html .env.local .env.*.local +# 线上部署专用变量,不入库 +.env.youlai + # Editor directories and files .idea/ .vscode/* @@ -34,4 +37,7 @@ package-lock.json yarn.lock # Local history -.history \ No newline at end of file +.history + +# Local deploy config +deploy.mjs \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index e77b172c..26fc3643 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,2 +1,2 @@ #!/usr/bin/env sh -npm run lint:lint-staged +pnpm exec lint-staged diff --git a/.stylelintrc.cjs b/.stylelintrc.cjs index 0cbaf9b2..f608406b 100644 --- a/.stylelintrc.cjs +++ b/.stylelintrc.cjs @@ -7,6 +7,10 @@ module.exports = { "stylelint-config-recess-order", ], overrides: [ + { + files: ["**/*.css"], + rules: { "declaration-property-value-no-unknown": true }, + }, { files: ["**/*.{vue,html}"], customSyntax: "postcss-html", diff --git a/README.md b/README.md index b6533df8..991c010c 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,12 @@ ## 快速开始 -**环境要求**:Node.js `^20.19.0` 或 `>=22.12.0` · pnpm `>=8.0.0` +**环境要求**:Node.js `^24.0.0` · pnpm `10.34.5` | 环境类型 | 版本要求 | 备注 | | -------- | -------- | ---- | -| **Node.js** | `^20.19.0` 或 `>=22.12.0` | 推荐 LTS 版本(主版本为偶数) | -| **包管理器** | `pnpm >= 8.0.0` | 项目使用 pnpm 作为包管理器 | +| **Node.js** | `^24.0.0` | 24 LTS;ESLint、lint-staged 等工具链要求 Node ≥ 22.22,统一到 24 | +| **包管理器** | `pnpm 10.34.5` | `packageManager` 字段已固定版本,请使用 Corepack 或同版本 pnpm | | **开发工具** | [Visual Studio Code](https://code.visualstudio.com/Download) | 推荐安装 Vue、TypeScript 相关插件 | ```bash @@ -85,7 +85,7 @@ git clone https://gitee.com/youlaiorg/vue3-element-admin.git cd vue3-element-admin # 安装 pnpm(已安装可跳过) -npm install pnpm -g +npm install --global pnpm@10.34.5 # 设置镜像源(可忽略) pnpm config set registry https://registry.npmmirror.com @@ -178,7 +178,7 @@ pnpm run build **本地 Mock**:将 `.env.development` 的 `VITE_MOCK_ENABLED` 设为 `true` 即启用本地 Mock 接口,无需后端即可独立开发。 -**对接后端**:九种后端默认端口均为 `8000`,将 `.env.development` 的 `VITE_PROXY_TARGET` 改为 `http://localhost:8000` 并启动对应后端即可(主推 Java 后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git),其余见各自仓库 README)。 +**对接后端**:九种后端默认端口均为 `8000`,将 `.env.development` 的 `API_PROXY_TARGET` 改为 `http://localhost:8000` 并启动对应后端即可(主推 Java 后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git),其余见各自仓库 README)。 ## 提交规范 diff --git a/deploy.example.mjs b/deploy.example.mjs deleted file mode 100644 index 9e898789..00000000 --- a/deploy.example.mjs +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable no-console */ -/** - * 部署模板:构建并上传到服务器 - * - * 用法:复制本文件为 deploy.mjs(已加入忽略,不入库),再创建 deploy.config.mjs - * 填写服务器信息,然后执行 pnpm run deploy - * - * 服务器信息取自 deploy.config.mjs,缺该文件时回落到环境变量 - * DEPLOY_HOST / DEPLOY_USER / DEPLOY_PORT / DEPLOY_TARGET - * - * 前置:SSH 免密登录(只需一次) - * 1. 没有 %USERPROFILE%\.ssh\id_rsa 就先 ssh-keygen -t rsa 回车到底 - * - * 2. 拷贝公钥到服务器(会提示输一次服务器密码): - * Windows: type %USERPROFILE%\.ssh\id_rsa.pub | ssh root@server "cat >> ~/.ssh/authorized_keys" - * Mac: ssh-copy-id root@server - * - * 3. ssh root@server 不弹密码直接连上即成功 - */ -import { execSync } from "node:child_process"; -import { existsSync } from "node:fs"; -import { resolve } from "node:path"; -import { pathToFileURL } from "node:url"; - -/** 读取部署配置,配置文件缺失时回落到环境变量 */ -async function readConfig() { - const configPath = resolve("deploy.config.mjs"); - if (existsSync(configPath)) { - return (await import(pathToFileURL(configPath).href)).default; - } - - return { - host: process.env.DEPLOY_HOST, - user: process.env.DEPLOY_USER, - port: Number(process.env.DEPLOY_PORT ?? 22), - target: process.env.DEPLOY_TARGET, - }; -} - -const config = await readConfig(); - -if (!config?.host || !config?.user || !config?.target) { - console.error("缺少部署配置:在项目根目录创建 deploy.config.mjs 填写 host / user / port / target"); - process.exit(1); -} - -const { host, user, port = 22, target } = config; - -console.log("构建中..."); -execSync("pnpm build-only", { stdio: "inherit" }); - -console.log(`部署 → ${user}@${host}:${target}`); -execSync(`scp -o StrictHostKeyChecking=no -P ${port} -r "dist/." "${user}@${host}:${target}/"`, { - stdio: "inherit", - shell: true, -}); - -console.log("部署完成"); diff --git a/deploy.mjs b/deploy.mjs deleted file mode 100644 index 255fc53e..00000000 --- a/deploy.mjs +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable no-console */ -/** - * 部署管理后台到服务器 | 用法:pnpm run deploy - * - * 前置:SSH 免密登录(只需一次) - * 1. 如果 %USERPROFILE%\.ssh\id_rsa 已存在跳过,否则 ssh-keygen -t rsa 回车到底 - * - * 2. 拷贝公钥到服务器(会提示输一次服务器密码): - * Windows: type %USERPROFILE%\.ssh\id_rsa.pub | ssh root@server "cat >> ~/.ssh/authorized_keys" - * Mac: ssh-copy-id root@server - * - * 3. ssh root@server 输完不弹密码直接连上即成功 - */ -import { execSync } from "node:child_process"; - -const HOST = "www.youlai.tech"; // 服务器 IP,改为你的 -const USER = "root"; // SSH 用户名,改为你的 -const PORT = 1010; // SSH 端口,改为你的 -const TARGET = "/mnt/nginx/html/vue"; // 部署目录,改为你的 -// ========================================================= - -console.log("构建中..."); -execSync("pnpm build-only", { stdio: "inherit" }); - -console.log(`部署 → ${USER}@${HOST}:${TARGET}`); -execSync(`scp -o StrictHostKeyChecking=no -P ${PORT} -r "dist/." "${USER}@${HOST}:${TARGET}/"`, { - stdio: "inherit", - shell: true, -}); - -console.log("部署完成"); diff --git a/eslint.config.ts b/eslint.config.ts index 96fb27d9..e71b979c 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -4,15 +4,6 @@ import * as typescriptEslint from "typescript-eslint"; import pluginVue from "eslint-plugin-vue"; import vueParser from "vue-eslint-parser"; import configPrettier from "eslint-config-prettier"; -import fs from "node:fs"; - -let autoImportGlobals: Record = {}; -try { - autoImportGlobals = - JSON.parse(fs.readFileSync("./.eslintrc-auto-import.json", "utf-8")).globals || {}; -} catch (error) { - console.warn("Could not load auto-import globals", error); -} export default [ { @@ -36,11 +27,7 @@ export default [ ecmaVersion: "latest", sourceType: "module", globals: { - ...globals.browser, - ...globals.node, ...globals.es2022, - ...autoImportGlobals, - __APP_INFO__: "readonly", }, }, plugins: { @@ -66,10 +53,23 @@ export default [ "no-unused-vars": "off", "no-undef": "off", "no-redeclare": "off", - "@typescript-eslint/ban-ts-comment": "off", }, }, + { + files: ["src/**/*.{js,ts,vue}"], + languageOptions: { globals: { ...globals.browser, __APP_INFO__: "readonly" } }, + }, + { + files: ["*.{js,mjs,cjs,ts}", "mock/**/*.ts"], + languageOptions: { globals: globals.node }, + rules: { "no-console": "off" }, + }, + { + files: ["**/*.{js,mjs,cjs}"], + rules: { "no-undef": "error" }, + }, + { files: ["**/*.vue"], languageOptions: { @@ -79,7 +79,7 @@ export default [ sourceType: "module", parser: typescriptEslint.parser, extraFileExtensions: [".vue"], - tsconfigRootDir: __dirname, + tsconfigRootDir: import.meta.dirname, }, }, rules: { @@ -88,10 +88,12 @@ export default [ "vue/require-default-prop": "off", "vue/require-explicit-emits": "error", "vue/no-unused-vars": "error", - "vue/no-mutating-props": "off", + "vue/no-mutating-props": "warn", "vue/valid-v-for": "warn", "vue/no-template-shadow": "warn", - "vue/return-in-computed-property": "warn", + // 组件由 unplugin-vue-components 自动注册,ESLint 侧拿不到已注册组件清单 + "vue/no-undef-components": "off", + "vue/return-in-computed-property": "error", "vue/block-order": [ "error", { @@ -111,7 +113,6 @@ export default [ }, ], "vue/component-name-in-template-casing": ["error", "PascalCase"], - "@typescript-eslint/no-explicit-any": "warn", }, }, @@ -123,15 +124,30 @@ export default [ ecmaVersion: "latest", sourceType: "module", project: "./tsconfig.eslint.json", - tsconfigRootDir: __dirname, + tsconfigRootDir: import.meta.dirname, }, }, rules: { - "@typescript-eslint/no-explicit-any": "warn", "@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-expressions": "warn", + }, + }, + + { + // Vue 与 TS 共用的脚本规则:同一段代码放在 .ts 或 diff --git a/src/components/CommandPalette/useCommandPalette.ts b/src/components/CommandPalette/useCommandPalette.ts index 58f8c055..c3f44bde 100644 --- a/src/components/CommandPalette/useCommandPalette.ts +++ b/src/components/CommandPalette/useCommandPalette.ts @@ -4,7 +4,9 @@ import router from "@/router"; import { usePermissionStore } from "@/stores"; import { isExternal } from "@/utils"; -/** 命令面板中的可搜索菜单项 */ +/** + * 命令面板中的可搜索菜单项 + */ interface SearchItem { title: string; path: string; @@ -24,13 +26,11 @@ const EXCLUDED_PATHS = ["/redirect", "/login", "/401", "/404"]; export function useCommandPalette() { const permissionStore = usePermissionStore(); - // 面板状态 const visible = ref(false); const keyword = ref(""); const activeIndex = ref(-1); const inputRef = ref(); - // 菜单数据 const menuItems = ref([]); const results = ref([]); const history = ref([]); diff --git a/src/components/Crud/CrudForm.vue b/src/components/Crud/CrudForm.vue new file mode 100644 index 00000000..0f21af72 --- /dev/null +++ b/src/components/Crud/CrudForm.vue @@ -0,0 +1,473 @@ + + + diff --git a/src/components/Crud/CrudModal.vue b/src/components/Crud/CrudModal.vue deleted file mode 100644 index b8388100..00000000 --- a/src/components/Crud/CrudModal.vue +++ /dev/null @@ -1,290 +0,0 @@ - - - - - diff --git a/src/components/Crud/CrudPage.vue b/src/components/Crud/CrudPage.vue new file mode 100644 index 00000000..db0da445 --- /dev/null +++ b/src/components/Crud/CrudPage.vue @@ -0,0 +1,152 @@ + + + diff --git a/src/components/Crud/CrudSearch.vue b/src/components/Crud/CrudSearch.vue index e80609b5..a7aebd40 100644 --- a/src/components/Crud/CrudSearch.vue +++ b/src/components/Crud/CrudSearch.vue @@ -1,64 +1,70 @@