smart-admin-web 10.20.1 → 10.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/api/index.d.ts +19 -1
  2. package/dist/api/index.js +85 -66
  3. package/dist/api/index.js.map +1 -1
  4. package/dist/api/schema.d.ts +448 -1
  5. package/dist/assets/icons/ph-subset.json.js +1 -1
  6. package/dist/components/FormContainer/index.vue.js +87 -75
  7. package/dist/components/FormContainer/index.vue.js.map +1 -1
  8. package/dist/components/UserPicker/UserAvatar.vue.d.ts +8 -0
  9. package/dist/components/UserPicker/UserAvatar.vue.js +8 -0
  10. package/dist/components/UserPicker/UserAvatar.vue.js.map +1 -0
  11. package/dist/components/UserPicker/UserAvatar2.vue.js +38 -0
  12. package/dist/components/UserPicker/UserAvatar2.vue.js.map +1 -0
  13. package/dist/components/UserPicker/index.vue.js +1 -1
  14. package/dist/components/UserPicker/index2.vue.js +328 -186
  15. package/dist/components/UserPicker/index2.vue.js.map +1 -1
  16. package/dist/composables/authMenuRoute.d.ts +10 -0
  17. package/dist/composables/authMenuRoute.js +39 -23
  18. package/dist/composables/authMenuRoute.js.map +1 -1
  19. package/dist/composables/useModule.d.ts +3 -1
  20. package/dist/composables/useModule.js +44 -32
  21. package/dist/composables/useModule.js.map +1 -1
  22. package/dist/index.js +246 -242
  23. package/dist/layouts/components/SideNav.vue.js +2 -2
  24. package/dist/layouts/components/SideNav2.vue.js.map +1 -1
  25. package/dist/lib/runtime.js +1 -1
  26. package/dist/lib/watermark.d.ts +1 -1
  27. package/dist/locales/en-US.d.ts +68 -3
  28. package/dist/locales/en-US.js +74 -8
  29. package/dist/locales/en-US.js.map +1 -1
  30. package/dist/locales/kernelMenuTitles.js +1 -0
  31. package/dist/locales/kernelMenuTitles.js.map +1 -1
  32. package/dist/locales/zh-CN.d.ts +68 -3
  33. package/dist/locales/zh-CN.js +74 -8
  34. package/dist/locales/zh-CN.js.map +1 -1
  35. package/dist/router/viewRegistry.js +6 -6
  36. package/dist/router/viewRegistry.js.map +1 -1
  37. package/dist/style.css +1 -1
  38. package/dist/theme/naive-theme.js +80 -77
  39. package/dist/theme/naive-theme.js.map +1 -1
  40. package/dist/types/api.d.ts +91 -0
  41. package/dist/types/api.js +17 -15
  42. package/dist/types/api.js.map +1 -1
  43. package/dist/utils/shake.d.ts +1 -0
  44. package/dist/utils/shake.js +30 -0
  45. package/dist/utils/shake.js.map +1 -0
  46. package/dist/utils/tree.d.ts +15 -0
  47. package/dist/utils/tree.js +43 -29
  48. package/dist/utils/tree.js.map +1 -1
  49. package/dist/utils/treeCard.d.ts +6 -0
  50. package/dist/utils/treeCard.js +9 -0
  51. package/dist/utils/treeCard.js.map +1 -0
  52. package/dist/views/error/404.vue.js +2 -2
  53. package/dist/views/error/4042.vue.js +81 -24
  54. package/dist/views/error/4042.vue.js.map +1 -1
  55. package/dist/views/login/LoginForm.vue.js +2 -2
  56. package/dist/views/login/LoginForm2.vue.js +135 -135
  57. package/dist/views/login/LoginForm2.vue.js.map +1 -1
  58. package/dist/views/login/components/LoginDesk.vue.js +2 -2
  59. package/dist/views/login/components/LoginDesk2.vue.js +59 -47
  60. package/dist/views/login/components/LoginDesk2.vue.js.map +1 -1
  61. package/dist/views/login/components/LoginHeroPanel.vue.js +2 -2
  62. package/dist/views/login/components/LoginHeroPanel2.vue.js.map +1 -1
  63. package/dist/views/personal/notice2.vue.js +1 -1
  64. package/dist/views/personal/notice2.vue.js.map +1 -1
  65. package/dist/views/personal/sessions.vue.js +2 -2
  66. package/dist/views/personal/sessions2.vue.js +41 -40
  67. package/dist/views/personal/sessions2.vue.js.map +1 -1
  68. package/dist/views/system/ai-usage/index.vue.js +3 -3
  69. package/dist/views/system/ai-usage/index2.vue.js +13 -12
  70. package/dist/views/system/ai-usage/index2.vue.js.map +1 -1
  71. package/dist/views/system/config/components/AdvancedSection.vue.js +3 -3
  72. package/dist/views/system/config/components/AdvancedSection2.vue.js +2 -1
  73. package/dist/views/system/config/components/AdvancedSection2.vue.js.map +1 -1
  74. package/dist/views/system/dict/index.vue.js +2 -2
  75. package/dist/views/system/dict/index2.vue.js +12 -1
  76. package/dist/views/system/dict/index2.vue.js.map +1 -1
  77. package/dist/views/system/file/index2.vue.js +1 -0
  78. package/dist/views/system/file/index2.vue.js.map +1 -1
  79. package/dist/views/system/job/index.vue.js +3 -3
  80. package/dist/views/system/job/index2.vue.js +182 -181
  81. package/dist/views/system/job/index2.vue.js.map +1 -1
  82. package/dist/views/system/job-log/components/AttemptTable2.vue.js +8 -3
  83. package/dist/views/system/job-log/components/AttemptTable2.vue.js.map +1 -1
  84. package/dist/views/system/job-log/index.vue.js +1 -1
  85. package/dist/views/system/job-log/index2.vue.js +5 -5
  86. package/dist/views/system/job-log/index2.vue.js.map +1 -1
  87. package/dist/views/system/job-monitor/index.vue.js +2 -2
  88. package/dist/views/system/job-monitor/index2.vue.js +46 -46
  89. package/dist/views/system/job-monitor/index2.vue.js.map +1 -1
  90. package/dist/views/system/log/exception/index.vue.js +2 -2
  91. package/dist/views/system/log/exception/index2.vue.js +8 -5
  92. package/dist/views/system/log/exception/index2.vue.js.map +1 -1
  93. package/dist/views/system/log/login/index2.vue.js +21 -12
  94. package/dist/views/system/log/login/index2.vue.js.map +1 -1
  95. package/dist/views/system/log/op/index.vue.js +2 -2
  96. package/dist/views/system/log/op/index2.vue.js +50 -40
  97. package/dist/views/system/log/op/index2.vue.js.map +1 -1
  98. package/dist/views/system/menu/components/ButtonManager.vue.js +2 -2
  99. package/dist/views/system/menu/components/ButtonManager2.vue.js +45 -36
  100. package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
  101. package/dist/views/system/menu/index.vue.js +1 -1
  102. package/dist/views/system/menu/index2.vue.js +162 -151
  103. package/dist/views/system/menu/index2.vue.js.map +1 -1
  104. package/dist/views/system/module/index.vue.js +1 -1
  105. package/dist/views/system/module/index2.vue.js +171 -117
  106. package/dist/views/system/module/index2.vue.js.map +1 -1
  107. package/dist/views/system/notice/index.vue.js +3 -3
  108. package/dist/views/system/notice/index2.vue.js +1 -0
  109. package/dist/views/system/notice/index2.vue.js.map +1 -1
  110. package/dist/views/system/org/index.vue.js +2 -2
  111. package/dist/views/system/org/index2.vue.js +204 -195
  112. package/dist/views/system/org/index2.vue.js.map +1 -1
  113. package/dist/views/system/position/index.vue.js +2 -2
  114. package/dist/views/system/position/index2.vue.js +57 -48
  115. package/dist/views/system/position/index2.vue.js.map +1 -1
  116. package/dist/views/system/recycle/index.vue.js +1 -1
  117. package/dist/views/system/recycle/index2.vue.js +3 -1
  118. package/dist/views/system/recycle/index2.vue.js.map +1 -1
  119. package/dist/views/system/role/components/GrantMenuSheet.vue.d.ts +2 -0
  120. package/dist/views/system/role/components/GrantMenuSheet.vue.js +2 -2
  121. package/dist/views/system/role/components/GrantMenuSheet2.vue.js +54 -49
  122. package/dist/views/system/role/components/GrantMenuSheet2.vue.js.map +1 -1
  123. package/dist/views/system/role/components/GrantMenuTable.vue.js +1 -1
  124. package/dist/views/system/role/components/GrantMenuTable2.vue.js +159 -195
  125. package/dist/views/system/role/components/GrantMenuTable2.vue.js.map +1 -1
  126. package/dist/views/system/role/components/grantMenuGroups.d.ts +54 -0
  127. package/dist/views/system/role/components/grantMenuGroups.js +116 -76
  128. package/dist/views/system/role/components/grantMenuGroups.js.map +1 -1
  129. package/dist/views/system/role/index.vue.js +2 -2
  130. package/dist/views/system/role/index2.vue.js +152 -145
  131. package/dist/views/system/role/index2.vue.js.map +1 -1
  132. package/dist/views/system/session/index2.vue.js +39 -35
  133. package/dist/views/system/session/index2.vue.js.map +1 -1
  134. package/dist/views/system/user/components/UserGrantEffectiveTable.vue.d.ts +10 -0
  135. package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js +5 -0
  136. package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js.map +1 -0
  137. package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js +96 -0
  138. package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js.map +1 -0
  139. package/dist/views/system/user/components/UserGrantMenuSheet.vue.d.ts +13 -0
  140. package/dist/views/system/user/components/UserGrantMenuSheet.vue.js +8 -0
  141. package/dist/views/system/user/components/UserGrantMenuSheet.vue.js.map +1 -0
  142. package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js +216 -0
  143. package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js.map +1 -0
  144. package/dist/views/system/user/components/UserGrantMenuTable.vue.d.ts +13 -0
  145. package/dist/views/system/user/components/UserGrantMenuTable.vue.js +8 -0
  146. package/dist/views/system/user/components/UserGrantMenuTable.vue.js.map +1 -0
  147. package/dist/views/system/user/components/UserGrantMenuTable2.vue.js +186 -0
  148. package/dist/views/system/user/components/UserGrantMenuTable2.vue.js.map +1 -0
  149. package/dist/views/system/user/components/UserGrantNodeRow.vue.d.ts +31 -0
  150. package/dist/views/system/user/components/UserGrantNodeRow.vue.js +8 -0
  151. package/dist/views/system/user/components/UserGrantNodeRow.vue.js.map +1 -0
  152. package/dist/views/system/user/components/UserGrantNodeRow2.vue.js +171 -0
  153. package/dist/views/system/user/components/UserGrantNodeRow2.vue.js.map +1 -0
  154. package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.d.ts +21 -0
  155. package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js +5 -0
  156. package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js.map +1 -0
  157. package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js +178 -0
  158. package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js.map +1 -0
  159. package/dist/views/system/user/components/userGrantState.d.ts +64 -0
  160. package/dist/views/system/user/components/userGrantState.js +117 -0
  161. package/dist/views/system/user/components/userGrantState.js.map +1 -0
  162. package/dist/views/system/user/index.vue.js +2 -2
  163. package/dist/views/system/user/index2.vue.js +369 -295
  164. package/dist/views/system/user/index2.vue.js.map +1 -1
  165. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"LoginForm2.vue.js","sources":["../../../src/views/login/LoginForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { reactive, ref, computed, nextTick, onMounted, onBeforeUnmount, h } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n NAlert,\n NButton,\n NForm,\n NFormItem,\n NInput,\n NCheckbox,\n NModal,\n NDropdown,\n useMessage,\n type DropdownOption,\n} from 'naive-ui'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { authApi, externalAuthApi, ApiError } from '#/api'\nimport type { LoginOutput } from '#/types/api'\nimport { useUserStore } from '#/stores/user'\nimport { useAuthStore } from '#/stores/auth'\nimport { useSite } from '#/composables/useSite'\nimport { resetRouter } from '#/router'\nimport { translateError } from '#/utils/error'\nimport {\n splitLoginProviders,\n PREVIEW_ALL_SSO_BRANDS,\n previewAllBrandProviders,\n claimWeComAutoLogin,\n} from '#/utils/oauthBrand'\nimport SmartLogo from '#/components/SmartLogo.vue'\nimport { runtime } from '#/lib/runtime'\nimport BrandIcon from '#/components/oauth/BrandIcon.vue'\n\n// 双栏布局下品牌区在左栏,卡内不重复 logo/标题,改用「欢迎回来」问候语(showGreeting);\n// 版权页脚由外壳画在卡外(showFooter=false)。单栏时全部默认:卡内顶部是 logo + 站点标题,再是「登录」标题。\nwithDefaults(\n defineProps<{\n showLogo?: boolean\n showTitle?: boolean\n showGreeting?: boolean\n showFooter?: boolean\n }>(),\n {\n showLogo: true,\n showTitle: true,\n showGreeting: false,\n showFooter: true,\n },\n)\n// 登录成功、即将跳转时通知外壳播退场动画(true);跳转没发生时撤销(false),免得页面停在淡出态\nconst emit = defineEmits<{ leave: [on: boolean] }>()\n\nconst router = useRouter()\nconst route = useRoute()\nconst message = useMessage()\nconst { t } = useI18n()\nconst user = useUserStore()\nconst auth = useAuthStore()\nconst { site, appVersion, loadSite } = useSite()\nconst year = new Date().getFullYear()\n\n/** 未绑定 SSO 后带回:登录成功后须确认再 claim(防静默抢绑) */\nconst pendingLink = computed(() =>\n typeof route.query.pendingLink === 'string' ? route.query.pendingLink : '',\n)\nconst pendingProvider = computed(() =>\n typeof route.query.provider === 'string' ? route.query.provider : '',\n)\nconst pendingDisplayName = computed(() =>\n typeof route.query.displayName === 'string' ? route.query.displayName : '',\n)\nconst pendingConfirmShow = ref(false)\nconst pendingClaimBusy = ref(false)\nconst pendingClaimToken = ref('')\n\n// 开发态(createSmartAdmin({ dev }))预填超管账号密码,免得每次手敲;生产留空。\n// SSO pending-link 场景故意不预填密码:解绑后 GitHub 会落到本页,预填+一点就 claim 看起来像「SSO 直接进了」。\nconst model = reactive(\n runtime.dev && !pendingLink.value\n ? { account: 'superAdmin', password: 'Aa123456', remember: true }\n : {\n account: runtime.dev ? 'superAdmin' : '',\n password: '',\n remember: true,\n },\n)\nconst loading = ref(false)\n\n// 登录形态:账号密码 / 短信免密 / 短信二次验证(40009) / TOTP 二次验证(40018)\n// 强制 MFA 未绑定(40020)用 Modal 引导,不切 mode,登录页默认也不常驻绑定链接\nconst mode = ref<'account' | 'sms' | 'mfa' | 'totp'>('account')\n\n// 短信免密登录\nconst smsModel = reactive({ phone: '', code: '' })\n// 二次验证挑战(40009 信令 args 下发)\nconst mfa = reactive({ challengeId: '', phoneMask: '', code: '' })\n// TOTP 挑战(40018)\nconst totp = reactive({ challengeId: '', code: '' })\n// 强制 MFA 但未绑定(40020):弹窗 + 带到 /mfa/bind 的账号\nconst bindRequiredShow = ref(false)\nconst bindAccount = ref('')\n\n// 发码/重发共用倒计时(同一时刻只有一个发码入口可见)\nconst countdown = ref(0)\nlet timer: number | undefined\nfunction startCountdown(s: number) {\n countdown.value = s\n window.clearInterval(timer)\n timer = window.setInterval(() => {\n countdown.value -= 1\n if (countdown.value <= 0) window.clearInterval(timer)\n }, 1000)\n}\nonBeforeUnmount(() => window.clearInterval(timer))\n\n// 验证码:是否启用由匿名站点信息(sys.security.captcha.enabled)运行时驱动;启用才拉取并展示。\n// 账号模式护登录,短信模式护发码(发码端点防脚本滥用)。\nconst captchaEnabled = ref(false)\nconst captchaId = ref('')\nconst captchaSvg = ref('')\nconst captchaCode = ref('')\nconst captchaType = ref('char')\n\n// math 类型需算出结果再输入,提示语不同;其余类型统一“请输入验证码”。\nconst captchaHint = computed(() =>\n captchaType.value === 'math' ? t('login.captchaMathPlaceholder') : t('login.captchaPlaceholder'),\n)\n\nasync function loadCaptcha() {\n try {\n const c = await authApi.captcha()\n captchaId.value = c.captchaId\n captchaSvg.value = c.svg\n captchaType.value = c.type || 'char'\n } catch {\n // 拉取失败不阻塞登录页渲染;点击图形可重试\n }\n}\n\n/** 验证码一次性消费:任何用掉票据的请求失败后必刷新,避免复用作废票据 */\nasync function refreshCaptchaAfterUse() {\n if (captchaEnabled.value) {\n captchaCode.value = ''\n await loadCaptcha()\n }\n}\n\nonMounted(async () => {\n // 站点信息全站共用(useSite 去重);验证码开关据此运行时驱动,启用才拉图。\n await loadSite()\n if (site.captchaEnabled) {\n captchaEnabled.value = true\n await loadCaptcha()\n }\n void loadSsoProviders()\n\n // SSO 回调带回的 TOTP 挑战:直接进入 totp 完成态\n const q = router.currentRoute.value.query\n const ch = typeof q.totpChallenge === 'string' ? q.totpChallenge : ''\n if (ch) {\n totp.challengeId = ch\n totp.code = ''\n mode.value = 'totp'\n }\n})\n\n// 第三方登录:默认后端 providers 驱动;PREVIEW_ALL_SSO_BRANDS 时铺全品牌图(图标验收)。\nconst ssoProviders = ref<{ code: string; displayName: string; icon?: string | null }[]>([])\nconst pendingProviderLabel = computed(() => {\n const code = pendingProvider.value\n if (!code) return t('oauth.thirdParty')\n const hit = ssoProviders.value.find(p => p.code === code)\n return hit?.displayName || code\n})\nconst ssoDisplayList = computed(() =>\n PREVIEW_ALL_SSO_BRANDS ? previewAllBrandProviders() : ssoProviders.value,\n)\n// 预览全图标时不截断;正常模式 N=4 溢出\nconst ssoSplit = computed(() =>\n PREVIEW_ALL_SSO_BRANDS\n ? { visible: ssoDisplayList.value, overflow: [] as typeof ssoDisplayList.value }\n : splitLoginProviders(ssoDisplayList.value),\n)\nconst ssoOverflowOptions = computed<DropdownOption[]>(() =>\n ssoSplit.value.overflow.map(p => ({\n key: p.code,\n label: () =>\n h(\n 'span',\n {\n class: 'lf-sso-menu-item',\n style: { display: 'inline-flex', alignItems: 'center', gap: '8px' },\n },\n [h(BrandIcon, { code: p.code, icon: p.icon, size: 20 }), h('span', null, p.displayName)],\n ),\n })),\n)\nasync function loadSsoProviders() {\n try {\n ssoProviders.value = await externalAuthApi.providers()\n } catch {\n // 未配置外部登录或拉取失败:整段 SSO 区不显(预览全图标模式仍会显示)\n return\n }\n // 企业微信客户端里直接走企业微信登录;SSO 带回来的认领 / 二次验证要留在本页完成\n const q = route.query\n if (\n !q.pendingLink &&\n !q.totpChallenge &&\n claimWeComAutoLogin({\n userAgent: navigator.userAgent,\n providers: ssoProviders.value,\n storage: sessionStorage,\n })\n ) {\n window.location.href = ssoHref('wecom')\n }\n}\n/** 顶层跳 IdP(与 Gitee 一样用 <a href>)。预览假项仍给 authorize 链,未配后端会失败。 */\nfunction ssoHref(code: string) {\n return externalAuthApi.authorizeUrl(code)\n}\nfunction onSsoOverflow(key: string | number) {\n window.location.href = ssoHref(String(key))\n}\n\nasync function enterHome() {\n await router.replace('/')\n}\n\n// 密码错 / 验证码错:表单左右抖一下(macOS 登录框的做法),比只弹 toast 更直接。\n// 先摘掉 class、等一帧再加,连续失败也能重播。\nconst shaking = ref(false)\nasync function shake() {\n shaking.value = false\n await nextTick()\n requestAnimationFrame(() => {\n shaking.value = true\n })\n}\n\n// 登录成功的收尾:先让页面淡出,再跳转。系统开了「减少动态效果」就不等。\nconst REDUCED_MOTION = '(prefers-reduced-motion: reduce)'\nasync function leaveThenEnter() {\n emit('leave', true)\n if (!window.matchMedia?.(REDUCED_MOTION).matches) {\n await new Promise<void>(resolve => setTimeout(resolve, 320))\n }\n await enterHome()\n emit('leave', false)\n}\n\nasync function finishLogin(res: LoginOutput) {\n // 每次登录都清动态路由/授权态,否则上次会话的 routesReady=true 会跳过 enterInitial,\n // 菜单壳在、业务路由未重建 → 进系统后处处 404(绑定 MFA 后回登录再登最易踩中)。\n resetRouter()\n auth.reset()\n user.setSession(res)\n\n // 现场绑定:不静默 claim——须用户确认,并依赖服务端 binder cookie 同浏览器校验\n const link = pendingLink.value\n if (link) {\n pendingClaimToken.value = link\n pendingConfirmShow.value = true\n return\n }\n message.success(t('login.success'))\n await leaveThenEnter()\n}\n\nconst pendingConfirmIdentity = computed(() =>\n pendingDisplayName.value\n ? t('oauth.pendingLinkIdentity', { display: pendingDisplayName.value })\n : '',\n)\n\nasync function confirmPendingBind() {\n if (pendingClaimBusy.value) return\n pendingClaimBusy.value = true\n try {\n await externalAuthApi.claimPendingLink(pendingClaimToken.value)\n message.success(t('oauth.pendingLinkSuccess', { name: pendingProviderLabel.value }))\n } catch (e) {\n message.warning(translateError(e))\n } finally {\n pendingClaimBusy.value = false\n pendingConfirmShow.value = false\n pendingClaimToken.value = ''\n }\n await enterHome()\n}\n\nasync function skipPendingBind() {\n pendingConfirmShow.value = false\n pendingClaimToken.value = ''\n message.success(t('login.success'))\n await enterHome()\n}\n\nasync function onSubmit() {\n if (mode.value === 'mfa') return onMfaSubmit()\n if (mode.value === 'totp') return onTotpSubmit()\n if (mode.value === 'sms') return onSmsSubmit()\n\n if (!model.account || !model.password) {\n message.warning(t('login.required'))\n return\n }\n if (captchaEnabled.value && !captchaCode.value) {\n message.warning(t('login.captchaRequired'))\n return\n }\n loading.value = true\n try {\n const res = await authApi.login({\n account: model.account,\n password: model.password,\n ...(captchaEnabled.value\n ? { captchaId: captchaId.value, captchaCode: captchaCode.value }\n : {}),\n })\n await finishLogin(res)\n } catch (e) {\n // 40018 = 密码已过、需 TOTP 二次验证\n if (e instanceof ApiError && e.code === 40018 && e.args) {\n totp.challengeId = String(e.args.challengeId ?? '')\n totp.code = ''\n mode.value = 'totp'\n } else if (e instanceof ApiError && e.code === 40020) {\n // 40020 = 强制 MFA 未绑定 → Modal 引导自助设置(登录页默认不常驻链接)\n bindAccount.value = model.account.trim()\n bindRequiredShow.value = true\n } else if (e instanceof ApiError && e.code === 40009 && e.args) {\n // 40009 = 密码已过、需短信二次验证\n mfa.challengeId = String(e.args.challengeId ?? '')\n mfa.phoneMask = String(e.args.phoneMask ?? '')\n mfa.code = ''\n mode.value = 'mfa'\n startCountdown(Number(e.args.resendSeconds ?? 60))\n } else {\n message.error(translateError(e))\n void shake()\n }\n await refreshCaptchaAfterUse()\n } finally {\n loading.value = false\n }\n}\n\nfunction closeBindRequired() {\n bindRequiredShow.value = false\n}\n\nfunction goBindAuthenticator() {\n const account = bindAccount.value || model.account.trim()\n bindRequiredShow.value = false\n void router.push({ path: '/mfa/bind', query: account ? { account } : {} })\n}\n\nfunction goRecovery() {\n const account = model.account.trim() || bindAccount.value\n void router.push({\n path: '/mfa/bind',\n query: { ...(account ? { account } : {}), mode: 'recovery' },\n })\n}\n\nasync function onMfaSubmit() {\n if (!mfa.code) {\n message.warning(t('login.smsCodePlaceholder'))\n return\n }\n loading.value = true\n try {\n await finishLogin(\n await authApi.smsChallengeLogin({ challengeId: mfa.challengeId, code: mfa.code }),\n )\n } catch (e) {\n message.error(translateError(e))\n void shake()\n } finally {\n loading.value = false\n }\n}\n\nasync function onTotpSubmit() {\n if (!totp.code) {\n message.warning(t('login.totpPlaceholder'))\n return\n }\n loading.value = true\n try {\n await finishLogin(\n await authApi.totpChallengeLogin({ challengeId: totp.challengeId, code: totp.code }),\n )\n } catch (e) {\n message.error(translateError(e))\n void shake()\n } finally {\n loading.value = false\n }\n}\n\nasync function onMfaResend() {\n if (countdown.value > 0) return\n try {\n const r = await authApi.smsChallengeResend({ challengeId: mfa.challengeId })\n message.success(t('login.smsSent'))\n startCountdown(r.resendSeconds)\n } catch (e) {\n message.error(translateError(e))\n }\n}\n\nfunction backToAccount() {\n mode.value = 'account'\n mfa.challengeId = ''\n mfa.code = ''\n totp.challengeId = ''\n totp.code = ''\n}\n\nasync function onSendSmsCode() {\n if (countdown.value > 0) return\n if (!smsModel.phone) {\n message.warning(t('login.phonePlaceholder'))\n return\n }\n if (captchaEnabled.value && !captchaCode.value) {\n message.warning(t('login.captchaRequired'))\n return\n }\n try {\n const r = await authApi.smsLoginSend({\n phone: smsModel.phone,\n ...(captchaEnabled.value\n ? { captchaId: captchaId.value, captchaCode: captchaCode.value }\n : {}),\n })\n message.success(t('login.smsSent'))\n startCountdown(r.resendSeconds)\n } catch (e) {\n message.error(translateError(e))\n }\n await refreshCaptchaAfterUse()\n}\n\nasync function onSmsSubmit() {\n if (!smsModel.phone || !smsModel.code) {\n message.warning(t('login.smsRequired'))\n return\n }\n loading.value = true\n try {\n await finishLogin(await authApi.smsLogin({ phone: smsModel.phone, code: smsModel.code }))\n } catch (e) {\n message.error(translateError(e))\n void shake()\n } finally {\n loading.value = false\n }\n}\n</script>\n\n<template>\n <div class=\"login-form\" :class=\"{ shake: shaking }\" @animationend.self=\"shaking = false\">\n <div v-if=\"showLogo\" class=\"lf-brand\">\n <!-- 站点 logo 的取值顺序(配置 → brand 选项 → 内置矢量标)统一在 SmartLogo 里 -->\n <SmartLogo :size=\"34\" />\n <span class=\"lf-word\">{{ site.title }}</span>\n </div>\n\n <n-alert\n v-if=\"pendingLink\"\n type=\"info\"\n :bordered=\"false\"\n class=\"lf-pending-alert\"\n :title=\"t('oauth.pendingLinkTitle', { name: pendingProviderLabel })\"\n >\n {{ t('oauth.pendingLinkHint', { name: pendingProviderLabel }) }}\n </n-alert>\n\n <!-- 登录方式切换:旧内容先淡出上移,新内容的逐项浮现由外壳(LoginDesk)的入场动画负责 -->\n <Transition name=\"lf-swap\" mode=\"out-in\">\n <div :key=\"mode\" class=\"lf-body\">\n <!-- 短信二次验证:密码已过,凭挑战 + 短信码完成登录 -->\n <template v-if=\"mode === 'mfa'\">\n <h2 v-if=\"showTitle || showGreeting\" class=\"lf-title\">\n {{ t('login.mfaTitle', { phone: mfa.phoneMask }) }}\n </h2>\n <p class=\"lf-hint-line\">{{ t('login.mfaSub') }}</p>\n <n-form :show-feedback=\"false\" @keyup.enter=\"onSubmit\">\n <n-form-item :label=\"t('login.smsCode')\" path=\"code\">\n <n-input\n v-model:value=\"mfa.code\"\n :placeholder=\"t('login.smsCodePlaceholder')\"\n size=\"large\"\n :maxlength=\"6\"\n >\n <template #prefix><Icon icon=\"ph:chat-circle-text\" /></template>\n </n-input>\n </n-form-item>\n <div class=\"row lf-between\">\n <n-button text type=\"primary\" @click=\"backToAccount\">\n {{ t('login.backToPassword') }}\n </n-button>\n <n-button\n text\n :type=\"countdown > 0 ? 'default' : 'primary'\"\n :disabled=\"countdown > 0\"\n @click=\"onMfaResend\"\n >\n {{ countdown > 0 ? t('login.resendAfter', { s: countdown }) : t('login.sendCode') }}\n </n-button>\n </div>\n <n-button\n class=\"hero-btn cta\"\n type=\"primary\"\n size=\"large\"\n block\n attr-type=\"button\"\n :loading=\"loading\"\n @click=\"onSubmit\"\n >\n {{ loading ? t('common.loading') : t('login.submit') }}\n </n-button>\n </n-form>\n </template>\n\n <!-- TOTP 二次验证:密码已过,凭 Authenticator 动态口令完成登录 -->\n <template v-else-if=\"mode === 'totp'\">\n <h2 v-if=\"showTitle || showGreeting\" class=\"lf-title\">{{ t('login.totpTitle') }}</h2>\n <p class=\"lf-hint-line\">{{ t('login.totpSub') }}</p>\n <n-form :show-feedback=\"false\" @keyup.enter=\"onSubmit\">\n <n-form-item :label=\"t('login.totpCode')\" path=\"code\">\n <n-input\n v-model:value=\"totp.code\"\n :placeholder=\"t('login.totpPlaceholder')\"\n size=\"large\"\n :maxlength=\"6\"\n >\n <template #prefix><Icon icon=\"ph:shield-check\" /></template>\n </n-input>\n </n-form-item>\n <div class=\"row lf-between\">\n <n-button text type=\"primary\" @click=\"backToAccount\">\n {{ t('login.backToPassword') }}\n </n-button>\n <n-button text type=\"primary\" @click=\"goRecovery\">\n {{ t('login.useRecovery') }}\n </n-button>\n </div>\n <n-button\n class=\"hero-btn cta\"\n type=\"primary\"\n size=\"large\"\n block\n attr-type=\"button\"\n :loading=\"loading\"\n @click=\"onSubmit\"\n >\n {{ loading ? t('common.loading') : t('login.submit') }}\n </n-button>\n </n-form>\n </template>\n\n <template v-else>\n <!-- 单栏:卡内标题「登录」;双栏:品牌在左栏,这里是问候语 -->\n <h2 v-if=\"showTitle\" class=\"lf-title\">{{ t('login.title') }}</h2>\n <div v-else-if=\"showGreeting\" class=\"lf-greet\">\n <h1>{{ t('login.welcome') }}</h1>\n <p>{{ t('login.welcomeSub') }}</p>\n </div>\n <n-form\n :model=\"mode === 'account' ? model : smsModel\"\n :show-feedback=\"false\"\n @keyup.enter=\"onSubmit\"\n >\n <template v-if=\"mode === 'account'\">\n <n-form-item :label=\"t('login.account')\" path=\"account\">\n <n-input\n v-model:value=\"model.account\"\n :placeholder=\"t('login.accountPlaceholder')\"\n size=\"large\"\n >\n <template #prefix><Icon icon=\"ph:user\" /></template>\n </n-input>\n </n-form-item>\n <n-form-item :label=\"t('login.password')\" path=\"password\">\n <n-input\n v-model:value=\"model.password\"\n type=\"password\"\n show-password-on=\"click\"\n :placeholder=\"t('login.passwordPlaceholder')\"\n size=\"large\"\n >\n <template #prefix><Icon icon=\"ph:lock\" /></template>\n </n-input>\n </n-form-item>\n </template>\n <template v-else>\n <n-form-item :label=\"t('login.phone')\" path=\"phone\">\n <n-input\n v-model:value=\"smsModel.phone\"\n :placeholder=\"t('login.phonePlaceholder')\"\n size=\"large\"\n :maxlength=\"20\"\n >\n <template #prefix><Icon icon=\"ph:device-mobile\" /></template>\n </n-input>\n </n-form-item>\n </template>\n <n-form-item v-if=\"captchaEnabled\" :label=\"t('login.captcha')\" path=\"captcha\">\n <div class=\"lf-captcha\">\n <n-input\n v-model:value=\"captchaCode\"\n :placeholder=\"captchaHint\"\n size=\"large\"\n @keyup.enter=\"onSubmit\"\n >\n <template #prefix><Icon icon=\"ph:shield-check\" /></template>\n </n-input>\n <!-- SVG 来自本站后端;点击重取一张(一次性票据);取图失败时图形位为空,点击重试 -->\n <button\n type=\"button\"\n class=\"lf-captcha-img\"\n :class=\"{ empty: !captchaSvg }\"\n :title=\"t('login.captchaPlaceholder')\"\n :aria-label=\"t('login.captchaPlaceholder')\"\n @click=\"loadCaptcha\"\n v-html=\"captchaSvg\"\n />\n </div>\n </n-form-item>\n <n-form-item v-if=\"mode === 'sms'\" :label=\"t('login.smsCode')\" path=\"code\">\n <div class=\"lf-captcha\">\n <n-input\n v-model:value=\"smsModel.code\"\n :placeholder=\"t('login.smsCodePlaceholder')\"\n size=\"large\"\n :maxlength=\"6\"\n >\n <template #prefix><Icon icon=\"ph:chat-circle-text\" /></template>\n </n-input>\n <n-button\n class=\"lf-send-btn\"\n secondary\n size=\"large\"\n attr-type=\"button\"\n :disabled=\"countdown > 0\"\n @click=\"onSendSmsCode\"\n >\n {{\n countdown > 0 ? t('login.resendAfter', { s: countdown }) : t('login.sendCode')\n }}\n </n-button>\n </div>\n </n-form-item>\n <div class=\"row lf-between\">\n <n-checkbox v-if=\"mode === 'account'\" v-model:checked=\"model.remember\">\n {{ t('login.remember') }}\n </n-checkbox>\n <span v-else />\n <n-button\n v-if=\"site.smsLoginEnabled\"\n text\n type=\"primary\"\n @click=\"mode = mode === 'account' ? 'sms' : 'account'\"\n >\n {{ mode === 'account' ? t('login.smsLogin') : t('login.accountLogin') }}\n </n-button>\n </div>\n <n-button\n class=\"hero-btn cta\"\n type=\"primary\"\n size=\"large\"\n block\n attr-type=\"button\"\n :loading=\"loading\"\n @click=\"onSubmit\"\n >\n {{ loading ? t('common.loading') : t('login.submit') }}\n </n-button>\n </n-form>\n\n <!-- 第三方登录:圆形品牌标;<a href>。PREVIEW_ALL_SSO_BRANDS 时展示全部品牌图。 -->\n <template v-if=\"ssoDisplayList.length\">\n <div class=\"lf-divider\">\n <span>{{ t('login.otherMethods') }}</span>\n </div>\n <div class=\"lf-sso\">\n <a\n v-for=\"p in ssoSplit.visible\"\n :key=\"p.code\"\n class=\"lf-sso-btn\"\n :href=\"ssoHref(p.code)\"\n :title=\"p.displayName\"\n :aria-label=\"p.displayName\"\n >\n <BrandIcon :code=\"p.code\" :icon=\"p.icon\" :size=\"28\" />\n </a>\n <n-dropdown\n v-if=\"ssoSplit.overflow.length\"\n trigger=\"click\"\n :options=\"ssoOverflowOptions\"\n @select=\"onSsoOverflow\"\n >\n <a\n class=\"lf-sso-btn lf-sso-more\"\n href=\"javascript:void(0)\"\n role=\"button\"\n :title=\"t('login.moreMethods')\"\n :aria-label=\"t('login.moreMethods')\"\n @click.prevent\n >\n <Icon icon=\"ph:dots-three\" :width=\"18\" />\n </a>\n </n-dropdown>\n </div>\n </template>\n </template>\n </div>\n </Transition>\n\n <!-- 页脚:版权(可选链接)+ 构建期版本号。双栏时外壳在卡外自绘页脚,传 :show-footer=\"false\" 关掉。 -->\n <footer v-if=\"showFooter\" class=\"lf-foot\">\n <span>\n © {{ year }}\n <a v-if=\"site.copyrightUrl\" :href=\"site.copyrightUrl\" target=\"_blank\" rel=\"noopener\">\n {{ site.copyright || site.title }}\n </a>\n <template v-else>{{ site.copyright || site.title }}</template>\n </span>\n <span v-if=\"appVersion\" class=\"lf-ver\">v{{ appVersion }}</span>\n </footer>\n\n <!-- 强制 MFA 未绑定(40020):遮罩 Modal,账密表单仍在底下;默认登录页不常驻绑定链接 -->\n <n-modal\n v-model:show=\"bindRequiredShow\"\n preset=\"dialog\"\n type=\"warning\"\n :title=\"t('login.totpBindTitle')\"\n :content=\"t('login.totpBindSub')\"\n :positive-text=\"t('login.setupAuthenticator')\"\n :negative-text=\"t('common.cancel')\"\n @positive-click=\"goBindAuthenticator\"\n @negative-click=\"closeBindRequired\"\n />\n <!-- pending-link:登录后显式确认绑定,防静默抢绑 -->\n <n-modal\n v-model:show=\"pendingConfirmShow\"\n preset=\"dialog\"\n type=\"info\"\n :title=\"t('oauth.pendingLinkConfirmTitle')\"\n :content=\"\n t('oauth.pendingLinkConfirmContent', {\n name: pendingProviderLabel,\n identity: pendingConfirmIdentity,\n account: user.userInfo?.account ?? '',\n })\n \"\n :positive-text=\"t('oauth.pendingLinkConfirmOk')\"\n :negative-text=\"t('oauth.pendingLinkConfirmSkip')\"\n :loading=\"pendingClaimBusy\"\n :closable=\"false\"\n :mask-closable=\"false\"\n @positive-click=\"confirmPendingBind\"\n @negative-click=\"skipPendingBind\"\n />\n </div>\n</template>\n\n<style scoped>\n.login-form {\n width: 100%;\n}\n/* 登录失败:左右抖一下并逐渐收住(macOS 登录框的做法),不回弹到别处 */\n.login-form.shake {\n animation: lf-shake 0.46s cubic-bezier(0.36, 0.07, 0.19, 0.97);\n}\n@keyframes lf-shake {\n 0%,\n 100% {\n transform: none;\n }\n 15% {\n transform: translate3d(-9px, 0, 0);\n }\n 30% {\n transform: translate3d(8px, 0, 0);\n }\n 45% {\n transform: translate3d(-6px, 0, 0);\n }\n 60% {\n transform: translate3d(4px, 0, 0);\n }\n 80% {\n transform: translate3d(-2px, 0, 0);\n }\n}\n/* 登录方式切换:旧内容淡出并轻微上移变虚 */\n.lf-swap-leave-active {\n transition:\n opacity 0.16s ease-in,\n transform 0.16s ease-in,\n filter 0.16s ease-in;\n}\n.lf-swap-leave-to {\n opacity: 0;\n transform: translate3d(0, -6px, 0);\n filter: blur(4px);\n}\n@media (prefers-reduced-motion: reduce) {\n .login-form.shake {\n animation: none;\n }\n}\n.lf-pending-alert {\n margin-bottom: 16px;\n border-radius: var(--radius-md);\n text-align: left;\n}\n/* 单栏卡内顶部:站点 logo + 标题。logo 取值顺序见 SmartLogo */\n.lf-brand {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 18px;\n}\n.lf-word {\n font-size: 22px;\n font-weight: 700;\n letter-spacing: -0.01em;\n color: var(--text-1);\n}\n.lf-title {\n font-size: 20px;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.3;\n margin: 0 0 16px;\n color: var(--text-1);\n}\n/* 双栏卡内问候语 */\n.lf-greet {\n text-align: center;\n margin: 2px 0 20px;\n}\n.lf-greet h1 {\n font-size: 26px;\n font-weight: 600;\n letter-spacing: -0.035em;\n margin: 0 0 4px;\n color: var(--text-1);\n}\n.lf-greet p {\n margin: 0;\n font-size: 13.5px;\n color: var(--text-3);\n}\n/* 二次验证副标题:标题下的弱化说明行 */\n.lf-hint-line {\n margin: -8px 0 18px;\n font-size: 13px;\n color: var(--text-3);\n}\n/* 表单项之间的间距(表单关了反馈行,没有校验规则要显示) */\n.login-form :deep(.n-form-item) {\n margin-bottom: 14px;\n}\n.row {\n margin: 2px 0 14px;\n min-height: 32px;\n}\n.lf-between {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.lf-between > :deep(.n-button) {\n height: 32px;\n padding: 0 2px;\n}\n/* 验证码:输入框 + 可点击刷新的 SVG 图形(等高对齐) */\n.lf-captcha {\n display: flex;\n gap: 10px;\n width: 100%;\n align-items: stretch;\n}\n.lf-captcha > :deep(.n-input) {\n flex: 1;\n min-width: 0;\n}\n.lf-captcha-img {\n flex: none;\n height: 42px;\n min-width: 100px;\n padding: 0;\n border: 1px solid var(--hairline);\n border-radius: var(--radius-md);\n background: #fff;\n cursor: pointer;\n overflow: hidden;\n display: grid;\n place-items: center;\n line-height: 0;\n}\n/* 取图失败时图形位为空:换成输入框同款底色,否则空白框像 bug;点击重试 */\n.lf-captcha-img.empty {\n background: var(--fill);\n}\n.lf-captcha-img:focus-visible {\n outline: 2px solid var(--signal);\n outline-offset: 2px;\n}\n.lf-captcha-img :deep(svg) {\n display: block;\n height: 40px;\n width: auto;\n}\n/* 发码按钮:与验证码图形同尺寸位 */\n.lf-send-btn {\n flex: none;\n min-width: 104px;\n}\n/* 第三方登录:分隔线 + 圆形品牌标 */\n.lf-divider {\n display: flex;\n align-items: center;\n gap: 14px;\n margin: 22px 0 14px;\n}\n.lf-divider::before,\n.lf-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--hairline);\n}\n.lf-divider span {\n font-size: 13px;\n color: var(--text-3);\n}\n.lf-sso {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: 12px;\n}\n/* 第三方登录:只露品牌标本身,不加灰底 / 描边 / 圆形裁切(会吃掉品牌标的轮廓);\n 40px 是点击热区,hover 才浮出一层极淡的圆角底,macOS 工具栏按钮的做法 */\n.lf-sso-btn {\n width: 40px;\n height: 40px;\n padding: 0;\n display: inline-grid;\n place-items: center;\n flex: none;\n border: none;\n border-radius: var(--radius-lg);\n background: transparent;\n color: var(--text-1);\n text-decoration: none;\n cursor: pointer;\n transition: background var(--transition-base);\n}\n.lf-sso-btn :deep(.oauth-brand-badge) {\n width: 28px;\n height: 28px;\n}\n.lf-sso-btn:hover {\n background: var(--hover);\n color: var(--text-1);\n text-decoration: none;\n}\n.lf-sso-btn:active {\n background: var(--pressed);\n}\n.lf-sso-btn:focus-visible {\n outline: 2px solid var(--signal);\n outline-offset: 2px;\n}\n/* 页脚:版权 + 版本号,弱化色 */\n.lf-foot {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n margin-top: 20px;\n font-size: 13px;\n color: var(--text-3);\n}\n.lf-foot a {\n color: inherit;\n text-decoration: none;\n}\n.lf-foot a:hover {\n color: var(--signal);\n}\n.lf-ver {\n opacity: 0.75;\n}\n@media (max-width: 480px) {\n .lf-captcha-img {\n min-width: 92px;\n }\n}\n</style>\n"],"names":["REDUCED_MOTION","emit","__emit","router","useRouter","route","useRoute","message","useMessage","t","useI18n","user","useUserStore","auth","useAuthStore","site","appVersion","loadSite","useSite","year","pendingLink","computed","pendingProvider","pendingDisplayName","pendingConfirmShow","ref","pendingClaimBusy","pendingClaimToken","model","reactive","runtime","loading","mode","smsModel","mfa","totp","bindRequiredShow","bindAccount","countdown","timer","startCountdown","s","onBeforeUnmount","captchaEnabled","captchaId","captchaSvg","captchaCode","captchaType","captchaHint","loadCaptcha","c","authApi","refreshCaptchaAfterUse","onMounted","loadSsoProviders","q","ch","ssoProviders","pendingProviderLabel","code","hit","p","ssoDisplayList","ssoSplit","splitLoginProviders","ssoOverflowOptions","h","BrandIcon","externalAuthApi","claimWeComAutoLogin","ssoHref","onSsoOverflow","key","enterHome","shaking","shake","nextTick","leaveThenEnter","_a","resolve","finishLogin","res","resetRouter","link","pendingConfirmIdentity","confirmPendingBind","e","translateError","skipPendingBind","onSubmit","onMfaSubmit","onTotpSubmit","onSmsSubmit","ApiError","closeBindRequired","goBindAuthenticator","account","goRecovery","onMfaResend","r","backToAccount","onSendSmsCode","_createElementBlock","_normalizeClass","__props","_openBlock","_hoisted_1","_createVNode","SmartLogo","_createElementVNode","_hoisted_2","_toDisplayString","_unref","_createBlock","NAlert","_createTextVNode","_Transition","_Fragment","_hoisted_3","_hoisted_4","NForm","NFormItem","NInput","_cache","$event","Icon","_hoisted_5","NButton","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_13","_hoisted_14","NCheckbox","_hoisted_15","_hoisted_16","_hoisted_17","_renderList","NDropdown","_hoisted_20","_hoisted_21","_hoisted_22","NModal"],"mappings":"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"}
1
+ {"version":3,"file":"LoginForm2.vue.js","sources":["../../../src/views/login/LoginForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { reactive, ref, computed, onMounted, onBeforeUnmount, h } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n NAlert,\n NButton,\n NForm,\n NFormItem,\n NInput,\n NCheckbox,\n NModal,\n NDropdown,\n useMessage,\n type DropdownOption,\n} from 'naive-ui'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { authApi, externalAuthApi, ApiError } from '#/api'\nimport type { LoginOutput } from '#/types/api'\nimport { useUserStore } from '#/stores/user'\nimport { useAuthStore } from '#/stores/auth'\nimport { useSite } from '#/composables/useSite'\nimport { resetRouter } from '#/router'\nimport { translateError } from '#/utils/error'\nimport { shake } from '#/utils/shake'\nimport {\n splitLoginProviders,\n PREVIEW_ALL_SSO_BRANDS,\n previewAllBrandProviders,\n claimWeComAutoLogin,\n} from '#/utils/oauthBrand'\nimport SmartLogo from '#/components/SmartLogo.vue'\nimport { runtime } from '#/lib/runtime'\nimport BrandIcon from '#/components/oauth/BrandIcon.vue'\n\n// 双栏布局下品牌区在左栏,卡内不重复 logo/标题,改用「欢迎回来」问候语(showGreeting);\n// 版权页脚由外壳画在卡外(showFooter=false)。单栏时全部默认:卡内顶部是 logo + 站点标题,再是「登录」标题。\nwithDefaults(\n defineProps<{\n showLogo?: boolean\n showTitle?: boolean\n showGreeting?: boolean\n showFooter?: boolean\n }>(),\n {\n showLogo: true,\n showTitle: true,\n showGreeting: false,\n showFooter: true,\n },\n)\n// 登录成功、即将跳转时通知外壳播退场动画(true);跳转没发生时撤销(false),免得页面停在淡出态\nconst emit = defineEmits<{ leave: [on: boolean] }>()\n\nconst router = useRouter()\nconst route = useRoute()\nconst message = useMessage()\nconst { t } = useI18n()\nconst user = useUserStore()\nconst auth = useAuthStore()\nconst { site, appVersion, loadSite } = useSite()\nconst year = new Date().getFullYear()\n\n/** 未绑定 SSO 后带回:登录成功后须确认再 claim(防静默抢绑) */\nconst pendingLink = computed(() =>\n typeof route.query.pendingLink === 'string' ? route.query.pendingLink : '',\n)\nconst pendingProvider = computed(() =>\n typeof route.query.provider === 'string' ? route.query.provider : '',\n)\nconst pendingDisplayName = computed(() =>\n typeof route.query.displayName === 'string' ? route.query.displayName : '',\n)\nconst pendingConfirmShow = ref(false)\nconst pendingClaimBusy = ref(false)\nconst pendingClaimToken = ref('')\n\n// 开发态(createSmartAdmin({ dev }))预填超管账号密码,免得每次手敲;生产留空。\n// SSO pending-link 场景故意不预填密码:解绑后 GitHub 会落到本页,预填+一点就 claim 看起来像「SSO 直接进了」。\nconst model = reactive(\n runtime.dev && !pendingLink.value\n ? { account: 'superAdmin', password: 'Aa123456', remember: true }\n : {\n account: runtime.dev ? 'superAdmin' : '',\n password: '',\n remember: true,\n },\n)\nconst loading = ref(false)\n\n// 登录形态:账号密码 / 短信免密 / 短信二次验证(40009) / TOTP 二次验证(40018)\n// 强制 MFA 未绑定(40020)用 Modal 引导,不切 mode,登录页默认也不常驻绑定链接\nconst mode = ref<'account' | 'sms' | 'mfa' | 'totp'>('account')\n\n// 短信免密登录\nconst smsModel = reactive({ phone: '', code: '' })\n// 二次验证挑战(40009 信令 args 下发)\nconst mfa = reactive({ challengeId: '', phoneMask: '', code: '' })\n// TOTP 挑战(40018)\nconst totp = reactive({ challengeId: '', code: '' })\n// 强制 MFA 但未绑定(40020):弹窗 + 带到 /mfa/bind 的账号\nconst bindRequiredShow = ref(false)\nconst bindAccount = ref('')\n\n// 发码/重发共用倒计时(同一时刻只有一个发码入口可见)\nconst countdown = ref(0)\nlet timer: number | undefined\nfunction startCountdown(s: number) {\n countdown.value = s\n window.clearInterval(timer)\n timer = window.setInterval(() => {\n countdown.value -= 1\n if (countdown.value <= 0) window.clearInterval(timer)\n }, 1000)\n}\nonBeforeUnmount(() => window.clearInterval(timer))\n\n// 验证码:是否启用由匿名站点信息(sys.security.captcha.enabled)运行时驱动;启用才拉取并展示。\n// 账号模式护登录,短信模式护发码(发码端点防脚本滥用)。\nconst captchaEnabled = ref(false)\nconst captchaId = ref('')\nconst captchaSvg = ref('')\nconst captchaCode = ref('')\nconst captchaType = ref('char')\n\n// math 类型需算出结果再输入,提示语不同;其余类型统一“请输入验证码”。\nconst captchaHint = computed(() =>\n captchaType.value === 'math' ? t('login.captchaMathPlaceholder') : t('login.captchaPlaceholder'),\n)\n\nasync function loadCaptcha() {\n try {\n const c = await authApi.captcha()\n captchaId.value = c.captchaId\n captchaSvg.value = c.svg\n captchaType.value = c.type || 'char'\n } catch {\n // 拉取失败不阻塞登录页渲染;点击图形可重试\n }\n}\n\n/** 验证码一次性消费:任何用掉票据的请求失败后必刷新,避免复用作废票据 */\nasync function refreshCaptchaAfterUse() {\n if (captchaEnabled.value) {\n captchaCode.value = ''\n await loadCaptcha()\n }\n}\n\nonMounted(async () => {\n // 站点信息全站共用(useSite 去重);验证码开关据此运行时驱动,启用才拉图。\n await loadSite()\n if (site.captchaEnabled) {\n captchaEnabled.value = true\n await loadCaptcha()\n }\n void loadSsoProviders()\n\n // SSO 回调带回的 TOTP 挑战:直接进入 totp 完成态\n const q = router.currentRoute.value.query\n const ch = typeof q.totpChallenge === 'string' ? q.totpChallenge : ''\n if (ch) {\n totp.challengeId = ch\n totp.code = ''\n mode.value = 'totp'\n }\n})\n\n// 第三方登录:默认后端 providers 驱动;PREVIEW_ALL_SSO_BRANDS 时铺全品牌图(图标验收)。\nconst ssoProviders = ref<{ code: string; displayName: string; icon?: string | null }[]>([])\nconst pendingProviderLabel = computed(() => {\n const code = pendingProvider.value\n if (!code) return t('oauth.thirdParty')\n const hit = ssoProviders.value.find(p => p.code === code)\n return hit?.displayName || code\n})\nconst ssoDisplayList = computed(() =>\n PREVIEW_ALL_SSO_BRANDS ? previewAllBrandProviders() : ssoProviders.value,\n)\n// 预览全图标时不截断;正常模式 N=4 溢出\nconst ssoSplit = computed(() =>\n PREVIEW_ALL_SSO_BRANDS\n ? { visible: ssoDisplayList.value, overflow: [] as typeof ssoDisplayList.value }\n : splitLoginProviders(ssoDisplayList.value),\n)\nconst ssoOverflowOptions = computed<DropdownOption[]>(() =>\n ssoSplit.value.overflow.map(p => ({\n key: p.code,\n label: () =>\n h(\n 'span',\n {\n class: 'lf-sso-menu-item',\n style: { display: 'inline-flex', alignItems: 'center', gap: '8px' },\n },\n [h(BrandIcon, { code: p.code, icon: p.icon, size: 20 }), h('span', null, p.displayName)],\n ),\n })),\n)\nasync function loadSsoProviders() {\n try {\n ssoProviders.value = await externalAuthApi.providers()\n } catch {\n // 未配置外部登录或拉取失败:整段 SSO 区不显(预览全图标模式仍会显示)\n return\n }\n // 企业微信客户端里直接走企业微信登录;SSO 带回来的认领 / 二次验证要留在本页完成\n const q = route.query\n if (\n !q.pendingLink &&\n !q.totpChallenge &&\n claimWeComAutoLogin({\n userAgent: navigator.userAgent,\n providers: ssoProviders.value,\n storage: sessionStorage,\n })\n ) {\n window.location.href = ssoHref('wecom')\n }\n}\n/** 顶层跳 IdP(与 Gitee 一样用 <a href>)。预览假项仍给 authorize 链,未配后端会失败。 */\nfunction ssoHref(code: string) {\n return externalAuthApi.authorizeUrl(code)\n}\nfunction onSsoOverflow(key: string | number) {\n window.location.href = ssoHref(String(key))\n}\n\nasync function enterHome() {\n await router.replace('/')\n}\n\n// 密码错 / 验证码错:表单左右抖一下(macOS 登录框的做法),比只弹 toast 更直接。\n// 动画细节与「为什么不用 CSS class」见 utils/shake。\nconst formEl = ref<HTMLElement>()\nfunction shakeForm() {\n shake(formEl.value)\n}\n\n// 登录成功的收尾:先让页面淡出,再跳转。系统开了「减少动态效果」就不等。\nconst REDUCED_MOTION = '(prefers-reduced-motion: reduce)'\nasync function leaveThenEnter() {\n emit('leave', true)\n if (!window.matchMedia?.(REDUCED_MOTION).matches) {\n await new Promise<void>(resolve => setTimeout(resolve, 320))\n }\n await enterHome()\n emit('leave', false)\n}\n\nasync function finishLogin(res: LoginOutput) {\n // 每次登录都清动态路由/授权态,否则上次会话的 routesReady=true 会跳过 enterInitial,\n // 菜单壳在、业务路由未重建 → 进系统后处处 404(绑定 MFA 后回登录再登最易踩中)。\n resetRouter()\n auth.reset()\n user.setSession(res)\n\n // 现场绑定:不静默 claim——须用户确认,并依赖服务端 binder cookie 同浏览器校验\n const link = pendingLink.value\n if (link) {\n pendingClaimToken.value = link\n pendingConfirmShow.value = true\n return\n }\n message.success(t('login.success'))\n await leaveThenEnter()\n}\n\nconst pendingConfirmIdentity = computed(() =>\n pendingDisplayName.value\n ? t('oauth.pendingLinkIdentity', { display: pendingDisplayName.value })\n : '',\n)\n\nasync function confirmPendingBind() {\n if (pendingClaimBusy.value) return\n pendingClaimBusy.value = true\n try {\n await externalAuthApi.claimPendingLink(pendingClaimToken.value)\n message.success(t('oauth.pendingLinkSuccess', { name: pendingProviderLabel.value }))\n } catch (e) {\n message.warning(translateError(e))\n } finally {\n pendingClaimBusy.value = false\n pendingConfirmShow.value = false\n pendingClaimToken.value = ''\n }\n await enterHome()\n}\n\nasync function skipPendingBind() {\n pendingConfirmShow.value = false\n pendingClaimToken.value = ''\n message.success(t('login.success'))\n await enterHome()\n}\n\nasync function onSubmit() {\n if (mode.value === 'mfa') return onMfaSubmit()\n if (mode.value === 'totp') return onTotpSubmit()\n if (mode.value === 'sms') return onSmsSubmit()\n\n if (!model.account || !model.password) {\n message.warning(t('login.required'))\n return\n }\n if (captchaEnabled.value && !captchaCode.value) {\n message.warning(t('login.captchaRequired'))\n return\n }\n loading.value = true\n try {\n const res = await authApi.login({\n account: model.account,\n password: model.password,\n ...(captchaEnabled.value\n ? { captchaId: captchaId.value, captchaCode: captchaCode.value }\n : {}),\n })\n await finishLogin(res)\n } catch (e) {\n // 40018 = 密码已过、需 TOTP 二次验证\n if (e instanceof ApiError && e.code === 40018 && e.args) {\n totp.challengeId = String(e.args.challengeId ?? '')\n totp.code = ''\n mode.value = 'totp'\n } else if (e instanceof ApiError && e.code === 40020) {\n // 40020 = 强制 MFA 未绑定 → Modal 引导自助设置(登录页默认不常驻链接)\n bindAccount.value = model.account.trim()\n bindRequiredShow.value = true\n } else if (e instanceof ApiError && e.code === 40009 && e.args) {\n // 40009 = 密码已过、需短信二次验证\n mfa.challengeId = String(e.args.challengeId ?? '')\n mfa.phoneMask = String(e.args.phoneMask ?? '')\n mfa.code = ''\n mode.value = 'mfa'\n startCountdown(Number(e.args.resendSeconds ?? 60))\n } else {\n message.error(translateError(e))\n shakeForm()\n }\n await refreshCaptchaAfterUse()\n } finally {\n loading.value = false\n }\n}\n\nfunction closeBindRequired() {\n bindRequiredShow.value = false\n}\n\nfunction goBindAuthenticator() {\n const account = bindAccount.value || model.account.trim()\n bindRequiredShow.value = false\n void router.push({ path: '/mfa/bind', query: account ? { account } : {} })\n}\n\nfunction goRecovery() {\n const account = model.account.trim() || bindAccount.value\n void router.push({\n path: '/mfa/bind',\n query: { ...(account ? { account } : {}), mode: 'recovery' },\n })\n}\n\nasync function onMfaSubmit() {\n if (!mfa.code) {\n message.warning(t('login.smsCodePlaceholder'))\n return\n }\n loading.value = true\n try {\n await finishLogin(\n await authApi.smsChallengeLogin({ challengeId: mfa.challengeId, code: mfa.code }),\n )\n } catch (e) {\n message.error(translateError(e))\n shakeForm()\n } finally {\n loading.value = false\n }\n}\n\nasync function onTotpSubmit() {\n if (!totp.code) {\n message.warning(t('login.totpPlaceholder'))\n return\n }\n loading.value = true\n try {\n await finishLogin(\n await authApi.totpChallengeLogin({ challengeId: totp.challengeId, code: totp.code }),\n )\n } catch (e) {\n message.error(translateError(e))\n shakeForm()\n } finally {\n loading.value = false\n }\n}\n\nasync function onMfaResend() {\n if (countdown.value > 0) return\n try {\n const r = await authApi.smsChallengeResend({ challengeId: mfa.challengeId })\n message.success(t('login.smsSent'))\n startCountdown(r.resendSeconds)\n } catch (e) {\n message.error(translateError(e))\n }\n}\n\nfunction backToAccount() {\n mode.value = 'account'\n mfa.challengeId = ''\n mfa.code = ''\n totp.challengeId = ''\n totp.code = ''\n}\n\nasync function onSendSmsCode() {\n if (countdown.value > 0) return\n if (!smsModel.phone) {\n message.warning(t('login.phonePlaceholder'))\n return\n }\n if (captchaEnabled.value && !captchaCode.value) {\n message.warning(t('login.captchaRequired'))\n return\n }\n try {\n const r = await authApi.smsLoginSend({\n phone: smsModel.phone,\n ...(captchaEnabled.value\n ? { captchaId: captchaId.value, captchaCode: captchaCode.value }\n : {}),\n })\n message.success(t('login.smsSent'))\n startCountdown(r.resendSeconds)\n } catch (e) {\n message.error(translateError(e))\n }\n await refreshCaptchaAfterUse()\n}\n\nasync function onSmsSubmit() {\n if (!smsModel.phone || !smsModel.code) {\n message.warning(t('login.smsRequired'))\n return\n }\n loading.value = true\n try {\n await finishLogin(await authApi.smsLogin({ phone: smsModel.phone, code: smsModel.code }))\n } catch (e) {\n message.error(translateError(e))\n shakeForm()\n } finally {\n loading.value = false\n }\n}\n</script>\n\n<template>\n <div ref=\"formEl\" class=\"login-form\">\n <div v-if=\"showLogo\" class=\"lf-brand\">\n <!-- 站点 logo 的取值顺序(配置 → brand 选项 → 内置矢量标)统一在 SmartLogo 里 -->\n <SmartLogo :size=\"34\" />\n <span class=\"lf-word\">{{ site.title }}</span>\n </div>\n\n <n-alert\n v-if=\"pendingLink\"\n type=\"info\"\n :bordered=\"false\"\n class=\"lf-pending-alert\"\n :title=\"t('oauth.pendingLinkTitle', { name: pendingProviderLabel })\"\n >\n {{ t('oauth.pendingLinkHint', { name: pendingProviderLabel }) }}\n </n-alert>\n\n <!-- 登录方式切换:旧内容先淡出上移,新内容的逐项浮现由外壳(LoginDesk)的入场动画负责 -->\n <Transition name=\"lf-swap\" mode=\"out-in\">\n <div :key=\"mode\" class=\"lf-body\">\n <!-- 短信二次验证:密码已过,凭挑战 + 短信码完成登录 -->\n <template v-if=\"mode === 'mfa'\">\n <h2 v-if=\"showTitle || showGreeting\" class=\"lf-title\">\n {{ t('login.mfaTitle', { phone: mfa.phoneMask }) }}\n </h2>\n <p class=\"lf-hint-line\">{{ t('login.mfaSub') }}</p>\n <n-form :show-feedback=\"false\" @keyup.enter=\"onSubmit\">\n <n-form-item :label=\"t('login.smsCode')\" path=\"code\">\n <n-input\n v-model:value=\"mfa.code\"\n :placeholder=\"t('login.smsCodePlaceholder')\"\n size=\"large\"\n :maxlength=\"6\"\n >\n <template #prefix><Icon icon=\"ph:chat-circle-text\" /></template>\n </n-input>\n </n-form-item>\n <div class=\"row lf-between\">\n <n-button text type=\"primary\" @click=\"backToAccount\">\n {{ t('login.backToPassword') }}\n </n-button>\n <n-button\n text\n :type=\"countdown > 0 ? 'default' : 'primary'\"\n :disabled=\"countdown > 0\"\n @click=\"onMfaResend\"\n >\n {{ countdown > 0 ? t('login.resendAfter', { s: countdown }) : t('login.sendCode') }}\n </n-button>\n </div>\n <n-button\n class=\"hero-btn cta\"\n type=\"primary\"\n size=\"large\"\n block\n attr-type=\"button\"\n :loading=\"loading\"\n @click=\"onSubmit\"\n >\n {{ loading ? t('common.loading') : t('login.submit') }}\n </n-button>\n </n-form>\n </template>\n\n <!-- TOTP 二次验证:密码已过,凭 Authenticator 动态口令完成登录 -->\n <template v-else-if=\"mode === 'totp'\">\n <h2 v-if=\"showTitle || showGreeting\" class=\"lf-title\">{{ t('login.totpTitle') }}</h2>\n <p class=\"lf-hint-line\">{{ t('login.totpSub') }}</p>\n <n-form :show-feedback=\"false\" @keyup.enter=\"onSubmit\">\n <n-form-item :label=\"t('login.totpCode')\" path=\"code\">\n <n-input\n v-model:value=\"totp.code\"\n :placeholder=\"t('login.totpPlaceholder')\"\n size=\"large\"\n :maxlength=\"6\"\n >\n <template #prefix><Icon icon=\"ph:shield-check\" /></template>\n </n-input>\n </n-form-item>\n <div class=\"row lf-between\">\n <n-button text type=\"primary\" @click=\"backToAccount\">\n {{ t('login.backToPassword') }}\n </n-button>\n <n-button text type=\"primary\" @click=\"goRecovery\">\n {{ t('login.useRecovery') }}\n </n-button>\n </div>\n <n-button\n class=\"hero-btn cta\"\n type=\"primary\"\n size=\"large\"\n block\n attr-type=\"button\"\n :loading=\"loading\"\n @click=\"onSubmit\"\n >\n {{ loading ? t('common.loading') : t('login.submit') }}\n </n-button>\n </n-form>\n </template>\n\n <template v-else>\n <!-- 单栏:卡内标题「登录」;双栏:品牌在左栏,这里是问候语 -->\n <h2 v-if=\"showTitle\" class=\"lf-title\">{{ t('login.title') }}</h2>\n <div v-else-if=\"showGreeting\" class=\"lf-greet\">\n <h1>{{ t('login.welcome') }}</h1>\n <p>{{ t('login.welcomeSub') }}</p>\n </div>\n <n-form\n :model=\"mode === 'account' ? model : smsModel\"\n :show-feedback=\"false\"\n @keyup.enter=\"onSubmit\"\n >\n <template v-if=\"mode === 'account'\">\n <n-form-item :label=\"t('login.account')\" path=\"account\">\n <n-input\n v-model:value=\"model.account\"\n :placeholder=\"t('login.accountPlaceholder')\"\n size=\"large\"\n >\n <template #prefix><Icon icon=\"ph:user\" /></template>\n </n-input>\n </n-form-item>\n <n-form-item :label=\"t('login.password')\" path=\"password\">\n <n-input\n v-model:value=\"model.password\"\n type=\"password\"\n show-password-on=\"click\"\n :placeholder=\"t('login.passwordPlaceholder')\"\n size=\"large\"\n >\n <template #prefix><Icon icon=\"ph:lock\" /></template>\n </n-input>\n </n-form-item>\n </template>\n <template v-else>\n <n-form-item :label=\"t('login.phone')\" path=\"phone\">\n <n-input\n v-model:value=\"smsModel.phone\"\n :placeholder=\"t('login.phonePlaceholder')\"\n size=\"large\"\n :maxlength=\"20\"\n >\n <template #prefix><Icon icon=\"ph:device-mobile\" /></template>\n </n-input>\n </n-form-item>\n </template>\n <n-form-item v-if=\"captchaEnabled\" :label=\"t('login.captcha')\" path=\"captcha\">\n <div class=\"lf-captcha\">\n <n-input\n v-model:value=\"captchaCode\"\n :placeholder=\"captchaHint\"\n size=\"large\"\n @keyup.enter=\"onSubmit\"\n >\n <template #prefix><Icon icon=\"ph:shield-check\" /></template>\n </n-input>\n <!-- SVG 来自本站后端;点击重取一张(一次性票据);取图失败时图形位为空,点击重试 -->\n <button\n type=\"button\"\n class=\"lf-captcha-img\"\n :class=\"{ empty: !captchaSvg }\"\n :title=\"t('login.captchaPlaceholder')\"\n :aria-label=\"t('login.captchaPlaceholder')\"\n @click=\"loadCaptcha\"\n v-html=\"captchaSvg\"\n />\n </div>\n </n-form-item>\n <n-form-item v-if=\"mode === 'sms'\" :label=\"t('login.smsCode')\" path=\"code\">\n <div class=\"lf-captcha\">\n <n-input\n v-model:value=\"smsModel.code\"\n :placeholder=\"t('login.smsCodePlaceholder')\"\n size=\"large\"\n :maxlength=\"6\"\n >\n <template #prefix><Icon icon=\"ph:chat-circle-text\" /></template>\n </n-input>\n <n-button\n class=\"lf-send-btn\"\n secondary\n size=\"large\"\n attr-type=\"button\"\n :disabled=\"countdown > 0\"\n @click=\"onSendSmsCode\"\n >\n {{\n countdown > 0 ? t('login.resendAfter', { s: countdown }) : t('login.sendCode')\n }}\n </n-button>\n </div>\n </n-form-item>\n <div class=\"row lf-between\">\n <n-checkbox v-if=\"mode === 'account'\" v-model:checked=\"model.remember\">\n {{ t('login.remember') }}\n </n-checkbox>\n <span v-else />\n <n-button\n v-if=\"site.smsLoginEnabled\"\n text\n type=\"primary\"\n @click=\"mode = mode === 'account' ? 'sms' : 'account'\"\n >\n {{ mode === 'account' ? t('login.smsLogin') : t('login.accountLogin') }}\n </n-button>\n </div>\n <n-button\n class=\"hero-btn cta\"\n type=\"primary\"\n size=\"large\"\n block\n attr-type=\"button\"\n :loading=\"loading\"\n @click=\"onSubmit\"\n >\n {{ loading ? t('common.loading') : t('login.submit') }}\n </n-button>\n </n-form>\n\n <!-- 第三方登录:圆形品牌标;<a href>。PREVIEW_ALL_SSO_BRANDS 时展示全部品牌图。 -->\n <template v-if=\"ssoDisplayList.length\">\n <div class=\"lf-divider\">\n <span>{{ t('login.otherMethods') }}</span>\n </div>\n <div class=\"lf-sso\">\n <a\n v-for=\"p in ssoSplit.visible\"\n :key=\"p.code\"\n class=\"lf-sso-btn\"\n :href=\"ssoHref(p.code)\"\n :title=\"p.displayName\"\n :aria-label=\"p.displayName\"\n >\n <BrandIcon :code=\"p.code\" :icon=\"p.icon\" :size=\"28\" />\n </a>\n <n-dropdown\n v-if=\"ssoSplit.overflow.length\"\n trigger=\"click\"\n :options=\"ssoOverflowOptions\"\n @select=\"onSsoOverflow\"\n >\n <a\n class=\"lf-sso-btn lf-sso-more\"\n href=\"javascript:void(0)\"\n role=\"button\"\n :title=\"t('login.moreMethods')\"\n :aria-label=\"t('login.moreMethods')\"\n @click.prevent\n >\n <Icon icon=\"ph:dots-three\" :width=\"18\" />\n </a>\n </n-dropdown>\n </div>\n </template>\n </template>\n </div>\n </Transition>\n\n <!-- 页脚:版权(可选链接)+ 构建期版本号。双栏时外壳在卡外自绘页脚,传 :show-footer=\"false\" 关掉。 -->\n <footer v-if=\"showFooter\" class=\"lf-foot\">\n <span>\n © {{ year }}\n <a v-if=\"site.copyrightUrl\" :href=\"site.copyrightUrl\" target=\"_blank\" rel=\"noopener\">\n {{ site.copyright || site.title }}\n </a>\n <template v-else>{{ site.copyright || site.title }}</template>\n </span>\n <span v-if=\"appVersion\" class=\"lf-ver\">v{{ appVersion }}</span>\n </footer>\n\n <!-- 强制 MFA 未绑定(40020):遮罩 Modal,账密表单仍在底下;默认登录页不常驻绑定链接 -->\n <n-modal\n v-model:show=\"bindRequiredShow\"\n preset=\"dialog\"\n type=\"warning\"\n :title=\"t('login.totpBindTitle')\"\n :content=\"t('login.totpBindSub')\"\n :positive-text=\"t('login.setupAuthenticator')\"\n :negative-text=\"t('common.cancel')\"\n @positive-click=\"goBindAuthenticator\"\n @negative-click=\"closeBindRequired\"\n />\n <!-- pending-link:登录后显式确认绑定,防静默抢绑 -->\n <n-modal\n v-model:show=\"pendingConfirmShow\"\n preset=\"dialog\"\n type=\"info\"\n :title=\"t('oauth.pendingLinkConfirmTitle')\"\n :content=\"\n t('oauth.pendingLinkConfirmContent', {\n name: pendingProviderLabel,\n identity: pendingConfirmIdentity,\n account: user.userInfo?.account ?? '',\n })\n \"\n :positive-text=\"t('oauth.pendingLinkConfirmOk')\"\n :negative-text=\"t('oauth.pendingLinkConfirmSkip')\"\n :loading=\"pendingClaimBusy\"\n :closable=\"false\"\n :mask-closable=\"false\"\n @positive-click=\"confirmPendingBind\"\n @negative-click=\"skipPendingBind\"\n />\n </div>\n</template>\n\n<style scoped>\n.login-form {\n width: 100%;\n}\n/* 登录方式切换:旧内容淡出并轻微上移变虚 */\n.lf-swap-leave-active {\n transition:\n opacity 0.16s ease-in,\n transform 0.16s ease-in,\n filter 0.16s ease-in;\n}\n.lf-swap-leave-to {\n opacity: 0;\n transform: translate3d(0, -6px, 0);\n filter: blur(4px);\n}\n.lf-pending-alert {\n margin-bottom: 16px;\n border-radius: var(--radius-md);\n text-align: left;\n}\n/* 单栏卡内顶部:站点 logo + 标题。logo 取值顺序见 SmartLogo */\n.lf-brand {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 18px;\n}\n.lf-word {\n font-size: 22px;\n font-weight: 700;\n letter-spacing: -0.01em;\n color: var(--text-1);\n}\n.lf-title {\n font-size: 20px;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.3;\n margin: 0 0 16px;\n color: var(--text-1);\n}\n/* 双栏卡内问候语 */\n.lf-greet {\n text-align: center;\n margin: 2px 0 20px;\n}\n.lf-greet h1 {\n font-size: 26px;\n font-weight: 600;\n letter-spacing: -0.035em;\n margin: 0 0 4px;\n color: var(--text-1);\n}\n.lf-greet p {\n margin: 0;\n font-size: 13.5px;\n color: var(--text-3);\n}\n/* 二次验证副标题:标题下的弱化说明行 */\n.lf-hint-line {\n margin: -8px 0 18px;\n font-size: 13px;\n color: var(--text-3);\n}\n/* 表单项之间的间距(表单关了反馈行,没有校验规则要显示) */\n.login-form :deep(.n-form-item) {\n margin-bottom: 14px;\n}\n.row {\n margin: 2px 0 14px;\n min-height: 32px;\n}\n.lf-between {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.lf-between > :deep(.n-button) {\n height: 32px;\n padding: 0 2px;\n}\n/* 验证码:输入框 + 可点击刷新的 SVG 图形(等高对齐) */\n.lf-captcha {\n display: flex;\n gap: 10px;\n width: 100%;\n align-items: stretch;\n}\n.lf-captcha > :deep(.n-input) {\n flex: 1;\n min-width: 0;\n}\n.lf-captcha-img {\n flex: none;\n height: 42px;\n min-width: 100px;\n padding: 0;\n border: 1px solid var(--hairline);\n border-radius: var(--radius-md);\n background: #fff;\n cursor: pointer;\n overflow: hidden;\n display: grid;\n place-items: center;\n line-height: 0;\n}\n/* 取图失败时图形位为空:换成输入框同款底色,否则空白框像 bug;点击重试 */\n.lf-captcha-img.empty {\n background: var(--fill);\n}\n.lf-captcha-img:focus-visible {\n outline: 2px solid var(--signal);\n outline-offset: 2px;\n}\n.lf-captcha-img :deep(svg) {\n display: block;\n height: 40px;\n width: auto;\n}\n/* 发码按钮:与验证码图形同尺寸位 */\n.lf-send-btn {\n flex: none;\n min-width: 104px;\n}\n/* 第三方登录:分隔线 + 圆形品牌标 */\n.lf-divider {\n display: flex;\n align-items: center;\n gap: 14px;\n margin: 22px 0 14px;\n}\n.lf-divider::before,\n.lf-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--hairline);\n}\n.lf-divider span {\n font-size: 13px;\n color: var(--text-3);\n}\n.lf-sso {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: 12px;\n}\n/* 第三方登录:只露品牌标本身,不加灰底 / 描边 / 圆形裁切(会吃掉品牌标的轮廓);\n 40px 是点击热区,hover 才浮出一层极淡的圆角底,macOS 工具栏按钮的做法 */\n.lf-sso-btn {\n width: 40px;\n height: 40px;\n padding: 0;\n display: inline-grid;\n place-items: center;\n flex: none;\n border: none;\n border-radius: var(--radius-lg);\n background: transparent;\n color: var(--text-1);\n text-decoration: none;\n cursor: pointer;\n transition: background var(--transition-base);\n}\n.lf-sso-btn :deep(.oauth-brand-badge) {\n width: 28px;\n height: 28px;\n}\n.lf-sso-btn:hover {\n background: var(--hover);\n color: var(--text-1);\n text-decoration: none;\n}\n.lf-sso-btn:active {\n background: var(--pressed);\n}\n.lf-sso-btn:focus-visible {\n outline: 2px solid var(--signal);\n outline-offset: 2px;\n}\n/* 页脚:版权 + 版本号,弱化色 */\n.lf-foot {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n margin-top: 20px;\n font-size: 13px;\n color: var(--text-3);\n}\n.lf-foot a {\n color: inherit;\n text-decoration: none;\n}\n.lf-foot a:hover {\n color: var(--signal);\n}\n.lf-ver {\n opacity: 0.75;\n}\n@media (max-width: 480px) {\n .lf-captcha-img {\n min-width: 92px;\n }\n}\n</style>\n"],"names":["REDUCED_MOTION","emit","__emit","router","useRouter","route","useRoute","message","useMessage","t","useI18n","user","useUserStore","auth","useAuthStore","site","appVersion","loadSite","useSite","year","pendingLink","computed","pendingProvider","pendingDisplayName","pendingConfirmShow","ref","pendingClaimBusy","pendingClaimToken","model","reactive","runtime","loading","mode","smsModel","mfa","totp","bindRequiredShow","bindAccount","countdown","timer","startCountdown","s","onBeforeUnmount","captchaEnabled","captchaId","captchaSvg","captchaCode","captchaType","captchaHint","loadCaptcha","c","authApi","refreshCaptchaAfterUse","onMounted","loadSsoProviders","q","ch","ssoProviders","pendingProviderLabel","code","hit","p","ssoDisplayList","ssoSplit","splitLoginProviders","ssoOverflowOptions","h","BrandIcon","externalAuthApi","claimWeComAutoLogin","ssoHref","onSsoOverflow","key","enterHome","formEl","shakeForm","shake","leaveThenEnter","_a","resolve","finishLogin","res","resetRouter","link","pendingConfirmIdentity","confirmPendingBind","e","translateError","skipPendingBind","onSubmit","onMfaSubmit","onTotpSubmit","onSmsSubmit","ApiError","closeBindRequired","goBindAuthenticator","account","goRecovery","onMfaResend","r","backToAccount","onSendSmsCode","_createElementBlock","__props","_openBlock","_hoisted_1","_createVNode","SmartLogo","_createElementVNode","_hoisted_2","_toDisplayString","_unref","_createBlock","NAlert","_createTextVNode","_Transition","_Fragment","_hoisted_3","_hoisted_4","NForm","NFormItem","NInput","_cache","$event","Icon","_hoisted_5","NButton","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_normalizeClass","_hoisted_13","_hoisted_14","NCheckbox","_hoisted_15","_hoisted_16","_hoisted_17","_renderList","NDropdown","_hoisted_20","_hoisted_21","_hoisted_22","NModal"],"mappings":"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"}
@@ -2,8 +2,8 @@ import o from "./LoginDesk2.vue.js";
2
2
  /* empty css */
3
3
  /* empty css */
4
4
  import t from "../../../_virtual/_plugin-vue_export-helper.js";
5
- const e = /* @__PURE__ */ t(o, [["__scopeId", "data-v-2da0e5ac"]]);
5
+ const i = /* @__PURE__ */ t(o, [["__scopeId", "data-v-e8bd5462"]]);
6
6
  export {
7
- e as default
7
+ i as default
8
8
  };
9
9
  //# sourceMappingURL=LoginDesk.vue.js.map
@@ -1,12 +1,12 @@
1
- import { defineComponent as h, ref as w, onBeforeUnmount as b, openBlock as v, createElementBlock as g, normalizeClass as y, createElementVNode as o, renderSlot as l, createCommentVNode as $, createVNode as d, unref as e, withCtx as u, createTextVNode as x, toDisplayString as N, normalizeStyle as B } from "vue";
2
- import { NTooltip as C, NButton as D, NCard as P } from "naive-ui";
3
- import { Icon as S } from "@iconify/vue";
4
- import { useI18n as F } from "vue-i18n";
5
- import { useAppStore as A } from "../../../stores/app.js";
6
- const V = {
1
+ import { defineComponent as $, ref as w, onBeforeUnmount as b, openBlock as k, createElementBlock as h, normalizeClass as x, createElementVNode as o, renderSlot as l, createCommentVNode as B, createVNode as p, unref as t, withCtx as c, createTextVNode as C, toDisplayString as F, normalizeStyle as N } from "vue";
2
+ import { NTooltip as S, NButton as D, NCard as A } from "naive-ui";
3
+ import { Icon as V } from "@iconify/vue";
4
+ import { useI18n as q } from "vue-i18n";
5
+ import { useAppStore as T } from "../../../stores/app.js";
6
+ const z = {
7
7
  key: 0,
8
8
  class: "login-brand"
9
- }, T = { class: "login-brand-inner" }, q = { class: "login-stage" }, z = { class: "login-corner" }, U = /* @__PURE__ */ h({
9
+ }, E = { class: "login-brand-inner" }, I = { class: "login-stage" }, H = { class: "login-corner" }, X = /* @__PURE__ */ $({
10
10
  __name: "LoginDesk",
11
11
  props: {
12
12
  split: { type: Boolean, default: !1 },
@@ -14,24 +14,36 @@ const V = {
14
14
  contentStyle: { default: "padding: 26px 26px 22px" },
15
15
  leaving: { type: Boolean, default: !1 }
16
16
  },
17
- setup(i) {
18
- const r = A(), { t: a } = F(), p = w(null);
19
- let c = 0;
20
- function m(t, n) {
21
- cancelAnimationFrame(c), c = requestAnimationFrame(() => {
22
- var s, f;
23
- (s = p.value) == null || s.style.setProperty("--px", t.toFixed(3)), (f = p.value) == null || f.style.setProperty("--py", n.toFixed(3));
17
+ setup(r) {
18
+ const s = T(), { t: a } = q(), m = w(null);
19
+ let u = 0;
20
+ function y(e, n, d, v) {
21
+ cancelAnimationFrame(u), u = requestAnimationFrame(() => {
22
+ var g;
23
+ const i = m.value;
24
+ if (!i) return;
25
+ if (i.style.setProperty("--px", e.toFixed(3)), i.style.setProperty("--py", n.toFixed(3)), d === void 0 || v === void 0) {
26
+ i.style.setProperty("--lit", "0");
27
+ return;
28
+ }
29
+ const f = (g = i.querySelector(".desk-card")) == null ? void 0 : g.getBoundingClientRect();
30
+ f && (i.style.setProperty("--mx", `${(d - f.left).toFixed(1)}px`), i.style.setProperty("--my", `${(v - f.top).toFixed(1)}px`)), i.style.setProperty("--lit", "1");
24
31
  });
25
32
  }
26
- function k(t) {
27
- t.pointerType === "mouse" && m(t.clientX / window.innerWidth * 2 - 1, t.clientY / window.innerHeight * 2 - 1);
33
+ function P(e) {
34
+ e.pointerType === "mouse" && y(
35
+ e.clientX / window.innerWidth * 2 - 1,
36
+ e.clientY / window.innerHeight * 2 - 1,
37
+ e.clientX,
38
+ e.clientY
39
+ );
28
40
  }
29
- return b(() => cancelAnimationFrame(c)), (t, n) => (v(), g("div", {
41
+ return b(() => cancelAnimationFrame(u)), (e, n) => (k(), h("div", {
30
42
  ref_key: "root",
31
- ref: p,
32
- class: y(["login desk-bg", { split: i.split, leaving: i.leaving }]),
33
- onPointermove: k,
34
- onPointerleave: n[1] || (n[1] = (s) => m(0, 0))
43
+ ref: m,
44
+ class: x(["login desk-bg", { split: r.split, leaving: r.leaving }]),
45
+ onPointermove: P,
46
+ onPointerleave: n[1] || (n[1] = (d) => y(0, 0))
35
47
  }, [
36
48
  n[2] || (n[2] = o("div", {
37
49
  class: "desk",
@@ -42,59 +54,59 @@ const V = {
42
54
  o("i"),
43
55
  o("i")
44
56
  ], -1)),
45
- i.split ? (v(), g("div", V, [
46
- o("div", T, [
47
- l(t.$slots, "brand", {}, void 0, !0)
57
+ r.split ? (k(), h("div", z, [
58
+ o("div", E, [
59
+ l(e.$slots, "brand", {}, void 0, !0)
48
60
  ])
49
- ])) : $("", !0),
50
- o("div", q, [
51
- o("div", z, [
52
- l(t.$slots, "corner", {}, void 0, !0),
53
- d(e(C), null, {
54
- trigger: u(() => [
55
- d(e(D), {
61
+ ])) : B("", !0),
62
+ o("div", I, [
63
+ o("div", H, [
64
+ l(e.$slots, "corner", {}, void 0, !0),
65
+ p(t(S), null, {
66
+ trigger: c(() => [
67
+ p(t(D), {
56
68
  quaternary: "",
57
69
  circle: "",
58
- "aria-label": e(r).isDark ? e(a)("app.light") : e(a)("app.dark"),
59
- onClick: n[0] || (n[0] = (s) => e(r).toggleDark())
70
+ "aria-label": t(s).isDark ? t(a)("app.light") : t(a)("app.dark"),
71
+ onClick: n[0] || (n[0] = (d) => t(s).toggleDark())
60
72
  }, {
61
- icon: u(() => [
62
- d(e(S), {
63
- icon: e(r).isDark ? "ph:moon" : "ph:sun",
73
+ icon: c(() => [
74
+ p(t(V), {
75
+ icon: t(s).isDark ? "ph:moon" : "ph:sun",
64
76
  width: 18
65
77
  }, null, 8, ["icon"])
66
78
  ]),
67
79
  _: 1
68
80
  }, 8, ["aria-label"])
69
81
  ]),
70
- default: u(() => [
71
- x(" " + N(e(r).isDark ? e(a)("app.light") : e(a)("app.dark")), 1)
82
+ default: c(() => [
83
+ C(" " + F(t(s).isDark ? t(a)("app.light") : t(a)("app.dark")), 1)
72
84
  ]),
73
85
  _: 1
74
86
  })
75
87
  ]),
76
88
  o("div", {
77
- class: y(["login-card", { "on-desk": i.split }]),
78
- style: B(i.split ? void 0 : { width: `${i.width}px` })
89
+ class: x(["login-card", { "on-desk": r.split }]),
90
+ style: N(r.split ? void 0 : { width: `${r.width}px` })
79
91
  }, [
80
- l(t.$slots, "above", {}, void 0, !0),
81
- d(e(P), {
92
+ l(e.$slots, "above", {}, void 0, !0),
93
+ p(t(A), {
82
94
  bordered: !0,
83
95
  class: "desk-card",
84
- "content-style": i.contentStyle
96
+ "content-style": r.contentStyle
85
97
  }, {
86
- default: u(() => [
87
- l(t.$slots, "default", {}, void 0, !0)
98
+ default: c(() => [
99
+ l(e.$slots, "default", {}, void 0, !0)
88
100
  ]),
89
101
  _: 3
90
102
  }, 8, ["content-style"]),
91
- l(t.$slots, "after", {}, void 0, !0)
103
+ l(e.$slots, "after", {}, void 0, !0)
92
104
  ], 6)
93
105
  ])
94
106
  ], 34));
95
107
  }
96
108
  });
97
109
  export {
98
- U as default
110
+ X as default
99
111
  };
100
112
  //# sourceMappingURL=LoginDesk2.vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoginDesk2.vue.js","sources":["../../../../src/views/login/components/LoginDesk.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 入口类全屏页(登录 / 二次验证 / 第三方回调 / 门户加载失败)共用的外壳:macOS 锁屏语言。\n// 整页一张慢慢飘的柔光色场(.desk,颜色全由强调色推导,无网格)+ 浮在上面的毛玻璃卡,右上角是工具栏。\n// 单栏 = 居中一张卡;双栏(split)= 左栏品牌区(brand 插槽)压在色场上,右栏是这张卡。\n// 只管视觉外壳:卡里放什么、表单怎么提交都在各页自己手里。\n//\n// 动效(全部不回弹):入场按「卡 → 标题 → 字段 → 按钮」依次浮现(先模糊后清晰);\n// 色场与卡跟着指针做反向视差;leaving 为真时整页淡出放大一点(像 macOS 解锁),给登录成功收尾。\nimport { onBeforeUnmount, ref } from 'vue'\nimport { NButton, NCard, NTooltip } from 'naive-ui'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { useAppStore } from '#/stores/app'\n\nwithDefaults(\n defineProps<{\n /** 双栏:左栏渲染 brand 插槽,卡片宽度收到 384 */\n split?: boolean\n /** 卡片宽度(px),窄屏被 max-width:100% 收住 */\n width?: number\n /** NCard 内容区内边距 */\n contentStyle?: string\n /** 退场:登录成功后由页面置真,整页淡出,等动画播完再跳转 */\n leaving?: boolean\n }>(),\n { split: false, width: 400, contentStyle: 'padding: 26px 26px 22px', leaving: false },\n)\n\nconst app = useAppStore()\nconst { t } = useI18n()\n\n// 指针视差:只认鼠标(触屏没有悬停位置),坐标归一到 -1..1 写成 CSS 变量,位移与过渡都在 CSS 里\nconst root = ref<HTMLElement | null>(null)\nlet raf = 0\nfunction setParallax(x: number, y: number) {\n cancelAnimationFrame(raf)\n raf = requestAnimationFrame(() => {\n root.value?.style.setProperty('--px', x.toFixed(3))\n root.value?.style.setProperty('--py', y.toFixed(3))\n })\n}\nfunction onPointerMove(e: PointerEvent) {\n if (e.pointerType !== 'mouse') return\n setParallax((e.clientX / window.innerWidth) * 2 - 1, (e.clientY / window.innerHeight) * 2 - 1)\n}\nonBeforeUnmount(() => cancelAnimationFrame(raf))\n</script>\n\n<template>\n <div\n ref=\"root\"\n class=\"login desk-bg\"\n :class=\"{ split, leaving }\"\n @pointermove=\"onPointerMove\"\n @pointerleave=\"setParallax(0, 0)\"\n >\n <div class=\"desk\" aria-hidden=\"true\">\n <i />\n <i />\n <i />\n <i />\n </div>\n\n <div v-if=\"split\" class=\"login-brand\">\n <div class=\"login-brand-inner\"><slot name=\"brand\" /></div>\n </div>\n\n <div class=\"login-stage\">\n <!-- 右上角工具栏:页面自己的按钮(布局切换、语言)在前,主题切换永远贴最右角 -->\n <div class=\"login-corner\">\n <slot name=\"corner\" />\n <n-tooltip>\n <template #trigger>\n <n-button\n quaternary\n circle\n :aria-label=\"app.isDark ? t('app.light') : t('app.dark')\"\n @click=\"app.toggleDark()\"\n >\n <template #icon>\n <Icon :icon=\"app.isDark ? 'ph:moon' : 'ph:sun'\" :width=\"18\" />\n </template>\n </n-button>\n </template>\n {{ app.isDark ? t('app.light') : t('app.dark') }}\n </n-tooltip>\n </div>\n\n <!-- margin-top 给右上角工具栏留位:窄屏内容比视口高时,顶部不会被工具栏盖住 -->\n <div\n class=\"login-card\"\n :class=\"{ 'on-desk': split }\"\n :style=\"split ? undefined : { width: `${width}px` }\"\n >\n <slot name=\"above\" />\n <n-card :bordered=\"true\" class=\"desk-card\" :content-style=\"contentStyle\">\n <slot />\n </n-card>\n <slot name=\"after\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* 登录页(单栏 / 双栏都算)是 macOS 桌面那种纯色场:不要网格线,不要透视地面 */\n.login {\n /* 减速到底、不过冲:入场与视差共用这条曲线 */\n --rise: cubic-bezier(0.22, 1, 0.36, 1);\n min-height: 100dvh;\n background: var(--desk-base);\n position: relative;\n overflow: hidden;\n}\n.login.split {\n display: grid;\n grid-template-columns: minmax(340px, 46%) 1fr;\n}\n.login-stage {\n min-height: 100dvh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n position: relative;\n overflow: hidden;\n}\n\n/* 色场:四团 blur(70px) 的柔光球慢速漂移,颜色由强调色推导(--desk-1..4);\n 整体再按指针反向平移一点(用独立的 translate 属性,不和漂移动画的 transform 打架) */\n.desk {\n position: absolute;\n inset: -20%;\n pointer-events: none;\n z-index: 0;\n translate: calc(var(--px, 0) * -22px) calc(var(--py, 0) * -16px);\n transition: translate 1.4s var(--rise);\n}\n.desk i {\n position: absolute;\n display: block;\n border-radius: 50%;\n filter: blur(70px);\n opacity: var(--desk-op);\n}\n.desk i:nth-child(1) {\n left: 2%;\n top: 4%;\n width: 46%;\n height: 62%;\n background: var(--desk-1);\n animation: drift1 26s ease-in-out infinite alternate;\n}\n.desk i:nth-child(2) {\n left: 26%;\n top: 34%;\n width: 44%;\n height: 56%;\n background: var(--desk-2);\n animation: drift2 32s ease-in-out infinite alternate;\n}\n.desk i:nth-child(3) {\n left: 54%;\n top: -6%;\n width: 40%;\n height: 58%;\n background: var(--desk-3);\n animation: drift3 29s ease-in-out infinite alternate;\n}\n.desk i:nth-child(4) {\n left: 62%;\n top: 46%;\n width: 46%;\n height: 62%;\n background: var(--desk-4);\n animation: drift1 35s ease-in-out infinite alternate-reverse;\n}\n@keyframes drift1 {\n from {\n transform: none;\n }\n to {\n transform: translate3d(6%, -5%, 0) scale(1.12);\n }\n}\n@keyframes drift2 {\n from {\n transform: none;\n }\n to {\n transform: translate3d(-7%, 4%, 0) scale(1.08);\n }\n}\n@keyframes drift3 {\n from {\n transform: none;\n }\n to {\n transform: translate3d(4%, 7%, 0) scale(1.15);\n }\n}\n\n.login-corner {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 3;\n display: flex;\n gap: 8px;\n animation: fadeIn 0.9s var(--rise) 0.7s both;\n}\n\n/* 双栏左栏:文字直接压在色场上(像 macOS 桌面上的字),不单独做一块彩色面板 */\n.login-brand {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 48px clamp(32px, 5vw, 76px);\n}\n.login-brand-inner {\n max-width: 440px;\n}\n\n.login-card {\n width: 400px;\n max-width: 100%;\n position: relative;\n z-index: 2;\n margin-top: 44px;\n}\n.login-card.on-desk {\n width: 384px;\n}\n\n/* 表单是一块浮在色场上的毛玻璃卡(macOS 的 vibrancy 材质),单栏双栏同一套 */\n.desk-card {\n border: 1px solid var(--glass-border) !important;\n border-radius: 22px !important;\n background: var(--glass-panel) !important;\n box-shadow: var(--glass-shadow) !important;\n backdrop-filter: blur(40px) saturate(180%);\n -webkit-backdrop-filter: blur(40px) saturate(180%);\n /* 反向视差:卡朝指针方向挪一点,色场反方向挪,拉出前后景深 */\n translate: calc(var(--px, 0) * 6px) calc(var(--py, 0) * 4px);\n transition:\n translate 1s var(--rise),\n background-color 0.3s,\n border-color 0.3s,\n box-shadow 0.3s;\n}\n.login-card :deep(.n-input) {\n border-radius: var(--radius-md);\n}\n/* 主按钮:强调色 → 第二色渐变,带一层强调色辉光;hover 只加深辉光并微提亮、不位移,按下轻轻收一下 */\n.login-card :deep(.n-button.cta) {\n background-image: linear-gradient(120deg, var(--signal), var(--login-accent-2));\n border-color: transparent;\n box-shadow: 0 10px 26px -10px var(--signal-glow);\n transition:\n box-shadow 0.22s var(--ease),\n transform 0.2s var(--ease),\n filter 0.2s var(--ease);\n}\n.login-card :deep(.n-button.cta:hover) {\n box-shadow: 0 16px 34px -10px var(--signal-glow);\n filter: brightness(1.04);\n}\n.login-card :deep(.n-button.cta:active:not(:disabled)) {\n transform: scale(0.985);\n filter: brightness(0.97);\n box-shadow: 0 6px 18px -10px var(--signal-glow);\n}\n\n/* ── 入场编排 ─────────────────────────────────────────────\n 动画必须挂在 .desk-card 自己身上,不能挂它的父级 .login-card:父级 opacity < 1 会让子元素的\n backdrop-filter 失去背景,动画期间卡是不模糊的,结束那一帧模糊才「啪」地打上去,看着就是生硬。\n 内容项(标题 / 字段 / 按钮 / 左栏文字)各自带延迟依次浮现,先模糊后清晰,不回弹。 */\n.desk-card {\n animation: cardIn 0.95s var(--rise) 0.08s both;\n}\n.login-card > :deep(:not(.desk-card)) {\n animation: fadeIn 0.9s var(--rise) 0.55s both;\n}\n\n/* 左栏:Logo → 标语 → 说明 → 亮点逐行 */\n.login-brand-inner :deep(.login-hero-panel > :not(.points)),\n.login-brand-inner :deep(.points li) {\n animation: riseIn 0.9s var(--rise) both;\n}\n.login-brand-inner :deep(.login-hero-panel > :nth-child(1)) {\n animation-delay: 0.1s;\n}\n.login-brand-inner :deep(.login-hero-panel > :nth-child(2)) {\n animation-delay: 0.18s;\n}\n.login-brand-inner :deep(.login-hero-panel > :nth-child(3)) {\n animation-delay: 0.26s;\n}\n.login-brand-inner :deep(.points li:nth-child(1)) {\n animation-delay: 0.36s;\n}\n.login-brand-inner :deep(.points li:nth-child(2)) {\n animation-delay: 0.43s;\n}\n.login-brand-inner :deep(.points li:nth-child(n + 3)) {\n animation-delay: 0.5s;\n}\n\n/* 卡内:LoginForm 的 .login-form > .lf-brand / .lf-body(标题、表单、第三方登录)和表单里的各行 */\n.login-card :deep(.login-form > .lf-brand),\n.login-card :deep(.login-form > .lf-pending-alert),\n.login-card :deep(.lf-body > :not(form)),\n.login-card :deep(.lf-body form > *) {\n animation: riseIn 0.8s var(--rise) both;\n}\n.login-card :deep(.login-form > .lf-brand),\n.login-card :deep(.login-form > .lf-pending-alert) {\n animation-delay: 0.18s;\n}\n.login-card :deep(.lf-body > :not(form)) {\n animation-delay: 0.22s;\n}\n.login-card :deep(.lf-body form > :nth-child(1)) {\n animation-delay: 0.28s;\n}\n.login-card :deep(.lf-body form > :nth-child(2)) {\n animation-delay: 0.34s;\n}\n.login-card :deep(.lf-body form > :nth-child(3)) {\n animation-delay: 0.4s;\n}\n.login-card :deep(.lf-body form > :nth-child(4)) {\n animation-delay: 0.46s;\n}\n.login-card :deep(.lf-body form > :nth-child(n + 5)) {\n animation-delay: 0.52s;\n}\n.login-card :deep(.lf-body > .lf-divider) {\n animation-delay: 0.56s;\n}\n.login-card :deep(.lf-body > .lf-sso) {\n animation-delay: 0.62s;\n}\n\n@keyframes cardIn {\n from {\n opacity: 0;\n transform: translate3d(0, 28px, 0) scale(0.965);\n }\n to {\n opacity: 1;\n transform: none;\n }\n}\n@keyframes riseIn {\n from {\n opacity: 0;\n transform: translate3d(0, 12px, 0);\n filter: blur(6px);\n }\n to {\n opacity: 1;\n transform: none;\n filter: none;\n }\n}\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n/* ── 退场:登录成功后整页淡出并微微放大(macOS 解锁的感觉),色场留着不动 ── */\n.login.leaving .desk-card {\n animation: cardOut 0.34s cubic-bezier(0.4, 0, 1, 1) both;\n}\n.login.leaving .login-brand-inner,\n.login.leaving .login-corner,\n.login.leaving .login-card > :deep(:not(.desk-card)) {\n animation: fadeOut 0.28s ease-in both;\n}\n@keyframes cardOut {\n to {\n opacity: 0;\n transform: translate3d(0, -8px, 0) scale(1.02);\n }\n}\n@keyframes fadeOut {\n to {\n opacity: 0;\n }\n}\n\n/* 减少动效:全局规则已把时长压到近零,这里再清掉延迟(否则 both 的起始态会把内容藏到延迟结束)并取消视差与漂移 */\n@media (prefers-reduced-motion: reduce) {\n .desk i {\n animation: none !important;\n }\n .desk,\n .desk-card {\n translate: none;\n }\n .login :deep(*),\n .login .desk-card,\n .login .login-corner {\n animation-delay: 0s !important;\n }\n}\n/* 窄屏收紧:卡片侧边留 16px,卡内边距随之压小(内联的 content-style 要靠 !important 盖住) */\n@media (max-width: 480px) {\n .login-stage {\n padding: 16px;\n }\n .desk-card :deep(.n-card__content) {\n padding: 22px 20px 18px !important;\n }\n}\n</style>\n\n<!-- View Transitions 的伪元素在文档根上,scoped 样式够不到,所以单独一块全局样式。\n 只在布局切换那一刻(.switching)给卡片命名:常驻的 view-transition-name 会让它多出独立图层。 -->\n<style>\n.login.switching .desk-card {\n view-transition-name: login-card;\n}\n::view-transition-group(login-card) {\n animation-duration: 0.62s;\n animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);\n}\n</style>\n"],"names":["app","useAppStore","t","useI18n","root","ref","raf","setParallax","x","y","_a","_b","onPointerMove","e","onBeforeUnmount","_createElementBlock","_normalizeClass","__props","_createElementVNode","_openBlock","_hoisted_1","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_hoisted_4","_createVNode","_unref","NTooltip","NButton","_cache","$event","Icon","_createTextVNode","_normalizeStyle","NCard"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,UAAMA,IAAMC,EAAA,GACN,EAAE,GAAAC,EAAA,IAAMC,EAAA,GAGRC,IAAOC,EAAwB,IAAI;AACzC,QAAIC,IAAM;AACV,aAASC,EAAYC,GAAWC,GAAW;AACzC,2BAAqBH,CAAG,GACxBA,IAAM,sBAAsB,MAAM;;AAChC,SAAAI,IAAAN,EAAK,UAAL,QAAAM,EAAY,MAAM,YAAY,QAAQF,EAAE,QAAQ,CAAC,KACjDG,IAAAP,EAAK,UAAL,QAAAO,EAAY,MAAM,YAAY,QAAQF,EAAE,QAAQ,CAAC;AAAA,MACnD,CAAC;AAAA,IACH;AACA,aAASG,EAAcC,GAAiB;AACtC,MAAIA,EAAE,gBAAgB,WACtBN,EAAaM,EAAE,UAAU,OAAO,aAAc,IAAI,GAAIA,EAAE,UAAU,OAAO,cAAe,IAAI,CAAC;AAAA,IAC/F;AACA,WAAAC,EAAgB,MAAM,qBAAqBR,CAAG,CAAC,mBAI7CS,EAoDM,OAAA;AAAA,eAnDA;AAAA,MAAJ,KAAIX;AAAA,MACJ,OAAKY,EAAA,CAAC,iBAAe,EAAA,OACXC,EAAA,gBAAOA,EAAA,QAAA,CAAO,CAAA;AAAA,MACvB,eAAaL;AAAA,MACb,uCAAcL,EAAW,GAAA,CAAA;AAAA,IAAA;sBAE1BW,EAKM,OAAA;AAAA,QALD,OAAM;AAAA,QAAO,eAAY;AAAA,MAAA;QAC5BA,EAAK,GAAA;AAAA,QACLA,EAAK,GAAA;AAAA,QACLA,EAAK,GAAA;AAAA,QACLA,EAAK,GAAA;AAAA,MAAA;MAGID,EAAA,SAAXE,EAAA,GAAAJ,EAEM,OAFNK,GAEM;AAAA,QADJF,EAA0D,OAA1DG,GAA0D;AAAA,UAA3BC,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,QAAA;;MAGtDL,EAiCM,OAjCNM,GAiCM;AAAA,QA/BJN,EAiBM,OAjBNO,GAiBM;AAAA,UAhBJH,EAAsBC,EAAA,QAAA,UAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UACtBG,EAcYC,EAAAC,CAAA,GAAA,MAAA;AAAA,YAbC,WACT,MASW;AAAA,cATXF,EASWC,EAAAE,CAAA,GAAA;AAAA,gBART,YAAA;AAAA,gBACA,QAAA;AAAA,gBACC,cAAYF,EAAA3B,CAAA,EAAI,SAAS2B,EAAAzB,CAAA,iBAAiByB,EAAAzB,CAAA,EAAC,UAAA;AAAA,gBAC3C,SAAK4B,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEJ,EAAA3B,CAAA,EAAI,WAAA;AAAA,cAAU;gBAEX,QACT,MAA8D;AAAA,kBAA9D0B,EAA8DC,EAAAK,CAAA,GAAA;AAAA,oBAAvD,MAAML,EAAA3B,CAAA,EAAI,SAAM,YAAA;AAAA,oBAA0B,OAAO;AAAA,kBAAA;;;;;uBAGnD,MACX;AAAA,cADWiC,EAAA,QACRN,EAAA3B,CAAA,EAAI,SAAS2B,EAAAzB,CAAA,iBAAiByB,EAAAzB,CAAA,EAAC,UAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;QAKtCgB,EAUM,OAAA;AAAA,UATJ,OAAKF,EAAA,CAAC,cAAY,EAAA,WACGC,EAAA,MAAA,CAAK,CAAA;AAAA,UACzB,OAAKiB,EAAEjB,EAAA,QAAQ,qBAAwBA,EAAA,KAAK,KAAA,CAAA;AAAA,QAAA;UAE7CK,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UACrBG,EAESC,EAAAQ,CAAA,GAAA;AAAA,YAFA,UAAU;AAAA,YAAM,OAAM;AAAA,YAAa,iBAAelB,EAAA;AAAA,UAAA;uBACzD,MAAQ;AAAA,cAARK,EAAQC,EAAA,QAAA,WAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YAAA;;;UAEVD,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,QAAA;;;;;"}
1
+ {"version":3,"file":"LoginDesk2.vue.js","sources":["../../../../src/views/login/components/LoginDesk.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 入口类全屏页(登录 / 二次验证 / 第三方回调 / 门户加载失败)共用的外壳:macOS 锁屏语言。\n// 整页一张慢慢飘的柔光色场(.desk,颜色全由强调色推导,无网格)+ 浮在上面的毛玻璃卡,右上角是工具栏。\n// 单栏 = 居中一张卡;双栏(split)= 左栏品牌区(brand 插槽)压在色场上,右栏是这张卡。\n// 只管视觉外壳:卡里放什么、表单怎么提交都在各页自己手里。\n//\n// 动效(全部不回弹):入场按「卡 → 标题 → 字段 → 按钮」依次浮现(先模糊后清晰);\n// 色场与卡跟着指针做反向视差;leaving 为真时整页淡出放大一点(像 macOS 解锁),给登录成功收尾。\nimport { onBeforeUnmount, ref } from 'vue'\nimport { NButton, NCard, NTooltip } from 'naive-ui'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { useAppStore } from '#/stores/app'\n\nwithDefaults(\n defineProps<{\n /** 双栏:左栏渲染 brand 插槽,卡片宽度收到 384 */\n split?: boolean\n /** 卡片宽度(px),窄屏被 max-width:100% 收住 */\n width?: number\n /** NCard 内容区内边距 */\n contentStyle?: string\n /** 退场:登录成功后由页面置真,整页淡出,等动画播完再跳转 */\n leaving?: boolean\n }>(),\n { split: false, width: 400, contentStyle: 'padding: 26px 26px 22px', leaving: false },\n)\n\nconst app = useAppStore()\nconst { t } = useI18n()\n\n// 指针视差:只认鼠标(触屏没有悬停位置),坐标归一到 -1..1 写成 CSS 变量,位移与过渡都在 CSS 里。\n// 另外把指针相对卡片左上角的位置(--mx / --my,px)和「指针在窗口内」(--lit)写出来,\n// 给卡片面上那束跟着指针走的柔光用:指针停在卡片上时视差位移趋近于零,光斑是这时唯一的反馈。\nconst root = ref<HTMLElement | null>(null)\nlet raf = 0\nfunction setPointer(x: number, y: number, clientX?: number, clientY?: number) {\n cancelAnimationFrame(raf)\n raf = requestAnimationFrame(() => {\n const el = root.value\n if (!el) return\n el.style.setProperty('--px', x.toFixed(3))\n el.style.setProperty('--py', y.toFixed(3))\n if (clientX === undefined || clientY === undefined) {\n // 指针离开:只熄灭柔光,保留最后位置,淡出时光斑不跳\n el.style.setProperty('--lit', '0')\n return\n }\n const card = el.querySelector('.desk-card')?.getBoundingClientRect()\n if (card) {\n el.style.setProperty('--mx', `${(clientX - card.left).toFixed(1)}px`)\n el.style.setProperty('--my', `${(clientY - card.top).toFixed(1)}px`)\n }\n el.style.setProperty('--lit', '1')\n })\n}\nfunction onPointerMove(e: PointerEvent) {\n if (e.pointerType !== 'mouse') return\n setPointer(\n (e.clientX / window.innerWidth) * 2 - 1,\n (e.clientY / window.innerHeight) * 2 - 1,\n e.clientX,\n e.clientY,\n )\n}\nonBeforeUnmount(() => cancelAnimationFrame(raf))\n</script>\n\n<template>\n <div\n ref=\"root\"\n class=\"login desk-bg\"\n :class=\"{ split, leaving }\"\n @pointermove=\"onPointerMove\"\n @pointerleave=\"setPointer(0, 0)\"\n >\n <div class=\"desk\" aria-hidden=\"true\">\n <i />\n <i />\n <i />\n <i />\n </div>\n\n <div v-if=\"split\" class=\"login-brand\">\n <div class=\"login-brand-inner\"><slot name=\"brand\" /></div>\n </div>\n\n <div class=\"login-stage\">\n <!-- 右上角工具栏:页面自己的按钮(布局切换、语言)在前,主题切换永远贴最右角 -->\n <div class=\"login-corner\">\n <slot name=\"corner\" />\n <n-tooltip>\n <template #trigger>\n <n-button\n quaternary\n circle\n :aria-label=\"app.isDark ? t('app.light') : t('app.dark')\"\n @click=\"app.toggleDark()\"\n >\n <template #icon>\n <Icon :icon=\"app.isDark ? 'ph:moon' : 'ph:sun'\" :width=\"18\" />\n </template>\n </n-button>\n </template>\n {{ app.isDark ? t('app.light') : t('app.dark') }}\n </n-tooltip>\n </div>\n\n <!-- margin-top 给右上角工具栏留位:窄屏内容比视口高时,顶部不会被工具栏盖住 -->\n <div\n class=\"login-card\"\n :class=\"{ 'on-desk': split }\"\n :style=\"split ? undefined : { width: `${width}px` }\"\n >\n <slot name=\"above\" />\n <n-card :bordered=\"true\" class=\"desk-card\" :content-style=\"contentStyle\">\n <slot />\n </n-card>\n <slot name=\"after\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* 登录页(单栏 / 双栏都算)是 macOS 桌面那种纯色场:不要网格线,不要透视地面 */\n.login {\n /* 减速到底、不过冲:入场与视差共用这条曲线 */\n --rise: cubic-bezier(0.22, 1, 0.36, 1);\n min-height: 100dvh;\n background: var(--desk-base);\n position: relative;\n overflow: hidden;\n}\n.login.split {\n display: grid;\n grid-template-columns: minmax(340px, 46%) 1fr;\n}\n.login-stage {\n min-height: 100dvh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n position: relative;\n overflow: hidden;\n}\n\n/* 色场:四团 blur(70px) 的柔光球慢速漂移,颜色由强调色推导(--desk-1..4);\n 整体再按指针反向平移一点(用独立的 translate 属性,不和漂移动画的 transform 打架) */\n.desk {\n position: absolute;\n inset: -20%;\n pointer-events: none;\n z-index: 0;\n translate: calc(var(--px, 0) * -44px) calc(var(--py, 0) * -30px);\n transition: translate 1.2s var(--rise);\n}\n.desk i {\n position: absolute;\n display: block;\n border-radius: 50%;\n filter: blur(70px);\n opacity: var(--desk-op);\n}\n.desk i:nth-child(1) {\n left: 2%;\n top: 4%;\n width: 46%;\n height: 62%;\n background: var(--desk-1);\n animation: drift1 26s ease-in-out infinite alternate;\n}\n.desk i:nth-child(2) {\n left: 26%;\n top: 34%;\n width: 44%;\n height: 56%;\n background: var(--desk-2);\n animation: drift2 32s ease-in-out infinite alternate;\n}\n.desk i:nth-child(3) {\n left: 54%;\n top: -6%;\n width: 40%;\n height: 58%;\n background: var(--desk-3);\n animation: drift3 29s ease-in-out infinite alternate;\n}\n.desk i:nth-child(4) {\n left: 62%;\n top: 46%;\n width: 46%;\n height: 62%;\n background: var(--desk-4);\n animation: drift1 35s ease-in-out infinite alternate-reverse;\n}\n@keyframes drift1 {\n from {\n transform: none;\n }\n to {\n transform: translate3d(6%, -5%, 0) scale(1.12);\n }\n}\n@keyframes drift2 {\n from {\n transform: none;\n }\n to {\n transform: translate3d(-7%, 4%, 0) scale(1.08);\n }\n}\n@keyframes drift3 {\n from {\n transform: none;\n }\n to {\n transform: translate3d(4%, 7%, 0) scale(1.15);\n }\n}\n\n.login-corner {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 3;\n display: flex;\n gap: 8px;\n animation: fadeIn 0.9s var(--rise) 0.7s both;\n}\n\n/* 双栏左栏:文字直接压在色场上(像 macOS 桌面上的字),不单独做一块彩色面板 */\n.login-brand {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 48px clamp(32px, 5vw, 76px);\n}\n.login-brand-inner {\n max-width: 440px;\n}\n\n.login-card {\n width: 400px;\n max-width: 100%;\n position: relative;\n z-index: 2;\n margin-top: 44px;\n}\n.login-card.on-desk {\n width: 384px;\n}\n\n/* 表单是一块浮在色场上的毛玻璃卡(macOS 的 vibrancy 材质),单栏双栏同一套 */\n.desk-card {\n border: 1px solid var(--glass-border) !important;\n border-radius: 22px !important;\n background: var(--glass-panel) !important;\n box-shadow: var(--glass-shadow) !important;\n backdrop-filter: blur(40px) saturate(180%);\n -webkit-backdrop-filter: blur(40px) saturate(180%);\n /* 反向视差:卡朝指针方向挪,色场反方向挪,拉出前后景深。\n 卡的跟随比色场快(0.45s 对 1.2s),指针一动卡就有回应;位移在窗口边缘最大,\n 指针停在卡片上时趋近于零,不会让要点的按钮在手下漂走 */\n translate: calc(var(--px, 0) * 16px) calc(var(--py, 0) * 11px);\n transition:\n translate 0.45s var(--rise),\n background-color 0.3s,\n border-color 0.3s,\n box-shadow 0.3s;\n isolation: isolate;\n}\n/* 跟着指针走的柔光:画在毛玻璃面上、内容的下面(isolation + z-index:-1),强调色推导,亮暗主题通用。\n 指针不在窗口内时淡出;--mx / --my 是指针相对卡片左上角的位置(见脚本) */\n.desk-card::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: -1;\n border-radius: inherit;\n pointer-events: none;\n background: radial-gradient(\n 340px circle at var(--mx, 50%) var(--my, 0%),\n color-mix(in srgb, var(--signal) var(--spot, 28%), transparent),\n transparent 72%\n );\n opacity: var(--lit, 0);\n transition: opacity 0.5s var(--rise);\n}\n/* 亮色的玻璃底本来就亮,同样的强调色占比要给高一点才看得出;暗色里 20% 已经够醒目 */\n:root[data-theme='dark'] .desk-card {\n --spot: 20%;\n}\n.login-card :deep(.n-input) {\n border-radius: var(--radius-md);\n}\n/* 主按钮:强调色 → 第二色渐变,带一层强调色辉光;hover 只加深辉光并微提亮、不位移,按下轻轻收一下 */\n.login-card :deep(.n-button.cta) {\n background-image: linear-gradient(120deg, var(--signal), var(--login-accent-2));\n border-color: transparent;\n box-shadow: 0 10px 26px -10px var(--signal-glow);\n transition:\n box-shadow 0.22s var(--ease),\n transform 0.2s var(--ease),\n filter 0.2s var(--ease);\n}\n.login-card :deep(.n-button.cta:hover) {\n box-shadow: 0 16px 34px -10px var(--signal-glow);\n filter: brightness(1.04);\n}\n.login-card :deep(.n-button.cta:active:not(:disabled)) {\n transform: scale(0.985);\n filter: brightness(0.97);\n box-shadow: 0 6px 18px -10px var(--signal-glow);\n}\n\n/* ── 入场编排 ─────────────────────────────────────────────\n 动画必须挂在 .desk-card 自己身上,不能挂它的父级 .login-card:父级 opacity < 1 会让子元素的\n backdrop-filter 失去背景,动画期间卡是不模糊的,结束那一帧模糊才「啪」地打上去,看着就是生硬。\n 内容项(标题 / 字段 / 按钮 / 左栏文字)各自带延迟依次浮现,先模糊后清晰,不回弹。 */\n.desk-card {\n animation: cardIn 0.95s var(--rise) 0.08s both;\n}\n.login-card > :deep(:not(.desk-card)) {\n animation: fadeIn 0.9s var(--rise) 0.55s both;\n}\n\n/* 左栏:Logo → 标语 → 说明 → 亮点逐行 */\n.login-brand-inner :deep(.login-hero-panel > :not(.points)),\n.login-brand-inner :deep(.points li) {\n animation: riseIn 0.9s var(--rise) both;\n}\n.login-brand-inner :deep(.login-hero-panel > :nth-child(1)) {\n animation-delay: 0.1s;\n}\n.login-brand-inner :deep(.login-hero-panel > :nth-child(2)) {\n animation-delay: 0.18s;\n}\n.login-brand-inner :deep(.login-hero-panel > :nth-child(3)) {\n animation-delay: 0.26s;\n}\n.login-brand-inner :deep(.points li:nth-child(1)) {\n animation-delay: 0.36s;\n}\n.login-brand-inner :deep(.points li:nth-child(2)) {\n animation-delay: 0.43s;\n}\n.login-brand-inner :deep(.points li:nth-child(n + 3)) {\n animation-delay: 0.5s;\n}\n\n/* 卡内:LoginForm 的 .login-form > .lf-brand / .lf-body(标题、表单、第三方登录)和表单里的各行 */\n.login-card :deep(.login-form > .lf-brand),\n.login-card :deep(.login-form > .lf-pending-alert),\n.login-card :deep(.lf-body > :not(form)),\n.login-card :deep(.lf-body form > *) {\n animation: riseIn 0.8s var(--rise) both;\n}\n.login-card :deep(.login-form > .lf-brand),\n.login-card :deep(.login-form > .lf-pending-alert) {\n animation-delay: 0.18s;\n}\n.login-card :deep(.lf-body > :not(form)) {\n animation-delay: 0.22s;\n}\n.login-card :deep(.lf-body form > :nth-child(1)) {\n animation-delay: 0.28s;\n}\n.login-card :deep(.lf-body form > :nth-child(2)) {\n animation-delay: 0.34s;\n}\n.login-card :deep(.lf-body form > :nth-child(3)) {\n animation-delay: 0.4s;\n}\n.login-card :deep(.lf-body form > :nth-child(4)) {\n animation-delay: 0.46s;\n}\n.login-card :deep(.lf-body form > :nth-child(n + 5)) {\n animation-delay: 0.52s;\n}\n.login-card :deep(.lf-body > .lf-divider) {\n animation-delay: 0.56s;\n}\n.login-card :deep(.lf-body > .lf-sso) {\n animation-delay: 0.62s;\n}\n\n@keyframes cardIn {\n from {\n opacity: 0;\n transform: translate3d(0, 28px, 0) scale(0.965);\n }\n to {\n opacity: 1;\n transform: none;\n }\n}\n@keyframes riseIn {\n from {\n opacity: 0;\n transform: translate3d(0, 12px, 0);\n filter: blur(6px);\n }\n to {\n opacity: 1;\n transform: none;\n filter: none;\n }\n}\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n/* ── 退场:登录成功后整页淡出并微微放大(macOS 解锁的感觉),色场留着不动 ── */\n.login.leaving .desk-card {\n animation: cardOut 0.34s cubic-bezier(0.4, 0, 1, 1) both;\n}\n.login.leaving .login-brand-inner,\n.login.leaving .login-corner,\n.login.leaving .login-card > :deep(:not(.desk-card)) {\n animation: fadeOut 0.28s ease-in both;\n}\n@keyframes cardOut {\n to {\n opacity: 0;\n transform: translate3d(0, -8px, 0) scale(1.02);\n }\n}\n@keyframes fadeOut {\n to {\n opacity: 0;\n }\n}\n\n/* 减少动效:全局规则已把时长压到近零,这里再清掉延迟(否则 both 的起始态会把内容藏到延迟结束)并取消色场漂移。\n 指针视差与柔光不跟随这个偏好:Windows 关了「动画效果」、远程桌面、省电模式下浏览器都会报 reduce,\n 而它们是这页唯一的鼠标反馈。全局规则会把它们的 transition 压成 0.01ms(位移变瞬跳),\n 所以把缓动时长还回去;卡片的颜色 / 边框 / 阴影三项仍按全局规则保持瞬时。 */\n@media (prefers-reduced-motion: reduce) {\n .desk i {\n animation: none !important;\n }\n .desk {\n transition-duration: 1.2s !important;\n }\n .desk-card {\n transition-duration: 0.45s, 0.01ms, 0.01ms, 0.01ms !important;\n }\n .desk-card::after {\n transition-duration: 0.5s !important;\n }\n .login :deep(*),\n .login .desk-card,\n .login .login-corner {\n animation-delay: 0s !important;\n }\n}\n/* 窄屏收紧:卡片侧边留 16px,卡内边距随之压小(内联的 content-style 要靠 !important 盖住) */\n@media (max-width: 480px) {\n .login-stage {\n padding: 16px;\n }\n .desk-card :deep(.n-card__content) {\n padding: 22px 20px 18px !important;\n }\n}\n</style>\n\n<!-- View Transitions 的伪元素在文档根上,scoped 样式够不到,所以单独一块全局样式。\n 只在布局切换那一刻(.switching)给卡片命名:常驻的 view-transition-name 会让它多出独立图层。 -->\n<style>\n.login.switching .desk-card {\n view-transition-name: login-card;\n}\n::view-transition-group(login-card) {\n animation-duration: 0.62s;\n animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);\n}\n</style>\n"],"names":["app","useAppStore","t","useI18n","root","ref","raf","setPointer","x","y","clientX","clientY","el","card","_a","onPointerMove","onBeforeUnmount","_createElementBlock","_normalizeClass","__props","_createElementVNode","_openBlock","_hoisted_1","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_hoisted_4","_createVNode","_unref","NTooltip","NButton","_cache","$event","Icon","_createTextVNode","_normalizeStyle","NCard"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,UAAMA,IAAMC,EAAA,GACN,EAAE,GAAAC,EAAA,IAAMC,EAAA,GAKRC,IAAOC,EAAwB,IAAI;AACzC,QAAIC,IAAM;AACV,aAASC,EAAWC,GAAWC,GAAWC,GAAkBC,GAAkB;AAC5E,2BAAqBL,CAAG,GACxBA,IAAM,sBAAsB,MAAM;;AAChC,cAAMM,IAAKR,EAAK;AAChB,YAAI,CAACQ,EAAI;AAGT,YAFAA,EAAG,MAAM,YAAY,QAAQJ,EAAE,QAAQ,CAAC,CAAC,GACzCI,EAAG,MAAM,YAAY,QAAQH,EAAE,QAAQ,CAAC,CAAC,GACrCC,MAAY,UAAaC,MAAY,QAAW;AAElD,UAAAC,EAAG,MAAM,YAAY,SAAS,GAAG;AACjC;AAAA,QACF;AACA,cAAMC,KAAOC,IAAAF,EAAG,cAAc,YAAY,MAA7B,gBAAAE,EAAgC;AAC7C,QAAID,MACFD,EAAG,MAAM,YAAY,QAAQ,IAAIF,IAAUG,EAAK,MAAM,QAAQ,CAAC,CAAC,IAAI,GACpED,EAAG,MAAM,YAAY,QAAQ,IAAID,IAAUE,EAAK,KAAK,QAAQ,CAAC,CAAC,IAAI,IAErED,EAAG,MAAM,YAAY,SAAS,GAAG;AAAA,MACnC,CAAC;AAAA,IACH;AACA,aAASG,EAAc,GAAiB;AACtC,MAAI,EAAE,gBAAgB,WACtBR;AAAA,QACG,EAAE,UAAU,OAAO,aAAc,IAAI;AAAA,QACrC,EAAE,UAAU,OAAO,cAAe,IAAI;AAAA,QACvC,EAAE;AAAA,QACF,EAAE;AAAA,MAAA;AAAA,IAEN;AACA,WAAAS,EAAgB,MAAM,qBAAqBV,CAAG,CAAC,mBAI7CW,EAoDM,OAAA;AAAA,eAnDA;AAAA,MAAJ,KAAIb;AAAA,MACJ,OAAKc,EAAA,CAAC,iBAAe,EAAA,OACXC,EAAA,gBAAOA,EAAA,QAAA,CAAO,CAAA;AAAA,MACvB,eAAaJ;AAAA,MACb,uCAAcR,EAAU,GAAA,CAAA;AAAA,IAAA;sBAEzBa,EAKM,OAAA;AAAA,QALD,OAAM;AAAA,QAAO,eAAY;AAAA,MAAA;QAC5BA,EAAK,GAAA;AAAA,QACLA,EAAK,GAAA;AAAA,QACLA,EAAK,GAAA;AAAA,QACLA,EAAK,GAAA;AAAA,MAAA;MAGID,EAAA,SAAXE,EAAA,GAAAJ,EAEM,OAFNK,GAEM;AAAA,QADJF,EAA0D,OAA1DG,GAA0D;AAAA,UAA3BC,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,QAAA;;MAGtDL,EAiCM,OAjCNM,GAiCM;AAAA,QA/BJN,EAiBM,OAjBNO,GAiBM;AAAA,UAhBJH,EAAsBC,EAAA,QAAA,UAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UACtBG,EAcYC,EAAAC,CAAA,GAAA,MAAA;AAAA,YAbC,WACT,MASW;AAAA,cATXF,EASWC,EAAAE,CAAA,GAAA;AAAA,gBART,YAAA;AAAA,gBACA,QAAA;AAAA,gBACC,cAAYF,EAAA7B,CAAA,EAAI,SAAS6B,EAAA3B,CAAA,iBAAiB2B,EAAA3B,CAAA,EAAC,UAAA;AAAA,gBAC3C,SAAK8B,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEJ,EAAA7B,CAAA,EAAI,WAAA;AAAA,cAAU;gBAEX,QACT,MAA8D;AAAA,kBAA9D4B,EAA8DC,EAAAK,CAAA,GAAA;AAAA,oBAAvD,MAAML,EAAA7B,CAAA,EAAI,SAAM,YAAA;AAAA,oBAA0B,OAAO;AAAA,kBAAA;;;;;uBAGnD,MACX;AAAA,cADWmC,EAAA,QACRN,EAAA7B,CAAA,EAAI,SAAS6B,EAAA3B,CAAA,iBAAiB2B,EAAA3B,CAAA,EAAC,UAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;QAKtCkB,EAUM,OAAA;AAAA,UATJ,OAAKF,EAAA,CAAC,cAAY,EAAA,WACGC,EAAA,MAAA,CAAK,CAAA;AAAA,UACzB,OAAKiB,EAAEjB,EAAA,QAAQ,qBAAwBA,EAAA,KAAK,KAAA,CAAA;AAAA,QAAA;UAE7CK,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,UACrBG,EAESC,EAAAQ,CAAA,GAAA;AAAA,YAFA,UAAU;AAAA,YAAM,OAAM;AAAA,YAAa,iBAAelB,EAAA;AAAA,UAAA;uBACzD,MAAQ;AAAA,cAARK,EAAQC,EAAA,QAAA,WAAA,CAAA,GAAA,QAAA,EAAA;AAAA,YAAA;;;UAEVD,EAAqBC,EAAA,QAAA,SAAA,CAAA,GAAA,QAAA,EAAA;AAAA,QAAA;;;;;"}
@@ -1,7 +1,7 @@
1
1
  import o from "./LoginHeroPanel2.vue.js";
2
2
  /* empty css */
3
- import r from "../../../_virtual/_plugin-vue_export-helper.js";
4
- const m = /* @__PURE__ */ r(o, [["__scopeId", "data-v-85dbd38a"]]);
3
+ import e from "../../../_virtual/_plugin-vue_export-helper.js";
4
+ const m = /* @__PURE__ */ e(o, [["__scopeId", "data-v-38ce8a2e"]]);
5
5
  export {
6
6
  m as default
7
7
  };
@@ -1 +1 @@
1
- {"version":3,"file":"LoginHeroPanel2.vue.js","sources":["../../../../src/views/login/components/LoginHeroPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 登录页双栏左栏的品牌区(也是配置中心登录预览的左半边):文字直接压在柔光色场上,\n// Logo 锁定 + 大标语(强调词用强调色渐变高亮)+ 一行说明 + 单行勾选亮点。\nimport { computed } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useSite } from '#/composables/useSite'\nimport SmartLogo from '#/components/SmartLogo.vue'\n\nconst props = withDefaults(\n defineProps<{\n headline: string\n highlight?: string\n subtitle?: string\n features?: string[]\n /** 以下两项供配置中心预览未保存的草稿;不传则取当前站点信息 */\n title?: string\n logo?: string\n }>(),\n {\n highlight: '',\n subtitle: '',\n features: () => [],\n title: undefined,\n logo: undefined,\n },\n)\n\nconst { site } = useSite()\n\n// 只做纯文本切片,不用 v-html;强调词只会成为一个普通 span。\nconst headlineParts = computed(() => {\n const index = props.highlight ? props.headline.indexOf(props.highlight) : -1\n if (index < 0) return [{ text: props.headline, accent: false }]\n return [\n { text: props.headline.slice(0, index), accent: false },\n { text: props.highlight, accent: true },\n { text: props.headline.slice(index + props.highlight.length), accent: false },\n ].filter(part => part.text)\n})\n</script>\n\n<template>\n <div class=\"login-hero-panel\">\n <div class=\"logo\">\n <SmartLogo :size=\"46\" :src=\"logo\" />\n <span>{{ title ?? site.title }}</span>\n </div>\n <h2 class=\"headline\">\n <span\n v-for=\"(part, index) in headlineParts\"\n :key=\"index\"\n :class=\"{ hl: part.accent }\"\n v-text=\"part.text\"\n />\n </h2>\n <p v-if=\"subtitle\" class=\"sub\">{{ subtitle }}</p>\n <ul v-if=\"features.length\" class=\"points\">\n <li v-for=\"feature in features\" :key=\"feature\">\n <Icon icon=\"ph:check\" :width=\"15\" />\n {{ feature }}\n </li>\n </ul>\n </div>\n</template>\n\n<style scoped>\n.login-hero-panel {\n min-width: 0;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 13px;\n margin-bottom: 30px;\n font-size: 22px;\n font-weight: 600;\n letter-spacing: -0.015em;\n color: var(--text-1);\n}\n.headline {\n margin: 0 0 14px;\n color: var(--text-1);\n font-size: clamp(30px, 2.5vw, 40px);\n font-weight: 600;\n letter-spacing: -0.04em;\n line-height: 1.18;\n}\n.headline .hl {\n background: linear-gradient(110deg, var(--signal), var(--login-accent-2));\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n/* 色场上的正文一律用 --text-1:--text-2 是配白底卡片的,直接压在饱和色场上对比度不够 */\n.sub {\n margin: 0 0 34px;\n color: var(--text-1);\n font-size: 17px;\n line-height: 1.6;\n}\n.points {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n/* 字重 500 + 16px:同样是 --text-1,靠字重和字号把亮点和上面那句说明分开,不靠深浅 */\n.points li {\n display: flex;\n align-items: center;\n gap: 12px;\n color: var(--text-1);\n font-size: 16px;\n font-weight: 500;\n}\n.points li > :deep(svg) {\n flex-shrink: 0;\n box-sizing: content-box;\n width: 15px;\n height: 15px;\n padding: 5px;\n border-radius: 50%;\n color: var(--signal);\n background: var(--sel-bg);\n box-shadow: inset 0 0 0 1px var(--sel-ring);\n}\n</style>\n"],"names":["props","__props","site","useSite","headlineParts","computed","index","part","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","SmartLogo","_toDisplayString","_unref","_hoisted_3","_Fragment","_renderList","_normalizeClass","_hoisted_5","_hoisted_6","feature","Icon","_createTextVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAQA,UAAMA,IAAQC,GAmBR,EAAE,MAAAC,EAAA,IAASC,EAAA,GAGXC,IAAgBC,EAAS,MAAM;AACnC,YAAMC,IAAQN,EAAM,YAAYA,EAAM,SAAS,QAAQA,EAAM,SAAS,IAAI;AAC1E,aAAIM,IAAQ,IAAU,CAAC,EAAE,MAAMN,EAAM,UAAU,QAAQ,IAAO,IACvD;AAAA,QACL,EAAE,MAAMA,EAAM,SAAS,MAAM,GAAGM,CAAK,GAAG,QAAQ,GAAA;AAAA,QAChD,EAAE,MAAMN,EAAM,WAAW,QAAQ,GAAA;AAAA,QACjC,EAAE,MAAMA,EAAM,SAAS,MAAMM,IAAQN,EAAM,UAAU,MAAM,GAAG,QAAQ,GAAA;AAAA,MAAM,EAC5E,OAAO,CAAAO,MAAQA,EAAK,IAAI;AAAA,IAC5B,CAAC;sBAICC,EAAA,GAAAC,EAoBM,OApBNC,GAoBM;AAAA,MAnBJC,EAGM,OAHNC,GAGM;AAAA,QAFJC,EAAoCC,GAAA;AAAA,UAAxB,MAAM;AAAA,UAAK,KAAKb,EAAA;AAAA,QAAA;QAC5BU,EAAsC,QAAA,MAAAI,EAA7Bd,EAAA,SAASe,EAAAd,CAAA,EAAK,KAAK,GAAA,CAAA;AAAA,MAAA;MAE9BS,EAOK,MAPLM,GAOK;AAAA,SANHT,EAAA,EAAA,GAAAC,EAKES,GAAA,MAAAC,EAJwBf,EAAA,OAAa,CAA7BG,GAAMD,YADhBG,EAKE,QAAA;AAAA,UAHC,KAAKH;AAAA,UACL,OAAKc,EAAA,EAAA,IAAQb,EAAK,QAAM;AAAA,uBACzBQ,EAAkBR,EAAL,IAAI;AAAA,QAAA;;MAGZN,EAAA,iBAATQ,EAAiD,KAAjDY,GAAiDN,EAAfd,EAAA,QAAQ,GAAA,CAAA;MAChCA,EAAA,SAAS,UAAnBO,KAAAC,EAKK,MALLa,GAKK;AAAA,gBAJHb,EAGKS,GAAA,MAAAC,EAHiBlB,EAAA,UAAQ,CAAnBsB,YAAXd,EAGK,MAAA,EAH4B,KAAKc,KAAO;AAAA,UAC3CV,EAAoCG,EAAAQ,CAAA,GAAA;AAAA,YAA9B,MAAK;AAAA,YAAY,OAAO;AAAA,UAAA;UAAMC,EAAA,QACjCF,CAAO,GAAA,CAAA;AAAA,QAAA;;;;;"}
1
+ {"version":3,"file":"LoginHeroPanel2.vue.js","sources":["../../../../src/views/login/components/LoginHeroPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 登录页双栏左栏的品牌区(也是配置中心登录预览的左半边):文字直接压在柔光色场上,\n// Logo 锁定 + 大标语(强调词用强调色渐变高亮)+ 一行说明 + 单行勾选亮点。\nimport { computed } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useSite } from '#/composables/useSite'\nimport SmartLogo from '#/components/SmartLogo.vue'\n\nconst props = withDefaults(\n defineProps<{\n headline: string\n highlight?: string\n subtitle?: string\n features?: string[]\n /** 以下两项供配置中心预览未保存的草稿;不传则取当前站点信息 */\n title?: string\n logo?: string\n }>(),\n {\n highlight: '',\n subtitle: '',\n features: () => [],\n title: undefined,\n logo: undefined,\n },\n)\n\nconst { site } = useSite()\n\n// 只做纯文本切片,不用 v-html;强调词只会成为一个普通 span。\nconst headlineParts = computed(() => {\n const index = props.highlight ? props.headline.indexOf(props.highlight) : -1\n if (index < 0) return [{ text: props.headline, accent: false }]\n return [\n { text: props.headline.slice(0, index), accent: false },\n { text: props.highlight, accent: true },\n { text: props.headline.slice(index + props.highlight.length), accent: false },\n ].filter(part => part.text)\n})\n</script>\n\n<template>\n <div class=\"login-hero-panel\">\n <div class=\"logo\">\n <SmartLogo :size=\"46\" :src=\"logo\" />\n <span>{{ title ?? site.title }}</span>\n </div>\n <h2 class=\"headline\">\n <span\n v-for=\"(part, index) in headlineParts\"\n :key=\"index\"\n :class=\"{ hl: part.accent }\"\n v-text=\"part.text\"\n />\n </h2>\n <p v-if=\"subtitle\" class=\"sub\">{{ subtitle }}</p>\n <ul v-if=\"features.length\" class=\"points\">\n <li v-for=\"feature in features\" :key=\"feature\">\n <Icon icon=\"ph:check\" :width=\"15\" />\n {{ feature }}\n </li>\n </ul>\n </div>\n</template>\n\n<style scoped>\n.login-hero-panel {\n min-width: 0;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 13px;\n margin-bottom: 30px;\n font-size: 28px;\n font-weight: 600;\n letter-spacing: -0.015em;\n color: var(--text-1);\n}\n.headline {\n margin: 0 0 14px;\n color: var(--text-1);\n font-size: clamp(30px, 2.5vw, 40px);\n font-weight: 600;\n letter-spacing: -0.04em;\n line-height: 1.18;\n}\n.headline .hl {\n background: linear-gradient(110deg, var(--signal), var(--login-accent-2));\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n/* 色场上的正文一律用 --text-1:--text-2 是配白底卡片的,直接压在饱和色场上对比度不够 */\n.sub {\n margin: 0 0 34px;\n color: var(--text-1);\n font-size: 17px;\n line-height: 1.6;\n}\n.points {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n/* 字重 500 + 16px:同样是 --text-1,靠字重和字号把亮点和上面那句说明分开,不靠深浅 */\n.points li {\n display: flex;\n align-items: center;\n gap: 12px;\n color: var(--text-1);\n font-size: 16px;\n font-weight: 500;\n}\n.points li > :deep(svg) {\n flex-shrink: 0;\n box-sizing: content-box;\n width: 15px;\n height: 15px;\n padding: 5px;\n border-radius: 50%;\n color: var(--signal);\n background: var(--sel-bg);\n box-shadow: inset 0 0 0 1px var(--sel-ring);\n}\n</style>\n"],"names":["props","__props","site","useSite","headlineParts","computed","index","part","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","SmartLogo","_toDisplayString","_unref","_hoisted_3","_Fragment","_renderList","_normalizeClass","_hoisted_5","_hoisted_6","feature","Icon","_createTextVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAQA,UAAMA,IAAQC,GAmBR,EAAE,MAAAC,EAAA,IAASC,EAAA,GAGXC,IAAgBC,EAAS,MAAM;AACnC,YAAMC,IAAQN,EAAM,YAAYA,EAAM,SAAS,QAAQA,EAAM,SAAS,IAAI;AAC1E,aAAIM,IAAQ,IAAU,CAAC,EAAE,MAAMN,EAAM,UAAU,QAAQ,IAAO,IACvD;AAAA,QACL,EAAE,MAAMA,EAAM,SAAS,MAAM,GAAGM,CAAK,GAAG,QAAQ,GAAA;AAAA,QAChD,EAAE,MAAMN,EAAM,WAAW,QAAQ,GAAA;AAAA,QACjC,EAAE,MAAMA,EAAM,SAAS,MAAMM,IAAQN,EAAM,UAAU,MAAM,GAAG,QAAQ,GAAA;AAAA,MAAM,EAC5E,OAAO,CAAAO,MAAQA,EAAK,IAAI;AAAA,IAC5B,CAAC;sBAICC,EAAA,GAAAC,EAoBM,OApBNC,GAoBM;AAAA,MAnBJC,EAGM,OAHNC,GAGM;AAAA,QAFJC,EAAoCC,GAAA;AAAA,UAAxB,MAAM;AAAA,UAAK,KAAKb,EAAA;AAAA,QAAA;QAC5BU,EAAsC,QAAA,MAAAI,EAA7Bd,EAAA,SAASe,EAAAd,CAAA,EAAK,KAAK,GAAA,CAAA;AAAA,MAAA;MAE9BS,EAOK,MAPLM,GAOK;AAAA,SANHT,EAAA,EAAA,GAAAC,EAKES,GAAA,MAAAC,EAJwBf,EAAA,OAAa,CAA7BG,GAAMD,YADhBG,EAKE,QAAA;AAAA,UAHC,KAAKH;AAAA,UACL,OAAKc,EAAA,EAAA,IAAQb,EAAK,QAAM;AAAA,uBACzBQ,EAAkBR,EAAL,IAAI;AAAA,QAAA;;MAGZN,EAAA,iBAATQ,EAAiD,KAAjDY,GAAiDN,EAAfd,EAAA,QAAQ,GAAA,CAAA;MAChCA,EAAA,SAAS,UAAnBO,KAAAC,EAKK,MALLa,GAKK;AAAA,gBAJHb,EAGKS,GAAA,MAAAC,EAHiBlB,EAAA,UAAQ,CAAnBsB,YAAXd,EAGK,MAAA,EAH4B,KAAKc,KAAO;AAAA,UAC3CV,EAAoCG,EAAAQ,CAAA,GAAA;AAAA,YAA9B,MAAK;AAAA,YAAY,OAAO;AAAA,UAAA;UAAMC,EAAA,QACjCF,CAAO,GAAA,CAAA;AAAA,QAAA;;;;;"}
@@ -34,7 +34,7 @@ const te = { class: "fill-page" }, ge = /* @__PURE__ */ F({
34
34
  () => e.isRead ? t("notice.read") : t("notice.unread")
35
35
  )
36
36
  },
37
- { key: "title", title: () => t("notice.noticeTitle"), search: {} },
37
+ { key: "title", ellipsis: { tooltip: !0 }, title: () => t("notice.noticeTitle"), search: {} },
38
38
  {
39
39
  key: "type",
40
40
  title: () => t("notice.type"),