mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 05:50:44 +08:00
904 lines
44 KiB
JavaScript
904 lines
44 KiB
JavaScript
/**
|
||
* uds-auth Client Plugin - User info display + QR code login
|
||
* Includes embedded QR code library
|
||
*/
|
||
(function() {
|
||
// QR Code Library - Minimal but functional implementation
|
||
var QRCodeLib = (function() {
|
||
var ALPHANUMERIC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
|
||
|
||
function QRCode(data) {
|
||
this.data = data;
|
||
this.modules = null;
|
||
this.moduleCount = 0;
|
||
this.version = 1;
|
||
}
|
||
|
||
QRCode.prototype.getMinimumVersion = function() {
|
||
var len = this.data.length;
|
||
if (len <= 25) return 1;
|
||
if (len <= 47) return 2;
|
||
if (len <= 77) return 3;
|
||
if (len <= 114) return 4;
|
||
if (len <= 154) return 5;
|
||
if (len <= 195) return 6;
|
||
if (len <= 224) return 7;
|
||
return 8;
|
||
};
|
||
|
||
QRCode.prototype.make = function() {
|
||
this.version = this.getMinimumVersion();
|
||
this.moduleCount = this.version * 4 + 17;
|
||
this.modules = [];
|
||
|
||
for (var r = 0; r < this.moduleCount; r++) {
|
||
this.modules[r] = new Array(this.moduleCount);
|
||
for (var c = 0; c < this.moduleCount; c++) {
|
||
this.modules[r][c] = null;
|
||
}
|
||
}
|
||
|
||
// Add finder patterns with separator
|
||
this.addFinderPatternWithSeparator(0, 0);
|
||
this.addFinderPatternWithSeparator(this.moduleCount - 7, 0);
|
||
this.addFinderPatternWithSeparator(0, this.moduleCount - 7);
|
||
|
||
// Timing patterns
|
||
for (var i = 8; i < this.moduleCount - 8; i++) {
|
||
if (this.modules[6][i] === null) this.modules[6][i] = (i % 2 === 0);
|
||
if (this.modules[i][6] === null) this.modules[i][6] = (i % 2 === 0);
|
||
}
|
||
|
||
// Alignment patterns
|
||
var alignPos = this.getAlignmentPositions();
|
||
for (var ai = 0; ai < alignPos.length; ai++) {
|
||
for (var aj = 0; aj < alignPos.length; aj++) {
|
||
var row = alignPos[ai], col = alignPos[aj];
|
||
if (this.modules[row][col] === null) {
|
||
this.addAlignmentPattern(row, col);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Dark module
|
||
this.modules[this.moduleCount - 8][8] = true;
|
||
|
||
// Reserve format info areas
|
||
this.reserveFormatAreas();
|
||
|
||
// Place data
|
||
var bits = this.createData();
|
||
this.placeData(bits, 0);
|
||
};
|
||
|
||
QRCode.prototype.addFinderPatternWithSeparator = function(row, col) {
|
||
for (var r = -1; r <= 8; r++) {
|
||
for (var c = -1; c <= 8; c++) {
|
||
var tr = row + r, tc = col + c;
|
||
if (tr < 0 || tr >= this.moduleCount || tc < 0 || tc >= this.moduleCount) continue;
|
||
if (r === -1 || r === 8 || c === -1 || c === 8) {
|
||
this.modules[tr][tc] = false; // separator
|
||
} else if (r >= 0 && r < 7 && c >= 0 && c < 7) {
|
||
var inside = (r >= 1 && r <= 5 && (c === 1 || c === 5)) ||
|
||
(c >= 1 && c <= 5 && (r === 1 || r === 5)) ||
|
||
(r >= 2 && r <= 4 && c >= 2 && c <= 4);
|
||
this.modules[tr][tc] = inside;
|
||
} else {
|
||
this.modules[tr][tc] = true;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
QRCode.prototype.addAlignmentPattern = function(row, col) {
|
||
for (var r = -2; r <= 2; r++) {
|
||
for (var c = -2; c <= 2; c++) {
|
||
var tr = row + r, tc = col + c;
|
||
if (tr < 0 || tr >= this.moduleCount || tc < 0 || tc >= this.moduleCount) continue;
|
||
if (r === -2 || r === 2 || c === -2 || c === 2) {
|
||
this.modules[tr][tc] = true;
|
||
} else if (r === 0 && c === 0) {
|
||
this.modules[tr][tc] = true;
|
||
} else {
|
||
this.modules[tr][tc] = false;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
QRCode.prototype.getAlignmentPositions = function() {
|
||
if (this.version === 1) return [];
|
||
var positions = [];
|
||
var step = Math.ceil((this.version * 4 + 10) / (Math.floor(this.version / 7) + 1));
|
||
step = Math.ceil(step / 2) * 2;
|
||
var last = this.moduleCount - 7;
|
||
for (var pos = last; pos >= 0; pos -= step) {
|
||
positions.unshift(pos);
|
||
}
|
||
if (positions[0] !== 6) positions.unshift(6);
|
||
return positions;
|
||
};
|
||
|
||
QRCode.prototype.reserveFormatAreas = function() {
|
||
// Reserve format info around finder patterns
|
||
for (var i = 0; i < 9; i++) {
|
||
if (this.modules[8][i] === null) this.modules[8][i] = null; // reserved
|
||
if (this.modules[i][8] === null) this.modules[i][8] = null; // reserved
|
||
}
|
||
for (var i = 0; i < 8; i++) {
|
||
if (this.modules[this.moduleCount - 1 - i][8] === null) this.modules[this.moduleCount - 1 - i][8] = null;
|
||
if (this.modules[8][this.moduleCount - 1 - i] === null) this.modules[8][this.moduleCount - 1 - i] = null;
|
||
}
|
||
};
|
||
|
||
QRCode.prototype.createData = function() {
|
||
var bits = [];
|
||
// Mode: alphanumeric
|
||
bits.push(0, 0, 0, 1, 0);
|
||
// Length
|
||
var len = this.data.length;
|
||
var lenBits = this.version <= 9 ? 8 : 16;
|
||
for (var i = lenBits - 1; i >= 0; i--) {
|
||
bits.push((len >> i) & 1);
|
||
}
|
||
// Data
|
||
for (var i = 0; i < this.data.length; i += 2) {
|
||
var c1 = this.data.charAt(i);
|
||
var c2 = this.data.charAt(i + 1) || '';
|
||
var v1 = ALPHANUMERIC.indexOf(c1);
|
||
if (v1 < 0) v1 = 0;
|
||
var v2 = ALPHANUMERIC.indexOf(c2);
|
||
if (v2 < 0) v2 = 0;
|
||
var val = v1 * 45 + v2;
|
||
var bin = val.toString(2);
|
||
while (bin.length < 11) bin = '0' + bin;
|
||
for (var j = 0; j < 11; j++) {
|
||
bits.push(bin.charAt(j) === '1' ? 1 : 0);
|
||
}
|
||
}
|
||
// Terminator
|
||
var capacity = this.getDataCapacity();
|
||
bits.push(0, 0, 0, 0);
|
||
// Pad to byte boundary
|
||
while (bits.length % 8 !== 0) bits.push(0);
|
||
// Pad codewords
|
||
var pad1 = [1, 1, 0, 0, 0, 1, 0, 0];
|
||
var pad2 = [0, 0, 0, 1, 0, 0, 0, 1];
|
||
while (bits.length < capacity) {
|
||
for (var i = 0; i < 8 && bits.length < capacity; i++) bits.push(pad1[i]);
|
||
for (var i = 0; i < 8 && bits.length < capacity; i++) bits.push(pad2[i]);
|
||
}
|
||
return bits.slice(0, capacity);
|
||
};
|
||
|
||
QRCode.prototype.getDataCapacity = function() {
|
||
var capacities = [0, 152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192];
|
||
return capacities[this.version] || 272;
|
||
};
|
||
|
||
QRCode.prototype.placeData = function(bits, maskPattern) {
|
||
var upward = true;
|
||
var col = this.moduleCount - 1;
|
||
var bitIndex = 0;
|
||
|
||
while (col > 0) {
|
||
if (col === 6) col = 5; // skip timing column
|
||
|
||
for (var row = upward ? this.moduleCount - 1 : 0;
|
||
upward ? row >= 0 : row < this.moduleCount;
|
||
row += upward ? -1 : 1) {
|
||
for (var c = 0; c < 2; c++) {
|
||
var currentCol = col - c;
|
||
if (this.modules[row][currentCol] === null) {
|
||
var dark = bitIndex < bits.length ? bits[bitIndex++] === 1 : false;
|
||
if (this.getMask(maskPattern, row, currentCol)) dark = !dark;
|
||
this.modules[row][currentCol] = dark;
|
||
}
|
||
}
|
||
}
|
||
col -= 2;
|
||
upward = !upward;
|
||
}
|
||
};
|
||
|
||
QRCode.prototype.getMask = function(pattern, row, col) {
|
||
switch (pattern) {
|
||
case 0: return (row + col) % 2 === 0;
|
||
case 1: return row % 2 === 0;
|
||
case 2: return col % 3 === 0;
|
||
case 3: return (row + col) % 3 === 0;
|
||
case 4: return (Math.floor(row / 2) + Math.floor(col / 3)) % 2 === 0;
|
||
case 5: return ((row * col) % 2 + (row * col) % 3) === 0;
|
||
case 6: return (((row * col) % 2 + (row * col) % 3) % 2) === 0;
|
||
case 7: return (((row * col) % 3 + (row + col) % 2) % 2) === 0;
|
||
default: return false;
|
||
}
|
||
};
|
||
|
||
QRCode.prototype.isDark = function(row, col) {
|
||
if (row < 0 || row >= this.moduleCount || col < 0 || col >= this.moduleCount) return false;
|
||
return this.modules[row][col] === true;
|
||
};
|
||
|
||
QRCode.prototype.getModuleCount = function() {
|
||
return this.moduleCount;
|
||
};
|
||
|
||
return function(data) {
|
||
return new QRCode(data);
|
||
};
|
||
})();
|
||
|
||
function createQR(data, canvas, size) {
|
||
size = size || 160;
|
||
var qr = QRCodeLib(data);
|
||
qr.make();
|
||
var ctx = canvas.getContext('2d');
|
||
var count = qr.getModuleCount();
|
||
var tileSize = size / count;
|
||
canvas.width = size;
|
||
canvas.height = size;
|
||
ctx.fillStyle = '#ffffff';
|
||
ctx.fillRect(0, 0, size, size);
|
||
ctx.fillStyle = '#000000';
|
||
for (var row = 0; row < count; row++) {
|
||
for (var col = 0; col < count; col++) {
|
||
if (qr.isDark(row, col)) {
|
||
ctx.fillRect(Math.floor(col * tileSize), Math.floor(row * tileSize), Math.ceil(tileSize), Math.ceil(tileSize));
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
window.__ModuleLoader__.load({
|
||
id: 'uds-auth',
|
||
factory: function(require) {
|
||
var module = { exports: {} };
|
||
var exports = module.exports;
|
||
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||
|
||
var name = 'uds-auth';
|
||
var COOKIE_EMP_NO = 'PORTALSSOUser';
|
||
var COOKIE_AUTH_VALUE = 'PORTALSSOCookie';
|
||
var ALT_COOKIE_EMP_NO = 'ZTEDPGSSOUser';
|
||
var ALT_COOKIE_AUTH_VALUE = 'ZTEDPGSSOCookie';
|
||
var UAC_VERIFY_URL = 'https://uac.zte.com.cn/uacqr/auth/qrcode/verify.serv';
|
||
var LOGIN_SYSTEM_CODE = '100000455558';
|
||
|
||
var CSS = '.uds-auth-badge{display:flex;align-items:center;gap:6px;padding:4px 10px;border-radius:4px;background:var(--dsw-alias-bg-module-platform,rgba(242,243,245,1));border:1px solid var(--dsw-alias-border-l2,#dfe1e5);color:var(--dsw-alias-label-primary,#1f2329);font-size:13px;cursor:pointer;font-weight:500;transition:all .15s}.uds-auth-badge:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(247,248,250,1))}.uds-auth-badge-unauth{color:var(--dsw-alias-label-tertiary,#8f959e)}.uds-auth-badge-unauth:hover{color:var(--dsw-alias-label-secondary,#646a73)}.uds-auth-avatar{width:22px;height:22px;border-radius:50%;background:linear-gradient(135deg,var(--dsw-alias-state-business-primary,#3370ff),var(--dsw-alias-state-success-primary,#20a162));display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff}.uds-auth-badge-unauth .uds-auth-avatar{background:var(--dsw-alias-bg-module-platform,rgba(242,243,245,1));color:var(--dsw-alias-label-tertiary,#8f959e)}.uds-auth-role-badge{font-size:10px;padding:1px 6px;border-radius:10px;font-weight:600}.uds-auth-role-super_admin{background:rgba(213,73,65,.12);color:var(--dsw-alias-state-error-primary,#d54941)}.uds-auth-role-admin{background:rgba(217,119,6,.12);color:var(--dsw-alias-state-warn-primary,#d97706)}.uds-auth-role-fallback_admin{background:rgba(51,112,255,.12);color:var(--dsw-alias-state-business-primary,#3370ff)}.uds-auth-dropdown{position:absolute;top:100%;right:0;margin-top:8px;background:var(--dsw-alias-bg-layer-1,#fff);border-radius:8px;border:1px solid var(--dsw-alias-border-l2,#dee0e3);box-shadow:0 8px 28px rgba(0,0,0,.08);min-width:220px;z-index:1000;overflow:hidden}.uds-auth-info{padding:14px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,#eef0f3)}.uds-auth-info-name{font-weight:600;margin-bottom:4px;color:var(--dsw-alias-label-primary,#1f2329);font-size:14px}.uds-auth-info-detail{font-size:12px;color:var(--dsw-alias-label-secondary,#646a73);margin-top:2px}.uds-auth-info-detail-label{color:var(--dsw-alias-label-tertiary,#8f959e);margin-right:4px}.uds-auth-settings-btn{width:100%;padding:8px 16px;border:none;background:transparent;color:var(--dsw-alias-state-business-primary,#3370ff);font-size:13px;cursor:pointer;transition:background .15s;text-align:left}.uds-auth-settings-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(247,248,250,1))}.uds-auth-logout{width:100%;padding:10px 16px;border:none;background:transparent;color:var(--dsw-alias-state-error-primary,#d54941);font-size:13px;cursor:pointer;transition:background .15s}.uds-auth-logout:hover{background:rgba(213,73,65,.08)}.uds-auth-qr-container{text-align:center;padding:16px}.uds-auth-qr-canvas{margin:0 auto 12px;display:block}.uds-auth-qr-status{font-size:12px;color:var(--dsw-alias-label-secondary,#646a73);margin-top:8px}.uds-auth-qr-refresh{background:none;border:none;color:var(--dsw-alias-state-business-primary,#3370ff);cursor:pointer;font-size:12px;margin-top:8px;padding:4px 8px}.uds-auth-qr-refresh:hover{text-decoration:underline}.uds-auth-modal{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.4);z-index:9999;display:flex;align-items:center;justify-content:center}.uds-auth-modal-content{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#dee0e3);border-radius:8px;padding:20px;max-width:600px;width:90%;max-height:80vh;overflow:auto}.uds-auth-user-table{width:100%;border-collapse:collapse;font-size:13px}.uds-auth-user-table th,.uds-auth-user-table td{padding:8px;border-bottom:1px solid var(--dsw-alias-border-l1,#eef0f3);text-align:left}.uds-auth-user-table th{color:var(--dsw-alias-label-tertiary,#8f959e);font-weight:500}.uds-auth-del-btn{background:rgba(213,73,65,.1);color:var(--dsw-alias-state-error-primary,#d54941);border:none;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px;margin-left:8px}.uds-auth-del-btn:hover{background:rgba(213,73,65,.18)}.uds-auth-add-user{margin-top:16px;display:flex;gap:8px}.uds-auth-add-user input,.uds-auth-add-user select{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#dfe1e5);color:var(--dsw-alias-label-primary,#1f2329);padding:6px 10px;border-radius:4px;font-size:13px}.uds-auth-add-user button{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-state-business-primary,#3370ff);border:1px solid var(--dsw-alias-border-l2,#dfe1e5);padding:6px 14px;border-radius:4px;cursor:pointer;font-size:13px}.uds-auth-add-user button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(247,248,250,1))}';
|
||
|
||
function getCookie(name) {
|
||
var cookieName = encodeURIComponent(name) + '=';
|
||
var cookieStart = document.cookie.indexOf(cookieName);
|
||
if (cookieStart === -1) return null;
|
||
var cookieEnd = document.cookie.indexOf(';', cookieStart);
|
||
if (cookieEnd === -1) cookieEnd = document.cookie.length;
|
||
return decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
|
||
}
|
||
|
||
function setCookie(name, value, days) {
|
||
var expires = '';
|
||
if (days) {
|
||
var date = new Date();
|
||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||
expires = '; expires=' + date.toUTCString();
|
||
}
|
||
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + expires + '; path=/';
|
||
}
|
||
|
||
function getEmpNo() {
|
||
return getCookie(COOKIE_EMP_NO) || getCookie(ALT_COOKIE_EMP_NO) || null;
|
||
}
|
||
|
||
function injectCss() {
|
||
if (!document.getElementById('uds-auth-styles')) {
|
||
var style = document.createElement('style');
|
||
style.id = 'uds-auth-styles';
|
||
style.textContent = CSS;
|
||
document.head.appendChild(style);
|
||
}
|
||
}
|
||
|
||
function findHeaderArea() {
|
||
var selectors = ['[class*="headerUtilities"]', '[class*="header-actions"]', '[class*="sessionHeader"]'];
|
||
for (var i = 0; i < selectors.length; i++) {
|
||
var el = document.querySelector(selectors[i]);
|
||
if (el) return el;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function updateBadge(user) {
|
||
var badge = document.querySelector('#uds-auth-badge .uds-auth-badge');
|
||
var dropdown = document.getElementById('uds-auth-dropdown');
|
||
if (!badge || !dropdown) return;
|
||
|
||
if (!user) {
|
||
badge.innerHTML = '<span class="uds-auth-avatar">?</span><span>未登录</span>';
|
||
badge.className = 'uds-auth-badge uds-auth-badge-unauth';
|
||
dropdown.innerHTML = '<div class="uds-auth-info"><div class="uds-auth-info-name">未登录</div><div class="uds-auth-info-detail">请扫码登录</div></div>' +
|
||
'<div class="uds-auth-qr-container"><canvas id="qr-canvas" class="uds-auth-qr-canvas" width="160" height="160"></canvas><div id="qr-status" class="uds-auth-qr-status">加载中...</div><button id="qr-refresh" class="uds-auth-qr-refresh">刷新二维码</button></div>';
|
||
initQRLogin();
|
||
return;
|
||
}
|
||
|
||
var displayName = user.userName || user.name || '用户' + user.empNo;
|
||
var initials = displayName.slice(0, 2).toUpperCase();
|
||
var role = user.role || 'user';
|
||
var perms = user.permissions || {};
|
||
|
||
// 角色标签
|
||
var roleLabel = { super_admin: '超管', admin: '管理员', fallback_admin: '兜底', user: '' }[role] || '';
|
||
var roleBadge = roleLabel ? '<span class="uds-auth-role-badge uds-auth-role-' + role + '">' + roleLabel + '</span>' : '';
|
||
|
||
badge.innerHTML = '<span class="uds-auth-avatar">' + initials + '</span><span>' + displayName + '</span>' + roleBadge;
|
||
badge.className = 'uds-auth-badge';
|
||
|
||
var info = '<div class="uds-auth-info"><div class="uds-auth-info-name">' + displayName + '</div>';
|
||
info += '<div class="uds-auth-info-detail"><span class="uds-auth-info-detail-label">工号</span>' + (user.empNo || '-') + '</div>';
|
||
info += '<div class="uds-auth-info-detail"><span class="uds-auth-info-detail-label">角色</span>' + roleLabel + '</div>';
|
||
if (user.department) info += '<div class="uds-auth-info-detail"><span class="uds-auth-info-detail-label">部门</span>' + user.department + '</div>';
|
||
if (user.email) info += '<div class="uds-auth-info-detail"><span class="uds-auth-info-detail-label">邮箱</span>' + user.email + '</div>';
|
||
if (user.phone) info += '<div class="uds-auth-info-detail"><span class="uds-auth-info-detail-label">手机</span>' + user.phone + '</div>';
|
||
info += '</div>';
|
||
|
||
// 用户管理入口 (super_admin only)
|
||
if (perms.canManageUsers) {
|
||
info += '<button id="uds-user-mgmt-btn" class="uds-auth-settings-btn">👥 用户管理</button>';
|
||
}
|
||
// 设置入口 (admin+ only)
|
||
if (perms.canAccessSettings) {
|
||
info += '<button id="uds-goto-settings-btn" class="uds-auth-settings-btn">⚙️ 系统设置</button>';
|
||
}
|
||
info += '<button id="uds-logout-btn" class="uds-auth-logout">退出登录</button>';
|
||
dropdown.innerHTML = info;
|
||
|
||
var settingsBtn = document.getElementById('uds-goto-settings-btn');
|
||
if (settingsBtn) settingsBtn.onclick = function() { window.open('/settings', '_self'); };
|
||
|
||
var mgmtBtn = document.getElementById('uds-user-mgmt-btn');
|
||
if (mgmtBtn) mgmtBtn.onclick = function() { openUserManagement(); };
|
||
|
||
var logoutBtn = document.getElementById('uds-logout-btn');
|
||
if (logoutBtn) {
|
||
logoutBtn.onclick = function() {
|
||
setCookie(COOKIE_EMP_NO, '', -1);
|
||
setCookie(COOKIE_AUTH_VALUE, '', -1);
|
||
setCookie(ALT_COOKIE_EMP_NO, '', -1);
|
||
setCookie(ALT_COOKIE_AUTH_VALUE, '', -1);
|
||
setCookie('UDS_FALLBACK_USER', '', -1);
|
||
location.reload();
|
||
};
|
||
}
|
||
}
|
||
|
||
function openUserManagement() {
|
||
fetch('/uds-auth/api/users')
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
var users = data.users || [];
|
||
var html = '<div class="uds-auth-user-mgmt"><h4>用户管理</h4>';
|
||
html += '<table class="uds-auth-user-table"><thead><tr><th>工号</th><th>角色</th><th>操作</th></tr></thead><tbody>';
|
||
users.forEach(function(u) {
|
||
var roleLabel = { super_admin: '超级管理员', admin: '管理员', user: '普通用户', fallback_admin: '兜底管理员' }[u.role] || u.role;
|
||
var rolesOpts = '<option value="user"' + (u.role === 'user' ? ' selected' : '') + '>普通用户</option>' +
|
||
'<option value="admin"' + (u.role === 'admin' ? ' selected' : '') + '>管理员</option>' +
|
||
'<option value="super_admin"' + (u.role === 'super_admin' ? ' selected' : '') + '>超级管理员</option>';
|
||
html += '<tr><td>' + u.empNo + '</td><td>' + roleLabel + '</td><td>' +
|
||
'<select onchange="udsChangeRole(\'' + u.empNo + '\', this.value)">' + rolesOpts + '</select> ' +
|
||
'<button onclick="udsDeleteUser(\'' + u.empNo + '\')" class="uds-auth-del-btn">删除</button></td></tr>';
|
||
});
|
||
html += '</tbody></table>';
|
||
html += '<div class="uds-auth-add-user"><input id="uds-new-empno" placeholder="工号"><select id="uds-new-role"><option value="user">普通用户</option><option value="admin">管理员</option></select><button onclick="udsAddUser()">添加</button></div>';
|
||
html += '</div>';
|
||
|
||
var panel = document.createElement('div');
|
||
panel.className = 'uds-auth-modal';
|
||
panel.innerHTML = '<div class="uds-auth-modal-content" id="uds-user-mgmt-panel">' + html + '</div>';
|
||
document.body.appendChild(panel);
|
||
panel.onclick = function(e) { if (e.target === panel) panel.remove(); };
|
||
})
|
||
.catch(function(err) { alert('加载用户列表失败: ' + err.message); });
|
||
}
|
||
|
||
// 全局 API 供 onclick 用
|
||
window.udsChangeRole = function(empNo, role) {
|
||
fetch('/uds-auth/api/users/role', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ empNo: empNo, role: role })
|
||
}).then(function(r) { return r.json(); })
|
||
.then(function(d) { if (d.error) alert(d.error); else alert(d.message); openUserManagement(); })
|
||
.catch(function(err) { alert('操作失败: ' + err.message); });
|
||
};
|
||
window.udsDeleteUser = function(empNo) {
|
||
if (!confirm('确认删除用户 ' + empNo + '?')) return;
|
||
fetch('/uds-auth/api/users/delete', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ empNo: empNo })
|
||
}).then(function(r) { return r.json(); })
|
||
.then(function(d) { if (d.error) alert(d.error); else alert(d.message); openUserManagement(); })
|
||
.catch(function(err) { alert('操作失败: ' + err.message); });
|
||
};
|
||
window.udsAddUser = function() {
|
||
var empNo = document.getElementById('uds-new-empno').value.trim();
|
||
var role = document.getElementById('uds-new-role').value;
|
||
if (!empNo) return;
|
||
fetch('/uds-auth/api/users', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ empNo: empNo, role: role })
|
||
}).then(function(r) { return r.json(); })
|
||
.then(function(d) { if (d.error) alert(d.error); else openUserManagement(); })
|
||
.catch(function(err) { alert('操作失败: ' + err.message); });
|
||
};
|
||
|
||
var currentQRKey = null;
|
||
var currentQRValue = null;
|
||
var qrPollingTimer = null;
|
||
|
||
function generateUUID() {
|
||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||
var r = Math.random() * 16 | 0;
|
||
var v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||
return v.toString(16);
|
||
});
|
||
}
|
||
|
||
function md5(str) {
|
||
function md5cycle(x, k) {
|
||
var a = x[0], b = x[1], c = x[2], d = x[3];
|
||
a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586);
|
||
c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330);
|
||
a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426);
|
||
c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983);
|
||
a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417);
|
||
c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162);
|
||
a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101);
|
||
c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329);
|
||
a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632);
|
||
c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302);
|
||
a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083);
|
||
c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848);
|
||
a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690);
|
||
c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501);
|
||
a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784);
|
||
c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734);
|
||
a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463);
|
||
c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556);
|
||
a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353);
|
||
c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640);
|
||
a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222);
|
||
c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189);
|
||
a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835);
|
||
c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651);
|
||
a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415);
|
||
c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[3], 21, -57434055);
|
||
a = ii(a, b, c, d, k[10], 6, 1700485571); d = ii(d, a, b, c, k[1], 10, -1894986606);
|
||
c = ii(c, d, a, b, k[8], 15, -1051523); b = ii(b, c, d, a, k[15], 21, -2054922799);
|
||
a = ii(a, b, c, d, k[6], 6, 1873313359); d = ii(d, a, b, c, k[13], 10, -30611744);
|
||
c = ii(c, d, a, b, k[4], 15, -1560198380); b = ii(b, c, d, a, k[11], 21, 1309151649);
|
||
a = ii(a, b, c, d, k[2], 6, -145523070); d = ii(d, a, b, c, k[9], 10, -1120210379);
|
||
c = ii(c, d, a, b, k[0], 15, 718787259); b = ii(b, c, d, a, k[7], 21, -343485551);
|
||
x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]);
|
||
}
|
||
function cmn(q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); }
|
||
function ff(a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }
|
||
function gg(a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }
|
||
function hh(a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }
|
||
function ii(a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }
|
||
function md51(s) {
|
||
var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i;
|
||
for (i = 64; i <= s.length; i += 64) md5cycle(state, md5blk(s.substring(i - 64, i)));
|
||
s = s.substring(i - 64);
|
||
var tail = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||
for (i = 0; i < s.length; i++) tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
|
||
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
||
if (i > 55) { md5cycle(state, tail); tail = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; }
|
||
tail[14] = n * 8; md5cycle(state, tail);
|
||
return state;
|
||
}
|
||
function md5blk(s) {
|
||
var md5blks = [], i;
|
||
for (i = 0; i < 64; i += 4) md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
||
return md5blks;
|
||
}
|
||
var hex_chr = '0123456789abcdef'.split('');
|
||
function rhex(n) { var s = '', j = 0; for (; j < 4; j++) s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; return s; }
|
||
function hex(x) { for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]); return x.join(''); }
|
||
function add32(a, b) { return (a + b) & 0xFFFFFFFF; }
|
||
return hex(md51(str));
|
||
}
|
||
|
||
// SHA256 implementation for verifyCode
|
||
function sha256(str) {
|
||
function rotl(n, s) { return (n << s) | (n >>> (32 - s)); }
|
||
function sha256_0(x, y, z) { return (x & y) ^ (~x & z); }
|
||
function sha256_1(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); }
|
||
function sha256_2(x) { return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10); }
|
||
function sha256_3(x) { return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7); }
|
||
function sha256_4(x) { return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10); }
|
||
function sha256_5(x) { return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7); }
|
||
function sha256_6(x) { return (x >>> 2 | x << 30) ^ (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13); }
|
||
function sha256_7(x) { return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3); }
|
||
|
||
var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
|
||
|
||
var h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
|
||
|
||
var msg = [];
|
||
for (var i = 0; i < str.length; i++) {
|
||
msg.push(str.charCodeAt(i));
|
||
}
|
||
var msgLen = msg.length;
|
||
var bitLen = msgLen * 8;
|
||
|
||
msg.push(0x80);
|
||
while ((msg.length % 64) !== 56) msg.push(0);
|
||
for (var i = 7; i >= 0; i--) msg.push((bitLen >>> (i * 8)) & 0xff);
|
||
|
||
for (var i = 0; i < msg.length; i += 64) {
|
||
var w = [];
|
||
for (var j = 0; j < 16; j++) {
|
||
w[j] = (msg[i + j * 4] << 24) | (msg[i + j * 4 + 1] << 16) | (msg[i + j * 4 + 2] << 8) | msg[i + j * 4 + 3];
|
||
}
|
||
for (var j = 16; j < 64; j++) {
|
||
var s0 = sha256_7(w[j - 15]);
|
||
var s1 = sha256_6(w[j - 2]);
|
||
w[j] = (w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff;
|
||
}
|
||
|
||
var a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];
|
||
|
||
for (var j = 0; j < 64; j++) {
|
||
var s1 = sha256_3(e);
|
||
var ch = sha256_0(e, f, g);
|
||
var temp1 = (hh + s1 + ch + K[j] + w[j]) & 0xffffffff;
|
||
var s0 = sha256_2(a);
|
||
var maj = sha256_1(a, b, c);
|
||
var temp2 = (s0 + maj) & 0xffffffff;
|
||
|
||
hh = g; g = f; f = e; e = (d + temp1) & 0xffffffff;
|
||
d = c; c = b; b = a; a = (temp1 + temp2) & 0xffffffff;
|
||
}
|
||
|
||
h[0] = (h[0] + a) & 0xffffffff;
|
||
h[1] = (h[1] + b) & 0xffffffff;
|
||
h[2] = (h[2] + c) & 0xffffffff;
|
||
h[3] = (h[3] + d) & 0xffffffff;
|
||
h[4] = (h[4] + e) & 0xffffffff;
|
||
h[5] = (h[5] + f) & 0xffffffff;
|
||
h[6] = (h[6] + g) & 0xffffffff;
|
||
h[7] = (h[7] + hh) & 0xffffffff;
|
||
}
|
||
|
||
var hex = '0123456789abcdef';
|
||
var result = '';
|
||
for (var i = 0; i < 8; i++) {
|
||
result += hex[(h[i] >>> 28) & 0xf];
|
||
result += hex[(h[i] >>> 24) & 0xf];
|
||
result += hex[(h[i] >>> 20) & 0xf];
|
||
result += hex[(h[i] >>> 16) & 0xf];
|
||
result += hex[(h[i] >>> 12) & 0xf];
|
||
result += hex[(h[i] >>> 8) & 0xf];
|
||
result += hex[(h[i] >>> 4) & 0xf];
|
||
result += hex[h[i] & 0xf];
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function initQRLogin() {
|
||
var canvas = document.getElementById('qr-canvas');
|
||
var qrStatus = document.getElementById('qr-status');
|
||
var refreshBtn = document.getElementById('qr-refresh');
|
||
if (!canvas || !qrStatus) { setTimeout(initQRLogin, 500); return; }
|
||
if (refreshBtn) refreshBtn.onclick = startQRLogin;
|
||
startQRLogin();
|
||
}
|
||
|
||
// 从本地 vendor 加载 jQuery 和 UAC md5 插件
|
||
function loadUACMd5Plugin() {
|
||
return new Promise(function(resolve, reject) {
|
||
function ensureJQuery(callback) {
|
||
if (window.jQuery) { callback(window.jQuery); return; }
|
||
var script = document.createElement('script');
|
||
script.src = '/uds-auth/vendor/jquery.min.js';
|
||
script.onload = function() { callback(window.jQuery); };
|
||
script.onerror = function() { reject(new Error('Failed to load jQuery')); };
|
||
document.head.appendChild(script);
|
||
}
|
||
|
||
ensureJQuery(function($) {
|
||
if ($.md5QrCode) { resolve($); return; }
|
||
var script = document.createElement('script');
|
||
script.src = '/uds-auth/vendor/jquery.md5.min.js';
|
||
script.onload = function() { resolve($); };
|
||
script.onerror = function() { reject(new Error('Failed to load UAC md5 plugin')); };
|
||
document.head.appendChild(script);
|
||
});
|
||
});
|
||
}
|
||
|
||
function startQRLogin() {
|
||
var canvas = document.getElementById('qr-canvas');
|
||
var qrStatus = document.getElementById('qr-status');
|
||
if (!canvas || !qrStatus) return;
|
||
if (qrPollingTimer) { clearInterval(qrPollingTimer); qrPollingTimer = null; }
|
||
|
||
qrStatus.textContent = '正在生成二维码...';
|
||
|
||
loadUACMd5Plugin()
|
||
.then(function($) {
|
||
var qrCodeStr = $.md5QrCode('TwoDIMAuth');
|
||
var parts = qrCodeStr.split(':');
|
||
currentQRKey = parts[1];
|
||
currentQRValue = parts[2];
|
||
|
||
qrStatus.textContent = '请使用iCenter扫码登录...';
|
||
|
||
var container = canvas.parentElement;
|
||
if (container) {
|
||
canvas.style.display = 'none';
|
||
var img = container.querySelector('.uds-auth-qr-img');
|
||
if (!img) {
|
||
img = document.createElement('img');
|
||
img.className = 'uds-auth-qr-img';
|
||
img.style.width = '160px';
|
||
img.style.height = '160px';
|
||
img.style.margin = '0 auto';
|
||
img.style.display = 'block';
|
||
container.insertBefore(img, qrStatus);
|
||
}
|
||
img.src = '/uds-auth/qr?data=' + encodeURIComponent(qrCodeStr);
|
||
|
||
qrPollingTimer = setInterval(pollQRStatus, 2000);
|
||
}
|
||
})
|
||
.catch(function(err) {
|
||
qrStatus.textContent = '加载加密插件失败';
|
||
});
|
||
}
|
||
|
||
function pollQRStatus() {
|
||
var qrStatus = document.getElementById('qr-status');
|
||
if (!qrStatus || !currentQRKey) return;
|
||
|
||
var source = currentQRKey + currentQRValue + '127.0.0.1' + LOGIN_SYSTEM_CODE + '';
|
||
var verifyCode = window.jQuery ? window.jQuery.verfiyCode(source) : null;
|
||
|
||
var bodyPromise;
|
||
if (verifyCode) {
|
||
bodyPromise = Promise.resolve(JSON.stringify({
|
||
qrCodeKey: currentQRKey,
|
||
qrCodeValue: currentQRValue,
|
||
loginClientIp: '127.0.0.1',
|
||
originSystemCode: '',
|
||
loginSystemCode: LOGIN_SYSTEM_CODE,
|
||
verifyCode: verifyCode
|
||
}));
|
||
} else {
|
||
var verifyUrl = '/uds-auth/verify-code?qrCodeKey=' + encodeURIComponent(currentQRKey) + '&qrCodeValue=' + encodeURIComponent(currentQRValue) + '&loginClientIp=127.0.0.1&loginSystemCode=' + LOGIN_SYSTEM_CODE;
|
||
bodyPromise = fetch(verifyUrl)
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
return JSON.stringify({
|
||
qrCodeKey: currentQRKey,
|
||
qrCodeValue: currentQRValue,
|
||
loginClientIp: '127.0.0.1',
|
||
originSystemCode: '',
|
||
loginSystemCode: LOGIN_SYSTEM_CODE,
|
||
verifyCode: data.verifyCode
|
||
});
|
||
});
|
||
}
|
||
|
||
bodyPromise.then(function(body) {
|
||
return fetch('/uds-auth/qr-proxy', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: body });
|
||
})
|
||
.then(function(r) { return r.json(); })
|
||
.then(handleQRResult)
|
||
.catch(function(err) {
|
||
if (qrStatus) qrStatus.textContent = '网络错误,重试中...';
|
||
});
|
||
}
|
||
|
||
function handleQRResult(result) {
|
||
var qrStatus = document.getElementById('qr-status');
|
||
if (!qrStatus) return;
|
||
|
||
var code = result.code || {};
|
||
var codeCode = code.code || '';
|
||
var bo = result.bo || {};
|
||
var boCode = bo.code || '';
|
||
var boMsg = bo.msg || '';
|
||
|
||
// 成功
|
||
if (codeCode === '0000' && boCode === '0000') {
|
||
clearInterval(qrPollingTimer); qrPollingTimer = null;
|
||
currentQRKey = null; currentQRValue = null;
|
||
var other = result.other || {};
|
||
var empNo = other.account || other.empNo || '';
|
||
var token = other.token || other.authValue || '';
|
||
if (empNo) {
|
||
setCookie(COOKIE_EMP_NO, empNo, 7);
|
||
if (token) setCookie(COOKIE_AUTH_VALUE, token, 7);
|
||
qrStatus.textContent = '登录成功!';
|
||
fetchUserAndPermissions(function(user) { updateBadge(user); });
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 等待扫码
|
||
if (codeCode === '0000' && boCode === '4002') {
|
||
qrStatus.textContent = '等待扫码...';
|
||
return;
|
||
}
|
||
|
||
// 二维码已过期
|
||
if (codeCode === '0000' && boCode === '1002') {
|
||
clearInterval(qrPollingTimer); qrPollingTimer = null;
|
||
qrStatus.textContent = '二维码已过期,请点击刷新';
|
||
currentQRKey = null; currentQRValue = null;
|
||
return;
|
||
}
|
||
|
||
// 其他可恢复错误:继续轮询
|
||
if (codeCode !== '0000') {
|
||
return;
|
||
}
|
||
|
||
// 其他 bo.code 错误:停止轮询
|
||
clearInterval(qrPollingTimer); qrPollingTimer = null;
|
||
qrStatus.textContent = '⚠️ ' + (boMsg || boCode || '登录失败') + ',请刷新重试';
|
||
}
|
||
|
||
function getAuthToken() {
|
||
return getCookie(COOKIE_AUTH_VALUE) || getCookie(ALT_COOKIE_AUTH_VALUE) || null;
|
||
}
|
||
|
||
function fetchUserAndPermissions(cb) {
|
||
var empNo = getEmpNo();
|
||
if (!empNo) { updateBadge(null); return; }
|
||
// 先拉 /api/me 拿 role/permissions
|
||
fetch('/uds-auth/api/me')
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(me) {
|
||
var permissions = me?.data?.permissions || {};
|
||
var role = me?.data?.role || 'user';
|
||
cb({ empNo: empNo, userName: null, role: role, permissions: permissions });
|
||
// 再拉详细用户信息
|
||
fetchUserInfo(function(user) {
|
||
cb(Object.assign({ role: role, permissions: permissions }, user));
|
||
});
|
||
})
|
||
.catch(function() { fetchUserInfo(cb); });
|
||
}
|
||
|
||
function fetchUserInfo(cb) {
|
||
var empNo = getEmpNo();
|
||
if (!empNo) { updateBadge(null); return; }
|
||
var token = getAuthToken();
|
||
var url = '/uds-auth/user-info?empNo=' + encodeURIComponent(empNo);
|
||
if (token) url += '&token=' + encodeURIComponent(token);
|
||
fetch(url)
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(result) {
|
||
if (result.code && result.code.code === '0000' && result.bo && result.bo.length > 0) {
|
||
var emp = result.bo[0];
|
||
// icenterapi 返回字段名可能不一致,兼容多种常见命名
|
||
var dept = emp.deptName || emp.deptShortName || emp.deptFullName
|
||
|| emp.orgName || emp.orgShortName || emp.orgNamePath || emp.deptNamePath
|
||
|| emp.departmentName || emp.department || '';
|
||
var name = emp.name || emp.empName || emp.userName || emp.displayName || '用户' + empNo;
|
||
var user = {
|
||
empNo: emp.employeeShortId || emp.employeeNO || emp.employeeId || empNo,
|
||
userName: name,
|
||
department: dept,
|
||
email: emp.email || emp.mail || '',
|
||
phone: emp.mobile || emp.phone || emp.telephone || ''
|
||
};
|
||
if (cb) cb(user); else updateBadge(user);
|
||
} else {
|
||
var fallback = { empNo: empNo, userName: '用户' + empNo };
|
||
if (cb) cb(fallback); else updateBadge(fallback);
|
||
}
|
||
})
|
||
.catch(function() {
|
||
var fallback = { empNo: empNo, userName: '用户' + empNo };
|
||
if (cb) cb(fallback); else updateBadge(fallback);
|
||
});
|
||
}
|
||
|
||
function injectBadge() {
|
||
if (document.getElementById('uds-auth-badge')) return true;
|
||
var headerArea = findHeaderArea();
|
||
if (!headerArea) return false;
|
||
var container = document.createElement('div');
|
||
container.id = 'uds-auth-badge';
|
||
container.style.cssText = 'position:relative;display:flex;align-items:center;margin-left:8px';
|
||
var badge = document.createElement('button');
|
||
badge.className = 'uds-auth-badge uds-auth-badge-unauth';
|
||
badge.innerHTML = '<span class="uds-auth-avatar">...</span><span>加载中</span>';
|
||
var dropdown = document.createElement('div');
|
||
dropdown.id = 'uds-auth-dropdown';
|
||
dropdown.className = 'uds-auth-dropdown';
|
||
dropdown.style.display = 'none';
|
||
dropdown.innerHTML = '<div class="uds-auth-info"><div class="uds-auth-info-name">加载中...</div></div>';
|
||
badge.onclick = function() { dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none'; };
|
||
container.appendChild(badge);
|
||
container.appendChild(dropdown);
|
||
headerArea.appendChild(container);
|
||
fetchUserAndPermissions(function(user) { updateBadge(user); });
|
||
return true;
|
||
}
|
||
|
||
function init() {
|
||
injectCss();
|
||
var timer = setInterval(function() { if (injectBadge()) clearInterval(timer); }, 500);
|
||
}
|
||
|
||
function apply(ctx) {
|
||
// 兜底:无论 ctx.inject / connection 是否可用,都保证 init 被调用
|
||
var inited = false;
|
||
function safeInit() {
|
||
if (inited) return;
|
||
inited = true;
|
||
try { init(); } catch (e) { console.error('[uds-auth] init error:', e); }
|
||
}
|
||
// 立即兜底:如果 connection 注入迟迟不到,3 秒后自行初始化
|
||
var fallbackTimer = setTimeout(safeInit, 3000);
|
||
|
||
try {
|
||
if (ctx && typeof ctx.inject === 'function') {
|
||
ctx.inject(['connection'], function(connCtx) {
|
||
clearTimeout(fallbackTimer);
|
||
try {
|
||
var call = connCtx && connCtx.connection && connCtx.connection.rpc && connCtx.connection.rpc.call;
|
||
function loadConfigAndInit() {
|
||
if (typeof call !== 'function') { safeInit(); return; }
|
||
call('/uds-auth', 'config.get').then(function(resp) {
|
||
if (resp && resp.ok && resp.value) {
|
||
var c = resp.value;
|
||
if (c.loginSystemCode) LOGIN_SYSTEM_CODE = c.loginSystemCode;
|
||
if (c.uacBaseUrl) UAC_VERIFY_URL = c.uacBaseUrl.replace(/\/$/, '') + '/uacqr/auth/qrcode/verify.serv';
|
||
}
|
||
safeInit();
|
||
}).catch(function() { safeInit(); });
|
||
}
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', loadConfigAndInit);
|
||
else loadConfigAndInit();
|
||
if (connCtx && typeof connCtx.effect === 'function') {
|
||
connCtx.effect(function() {}, 'uds-auth client: rpc effect');
|
||
}
|
||
} catch (e) {
|
||
console.error('[uds-auth] apply inject error:', e);
|
||
safeInit();
|
||
}
|
||
});
|
||
} else {
|
||
clearTimeout(fallbackTimer);
|
||
safeInit();
|
||
}
|
||
} catch (e) {
|
||
console.error('[uds-auth] apply error:', e);
|
||
clearTimeout(fallbackTimer);
|
||
safeInit();
|
||
}
|
||
}
|
||
|
||
exports.name = 'uds-auth';
|
||
exports.inject = ['connection'];
|
||
exports.apply = apply;
|
||
return module.exports;
|
||
}
|
||
});
|
||
})();
|