Revert "merge: integrate extension auto-update"
This reverts commit322475860a, reversing changes made tof52d9d7413.
This commit is contained in:
1 parent
b2e33e2e5d
commit
81a0cdac8e
47 files changed
+169
-3108
No files matched your search
+19
-236
@@ -29,8 +29,6 @@ let erpAutomationEnabled = true;
|
||||
let erpHostAccessReady = true;
|
||||
let erpSessionReady = true;
|
||||
let extensionCompatible = false;
|
||||
let extensionUpdateBlocksExecution = true;
|
||||
let extensionReloadRequestedVersion = '';
|
||||
let latestAiStatus = null;
|
||||
let latestBridgeStatus = null;
|
||||
let activeStatusDetails = '';
|
||||
@@ -51,9 +49,6 @@ let parserRoutingBusy = false;
|
||||
let accountList = [];
|
||||
let accountTaskTypes = [];
|
||||
let accountSettingsBusy = false;
|
||||
let extensionReleaseBusy = false;
|
||||
let extensionReleaseEnabled = false;
|
||||
let extensionReleaseList = [];
|
||||
let accountAuthorizationTargetId = '';
|
||||
let accountAuthorizationDraft = new Set();
|
||||
let auditEvents = [];
|
||||
@@ -104,7 +99,7 @@ const persistedExtensionResultVersions = new Map();
|
||||
let taskCreateInProgress = false;
|
||||
const AUTO_HANDOFF_RETRY_MS = 30_000;
|
||||
|
||||
const REQUIRED_EXTENSION_VERSION = '0.5.167';
|
||||
const REQUIRED_EXTENSION_VERSION = '0.5.165';
|
||||
const MANUAL_HANDOFF_LABEL = '确认并提交到 ERP 插件';
|
||||
const RETRY_HANDOFF_LABEL = '继续提交到 ERP 插件';
|
||||
const RECONCILE_LABEL = '只读回查 ERP 现有结果';
|
||||
@@ -958,8 +953,6 @@ function validateAccountCreationValues(values = {}) {
|
||||
const password = String(values.password || '');
|
||||
const role = String(values.role || '');
|
||||
const erpAccount = String(values.erpAccount || '').trim();
|
||||
const ecsRegion = String(values.ecsRegion || '').trim().toLowerCase();
|
||||
const ecsInstance = String(values.ecsInstance || '').trim();
|
||||
if (!username || username.length > 160) {
|
||||
return { ok: false, field: 'accountUsername', message: '账号必须为 1—160 个字符。' };
|
||||
}
|
||||
@@ -972,15 +965,6 @@ function validateAccountCreationValues(values = {}) {
|
||||
if (role !== 'admin' && (!erpAccount || erpAccount.length > 200)) {
|
||||
return { ok: false, field: 'accountErpAccount', message: '普通用户或组长必须填写 1—200 个字符的 ERP 账号。' };
|
||||
}
|
||||
if (role !== 'admin' && Boolean(ecsRegion) !== Boolean(ecsInstance)) {
|
||||
return { ok: false, field: ecsRegion ? 'accountEcsInstance' : 'accountEcsRegion', message: 'ECS 地域与实例必须同时填写或同时留空。' };
|
||||
}
|
||||
if (ecsRegion && !/^[a-z0-9][a-z0-9-]{0,63}$/.test(ecsRegion)) {
|
||||
return { ok: false, field: 'accountEcsRegion', message: 'ECS 地域 ID 格式无效。' };
|
||||
}
|
||||
if (ecsInstance && !/^i-[A-Za-z0-9]{6,64}$/.test(ecsInstance)) {
|
||||
return { ok: false, field: 'accountEcsInstance', message: 'ECS 实例 ID 格式无效。' };
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
body: {
|
||||
@@ -988,8 +972,6 @@ function validateAccountCreationValues(values = {}) {
|
||||
password,
|
||||
role,
|
||||
erp_account: role === 'admin' ? '' : erpAccount,
|
||||
extension_ecs_region_id: role === 'admin' ? '' : ecsRegion,
|
||||
extension_ecs_instance_id: role === 'admin' ? '' : ecsInstance,
|
||||
business_route_ids: []
|
||||
}
|
||||
};
|
||||
@@ -1025,7 +1007,7 @@ function showAccountCreationValidationError(validation) {
|
||||
}
|
||||
|
||||
function clearAccountCreationValidationErrors() {
|
||||
for (const fieldId of ['accountUsername', 'accountPassword', 'accountRole', 'accountErpAccount', 'accountEcsRegion', 'accountEcsInstance']) {
|
||||
for (const fieldId of ['accountUsername', 'accountPassword', 'accountRole', 'accountErpAccount']) {
|
||||
document.getElementById(fieldId)?.removeAttribute('aria-invalid');
|
||||
}
|
||||
}
|
||||
@@ -1052,15 +1034,6 @@ function renderAccounts() {
|
||||
main.append(el('p', account.erp_account ? 'muted' : 'channel-error', account.role === 'admin'
|
||||
? 'ERP 执行身份:不绑定'
|
||||
: `ERP 执行身份:${account.erp_account || '未配置(禁止执行)'}`));
|
||||
main.append(el(
|
||||
'p',
|
||||
account.extension_ecs_instance_id ? 'muted' : 'channel-error',
|
||||
account.role === 'admin'
|
||||
? '插件云主机:不绑定'
|
||||
: account.extension_ecs_instance_id
|
||||
? `插件云主机:${account.extension_ecs_region_id} / ${account.extension_ecs_instance_id}`
|
||||
: '插件云主机:未配置(不自动更新)'
|
||||
));
|
||||
const authorizedCount = Array.isArray(account.authorized_business_route_ids)
|
||||
? account.authorized_business_route_ids.length
|
||||
: 0;
|
||||
@@ -1104,12 +1077,7 @@ function renderAccounts() {
|
||||
erpAccount.dataset.accountAction = 'erp-account';
|
||||
erpAccount.dataset.accountId = account.id;
|
||||
erpAccount.disabled = accountSettingsBusy || account.role === 'admin';
|
||||
const extensionHost = el('button', 'secondary-button', '云主机');
|
||||
extensionHost.type = 'button';
|
||||
extensionHost.dataset.accountAction = 'extension-host';
|
||||
extensionHost.dataset.accountId = account.id;
|
||||
extensionHost.disabled = accountSettingsBusy || account.role === 'admin';
|
||||
actions.append(role, authorizations, erpAccount, extensionHost, toggle, reset, revoke);
|
||||
actions.append(role, authorizations, erpAccount, toggle, reset, revoke);
|
||||
row.append(main, actions);
|
||||
container.append(row);
|
||||
}
|
||||
@@ -1208,67 +1176,6 @@ async function syncAccounts() {
|
||||
accountTaskTypes = Array.isArray(result.task_types) ? result.task_types : [];
|
||||
renderAccounts();
|
||||
renderChannelOwnerOptions();
|
||||
await syncExtensionReleases().catch((error) => {
|
||||
const message = $('#extensionReleaseMessage');
|
||||
if (message) message.textContent = error.message || String(error);
|
||||
});
|
||||
}
|
||||
|
||||
function renderExtensionReleases() {
|
||||
const summary = $('#extensionReleaseSummary');
|
||||
const button = $('#extensionReleasePublish');
|
||||
const file = $('#extensionReleaseFile');
|
||||
if (!summary || !button || !file) return;
|
||||
const active = extensionReleaseList.find((release) => release.is_active === true);
|
||||
summary.textContent = !extensionReleaseEnabled
|
||||
? '服务端自动更新尚未启用;启用配置后才能发布。'
|
||||
: active
|
||||
? `当前活动版本 ${active.version} · ${Math.round(Number(active.byte_size || 0) / 1024)} KB · ${new Date(active.created_at).toLocaleString('zh-CN')}`
|
||||
: '自动更新已启用,尚未发布任何插件包。';
|
||||
button.disabled = extensionReleaseBusy || !extensionReleaseEnabled || !file.files?.length;
|
||||
file.disabled = extensionReleaseBusy || !extensionReleaseEnabled;
|
||||
}
|
||||
|
||||
async function syncExtensionReleases() {
|
||||
if (!isAdministrator() || !IS_ACCOUNTS_PAGE) return;
|
||||
const result = await apiRequest('/api/extension-updates/releases');
|
||||
extensionReleaseEnabled = result.enabled === true;
|
||||
extensionReleaseList = Array.isArray(result.releases) ? result.releases : [];
|
||||
renderExtensionReleases();
|
||||
}
|
||||
|
||||
async function publishExtensionRelease() {
|
||||
if (!isAdministrator() || extensionReleaseBusy || !extensionReleaseEnabled) return;
|
||||
const input = $('#extensionReleaseFile');
|
||||
const file = input?.files?.[0];
|
||||
if (!file) return;
|
||||
if (!/\.zip$/i.test(String(file.name || '')) || !file.size || file.size > 50_000_000) {
|
||||
throw new Error('请选择不超过 50 MB 的版本化插件 ZIP。');
|
||||
}
|
||||
extensionReleaseBusy = true;
|
||||
renderExtensionReleases();
|
||||
const message = $('#extensionReleaseMessage');
|
||||
if (message) message.textContent = '正在校验并发布到 OSS…';
|
||||
try {
|
||||
const dataUrl = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => resolve(String(reader.result || '')), { once: true });
|
||||
reader.addEventListener('error', () => reject(new Error('无法读取插件 ZIP。')), { once: true });
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
const packageBase64 = String(dataUrl).slice(String(dataUrl).indexOf(',') + 1);
|
||||
const result = await apiRequest('/api/extension-updates/releases', {
|
||||
method: 'POST',
|
||||
timeoutMs: 120_000,
|
||||
body: { package_base64: packageBase64 }
|
||||
});
|
||||
if (message) message.textContent = `插件 ${result.release?.version || ''} 已发布;各云主机会在完全空闲后更新。`;
|
||||
if (input) input.value = '';
|
||||
await syncExtensionReleases();
|
||||
} finally {
|
||||
extensionReleaseBusy = false;
|
||||
renderExtensionReleases();
|
||||
}
|
||||
}
|
||||
|
||||
async function createAccountFromForm() {
|
||||
@@ -1277,9 +1184,7 @@ async function createAccountFromForm() {
|
||||
username: $('#accountUsername').value,
|
||||
password: $('#accountPassword').value,
|
||||
role: $('#accountRole').value,
|
||||
erpAccount: $('#accountErpAccount').value,
|
||||
ecsRegion: $('#accountEcsRegion').value,
|
||||
ecsInstance: $('#accountEcsInstance').value
|
||||
erpAccount: $('#accountErpAccount').value
|
||||
});
|
||||
if (!validation.ok) {
|
||||
showAccountCreationValidationError(validation);
|
||||
@@ -1299,8 +1204,6 @@ async function createAccountFromForm() {
|
||||
$('#accountUsername').value = '';
|
||||
$('#accountPassword').value = '';
|
||||
$('#accountErpAccount').value = '';
|
||||
$('#accountEcsRegion').value = '';
|
||||
$('#accountEcsInstance').value = '';
|
||||
if (message) {
|
||||
message.textContent = result.account?.role === 'admin'
|
||||
? '管理员账号已创建;该角色固定拥有全部任务权限,初始密码不会再次显示。'
|
||||
@@ -1320,36 +1223,6 @@ async function updateManagedAccountErpIdentity(accountId) {
|
||||
await updateManagedAccount(accountId, { erp_account: value.trim().slice(0, 200) });
|
||||
}
|
||||
|
||||
async function updateManagedAccountExtensionHost(accountId) {
|
||||
const account = accountList.find((item) => item.id === accountId);
|
||||
if (!account || account.role === 'admin') return;
|
||||
const region = window.prompt(
|
||||
'请输入 ECS 地域 ID(例如 cn-hangzhou)。留空会同时清除该账号的云主机绑定:',
|
||||
account.extension_ecs_region_id || ''
|
||||
);
|
||||
if (region === null) return;
|
||||
if (!region.trim()) {
|
||||
await updateManagedAccount(accountId, {
|
||||
extension_ecs_region_id: null,
|
||||
extension_ecs_instance_id: null
|
||||
});
|
||||
return;
|
||||
}
|
||||
const instance = window.prompt(
|
||||
'请输入 ECS 实例 ID(例如 i-bp...)。同一台 Windows Server 上的账号填写相同实例:',
|
||||
account.extension_ecs_instance_id || ''
|
||||
);
|
||||
if (instance === null || !instance.trim()) return;
|
||||
if (
|
||||
region.trim().toLowerCase() === account.extension_ecs_region_id
|
||||
&& instance.trim() === account.extension_ecs_instance_id
|
||||
) return;
|
||||
await updateManagedAccount(accountId, {
|
||||
extension_ecs_region_id: region.trim().toLowerCase().slice(0, 64),
|
||||
extension_ecs_instance_id: instance.trim().slice(0, 80)
|
||||
});
|
||||
}
|
||||
|
||||
async function updateManagedAccount(accountId, patch) {
|
||||
if (!isAdministrator() || accountSettingsBusy) return;
|
||||
accountSettingsBusy = true;
|
||||
@@ -2729,7 +2602,7 @@ function isAutomaticTask(task) {
|
||||
}
|
||||
|
||||
async function autoDispatchReadyTasks({ force = false } = {}) {
|
||||
if (!authUser || autoHandoffInProgress || extensionUpdateBlocksExecution) return;
|
||||
if (!authUser || autoHandoffInProgress) return;
|
||||
autoHandoffInProgress = true;
|
||||
try {
|
||||
const result = await apiRequest('/api/tasks?status=confirmed&limit=200&include_total=false&executable_by=me');
|
||||
@@ -4481,9 +4354,7 @@ function renderStatusDetails() {
|
||||
const status = latestBridgeStatus;
|
||||
const recoveryStatus = String(status?.erpLinkRecovery?.status || status?.erpSession?.erp_keepalive?.recovery_status || '');
|
||||
const recoveryPending = ['scheduled', 'already_running', 'running'].includes(recoveryStatus);
|
||||
const updateStatus = String(status?.extensionUpdate?.status || '');
|
||||
const updateWarning = Boolean(updateStatus && !['current', 'disabled', 'no_release'].includes(updateStatus));
|
||||
const hasWarning = status?.connected && (!status?.compatible || !status?.automationEnabled || !status?.erpHostAccessReady || status?.erpSessionReady === false || recoveryPending || updateWarning);
|
||||
const hasWarning = status?.connected && (!status?.compatible || !status?.automationEnabled || !status?.erpHostAccessReady || status?.erpSessionReady === false || recoveryPending);
|
||||
title.textContent = '插件状态详情';
|
||||
refresh.textContent = '重新检测插件';
|
||||
if (!status) {
|
||||
@@ -4504,8 +4375,6 @@ function renderStatusDetails() {
|
||||
statusDetailRow('插件版本', status?.version || '未知'),
|
||||
statusDetailRow('最低版本', REQUIRED_EXTENSION_VERSION),
|
||||
statusDetailRow('版本兼容', statusBoolean(status?.compatible, '兼容', '需要更新')),
|
||||
statusDetailRow('自动更新', status?.extensionUpdate?.message || '等待服务端检测'),
|
||||
statusDetailRow('目标版本', status?.extensionUpdate?.target_version || '尚未发布'),
|
||||
statusDetailRow('ERP 自动化', statusBoolean(status?.automationEnabled, '已开启', '已关闭')),
|
||||
statusDetailRow('ERP 页面权限', statusBoolean(status?.erpHostAccessReady, '可访问', '需在扩展设置中允许')),
|
||||
statusDetailRow('ERP 会话', statusBoolean(status?.erpSessionReady, '正常', '异常或待登录')),
|
||||
@@ -4566,6 +4435,8 @@ async function pingAi() {
|
||||
}
|
||||
|
||||
function applyBridgePayload(payload = {}) {
|
||||
const wasBridgeConnected = bridgeConnected;
|
||||
const previousBridgeInstalledAt = String(latestBridgeStatus?.installedAt || '');
|
||||
bridgeConnected = Boolean(payload.ok);
|
||||
if (!bridgeConnected) {
|
||||
erpAutomationEnabled = false;
|
||||
@@ -4576,7 +4447,6 @@ function applyBridgePayload(payload = {}) {
|
||||
connected: false,
|
||||
compatible: false,
|
||||
automationEnabled: false,
|
||||
extensionUpdate: null,
|
||||
version: payload.version || '',
|
||||
installedAt: payload.bridge_installed_at || '',
|
||||
error: payload.message || payload.error || '插件桥接未返回成功状态。',
|
||||
@@ -4626,7 +4496,6 @@ function applyBridgePayload(payload = {}) {
|
||||
status: recoveryStatus || 'not_started',
|
||||
pending: erpLinkRecoveryPending
|
||||
},
|
||||
extensionUpdate: latestBridgeStatus?.extensionUpdate || null,
|
||||
version: payload.version || '',
|
||||
installedAt: bridgeInstalledAt,
|
||||
error: !extensionCompatible
|
||||
@@ -4650,40 +4519,12 @@ function applyBridgePayload(payload = {}) {
|
||||
setBridgeState('已连接', 'state-ok');
|
||||
}
|
||||
renderStatusDetails();
|
||||
const bridgeReadyForDispatch = !wasBridgeConnected
|
||||
|| Boolean(bridgeInstalledAt && bridgeInstalledAt !== previousBridgeInstalledAt);
|
||||
queueMicrotask(() => autoDispatchReadyTasks({ force: bridgeReadyForDispatch }).catch(() => {}));
|
||||
return true;
|
||||
}
|
||||
|
||||
function extensionUpdateIsBlocking(update = {}) {
|
||||
return !['current', 'disabled', 'no_release'].includes(String(update?.status || ''));
|
||||
}
|
||||
|
||||
async function applyPreparedExtensionUpdate(update = {}) {
|
||||
const targetVersion = String(update.target_version || '');
|
||||
if (!targetVersion || extensionReloadRequestedVersion === targetVersion) return;
|
||||
extensionReloadRequestedVersion = targetVersion;
|
||||
try {
|
||||
const result = await sendToExtension('APPLY_EXTENSION_UPDATE', {
|
||||
target_version: targetVersion
|
||||
}, 5_000);
|
||||
if (result?.accepted !== true && result?.status !== 'already_current') {
|
||||
throw new Error(result?.message || '插件未接受安全重载请求。');
|
||||
}
|
||||
if (result?.status === 'already_current') {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
window.setTimeout(() => window.location.reload(), 900);
|
||||
} catch (error) {
|
||||
extensionReloadRequestedVersion = '';
|
||||
latestBridgeStatus = {
|
||||
...latestBridgeStatus,
|
||||
error: error.message || String(error)
|
||||
};
|
||||
setBridgeState('更新待重载', 'state-warn');
|
||||
renderStatusDetails();
|
||||
}
|
||||
}
|
||||
|
||||
function setTaskState(text) {
|
||||
const node = $('#taskState');
|
||||
if (!node) return;
|
||||
@@ -4729,8 +4570,7 @@ window.addEventListener('message', (event) => {
|
||||
const message = event.data || {};
|
||||
if (message.source !== EXTENSION_SOURCE) return;
|
||||
if (message.type === 'BRIDGE_READY') {
|
||||
if (authUser) void pingBridge();
|
||||
else applyBridgePayload(message.payload || { ok: true });
|
||||
applyBridgePayload(message.payload || { ok: true });
|
||||
return;
|
||||
}
|
||||
if (message.type === 'TASK_RESULT_CHANGED') {
|
||||
@@ -5055,7 +4895,7 @@ async function confirmAndSubmitToErpPlugin(task) {
|
||||
throw new Error('该任务分配给其他平台账号,当前账号只能查看,不能确认或执行。');
|
||||
}
|
||||
const bridgeReady = await pingBridge();
|
||||
if (!bridgeReady || !extensionCompatible || extensionUpdateBlocksExecution || !erpAutomationEnabled || !erpHostAccessReady || !erpSessionReady || latestBridgeStatus?.erpLinkRecovery?.pending === true) {
|
||||
if (!bridgeReady || !extensionCompatible || !erpAutomationEnabled || !erpHostAccessReady || !erpSessionReady || latestBridgeStatus?.erpLinkRecovery?.pending === true) {
|
||||
throw new Error(`插件未连接、版本低于 ${REQUIRED_EXTENSION_VERSION}、ERP 页面/会话不可用或 ERP 操作开关未开启;任务尚未确认,也未提交到插件。`);
|
||||
}
|
||||
const response = await apiRequest(`/api/tasks/${encodeURIComponent(task.task_id)}/confirm`, { method: 'POST', body: {} });
|
||||
@@ -5099,36 +4939,9 @@ async function pingBridge() {
|
||||
extension_version: result.version || '',
|
||||
erp_account: authUser.erp_account || '',
|
||||
erp_account_matched: result.erp_session?.account_matched === true,
|
||||
extension_update_safe: result.extension_update?.safe === true,
|
||||
metadata: {
|
||||
bridge_installed_at: result.bridge_installed_at || '',
|
||||
extension_update_reason: result.extension_update?.reason || ''
|
||||
}
|
||||
metadata: { bridge_installed_at: result.bridge_installed_at || '' }
|
||||
}
|
||||
});
|
||||
const extensionUpdate = heartbeat.extension_update || {
|
||||
status: 'disabled',
|
||||
current_version: result.version || '',
|
||||
target_version: '',
|
||||
message: '插件自动更新未启用。'
|
||||
};
|
||||
extensionUpdateBlocksExecution = extensionUpdateIsBlocking(extensionUpdate);
|
||||
latestBridgeStatus = {
|
||||
...latestBridgeStatus,
|
||||
extensionUpdate,
|
||||
error: extensionUpdateBlocksExecution
|
||||
? extensionUpdate.message || '插件正在更新,已暂停新 ERP 任务。'
|
||||
: latestBridgeStatus?.error || ''
|
||||
};
|
||||
renderStatusDetails();
|
||||
if (extensionUpdateBlocksExecution) {
|
||||
setBridgeState(extensionUpdate.status === 'reload_required' ? '更新待重载' : '插件更新中', 'state-warn');
|
||||
renderStatusDetails();
|
||||
}
|
||||
if (extensionUpdate.action === 'reload') {
|
||||
void applyPreparedExtensionUpdate(extensionUpdate);
|
||||
return false;
|
||||
}
|
||||
if (heartbeat.execution_ready !== true) {
|
||||
erpSessionReady = false;
|
||||
latestBridgeStatus = {
|
||||
@@ -5143,11 +4956,7 @@ async function pingBridge() {
|
||||
renderStatusDetails();
|
||||
return false;
|
||||
}
|
||||
if (!extensionUpdateBlocksExecution) {
|
||||
queueMicrotask(() => autoDispatchReadyTasks({ force: true }).catch(() => {}));
|
||||
}
|
||||
} catch (heartbeatError) {
|
||||
extensionUpdateBlocksExecution = true;
|
||||
erpSessionReady = false;
|
||||
latestBridgeStatus = {
|
||||
...latestBridgeStatus,
|
||||
@@ -5159,12 +4968,11 @@ async function pingBridge() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return bridgeConnected && erpSessionReady && !extensionUpdateBlocksExecution;
|
||||
return bridgeConnected && erpSessionReady;
|
||||
} catch (error) {
|
||||
bridgeConnected = false;
|
||||
erpAutomationEnabled = false;
|
||||
extensionCompatible = false;
|
||||
extensionUpdateBlocksExecution = true;
|
||||
latestBridgeStatus = {
|
||||
connected: false,
|
||||
compatible: false,
|
||||
@@ -5623,8 +5431,8 @@ async function pollTaskResult(taskId, { allowReconciliation = false } = {}) {
|
||||
async function reconcileTaskReceipt(task) {
|
||||
if (!canReconcileTask(task)) throw new Error('当前任务不在可只读回查的生命周期不确定状态。');
|
||||
if (!taskAssignedToCurrentAccount(task)) throw new Error('该任务分配给其他平台账号,当前云电脑不能执行回查。');
|
||||
await pingBridge();
|
||||
if (!bridgeConnected || !erpHostAccessReady) throw new Error('插件未连接或 ERP 页面权限不可用,未执行回查。');
|
||||
const bridgeReady = await pingBridge();
|
||||
if (!bridgeReady || !extensionCompatible || !erpHostAccessReady) throw new Error(`插件未连接、版本低于 ${REQUIRED_EXTENSION_VERSION} 或 ERP 页面权限不可用,未执行回查。`);
|
||||
taskReconciliationStates.set(task.task_id, 'running');
|
||||
renderTaskDetail();
|
||||
try {
|
||||
@@ -5919,9 +5727,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
taskAttachmentDrafts.clear();
|
||||
accountList = [];
|
||||
accountTaskTypes = [];
|
||||
extensionReleaseEnabled = false;
|
||||
extensionReleaseList = [];
|
||||
extensionReleaseBusy = false;
|
||||
accountAuthorizationTargetId = '';
|
||||
accountAuthorizationDraft = new Set();
|
||||
historySelectedTaskIds.clear();
|
||||
@@ -5931,8 +5736,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
historyBatchMessageKind = '';
|
||||
currentTaskId = '';
|
||||
browserConnectionId = '';
|
||||
extensionUpdateBlocksExecution = true;
|
||||
extensionReloadRequestedVersion = '';
|
||||
sessionStorage.removeItem('liansyn_platform_current_task_id');
|
||||
$('#rosterAttachment').value = '';
|
||||
$('#rosterAttachmentStatus').textContent = '';
|
||||
@@ -6027,11 +5830,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
const account = accountList.find((item) => item.id === control.dataset.accountId);
|
||||
if (!account) return;
|
||||
const patch = { role: control.value };
|
||||
if (control.value === 'admin') {
|
||||
patch.erp_account = null;
|
||||
patch.extension_ecs_region_id = null;
|
||||
patch.extension_ecs_instance_id = null;
|
||||
}
|
||||
if (control.value === 'admin') patch.erp_account = null;
|
||||
if (control.value !== 'admin' && !account.erp_account) {
|
||||
const erpAccount = window.prompt('该员工角色必须绑定 ERP 账号,请输入 ERP 页面显示的登录账号:', '');
|
||||
if (!erpAccount?.trim()) {
|
||||
@@ -6058,7 +5857,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
return;
|
||||
}
|
||||
if (action === 'erp-account') operation = updateManagedAccountErpIdentity(account.id);
|
||||
else if (action === 'extension-host') operation = updateManagedAccountExtensionHost(account.id);
|
||||
else if (action === 'toggle') operation = updateManagedAccount(account.id, { is_active: !account.is_active });
|
||||
else if (action === 'reset-password') operation = resetManagedAccountPassword(account.id);
|
||||
else if (action === 'revoke-sessions') operation = revokeManagedAccountSessions(account.id);
|
||||
@@ -6223,25 +6021,10 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
});
|
||||
$('#accountRole')?.addEventListener('change', (event) => {
|
||||
const erpInput = $('#accountErpAccount');
|
||||
const ecsRegionInput = $('#accountEcsRegion');
|
||||
const ecsInstanceInput = $('#accountEcsInstance');
|
||||
const admin = event.target.value === 'admin';
|
||||
erpInput.required = !admin;
|
||||
erpInput.disabled = admin;
|
||||
ecsRegionInput.disabled = admin;
|
||||
ecsInstanceInput.disabled = admin;
|
||||
if (admin) {
|
||||
erpInput.value = '';
|
||||
ecsRegionInput.value = '';
|
||||
ecsInstanceInput.value = '';
|
||||
}
|
||||
});
|
||||
$('#extensionReleaseFile')?.addEventListener('change', renderExtensionReleases);
|
||||
$('#extensionReleasePublish')?.addEventListener('click', () => {
|
||||
void publishExtensionRelease().catch((error) => {
|
||||
const message = $('#extensionReleaseMessage');
|
||||
if (message) message.textContent = error.message || String(error);
|
||||
});
|
||||
if (admin) erpInput.value = '';
|
||||
});
|
||||
$('#aiState').addEventListener('click', () => {
|
||||
toggleStatusDetails('ai').catch(() => {});
|
||||
|
||||
@@ -99,21 +99,8 @@
|
||||
<label>初始密码<input id="accountPassword" type="password" autocomplete="new-password" required></label>
|
||||
<label>角色<select id="accountRole"><option value="user">普通用户</option><option value="team_lead">组长</option><option value="admin">管理员</option></select></label>
|
||||
<label>ERP 账号<input id="accountErpAccount" maxlength="200" autocomplete="off" required placeholder="ERP 页面显示的登录账号"></label>
|
||||
<label>ECS 地域<input id="accountEcsRegion" maxlength="64" autocomplete="off" placeholder="例如 cn-hangzhou"></label>
|
||||
<label>ECS 实例<input id="accountEcsInstance" maxlength="80" autocomplete="off" placeholder="例如 i-bp...;同一台机器的账号填相同实例"></label>
|
||||
<button type="submit">创建账号</button>
|
||||
</form>
|
||||
<section class="account-extension-release" aria-labelledby="extensionReleaseTitle">
|
||||
<div>
|
||||
<h3 id="extensionReleaseTitle">插件版本发布</h3>
|
||||
<p id="extensionReleaseSummary" class="muted">正在读取服务端发布状态…</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<input id="extensionReleaseFile" type="file" accept=".zip,application/zip">
|
||||
<button id="extensionReleasePublish" type="button">发布到 OSS</button>
|
||||
</div>
|
||||
<p id="extensionReleaseMessage" class="channel-message" role="status"></p>
|
||||
</section>
|
||||
<section id="accountAuthorizationPanel" class="account-authorization-panel" hidden aria-labelledby="accountAuthorizationTitle">
|
||||
<div class="account-authorization-head">
|
||||
<div>
|
||||
|
||||
@@ -638,38 +638,6 @@ textarea {
|
||||
color: var(--ink) !important;
|
||||
}
|
||||
|
||||
.account-extension-release {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 16px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: var(--surface-muted);
|
||||
}
|
||||
|
||||
.account-extension-release h3 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.account-extension-release .actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.account-extension-release input[type='file'] {
|
||||
max-width: 280px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.account-extension-release .channel-message {
|
||||
flex-basis: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.account-authorization-panel {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
|
||||
Reference in new issue
Block a user