(() => { 'use strict'; const root = document.getElementById('workspace'); const login = document.getElementById('login'); const paths = { monitor: '', settings: '', sun: '', moon: '', user: '', star: '', search: '', refresh: '', arrow: '', back: '', network: '', info: '', logout: '', check: '', close: '' }; const icon = name => ``; const escape = value => String(value).replace(/[&<>"']/g, char => ({ '&':'&', '<':'<', '>':'>', '"':'"', "'":''' }[char])); const defaults = [ { id:'windows', os:'Windows 10', version:'64 位 · Windows', name:'设计工作站', resolution:'2560x1440', fps:60, endpoint:'192.168.1.50', app:'Desktop', note:'图形设计与日常办公', favorite:true }, { id:'ubuntu', os:'Ubuntu', version:'24.04 LTS · Linux', name:'研发环境', resolution:'2560x1440', fps:30, endpoint:'192.168.1.51', app:'Desktop', note:'开发工具与研发工作环境', favorite:false }, { id:'kylin', os:'银河麒麟', version:'桌面操作系统 · Linux', name:'办公桌面', resolution:'1920x1080', fps:30, endpoint:'', app:'Desktop', note:'暂未配置服务器,请联系管理员', favorite:false } ]; const read = () => { try { return JSON.parse(localStorage.getItem('yuexin-terminal-ui-v3') || 'null'); } catch { return null; } }; const saved = read(); let desktops = defaults.map(d => { const local = Array.isArray(saved) && saved.find(v => v.id === d.id); return local ? { ...d, endpoint:String(local.endpoint ?? d.endpoint), app:String(local.app || d.app), resolution:['1920x1080','2560x1440','3840x2160','1280x720'].includes(local.resolution) ? local.resolution : d.resolution, fps:[24,30,60].includes(local.fps) ? local.fps : d.fps, favorite:!!local.favorite } : { ...d }; }); const persist = () => { try { localStorage.setItem('yuexin-terminal-ui-v3', JSON.stringify(desktops)); return true; } catch { return false; } }; let tab = 'all', search = '', view = 'desktops', settingsTab = 'desktop', selectedId = 'windows', sessionState = 'connecting'; let timer, toastTimer; let identity = {name:'终端用户',mode:'preview',service:'cloud',organization:'越欣科技'}; const selected = () => desktops.find(d => d.id === selectedId) || desktops[0]; const mark = d => { const art = d.id === 'windows' ? '' : `${d.id === 'ubuntu' ? 'Ubuntu' : '麒麟软件'} 标识`; return `
${art}
`; }; const prettyResolution = d => d.resolution.replace('x', ' × '); const themeLabel = () => document.body.dataset.theme === 'dark' ? '切换浅色' : '切换深色'; const header = () => `
越欣科技云桌面终端
`; const footer = () => `
交互原型 · 设备与桌面状态为示例
`; function card(d) { const available = !!d.endpoint; return `
${mark(d)}
${escape(d.os)}
${escape(d.version)}

${escape(d.name)}

${available ? '可连接' : '待配置'}
分辨率
${prettyResolution(d)}
帧率
${d.fps} FPS

${available ? escape(d.id === 'kylin' ? '国产办公工作环境' : d.note) : '暂未配置服务器,请联系管理员'}

`; } function list() { return `

我的云桌面

选择工作环境,连接后即可开始使用。

${desktops.filter(d => d.endpoint).length} 台可连接 / 共 ${desktops.length} 台
`; } function renderGrid() { const rows = desktops.filter(d => (tab !== 'favorites' || d.favorite) && (d.os + d.name).toLowerCase().includes(search.trim().toLowerCase())); root.querySelector('#ws-grid').innerHTML = rows.length ? rows.map(card).join('') : `
${icon('search')}

${search ? '没有找到匹配的桌面' : '还没有收藏的桌面'}

${search ? '试试系统名称,或清除搜索条件。' : '点击桌面卡片右上角的星标即可收藏。'}

`; } const option = (value, label, current) => ``; function desktopSettings() { const d = selected(); return `

桌面连接设置

配置当前桌面的连接地址和显示参数。

`; } function settings() { let panel; if (settingsTab === 'desktop') panel = desktopSettings(); if (settingsTab === 'network') panel = `

网络状态

连接远程桌面前,请确认终端已接入工作网络。

连接方式
有线网络
网络状态
已连接
地址获取
自动获取(DHCP)

此处为设备状态示例。实际网络配置由终端系统管理。

`; if (settingsTab === 'device') panel = `

设备信息

联系管理员时,可提供设备序列号协助定位。

设备序列号
VDI-8F42A1
设备型号
RK3528 云终端
处理器架构
ARM64
管理快捷键
Ctrl + Alt + Shift + M
`; return `

终端设置

${panel}
`; } function session() { const d = selected(), failure = sessionState === 'error', recovery = sessionState === 'reconnect'; const title = failure ? '暂时无法连接' : recovery ? '正在恢复连接' : '正在连接桌面'; const steps = recovery ? ['检测网络连接','恢复桌面会话','恢复桌面画面'] : ['检查连接配置','建立远程会话','准备桌面画面']; return `
${mark(d)}

${title}

${escape(d.os)} · ${escape(d.name)}

${failure ? '
' : '
'}${failure ? `
尚未接入远程桌面服务
当前为交互原型,未向服务器发送连接请求。实际终端需由桌面服务返回连接结果。
` : `
    ${steps.map((step,i) => `
  1. ${i === 0 ? '✓' : i+1}${step}${i === 1 ? '' : ''}
  2. `).join('')}
${recovery ? '连接中断后将尝试恢复原会话,请保持设备联网。' : '请保持设备联网。连接完成后,桌面将全屏打开。'}
当前预览不发起真实远程连接。
`}
${failure ? `` : ''}
`; } function render() { root.innerHTML = header() + `
${view === 'desktops' ? list() : view === 'settings' ? settings() : session()}
` + footer(); if (view === 'desktops') renderGrid(); } function showWorkspace(next = 'desktops') { clearTimeout(timer); document.dispatchEvent(new CustomEvent('terminal:open-workspace')); view = next; login.classList.remove('active'); root.hidden = false; document.body.classList.add('in-workspace'); render(); window.scrollTo(0,0); } function home() { showWorkspace(); } function leave() { clearTimeout(timer); root.hidden = true; document.body.classList.remove('in-workspace'); login.classList.add('active'); identity = {name:'终端用户',mode:'preview',service:'cloud',organization:'越欣科技'}; tab='all';search=''; document.dispatchEvent(new CustomEvent('terminal:logout')); window.scrollTo(0,0); } function notify(message) { clearTimeout(toastTimer); root.querySelector('.ws-toast')?.remove(); const toast = document.createElement('div'); toast.className = 'ws-toast'; toast.setAttribute('role','status'); toast.textContent = message; root.append(toast); toastTimer = setTimeout(() => toast.remove(), 3000); } function start(id, state = 'connecting') { selectedId = id; if (!selected().endpoint) return; sessionState = state; showWorkspace('session'); if (state !== 'error') timer = setTimeout(() => { if (view === 'session') { sessionState = 'error'; render(); } }, 12000); } function toggleTheme() { const dark = document.body.dataset.theme === 'dark'; document.body.dataset.theme = dark ? 'light' : 'dark'; const oldTheme = document.getElementById('theme'); oldTheme.textContent = dark ? '◉ 浅色外观' : '▣ 深色外观'; if (!root.hidden) { const b = root.querySelector('[data-action="theme"]'); b.innerHTML = icon(dark ? 'moon' : 'sun');b.setAttribute('aria-label',themeLabel());b.title = themeLabel(); } } // Authentication interactions own login entry; direct preview URLs remain available for design review. document.addEventListener('terminal:login', event => { identity = {...identity,...event.detail};tab='all';search='';home(); const title = root.querySelector('h1'); if (title) { title.tabIndex = -1; title.focus({preventScroll:true}); } }); document.getElementById('theme').addEventListener('click', toggleTheme); root.addEventListener('input', event => { if (event.target.id === 'ws-search') { search = event.target.value; renderGrid(); } }); root.addEventListener('change', event => { if (event.target.id === 'ws-target') { selectedId = event.target.value; render(); } }); root.addEventListener('click', event => { const b = event.target.closest('[data-action]'); if (!b || b.disabled) return; switch (b.dataset.action) { case 'home': home(); break; case 'logout': leave(); break; case 'theme': toggleTheme(); break; case 'settings': settingsTab = 'desktop'; showWorkspace('settings'); break; case 'edit': selectedId = b.dataset.id; settingsTab = 'desktop'; showWorkspace('settings'); break; case 'settings-tab': settingsTab = b.dataset.tab; render(); break; case 'connect': start(b.dataset.id); break; case 'favorite': { const d = desktops.find(v => v.id === b.dataset.id); d.favorite = !d.favorite; persist(); render(); break; } case 'tab': tab = b.dataset.tab; render(); break; case 'clear-search': search = ''; tab = 'all'; render(); break; case 'refresh': render(); notify('示例桌面列表已刷新'); break; } }); root.addEventListener('submit', event => { if (event.target.id !== 'ws-settings-form') return; event.preventDefault(); const form = event.target, values = new FormData(form), endpoint = String(values.get('endpoint')).trim(), app = String(values.get('app')).trim(); const field = form.querySelector('[name="endpoint"]'); if (endpoint && !/^(?:[a-z\d](?:[a-z\d.-]*[a-z\d])?)(?::\d{1,5})?$/i.test(endpoint)) { form.querySelector('#ws-form-error').textContent = '请输入 IP 地址或域名,可附加端口号。';field.setAttribute('aria-invalid','true');field.focus();return; } if (!app) { form.querySelector('#ws-form-error').textContent = '请填写应用名,例如 Desktop。';form.querySelector('[name="app"]').focus();return; } const d = selected(); Object.assign(d,{endpoint,app,resolution:String(values.get('resolution')),fps:Number(values.get('fps'))}); const stored = persist();home();notify(stored ? '设置已保存到本机预览' : '设置已应用,本次会话内有效'); }); document.addEventListener('keydown', event => { if (event.ctrlKey && event.altKey && event.shiftKey && event.key.toLowerCase() === 'm') { event.preventDefault();settingsTab='desktop';showWorkspace('settings'); } if (event.key === 'Escape' && !root.hidden && view !== 'desktops') home(); }); const params = new URLSearchParams(location.search); const preview = params.get('preview'); if (preview) { if (['dark','light'].includes(params.get('theme'))) document.body.dataset.theme = params.get('theme'); if (['connecting','reconnect','error'].includes(preview)) start('windows',preview); else if (preview === 'settings') showWorkspace('settings'); else showWorkspace(); } })();