做网站被坑能找司法吗品牌建设对企业的作用
2026/4/18 14:26:06 网站建设 项目流程
做网站被坑能找司法吗,品牌建设对企业的作用,.net网站开发全过程,辽宁省建设工程信息网官网新网站入口官方背景与问题业务场景在企业级 SaaS 应用中#xff0c;我们需要在用户登录后进行多种认证检查#xff1a;用户类型认证#xff1a;个人用户需激活、企业用户需完成认证密码过期检查#xff1a;强制用户定期更新密码权限验证#xff1a;不同用户类型访问不同功能模块最初的实…背景与问题业务场景在企业级 SaaS 应用中我们需要在用户登录后进行多种认证检查用户类型认证个人用户需激活、企业用户需完成认证密码过期检查强制用户定期更新密码权限验证不同用户类型访问不同功能模块最初的实现采用了认证服务中心VerificationCenter的设计模式通过规则引擎统一管理所有认证逻辑。遇到的问题// ❌ 错误示例在路由守卫中直接调用 Composition API router.afterEach((to, from) { const { checkAndShowAuthDialog } useUserTypeAuth() checkAndShowAuthDialog() })报错信息SyntaxError: Must be called at the top of a setup function at useI18n (vue-i18n.js:314:17) at useConfirm (useConfirm.ts:165:17) at useUserTypeAuth (useUserTypeAuth.ts:72:23)问题根源Vue 3 的 Composition API如useI18n、useRouter、useRoute必须在 Vue 组件的setup函数顶层调用而路由守卫运行在 Vue 组件上下文之外直接调用会触发运行时错误。核心问题分析1. Composition API 的上下文限制// Vue 3 内部实现简化版 let currentInstance: ComponentInternalInstance | null null export function getCurrentInstance(): ComponentInternalInstance | null { return currentInstance } export function useRouter(): Router { const instance getCurrentInstance() if (!instance) { throw new Error(Must be called at the top of a setup function) } return instance.appContext.config.globalProperties.$router }关键点Composition API 依赖currentInstance获取 Vue 实例上下文路由守卫执行时currentInstance为null直接调用会抛出异常2. 架构过度设计的反思原有的 VerificationCenter 架构// 规则引擎模式 interface VerificationRule { id: string when: (login | appReady | routeChange)[] shouldRun: (ctx: VerificationContext) Promiseboolean run: (ctx: VerificationContext) Promisevoid } class VerificationCenter { private rules: Mapstring, VerificationRule new Map() register(rule: VerificationRule) { this.rules.set(rule.id, rule) } async run(trigger: string) { for (const rule of this.rules.values()) { if (rule.when.includes(trigger)) { if (await rule.shouldRun(ctx)) { await rule.run(ctx) } } } } }问题分析✅优点高度抽象、易扩展、规则解耦❌缺点只有 3 个规则不需要如此复杂的架构增加认知负担新人难以理解调用链路长调试困难性能开销函数调用、对象创建违反 YAGNI 原则You Arent Gonna Need It解决方案设计设计原则简单性优于灵活性当前需求简单不需要过度设计SOLID 原则单一职责、开闭原则上下文隔离路由守卫专用函数不依赖 Vue 上下文架构对比方案一直接调用✅ 采用// 路由守卫中直接调用 router.afterEach((to) { checkAuthInRouterGuard() checkPasswordExpiredInRouterGuard(router) })优点代码简洁直观调用链路清晰易于调试和维护性能最优方案二保留规则引擎❌ 放弃缺点过度设计增加复杂度不符合当前业务规模维护成本高技术方案核心思路为路由守卫创建独立的、不依赖 Composition API 的函数。// 设计模式Adapter Pattern适配器模式 // 将依赖 Composition API 的逻辑适配为独立函数 // 1. 组件内使用依赖 Composition API export function useUserTypeAuth() { const router useRouter() // ✅ 在 setup 中调用 const { t } useI18n() // ✅ 在 setup 中调用 // ... } // 2. 路由守卫使用不依赖 Composition API export function checkAuthInRouterGuard(): void { // ✅ 直接从 store 获取数据不依赖 Vue 上下文 const userType getUserTypeFromStore() const user getUserFromStore() // ✅ 使用安全的 i18n 包装器 const t getSafeI18n() // ✅ 动态导入 router 实例 const handleAction async () { const { default: router } await import(~/router) await router.push(/profile) } }代码实现1. 安全的 i18n 包装器// src/composables/ui/useConfirm.ts /** * 安全获取 i18n 翻译函数 * description 尝试调用 useI18n()失败则返回 fallback 翻译 */ function getSafeI18n(): (key: string) string { try { const { t } useI18n() return t } catch { // 路由守卫等非 Vue 组件上下文中使用 fallback return (key: string) { const fallbacks: Recordstring, string { common.confirmTitle: 提示, common.ok: 确定, common.cancel: 取消, common.closeWindow: 关闭窗口, ui.confirm.cancelTask: 取消任务, ui.confirm.continueOperation: 继续操作, } return fallbacks[key] || key } } } export function useConfirm() { const t getSafeI18n() // ✅ 安全调用 const confirm (message: string, options?: ConfirmOptions) { return ElMessageBox.confirm(message, { title: options?.title || t(common.confirmTitle), confirmButtonText: options?.okBtnText || t(common.ok), cancelButtonText: t(common.cancel), type: options?.type || info, // ... }) } return { confirm, alert } }设计亮点优雅降级有 Vue 上下文时使用useI18n()否则使用 fallback零侵入不影响现有组件的使用方式类型安全保持完整的 TypeScript 类型推导2. 用户认证检查路由守卫专用// src/composables/auth/useUserTypeAuth.ts /** * 路由守卫中检查用户认证状态 * description 不依赖 Composition API可在路由守卫中安全调用 */ export function checkAuthInRouterGuard(): void { // 1. 从 store 获取数据不依赖 Vue 上下文 const userType getUserTypeFromStore() if (!needsAuthPrompt(userType)) { return } const user getUserFromStore() if (!user) { return } // 2. 使用安全的 confirm内部使用 getSafeI18n const { confirm } useConfirm() const t getSafeI18n() // 3. 获取提示配置 const config getAuthPromptMessage(userType, user, t) if (!config.content) { return } // 4. 显示确认对话框 void (async () { try { if (config.showConfirmBtn) { await confirm(config.content, { title: config.title, type: warning, buttons: [ { text: config.confirmText, type: primary, customClass: customer-button-default customer-primary-button customer-button, onClick: async () { // ✅ 动态导入 router避免循环依赖 const { default: router } await import(~/router) await executeAuthActionForService(userType, user, router) }, }, { text: config.cancelText, type: default, customClass: trans-bg-btn, }, ], }) } else { // 企业认证审核中仅显示提示使用文字按钮 await confirm(config.content, { title: config.title, type: warning, buttons: [ { text: config.confirmText, type: primary, link: true, // ✅ 文字按钮样式 }, ], }) } } catch { // 用户取消操作 } })() } // 辅助函数 /** * 从 Store 获取用户类型 */ function getUserTypeFromStore(): number { const userStore useUserStoreWithOut() return userStore.userInfo?.userType ?? 0 } /** * 从 Store 获取用户信息 */ function getUserFromStore(): any { const userStore useUserStoreWithOut() return userStore.userInfo } /** * 判断是否需要显示认证提示 */ function needsAuthPrompt(userType: number): boolean { const NEEDS_AUTH_PROMPT_USER_TYPES [1, 2, 3, 4] return NEEDS_AUTH_PROMPT_USER_TYPES.includes(userType) } /** * 获取认证提示消息配置 */ function getAuthPromptMessage( userType: number, user: any, t: (key: string) string, ): AuthPromptConfig { // 个人用户未激活 if (userType 1 user.userStatus 0) { return { title: t(register.personalActivation), content: t(register.personalActivationTip), confirmText: t(register.goActivate), cancelText: t(common.cancel), showConfirmBtn: true, } } // 企业用户认证审核中 if ([2, 3, 4].includes(userType) user.verificationStatus 1) { return { title: t(register.enterpriseCertification), content: t(register.enterpriseCertificationPendingTip), confirmText: t(common.ok), cancelText: , showConfirmBtn: false, // ✅ 仅显示提示不需要确认按钮 } } // 企业用户认证被拒绝 if ([2, 3, 4].includes(userType) user.verificationStatus 3) { return { title: t(register.enterpriseCertification), content: t(register.enterpriseCertificationRejectedTip), confirmText: t(register.goResubmit), cancelText: t(common.cancel), showConfirmBtn: true, } } return { title: , content: , confirmText: , cancelText: , showConfirmBtn: false } }设计亮点职责分离数据获取、逻辑判断、UI 展示分离可测试性纯函数设计易于单元测试动态导入避免循环依赖按需加载错误处理优雅处理用户取消操作3. 密码过期检查路由守卫专用// src/composables/auth/usePasswordExpired.ts /** * 路由守卫中检查密码过期并显示重置弹窗 * description 不依赖 Composition API可在路由守卫中安全调用 * param routerInstance - 路由实例 */ export function checkPasswordExpiredInRouterGuard(routerInstance: Router): void { const route routerInstance.currentRoute.value // 1. 跳过 blank 布局页面登录、注册等 const isBlank route?.meta?.layout blank if (isBlank) return // 2. 只在内部页面检查 const category route?.meta?.category if (category ! internal) return // 3. 检查 sessionStorage 标记 const forceTokenReset sessionStorage.getItem(vc_force_reset_pwd) 1 const forceSelfReset sessionStorage.getItem(vc_force_reset_pwd_self) 1 if (forceTokenReset || forceSelfReset) { showResetPasswordDialogStandalone(routerInstance) } } /** * 显示密码重置弹窗独立函数不依赖 Composition API * param routerInstance - 路由实例 */ function showResetPasswordDialogStandalone(routerInstance: Router): void { const container document.createElement(div) document.body.appendChild(container) // ✅ 使用 createApp 动态挂载组件 const app createApp({ render() { const useTokenMode sessionStorage.getItem(vc_force_reset_pwd) 1 return h(ResetPassWord, { size: large, force: true, useToken: useTokenMode, onSuccess: () { // 清理标记 try { sessionStorage.removeItem(vc_force_reset_pwd) sessionStorage.removeItem(vc_force_reset_pwd_self) sessionStorage.removeItem(vc_pwd_reset_token) sessionStorage.removeItem(vc_origin_password) } catch {} // 清理登录状态 common.setWindowKeyValue(pwd_reset_token, undefined) common.removeLoginAuthToken() window.sessionStorage.clear() // 跳转到登录页 routerInstance.replace(RouteConfig.Login.path) // 卸载组件 app.unmount() if (container.parentNode) container.parentNode.removeChild(container) }, onClose: () { app.unmount() if (container.parentNode) container.parentNode.removeChild(container) }, }) }, }) // ✅ 注入全局 i18n从 window 获取避免依赖 useI18n try { if ((window as any).i18n) { app.use((window as any).i18n) } } catch {} app.mount(container) // ✅ 监听路由变化自动关闭弹窗 try { const unwatch routerInstance.afterEach((to: any) { const isBlank to?.meta?.layout blank if (isBlank) { try { sessionStorage.removeItem(vc_force_reset_pwd) sessionStorage.removeItem(vc_force_reset_pwd_self) } catch {} app.unmount() if (container.parentNode) container.parentNode.removeChild(container) unwatch() } }) } catch {} }设计亮点动态挂载使用createApph()渲染函数动态创建组件实例生命周期管理自动清理 DOM 和事件监听器全局 i18n 注入从window获取全局 i18n 实例避免依赖useI18n()路由监听自动响应路由变化关闭弹窗4. 路由守卫集成// src/router/index.ts import { createRouter, createWebHashHistory } from vue-router import { checkPasswordExpiredInRouterGuard } from ~/composables/auth/usePasswordExpired import { checkAuthInRouterGuard } from ~/composables/auth/useUserTypeAuth const router createRouter({ history: createWebHashHistory(), routes, }) // 路由后置守卫 router.afterEach((to, from) { try { // Keep-Alive 缓存管理 const keepAliveStore useKeepAliveStoreWithOut() if (to.meta?.keepAlive to.name) { keepAliveStore.addCachedView(to.name as string) } if (from.meta?.noCache from.name) { keepAliveStore.deleteCachedView(from.name as string) } // 重置滚动位置 window.scrollTo({ top: 0, left: 0, behavior: auto }) // 停止进度条 setTimeout(() { nprogressManager.done() CmcLoadingService.closeAll() }, 300) // 认证检查 // 跳过 blank 布局页面登录、注册等 if (to.meta?.layout ! blank) { // ✅ 用户认证检查不依赖 Composition API checkAuthInRouterGuard() // ✅ 密码过期检查不依赖 Composition API checkPasswordExpiredInRouterGuard(router) } } catch (error) { console.error(路由后置守卫执行失败:, error) } }) export default router设计亮点清晰的职责划分缓存管理、滚动控制、认证检查分离错误边界统一的 try-catch 错误处理条件执行根据路由元信息决定是否执行检查架构优化思考1. YAGNI 原则的实践You Arent Gonna Need It你不会需要它// ❌ 过度设计为未来可能的需求预留扩展 class VerificationCenter { private rules: Mapstring, VerificationRule new Map() private middleware: Middleware[] [] private eventBus: EventEmitter new EventEmitter() async run(trigger: string, ctx: VerificationContext) { // 复杂的规则引擎逻辑 // 中间件机制 // 事件发布订阅 } } // ✅ 简单设计满足当前需求即可 export function checkAuthInRouterGuard(): void { // 直接实现业务逻辑 }反思当前只有 3 个认证规则不需要规则引擎未来如果真的需要扩展如增加到 10 规则再重构也不迟过早优化是万恶之源2. 简单性 vs 灵活性维度规则引擎复杂直接调用简单代码行数~500 行~200 行认知负担高需理解规则引擎低直接阅读业务逻辑调试难度困难调用链长简单调用链短扩展性高添加规则中直接添加函数性能中函数调用开销高直接调用适用场景10 规则3-5 规则结论在当前业务规模下简单性优于灵活性。3. 上下文隔离的设计模式// 设计模式Adapter Pattern适配器模式 // 1. 组件内使用依赖 Vue 上下文 export function useUserTypeAuth() { const router useRouter() // 依赖 Vue 上下文 const { t } useI18n() // 依赖 Vue 上下文 return { checkAndShowAuthDialog: () { // 组件内逻辑 } } } // 2. 路由守卫使用不依赖 Vue 上下文 export function checkAuthInRouterGuard(): void { // 适配器将依赖 Vue 上下文的逻辑转换为独立函数 const userType getUserTypeFromStore() // 直接访问 store const t getSafeI18n() // 安全的 i18n 包装器 // 业务逻辑 }设计原则单一职责每个函数只做一件事依赖倒置依赖抽象store、全局对象而非具体实现Vue 实例开闭原则对扩展开放可添加新的检查函数对修改封闭不影响现有逻辑4. 错误处理策略// ✅ 优雅降级 function getSafeI18n(): (key: string) string { try { const { t } useI18n() return t } catch { // 降级到 fallback 翻译 return (key: string) fallbacks[key] || key } } // ✅ 静默失败用户取消操作 void (async () { try { await confirm(config.content, options) } catch { // 用户取消不需要处理 } })() // ✅ 全局错误边界 router.afterEach((to, from) { try { checkAuthInRouterGuard() checkPasswordExpiredInRouterGuard(router) } catch (error) { console.error(路由后置守卫执行失败:, error) // 不阻断路由导航 } })原则优雅降级功能不可用时提供 fallback静默失败用户主动取消的操作不需要错误提示全局边界关键路径添加 try-catch防止整个应用崩溃最佳实践总结✅ Dos推荐做法为路由守卫创建独立函数// ✅ 独立函数不依赖 Composition API export function checkAuthInRouterGuard(): void { const userType getUserTypeFromStore() // ... }使用安全的 i18n 包装器// ✅ 优雅降级 function getSafeI18n(): (key: string) string { try { const { t } useI18n() return t } catch { return (key: string) fallbacks[key] || key } }动态导入避免循环依赖// ✅ 按需加载 const handleAction async () { const { default: router } await import(~/router) await router.push(/profile) }从 Store 获取数据不依赖 Vue 实例// ✅ 直接访问 store const userStore useUserStoreWithOut() const userType userStore.userInfo?.userType使用 createApp 动态挂载组件// ✅ 独立的 Vue 应用实例 const app createApp({ render() { return h(ResetPassWord, { /* props */ }) } }) app.mount(container)❌ Donts避免做法不要在路由守卫中直接调用 Composition API// ❌ 会报错 router.afterEach(() { const router useRouter() // 错误 const { t } useI18n() // 错误 })不要过度设计// ❌ 3 个规则不需要规则引擎 class VerificationCenter { private rules: Mapstring, VerificationRule new Map() // 复杂的规则引擎逻辑 }不要忽略错误处理// ❌ 没有错误边界 router.afterEach(() { checkAuth() // 如果出错会导致路由导航失败 }) // ✅ 添加错误边界 router.afterEach(() { try { checkAuth() } catch (error) { console.error(error) } })不要忘记清理副作用// ❌ 没有清理 DOM 和事件监听器 const app createApp(Component) app.mount(container) // ✅ 清理副作用 const unwatch router.afterEach(() { app.unmount() container.remove() unwatch() }) 性能优化建议避免不必要的检查// ✅ 提前返回 if (to.meta?.layout blank) return if (to.meta?.category ! internal) return使用 sessionStorage 缓存标记// ✅ 避免重复检查 const forceReset sessionStorage.getItem(vc_force_reset_pwd) 1 if (!forceReset) return动态导入按需加载// ✅ 只在需要时加载 const { default: router } await import(~/router) 可测试性建议纯函数设计// ✅ 易于测试 function needsAuthPrompt(userType: number): boolean { return [1, 2, 3, 4].includes(userType) } // 测试 expect(needsAuthPrompt(1)).toBe(true) expect(needsAuthPrompt(5)).toBe(false)依赖注入// ✅ 可注入 mock router export function checkPasswordExpiredInRouterGuard( routerInstance: Router ): void { // 使用注入的 router 实例 }职责分离// ✅ 数据获取、逻辑判断、UI 展示分离 const userType getUserTypeFromStore() // 数据层 const needsAuth needsAuthPrompt(userType) // 逻辑层 if (needsAuth) showAuthDialog() // UI 层总结核心要点理解 Composition API 的上下文限制必须在 Vue 组件的setup函数顶层调用路由守卫运行在 Vue 上下文之外为路由守卫创建独立函数不依赖useRouter、useI18n等 Composition API从 Store 或全局对象获取数据使用安全的 i18n 包装器遵循 YAGNI 原则不要过度设计简单性优于灵活性满足当前需求即可优雅的错误处理优雅降级fallback静默失败用户取消全局错误边界适用场景✅ 路由守卫中需要使用 i18n、router 等 Composition API✅ 需要在非 Vue 组件上下文中执行 Vue 相关逻辑✅ 需要动态挂载组件如弹窗、通知✅ 需要简化过度设计的架构

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询