(() => {
'use strict';
const form = document.getElementById('auth-form');
const username = document.getElementById('username');
const password = document.getElementById('password');
const remember = document.getElementById('auth-remember');
const submit = document.getElementById('auth-submit');
const feedback = document.getElementById('auth-feedback');
const serviceButton = document.getElementById('auth-service');
const serviceName = document.getElementById('auth-service-name');
const subtitle = document.getElementById('auth-subtitle');
const passwordToggle = document.getElementById('auth-toggle-password');
const dialog = document.getElementById('auth-dialog');
const dialogContent = document.getElementById('auth-dialog-content');
const DEMO = Object.freeze({username:'demo', password:'Demo123!'});
const keys = {account:'yuexin-terminal-login-account',connection:'yuexin-terminal-login-connection'};
const escape = value => String(value).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const read = key => {try{return JSON.parse(localStorage.getItem(key)||'null');}catch{return null;}};
const write = (key,value) => {try{if(value===null)localStorage.removeItem(key);else localStorage.setItem(key,JSON.stringify(value));return true;}catch{return false;}};
function validEndpoint(value) {
try {
const u = new URL(value);
if (u.username || u.password || u.search || u.hash) return null;
if (u.protocol !== 'https:' && !(u.protocol === 'http:' && ['localhost','127.0.0.1','[::1]'].includes(u.hostname))) return null;
return u.href.replace(/\/$/,'');
} catch {return null;}
}
const savedConnection = read(keys.connection);
let connection = savedConnection?.mode === 'enterprise' && validEndpoint(savedConnection.url)
? {mode:'enterprise',url:validEndpoint(savedConnection.url),organization:String(savedConnection.organization||'').slice(0,80)}
: {mode:'cloud',url:'',organization:''};
let mode = connection.mode, serviceIntent = '', busy = false, loginTimer, modalTimer, modalBusy = false;
let credentials = null;
let serviceDraft = {url:connection.url,organization:connection.organization};
function restoreAccount() {
const saved = read(keys.account);
username.value = typeof saved?.username === 'string' ? saved.username.slice(0,100) : '';
remember.checked = saved?.remember !== false;
}
function updateService() {
serviceName.textContent = connection.mode === 'enterprise' ? '企业部署' : '云端服务';
serviceButton.setAttribute('aria-label',serviceName.textContent+',打开连接设置');
serviceButton.title = connection.mode === 'enterprise' ? connection.url : '云端服务';
subtitle.textContent = connection.mode === 'enterprise' ? '使用企业工作账号继续' : '使用云端工作账号继续';
}
function message(text,error = false) {
feedback.textContent = text;
feedback.classList.toggle('error',error);
}
function clearInvalid() {
username.removeAttribute('aria-invalid');password.removeAttribute('aria-invalid');
}
function setBusy(value) {
busy = value;
form.setAttribute('aria-busy',String(value));
form.querySelectorAll('input,button').forEach(el=>{el.disabled=value;});
serviceButton.disabled=value;
submit.innerHTML=value?'登录中…':'登 录';
}
function concealPassword() {
password.type='password';passwordToggle.setAttribute('aria-pressed','false');passwordToggle.setAttribute('aria-label','显示密码');
}
function enterWorkspace(identity) {
clearTimeout(loginTimer);credentials=null;password.value='';concealPassword();setBusy(false);message('');
document.dispatchEvent(new CustomEvent('terminal:login',{detail:identity}));
}
function submitLogin(event) {
event.preventDefault();
if(busy)return;
clearInvalid();
const account=username.value.trim();
if(!account){username.setAttribute('aria-invalid','true');message('请输入工作账号',true);username.focus();return;}
if(!password.value){password.setAttribute('aria-invalid','true');message('请输入密码',true);password.focus();return;}
credentials={username:account,password:password.value};
setBusy(true);message('正在验证工作账号…');
loginTimer=setTimeout(()=>{
const accepted=credentials?.username===DEMO.username&&credentials?.password===DEMO.password;
credentials=null;setBusy(false);
if(!accepted){password.value='';concealPassword();password.setAttribute('aria-invalid','true');message('账号或密码不正确,请重试或查看体验账号',true);password.focus();return;}
write(keys.account,{username:remember.checked?account:'',remember:remember.checked});
enterWorkspace({name:account,mode:'account',service:connection.mode,organization:connection.organization});
},900);
}
const heading = (title,sub) => `
`;
const cancel = (label='取消') => ``;
function showDialog(html) {
clearTimeout(modalTimer);modalBusy=false;
dialogContent.innerHTML=html;
if(!dialog.open)dialog.showModal();
}
function closeDialog() {clearTimeout(modalTimer);modalBusy=false;dialog.close();}
function rememberDraft() {
const url=dialogContent.querySelector('#auth-enterprise-url'),org=dialogContent.querySelector('#auth-enterprise-org');
if(url){serviceDraft.url=url.value;serviceDraft.organization=org.value;}
}
function serviceDialog(intent='') {
serviceIntent=intent;mode=intent==='sso'?'enterprise':connection.mode;
serviceDraft={url:connection.url,organization:connection.organization};
renderService();
}
function renderService() {
showDialog(heading('连接设置','选择云端服务或企业部署入口。')+
``+
``);
}
function saveService(event) {
event.preventDefault();
rememberDraft();
let url='';
if(mode==='enterprise'){
const input=dialogContent.querySelector('#auth-enterprise-url');url=validEndpoint(input.value.trim());
if(!url){input.setAttribute('aria-invalid','true');dialogContent.querySelector('#auth-service-error').textContent='请输入有效的 HTTPS 企业服务地址';input.focus();return;}
}
connection=mode==='enterprise'?{mode,url,organization:serviceDraft.organization.trim()}:{mode:'cloud',url:'',organization:''};
const stored=write(keys.connection,connection);updateService();
if(serviceIntent==='sso'&&connection.mode==='enterprise'){ssoDialog();return;}
closeDialog();message(stored?'连接设置已保存':'连接设置已应用,本次预览内有效');username.focus();
}
function demoDialog() {
showDialog(heading('体验账号','用于预览登录、桌面选择和连接设置流程。')+
`- 工作账号
- ${DEMO.username}
- 密码
- ${DEMO.password}
这是交互原型的公开体验账号,未连接真实认证服务。请使用体验信息。
${cancel('返回')}
`);
}
function forgotDialog() {
showDialog(heading('忘记密码','企业工作账号由管理员统一管理。')+
`请联系越欣科技管理员重置密码可提供工作账号与设备序列号 VDI-8F42A1,协助管理员定位。
原型体验账号无法修改密码,可重新查看体验信息。
${cancel('返回登录')}
`);
}
function ssoDialog() {
if(connection.mode!=='enterprise'){serviceDialog('sso');return;}
showDialog(heading('企业账号登录','通过企业统一身份进入云桌面。')+
`- 企业服务
- ${escape(connection.url)}
${connection.organization?`- 组织
- ${escape(connection.organization)}
`:''}- 体验身份
- 越欣科技 · 体验用户
当前为统一身份登录预览,不会跳转或访问真实企业身份服务。
${cancel('取消登录')}
`);
}
function ssoContinue(button) {
if(modalBusy)return;
modalBusy=true;button.disabled=true;button.innerHTML='登录中…';
dialogContent.querySelector('#auth-sso-status').textContent='正在验证企业身份…';
modalTimer=setTimeout(()=>{closeDialog();enterWorkspace({name:'企业体验用户',mode:'sso',service:'enterprise',organization:connection.organization});},900);
}
function offlineDialog() {
showDialog(heading('离线体验','先了解桌面列表和终端设置。')+
`无需工作账号可预览桌面、收藏和连接设置,设备与桌面信息为示例。配置仅保存在当前浏览器,不连接真实远程桌面。
${cancel()}
`);
}
form.addEventListener('submit',submitLogin);
form.addEventListener('input',event=>{
if(event.target===username||event.target===password){event.target.removeAttribute('aria-invalid');if(!busy)message('');}
});
remember.addEventListener('change',()=>{
if(!remember.checked)write(keys.account,{username:'',remember:false});
});
passwordToggle.addEventListener('click',()=>{
const reveal=password.type==='password';password.type=reveal?'text':'password';passwordToggle.setAttribute('aria-pressed',String(reveal));passwordToggle.setAttribute('aria-label',reveal?'隐藏密码':'显示密码');
});
serviceButton.addEventListener('click',()=>serviceDialog());
document.getElementById('auth-demo').addEventListener('click',demoDialog);
document.getElementById('auth-forgot').addEventListener('click',forgotDialog);
document.getElementById('auth-sso').addEventListener('click',ssoDialog);
document.getElementById('auth-offline').addEventListener('click',offlineDialog);
dialog.addEventListener('submit',event=>{if(event.target.id==='auth-service-form')saveService(event);});
dialog.addEventListener('input',event=>{event.target.removeAttribute('aria-invalid');const error=dialogContent.querySelector('#auth-service-error');if(error)error.textContent='';});
dialog.addEventListener('click',event=>{
const button=event.target.closest('[data-auth-action]');
if(!button||button.disabled)return;
switch(button.dataset.authAction){
case 'close':closeDialog();break;
case 'service-mode':rememberDraft();mode=button.dataset.mode;renderService();dialogContent.querySelector(`[data-mode="${mode}"]`).focus();break;
case 'fill-demo':username.value=DEMO.username;password.value=DEMO.password;concealPassword();clearInvalid();message('已填入体验账号,点击登录继续');closeDialog();submit.focus();break;
case 'demo':demoDialog();break;
case 'sso-continue':ssoContinue(button);break;
case 'offline-continue':closeDialog();enterWorkspace({name:'离线体验',mode:'offline',service:'offline',organization:'越欣科技'});break;
}
});
dialog.addEventListener('keydown',event=>{
const tab=event.target.closest('[role=tab]');
if(!tab||!['ArrowLeft','ArrowRight','Home','End'].includes(event.key))return;
event.preventDefault();rememberDraft();
mode=event.key==='Home'?'cloud':event.key==='End'?'enterprise':mode==='cloud'?'enterprise':'cloud';
renderService();dialogContent.querySelector(`[data-mode="${mode}"]`).focus();
});
dialog.addEventListener('cancel',()=>{clearTimeout(modalTimer);modalBusy=false;});
dialog.addEventListener('close',()=>{clearTimeout(modalTimer);modalBusy=false;});
document.addEventListener('terminal:logout',()=>{
clearTimeout(loginTimer);clearTimeout(modalTimer);credentials=null;setBusy(false);password.value='';concealPassword();clearInvalid();restoreAccount();message('已退出登录');username.focus();
});
document.addEventListener('terminal:open-workspace',()=>{
clearTimeout(loginTimer);credentials=null;setBusy(false);password.value='';concealPassword();if(dialog.open)closeDialog();
});
restoreAccount();updateService();
})();