| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733 |
- <template>
- <div :class="prefixCls" class="authorization-page">
- <div class="case-header-bar flex items-center justify-between">
- <AppLogo :showTitle="true" />
- <div class="flex items-center">
- <WindowMinimize />
- <WindowMaximize />
- <WindowClose />
- </div>
- </div>
- <div class="authorization-content">
- <section class="left-pane">
- <div class="left-pane__content">
- <div class="left-pane__header">
- <div class="left-pane__badge">
- <SafetyCertificateOutlined />
- <span>系统授权入口</span>
- </div>
- <h1 class="left-pane__title">智能研判分析平台</h1>
- <p class="left-pane__subtitle">
- 面向案件管理、对象关联和多源数据研判的专业系统,帮助团队更高效地完成线索梳理、关系挖掘与专题分析。
- </p>
- </div>
- <div class="left-pane__visual">
- <div class="device-illustration">
- <div class="device-illustration__screen-shell">
- <div class="device-illustration__screen-dots">
- <span></span>
- <span></span>
- <span></span>
- </div>
- <div class="device-illustration__screen-body">
- <div class="device-illustration__line device-illustration__line--lg"></div>
- <div class="device-illustration__line device-illustration__line--md"></div>
- <div class="device-illustration__chart">
- <span></span>
- <span></span>
- <span></span>
- </div>
- </div>
- </div>
- <div class="device-illustration__stand"></div>
- <div class="device-illustration__shadow"></div>
- <div class="floating-card floating-card--left">
- <div class="floating-card__icon">
- <DesktopOutlined />
- </div>
- <div class="floating-card__content">
- <div class="floating-card__label">对象画像</div>
- <div class="floating-card__title">多源归集</div>
- </div>
- </div>
- <div class="floating-card floating-card--right">
- <div class="floating-card__icon floating-card__icon--indigo">
- <ApartmentOutlined />
- </div>
- <div class="floating-card__content">
- <div class="floating-card__label">关系分析</div>
- <div class="floating-card__title">图谱联动</div>
- </div>
- </div>
- </div>
- </div>
- <div class="left-pane__features">
- <div v-for="item in capabilityList" :key="item.title" class="feature-item">
- <div class="feature-item__icon">
- <component :is="item.icon" />
- </div>
- <div class="feature-item__content">
- <h3 class="feature-item__title">{{ item.title }}</h3>
- <p class="feature-item__desc">{{ item.desc }}</p>
- </div>
- </div>
- </div>
- <div class="left-pane__footer">
- <p class="left-pane__footer-line">© 2026 重庆恒时乐科科技有限公司 | 向量探索团队</p>
- <p class="left-pane__footer-line">联系电话:18908093240</p>
- </div>
- </div>
- </section>
- <section class="right-pane">
- <svg class="right-pane__curve" preserveAspectRatio="none" viewBox="0 0 100 100">
- <path d="M100,0 Q-40,50 100,100 Z" fill="currentColor" />
- </svg>
- <div class="right-pane__scroll">
- <div class="auth-card">
- <div class="auth-card__header">
- <span class="auth-card__eyebrow">离线授权流程</span>
- <h2 class="auth-card__title">未授权时请先完成离线授权</h2>
- <div v-if="authorizationWarningMessage" class="auth-card__warning">
- {{ authorizationWarningMessage }}
- </div>
- <p class="auth-card__intro">请按下方流程完成离线授权,随后即可进入系统继续使用。</p>
- </div>
- <div class="auth-card__steps">
- <div v-for="(item, index) in authorizationSteps" :key="item.title" class="step-card">
- <div class="step-card__index">{{ index + 1 }}</div>
- <h4 class="step-card__title">{{ item.title }}</h4>
- <p class="step-card__desc">{{ item.desc }}</p>
- <RightOutlined v-if="index < authorizationSteps.length - 1" class="step-card__arrow" />
- </div>
- </div>
- <div class="auth-card__section">
- <div class="auth-card__section-head">
- <h3 class="auth-card__section-title">获取机器码</h3>
- <span class="auth-card__section-tip">用于申请当前设备的离线授权文件</span>
- </div>
- <div class="machine-panel">
- <div class="machine-panel__value">
- {{ machineCode || '点击“获取机器码”后显示当前设备机器码' }}
- </div>
- <div class="machine-panel__actions">
- <Button class="ui-btn ui-btn--ghost" :loading="machineCodeLoading" @click="handleGetMachineCode">
- 获取机器码
- </Button>
- <Button class="ui-btn ui-btn--ghost" :disabled="!machineCode" @click="handleCopyMachineCode">
- <CopyOutlined />
- <span>复制</span>
- </Button>
- </div>
- </div>
- </div>
- <div class="auth-card__section">
- <div class="auth-card__section-head">
- <h3 class="auth-card__section-title">上传授权文件</h3>
- <span class="auth-card__section-tip">选择本地授权文件后执行离线授权</span>
- </div>
- <div class="upload-panel">
- <div class="upload-panel__dropzone" @click="handleSelectLicenseFile">
- <UploadOutlined class="upload-panel__dropzone-icon" />
- <span class="upload-panel__dropzone-text">
- {{ licenseFilePath || '请选择离线授权文件' }}
- </span>
- </div>
- <div class="upload-panel__actions">
- <Button class="ui-btn ui-btn--ghost" @click="handleSelectLicenseFile">选择授权文件</Button>
- <Button
- type="primary"
- class="ui-btn ui-btn--primary"
- :loading="offlineAuthorizationLoading"
- :disabled="!licenseFilePath"
- @click="handleOfflineAuthorization"
- >
- 离线授权
- </Button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { onMounted, ref } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import { Button } from 'ant-design-vue';
- import {
- ApartmentOutlined,
- CopyOutlined,
- DatabaseOutlined,
- DesktopOutlined,
- NotificationOutlined,
- RightOutlined,
- SafetyCertificateOutlined,
- UploadOutlined,
- } from '@ant-design/icons-vue';
- import { PageEnum } from '@jeesite/core/enums/pageEnum';
- import {
- authorizationInfoApi,
- checkAuthApi,
- machineCodeApi,
- offlineAuthorizationApi,
- } from '@jeesite/core/api/sys/login';
- import { useCopyToClipboard } from '@jeesite/core/hooks/web/useCopyToClipboard';
- import { useDesign } from '@jeesite/core/hooks/web/useDesign';
- import { useMessage } from '@jeesite/core/hooks/web/useMessage';
- import { useUserStore } from '@jeesite/core/store/modules/case';
- import { AppLogo } from '@jeesite/core/components/Application';
- import { WindowMinimize, WindowMaximize, WindowClose } from '@jeesite/core/layouts/default/header/components';
- import { initGlobalCaseConfig } from '@jeesite/core/logics/caseConfigGuide';
- import {
- extractAuthorizationCache,
- getAuthorizationExpiredMessage,
- isAuthorizationExpired,
- } from '@jeesite/core/utils/license';
- const router = useRouter();
- const route = useRoute();
- const { prefixCls } = useDesign('authorization-entry');
- const { showMessage } = useMessage();
- const { clipboardRef, isSuccessRef } = useCopyToClipboard();
- const userStore = useUserStore();
- const machineCode = ref('');
- const machineCodeLoading = ref(false);
- const offlineAuthorizationLoading = ref(false);
- const licenseFilePath = ref('');
- const authorizationWarningMessage = ref('');
- const capabilityList = [
- {
- title: '多源数据融合',
- desc: '整合资金流水、话单、基站轨迹等异构数据,提升数据归集效率。',
- icon: DatabaseOutlined,
- },
- {
- title: '关系图谱分析',
- desc: '围绕对象、团伙与行为链路开展多端关联研判,快速定位关键关系。',
- icon: ApartmentOutlined,
- },
- {
- title: '专题预警支撑',
- desc: '结合时序分析与规则配置,辅助线索核查、案件推进和复盘总结。',
- icon: NotificationOutlined,
- },
- ];
- const authorizationSteps = [
- {
- title: '获取机器码',
- desc: '提取当前设备机器码,并提交给授权方生成对应的离线授权文件。',
- },
- {
- title: '选择授权文件',
- desc: '从本地导入授权文件,系统会自动进行授权校验。',
- },
- {
- title: '执行离线授权',
- desc: '授权成功后即可进入系统。',
- },
- ];
- function syncAuthorizationCache(source?: unknown) {
- const { authorizationLabel, overDateTime, authorizationUnit } = extractAuthorizationCache(source);
- userStore.setPageCache('authorizationLabel', authorizationLabel);
- userStore.setPageCache('licenseOverDateTime', overDateTime);
- userStore.setPageCache('authorizationUnit', authorizationUnit);
- return {
- authorizationLabel,
- overDateTime,
- authorizationUnit,
- };
- }
- function clearAuthorizationCache() {
- syncAuthorizationCache();
- }
- async function resolveAuthorizationCache(source?: unknown) {
- const cache = syncAuthorizationCache(source);
- if (cache.overDateTime) {
- return cache;
- }
- try {
- const res = await authorizationInfoApi();
- return syncAuthorizationCache(res);
- } catch {
- return cache;
- }
- }
- function showExpiredAuthorizationMessage(overDateTime?: unknown) {
- authorizationWarningMessage.value = getAuthorizationExpiredMessage(overDateTime);
- showMessage(authorizationWarningMessage.value, 'warning');
- }
- async function loadMachineCode() {
- machineCodeLoading.value = true;
- try {
- const data = await machineCodeApi();
- machineCode.value = data || '';
- } catch (error) {
- machineCode.value = '';
- } finally {
- machineCodeLoading.value = false;
- }
- }
- function handleCopyMachineCode() {
- if (!machineCode.value) return;
- clipboardRef.value = machineCode.value;
- if (isSuccessRef.value) {
- showMessage('机器码复制成功', 'success');
- }
- }
- async function handleGetMachineCode() {
- await loadMachineCode();
- if (machineCode.value) {
- showMessage('机器码获取成功', 'success');
- }
- }
- async function handleSelectLicenseFile() {
- const ipcRenderer = (window as any)?.electron?.ipcRenderer;
- if (!ipcRenderer?.invoke) {
- showMessage('无法获取文件路径,请在 Electron 环境下选择授权文件');
- return;
- }
- try {
- const resolvedPath = await ipcRenderer.invoke('controller/effect/selectFile');
- const filePaths = Array.isArray(resolvedPath)
- ? resolvedPath.filter((item: unknown): item is string => typeof item === 'string' && !!item.trim())
- : typeof resolvedPath === 'string' && resolvedPath.trim()
- ? [resolvedPath]
- : [];
- if (!filePaths.length) return;
- licenseFilePath.value = filePaths[0];
- } catch (error: any) {
- showMessage(error?.message || '选择授权文件失败,请稍后重试');
- }
- }
- async function handleOfflineAuthorization() {
- if (!licenseFilePath.value) {
- showMessage('请先选择授权文件');
- return;
- }
- offlineAuthorizationLoading.value = true;
- try {
- const res = await offlineAuthorizationApi(licenseFilePath.value);
- authorizationWarningMessage.value = '';
- if (res?.code === 200) {
- const entered = await checkAuthAndEnterSystem();
- if (entered) {
- showMessage('离线授权成功', 'success');
- }
- } else {
- showMessage(res?.message || '离线授权失败');
- }
- } catch (error: any) {
- showMessage(error?.message || '离线授权失败,请稍后重试');
- } finally {
- offlineAuthorizationLoading.value = false;
- }
- }
- async function checkAuthAndEnterSystem(showUnauthorizedMessage = true) {
- try {
- const res = await checkAuthApi();
- const code = res?.code;
- if (code === 401) {
- clearAuthorizationCache();
- if (showUnauthorizedMessage) {
- showMessage('当前设备未授权,请先完成授权!');
- }
- return false;
- }
- if (code === 200) {
- const cache = await resolveAuthorizationCache(res);
- if (isAuthorizationExpired(cache.overDateTime)) {
- showExpiredAuthorizationMessage(cache.overDateTime);
- return false;
- }
- await initGlobalCaseConfig();
- await router.replace(PageEnum.BASE_CASE);
- return true;
- }
- clearAuthorizationCache();
- if (showUnauthorizedMessage) {
- showMessage('授权校验失败,请稍后重试!');
- }
- return false;
- } catch (error: any) {
- const status = error?.response?.status || error?.status;
- if (status === 401) {
- clearAuthorizationCache();
- if (showUnauthorizedMessage) {
- showMessage('当前设备未授权,请先完成授权!');
- }
- return false;
- }
- clearAuthorizationCache();
- if (showUnauthorizedMessage) {
- showMessage(error?.message || '授权校验失败,请稍后重试!');
- }
- return false;
- }
- }
- onMounted(() => {
- if (route.query.expired === '1') {
- authorizationWarningMessage.value = String(
- route.query.message || '当前授权已过期,请重新导入有效授权文件或联系管理员。',
- );
- showMessage(authorizationWarningMessage.value, 'warning');
- }
- if (route.query.forceAuth === '1' || route.query.skipAuthCheck === '1') {
- return;
- }
- void checkAuthAndEnterSystem(false);
- });
- </script>
- <style lang="less">
- @prefix-cls: ~'jeesite-authorization-entry';
- .@{prefix-cls} {
- height: 100vh;
- width: 100%;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- background: #f4f7f9;
- font-family:
- -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
- .authorization-page {
- position: relative;
- display: flex;
- width: 100%;
- height: 100vh;
- overflow: hidden;
- }
- .case-header-bar {
- height: 48px;
- padding: 0 12px 0 0;
- background: #fff;
- flex-shrink: 0;
- -webkit-app-region: drag;
- .jeesite-app-logo {
- -webkit-app-region: no-drag;
- &__title {
- display: inline-flex;
- align-items: baseline;
- gap: 6px;
- font-size: 18px;
- font-weight: bold;
- color: #555;
- font-family: Arial, 'Microsoft YaHei', sans-serif;
- cursor: default;
- user-select: none;
- transition: all 0.5s;
- }
- }
- .jeesite-app-logo img {
- width: 32px;
- height: 32px;
- margin-right: 2px;
- }
- .jeesite-header-action-button {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 36px;
- height: 36px;
- border-radius: 10px;
- color: #333;
- font-size: 18px;
- cursor: pointer;
- transition: background-color 0.2s ease;
- -webkit-app-region: no-drag;
- &:hover {
- background-color: #f6f6f6;
- }
- }
- }
- .authorization-content {
- flex: 1;
- display: flex;
- min-height: 0;
- }
- .left-pane {
- position: relative;
- z-index: 0;
- display: none;
- flex-direction: column;
- width: 40%;
- padding: 32px 40px 24px;
- color: #fff;
- background: #165dff;
- min-height: 0;
- overflow: hidden;
- &__content {
- position: relative;
- z-index: 1;
- display: flex;
- flex: 1;
- min-height: 0;
- flex-direction: column;
- justify-content: space-between;
- gap: clamp(12px, 2vh, 24px);
- width: 100%;
- max-width: 520px;
- margin: 0 auto;
- }
- &__header {
- flex-shrink: 0;
- margin-top: 4px;
- }
- &__badge {
- display: none;
- align-items: center;
- gap: 6px;
- padding: 6px 12px;
- margin-bottom: 24px;
- font-size: 13px;
- font-weight: 500;
- color: #fff;
- letter-spacing: 0.04em;
- background: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(255, 255, 255, 0.12);
- border-radius: 999px;
- box-shadow: none;
- }
- &__title {
- margin: 0 0 16px;
- font-size: 36px;
- font-weight: 700;
- line-height: 1.18;
- letter-spacing: -0.02em;
- color: #fff;
- text-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
- }
- &__subtitle {
- max-width: 430px;
- margin: 0;
- font-size: 15px;
- font-weight: 500;
- line-height: 1.8;
- color: rgba(219, 234, 254, 0.92);
- }
- &__visual {
- display: flex;
- flex: 1 1 auto;
- align-items: center;
- justify-content: center;
- min-height: 60px;
- max-height: 200px;
- padding: 4px 0;
- }
- &__features {
- display: flex;
- flex-shrink: 0;
- flex-direction: column;
- gap: 18px;
- padding-bottom: 0;
- }
- &__footer {
- flex-shrink: 0;
- padding: 12px 0 0;
- }
- &__footer-line {
- margin: 0;
- font-size: 12px;
- line-height: 1.8;
- color: rgba(255, 255, 255, 0.5);
- }
- }
- .device-illustration {
- position: relative;
- width: 100%;
- max-width: 340px;
- aspect-ratio: 1 / 1;
- &__screen-shell {
- position: absolute;
- top: 50%;
- left: 50%;
- display: flex;
- flex-direction: column;
- width: 280px;
- height: 180px;
- padding: 20px;
- background: rgba(255, 255, 255, 0.1);
- border: 2px solid rgba(255, 255, 255, 0.2);
- border-radius: 18px;
- box-shadow: 0 24px 54px rgba(7, 24, 66, 0.24);
- backdrop-filter: blur(14px);
- transform: translate(-50%, -50%);
- }
- &__screen-dots {
- display: flex;
- gap: 8px;
- margin-bottom: 16px;
- span {
- width: 10px;
- height: 10px;
- background: rgba(255, 255, 255, 0.3);
- border-radius: 999px;
- }
- }
- &__screen-body {
- display: flex;
- flex: 1;
- flex-direction: column;
- gap: 10px;
- padding: 14px;
- background: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 12px;
- }
- &__line {
- height: 10px;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 999px;
- &--lg {
- width: 75%;
- }
- &--md {
- width: 50%;
- }
- }
- &__chart {
- display: flex;
- gap: 8px;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 54px;
- margin-top: auto;
- background: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 10px;
- span {
- width: 16px;
- height: 16px;
- background: rgba(96, 165, 250, 0.5);
- border-radius: 4px;
- }
- }
- &__stand {
- position: absolute;
- bottom: calc(50% - 126px);
- left: 50%;
- width: 96px;
- height: 24px;
- background: rgba(255, 255, 255, 0.1);
- backdrop-filter: blur(12px);
- transform: translateX(-50%);
- &::before {
- content: '';
- position: absolute;
- bottom: -10px;
- left: 50%;
- width: 144px;
- height: 8px;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 999px;
- filter: blur(2px);
- transform: translateX(-50%);
- }
- }
- &__shadow {
- position: absolute;
- inset: auto 22px 42px;
- height: 110px;
- background: radial-gradient(circle, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0) 72%);
- filter: blur(22px);
- }
- }
- .floating-card {
- position: absolute;
- z-index: 2;
- display: flex;
- gap: 12px;
- align-items: center;
- padding: 16px;
- color: #165dff;
- background: #fff;
- border-radius: 16px;
- box-shadow: 0 18px 36px rgba(10, 22, 70, 0.18);
- &--left {
- top: 25%;
- left: -40px;
- animation: float-bounce-left 4s infinite;
- }
- &--right {
- right: -48px;
- bottom: 32%;
- animation: float-bounce-right 5s infinite 0.5s;
- }
- &__icon {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 36px;
- height: 36px;
- font-size: 20px;
- color: #165dff;
- background: #eff6ff;
- border-radius: 12px;
- &--indigo {
- color: #4f46e5;
- background: #eef2ff;
- }
- }
- &__label {
- margin-bottom: 2px;
- font-size: 12px;
- font-weight: 500;
- color: #6b7280;
- }
- &__title {
- font-size: 14px;
- font-weight: 700;
- color: #111827;
- }
- }
- .feature-item {
- display: flex;
- gap: 16px;
- align-items: flex-start;
- &__icon {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- width: 48px;
- height: 48px;
- font-size: 24px;
- color: #fff;
- background: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 14px;
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
- }
- &__title {
- margin: 0 0 4px;
- font-size: 16px;
- font-weight: 600;
- letter-spacing: 0.02em;
- color: #fff;
- }
- &__desc {
- max-width: 320px;
- margin: 0;
- font-size: 14px;
- line-height: 1.7;
- color: rgba(219, 234, 254, 0.82);
- }
- }
- .right-pane {
- position: relative;
- z-index: 10;
- display: flex;
- flex: 1;
- align-items: center;
- justify-content: center;
- padding: 24px;
- background: transparent;
- box-sizing: border-box;
- min-height: 0;
- overflow: visible;
- &__curve {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 0;
- display: none;
- width: 120px;
- height: 100%;
- color: #f4f7f9;
- transform: translateX(-99%);
- pointer-events: none;
- }
- &__scroll {
- position: relative;
- z-index: 1;
- display: flex;
- flex: 1;
- align-items: center;
- justify-content: center;
- height: 100%;
- min-height: 0;
- overflow-y: auto;
- overflow-x: visible;
- scrollbar-width: none;
- &::-webkit-scrollbar {
- width: 0;
- height: 0;
- }
- }
- }
- .auth-card {
- position: relative;
- z-index: 10;
- width: 100%;
- max-width: 540px;
- padding: 16px;
- margin: auto 0;
- background: #fff;
- border: 1px solid #f3f4f6;
- border-radius: 18px;
- box-shadow: 0 8px 40px -12px rgba(0, 0, 0, 0.08);
- &__header {
- margin-bottom: 10px;
- }
- &__eyebrow {
- display: inline-block;
- padding: 1px 8px;
- margin-bottom: 6px;
- font-size: 11px;
- line-height: 18px;
- font-weight: 700;
- letter-spacing: 0.04em;
- color: #2563eb;
- background: #eff6ff;
- border: 1px solid #dbeafe;
- border-radius: 6px;
- }
- &__title {
- margin: 0 0 4px;
- font-size: 20px;
- font-weight: 700;
- letter-spacing: -0.02em;
- color: #111827;
- }
- &__intro {
- margin: 0;
- padding: 7px 9px;
- font-size: 13px;
- line-height: 1.5;
- color: #6b7280;
- background: #f8fafc;
- border: 1px solid #f3f4f6;
- border-radius: 12px;
- strong {
- font-weight: 700;
- color: #374151;
- }
- }
- &__warning {
- padding: 10px 12px;
- margin-bottom: 8px;
- font-size: 13px;
- font-weight: 700;
- line-height: 1.6;
- color: #b91c1c;
- background: #fef2f2;
- border: 1px solid #fecaca;
- border-radius: 10px;
- }
- &__steps {
- display: grid;
- grid-template-columns: 1fr;
- gap: 6px;
- margin-bottom: 10px;
- }
- &__section {
- margin-bottom: 8px;
- }
- &__section-head {
- display: flex;
- justify-content: space-between;
- gap: 8px;
- align-items: baseline;
- margin-bottom: 3px;
- }
- &__section-title {
- margin: 0;
- font-size: 16px;
- font-weight: 700;
- color: #111827;
- }
- &__section-tip {
- font-size: 12px;
- font-weight: 500;
- color: #9ca3af;
- }
- &__file-input {
- display: none;
- }
- }
- .step-card {
- position: relative;
- padding: 8px;
- background: #f8fafc;
- border: 1px solid #f3f4f6;
- border-radius: 10px;
- transition:
- background-color 0.2s ease,
- border-color 0.2s ease,
- transform 0.2s ease;
- &:hover {
- background: rgba(239, 246, 255, 0.6);
- border-color: #bfdbfe;
- transform: translateY(-1px);
- .step-card__index {
- transform: scale(1.1);
- }
- }
- &__index {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 18px;
- height: 18px;
- margin-bottom: 4px;
- font-size: 9px;
- font-weight: 700;
- color: #fff;
- background: #2563eb;
- border-radius: 999px;
- box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
- transition: transform 0.2s ease;
- }
- &__title {
- margin: 0 0 2px;
- font-size: 13px;
- font-weight: 700;
- color: #111827;
- }
- &__desc {
- margin: 0;
- font-size: 12px;
- line-height: 1.45;
- color: #6b7280;
- }
- &__arrow {
- position: absolute;
- top: 18px;
- right: -10px;
- z-index: 10;
- display: none;
- font-size: 14px;
- color: #d1d5db;
- }
- }
- .machine-panel,
- .upload-panel {
- padding: 7px;
- background: #f8fafc;
- border: 1px solid #e5e7eb;
- border-radius: 10px;
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
- }
- .machine-panel {
- &__value {
- min-height: 40px;
- margin-bottom: 6px;
- padding: 7px 9px;
- font-family: Consolas, 'Courier New', monospace;
- font-size: 12px;
- line-height: 1.5;
- color: #4b5563;
- word-break: break-all;
- background: #fff;
- border: 1px solid #e5e7eb;
- border-radius: 8px;
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
- }
- &__actions {
- display: flex;
- justify-content: flex-end;
- gap: 4px;
- }
- }
- .upload-panel {
- &__dropzone {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 2px;
- min-height: 64px;
- padding: 8px;
- margin-bottom: 6px;
- text-align: center;
- cursor: pointer;
- background: #fff;
- border: 2px dashed #d1d5db;
- border-radius: 8px;
- transition:
- background-color 0.2s ease,
- border-color 0.2s ease,
- transform 0.2s ease;
- &:hover {
- background: rgba(239, 246, 255, 0.6);
- border-color: #93c5fd;
- .upload-panel__dropzone-icon {
- transform: scale(1.1);
- }
- .upload-panel__dropzone-text {
- color: #3b82f6;
- }
- }
- }
- &__dropzone-icon {
- font-size: 18px;
- color: #3b82f6;
- transition: transform 0.2s ease;
- }
- &__dropzone-text {
- font-size: 12px;
- font-weight: 500;
- line-height: 1.45;
- color: #9ca3af;
- word-break: break-all;
- transition: color 0.2s ease;
- }
- &__actions {
- display: flex;
- justify-content: flex-end;
- gap: 4px;
- }
- }
- .ui-btn {
- display: inline-flex;
- gap: 4px;
- align-items: center;
- justify-content: center;
- height: auto;
- padding: 4px 9px;
- font-size: 12px;
- font-weight: 500;
- border-radius: 8px;
- transition:
- background-color 0.2s ease,
- border-color 0.2s ease,
- color 0.2s ease,
- box-shadow 0.2s ease,
- transform 0.2s ease;
- &.ant-btn {
- box-shadow: none;
- }
- &.ant-btn > span {
- display: inline-flex;
- gap: 4px;
- align-items: center;
- }
- &:active {
- transform: scale(0.99);
- }
- &--ghost {
- color: #374151;
- background: #fff;
- border-color: #d1d5db;
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
- &:hover,
- &:focus {
- color: #374151;
- background: #f9fafb;
- border-color: #d1d5db;
- }
- }
- &--primary {
- color: #fff;
- background: #165dff;
- border-color: #165dff;
- box-shadow: 0 6px 18px rgba(22, 93, 255, 0.28);
- &:hover,
- &:focus,
- &.ant-btn-primary:hover,
- &.ant-btn-primary:focus {
- color: #fff;
- background: #0f56f0;
- border-color: #0f56f0;
- }
- &.ant-btn-primary,
- &.ant-btn-color-primary,
- &.ant-btn-variant-solid {
- color: #fff;
- background: #165dff;
- border-color: #165dff;
- }
- &.ant-btn-primary:disabled,
- &.ant-btn-primary[disabled],
- &.ant-btn-color-primary:disabled,
- &.ant-btn-color-primary[disabled],
- &.ant-btn-variant-solid:disabled,
- &.ant-btn-variant-solid[disabled] {
- color: rgba(255, 255, 255, 0.88);
- background: rgba(22, 93, 255, 0.55);
- border-color: rgba(22, 93, 255, 0.55);
- box-shadow: none;
- opacity: 1;
- }
- }
- }
- @keyframes float-bounce-left {
- 0%,
- 100% {
- transform: translate3d(0, 0, 0);
- }
- 50% {
- transform: translate3d(0, -12px, 0);
- }
- }
- @keyframes float-bounce-right {
- 0%,
- 100% {
- transform: translate3d(0, 0, 0);
- }
- 50% {
- transform: translate3d(0, -10px, 0);
- }
- }
- }
- @media screen and (min-width: 992px) {
- .@{prefix-cls} {
- .auth-card {
- padding: 40px;
- }
- }
- }
- @media screen and (min-width: 1280px) {
- .@{prefix-cls} {
- .left-pane {
- display: flex;
- }
- .right-pane {
- padding: 48px;
- &__curve {
- display: block;
- }
- }
- .auth-card {
- &__steps {
- grid-template-columns: repeat(3, minmax(0, 1fr));
- }
- }
- .step-card {
- &__arrow {
- display: block;
- }
- }
- }
- }
- @media screen and (min-width: 1536px) {
- .@{prefix-cls} {
- .left-pane {
- width: 45%;
- padding: 56px;
- &__title {
- font-size: 42px;
- }
- &__subtitle {
- font-size: 16px;
- }
- }
- .right-pane__curve {
- width: 160px;
- }
- .device-illustration {
- max-width: 340px;
- &__screen-shell {
- width: 280px;
- height: 180px;
- }
- }
- .floating-card {
- padding: 16px;
- }
- .feature-item {
- &__icon {
- width: 48px;
- height: 48px;
- font-size: 24px;
- }
- &__title {
- font-size: 16px;
- }
- &__desc {
- font-size: 14px;
- }
- }
- }
- }
- @media screen and (max-width: 1279px) {
- .@{prefix-cls} {
- .left-pane {
- display: none;
- }
- .right-pane {
- align-items: center;
- padding: 24px 20px;
- }
- .right-pane__scroll {
- align-items: center;
- }
- .auth-card {
- margin: auto 0;
- }
- }
- }
- @media screen and (max-width: 767px) {
- .@{prefix-cls} {
- .right-pane {
- padding: 16px;
- align-items: flex-start;
- }
- .right-pane__scroll {
- align-items: flex-start;
- }
- .auth-card {
- padding: 22px 18px;
- margin: 0;
- &__header {
- margin-bottom: 24px;
- }
- &__section-head {
- flex-direction: column;
- align-items: flex-start;
- }
- }
- .machine-panel__actions,
- .upload-panel__actions {
- flex-wrap: wrap;
- }
- .machine-panel__actions > *,
- .upload-panel__actions > * {
- flex: 1 1 calc(50% - 5px);
- }
- }
- }
- @media screen and (max-width: 1366px) and (min-width: 1280px) and (max-height: 768px) {
- .@{prefix-cls} {
- .left-pane {
- width: 37%;
- padding: 20px 24px 16px;
- &__content {
- max-width: 420px;
- gap: 14px;
- }
- &__header {
- margin-top: 2px;
- }
- &__badge {
- margin-bottom: 14px;
- padding: 5px 10px;
- font-size: 12px;
- }
- &__title {
- margin-bottom: 10px;
- font-size: 28px;
- line-height: 1.14;
- }
- &__subtitle {
- max-width: 360px;
- font-size: 13px;
- line-height: 1.6;
- }
- &__visual {
- min-height: 40px;
- padding: 2px 0;
- }
- &__features {
- gap: 10px;
- padding-bottom: 0;
- }
- }
- .device-illustration {
- max-width: 236px;
- &__screen-shell {
- width: 208px;
- height: 136px;
- padding: 14px;
- border-radius: 16px;
- }
- &__screen-dots {
- gap: 6px;
- margin-bottom: 12px;
- span {
- width: 8px;
- height: 8px;
- }
- }
- &__screen-body {
- gap: 8px;
- padding: 10px;
- }
- &__line {
- height: 8px;
- }
- &__chart {
- height: 40px;
- span {
- width: 13px;
- height: 13px;
- }
- }
- &__stand {
- bottom: calc(50% - 94px);
- width: 78px;
- height: 18px;
- &::before {
- bottom: -8px;
- width: 112px;
- height: 6px;
- }
- }
- &__shadow {
- inset: auto 18px 32px;
- height: 72px;
- }
- }
- .floating-card {
- gap: 10px;
- padding: 10px 12px;
- border-radius: 14px;
- &--left {
- top: 23%;
- left: -12px;
- }
- &--right {
- right: -16px;
- bottom: 27%;
- }
- &__icon {
- width: 30px;
- height: 30px;
- font-size: 16px;
- border-radius: 10px;
- }
- &__label {
- font-size: 10px;
- }
- &__title {
- font-size: 13px;
- }
- }
- .feature-item {
- gap: 10px;
- &__icon {
- width: 36px;
- height: 36px;
- font-size: 18px;
- border-radius: 12px;
- }
- &__title {
- margin-bottom: 2px;
- font-size: 14px;
- }
- &__desc {
- max-width: 280px;
- font-size: 12px;
- line-height: 1.45;
- }
- }
- .right-pane {
- align-items: center;
- padding: 14px 18px;
- }
- .right-pane__scroll {
- align-items: center;
- }
- .auth-card {
- max-width: 522px;
- padding: 20px 20px 18px;
- border-radius: 18px;
- &__header {
- margin-bottom: 18px;
- }
- &__eyebrow {
- margin-bottom: 10px;
- font-size: 11px;
- }
- &__title {
- margin-bottom: 8px;
- font-size: 18px;
- }
- &__intro {
- padding: 10px 12px;
- font-size: 12px;
- line-height: 1.55;
- border-radius: 12px;
- }
- &__steps {
- gap: 8px;
- margin-bottom: 18px;
- }
- &__section {
- margin-bottom: 14px;
- }
- &__section-head {
- margin-bottom: 6px;
- }
- &__section-title {
- font-size: 13px;
- }
- &__section-tip {
- font-size: 11px;
- }
- &__footer {
- padding-top: 14px;
- margin-top: 18px;
- }
- }
- .step-card {
- padding: 12px 10px;
- border-radius: 12px;
- &__index {
- width: 22px;
- height: 22px;
- margin-bottom: 8px;
- font-size: 10px;
- }
- &__title {
- margin-bottom: 5px;
- font-size: 12px;
- }
- &__desc {
- font-size: 11px;
- line-height: 1.5;
- }
- &__arrow {
- top: 23px;
- right: -7px;
- font-size: 14px;
- }
- }
- .machine-panel,
- .upload-panel {
- padding: 10px;
- border-radius: 12px;
- }
- .machine-panel {
- &__value {
- min-height: 54px;
- margin-bottom: 10px;
- padding: 10px 12px;
- font-size: 11px;
- line-height: 1.6;
- border-radius: 10px;
- }
- &__actions {
- gap: 8px;
- }
- }
- .upload-panel {
- &__dropzone {
- min-height: 84px;
- padding: 12px;
- margin-bottom: 10px;
- }
- &__dropzone-icon {
- font-size: 22px;
- }
- &__dropzone-text {
- font-size: 12px;
- line-height: 1.5;
- }
- &__actions {
- gap: 8px;
- }
- }
- .ui-btn {
- padding: 7px 14px;
- font-size: 12px;
- border-radius: 9px;
- }
- }
- }
- @media screen and (max-height: 860px) and (min-width: 1280px) {
- .@{prefix-cls} {
- .left-pane {
- padding: 24px 32px 16px;
- &__content {
- gap: 18px;
- }
- &__title {
- margin-bottom: 10px;
- font-size: 30px;
- }
- &__subtitle {
- font-size: 14px;
- line-height: 1.6;
- }
- &__visual {
- min-height: 80px;
- padding: 4px 0;
- }
- &__features {
- gap: 14px;
- }
- }
- .device-illustration {
- max-width: 290px;
- &__screen-shell {
- width: 244px;
- height: 160px;
- padding: 16px;
- }
- &__screen-body {
- padding: 12px;
- gap: 8px;
- }
- &__chart {
- height: 48px;
- }
- }
- .floating-card {
- padding: 12px 14px;
- &--left {
- left: -22px;
- }
- &--right {
- right: -28px;
- }
- }
- .feature-item {
- gap: 12px;
- &__icon {
- width: 42px;
- height: 42px;
- font-size: 20px;
- }
- &__title {
- font-size: 15px;
- }
- &__desc {
- font-size: 13px;
- line-height: 1.6;
- }
- }
- .right-pane {
- align-items: flex-start;
- padding-top: 18px;
- padding-bottom: 18px;
- }
- .right-pane__scroll {
- align-items: flex-start;
- }
- .auth-card {
- padding: 26px 26px 22px;
- }
- .auth-card__steps {
- margin-bottom: 28px;
- }
- .step-card {
- padding: 14px;
- }
- }
- }
- </style>
|