feat: 完善本地书库与发布更新流程
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
de3e1d8a44
commit
b8c8d24107
+122
-2
@@ -84,19 +84,139 @@ $('zlibLogoutBtn').onclick = async () => {
|
||||
|
||||
refreshZlibStatus();
|
||||
|
||||
async function refreshLibraryDir() {
|
||||
const r = await window.api.library.getDir();
|
||||
if (r.ok && r.data) $('libDirPath').textContent = r.data.dir + (r.data.isDefault ? '(默认)' : '');
|
||||
}
|
||||
|
||||
$('libDirPickBtn').onclick = async () => {
|
||||
const pick = await window.api.library.pickDir();
|
||||
if (!pick.ok || !pick.data) return;
|
||||
const dest = pick.data;
|
||||
|
||||
const cur = await window.api.library.getDir();
|
||||
if (cur.ok && cur.data && cur.data.dir === dest) return;
|
||||
|
||||
// 让用户决定旧目录里已有的书怎么处理
|
||||
let migrate = false;
|
||||
const choice = await openModal('切换书库目录', `
|
||||
<p>新目录:</p>
|
||||
<div class="settings-path" style="margin:6px 0 12px;">${escapeHtml(dest)}</div>
|
||||
<label style="display:block;margin-bottom:6px;">
|
||||
<input type="radio" name="migMode" value="migrate" checked /> 迁移:把现有书库内容移动到新目录
|
||||
</label>
|
||||
<label style="display:block;">
|
||||
<input type="radio" name="migMode" value="switch" /> 直接切换:旧目录原样保留,新目录重新扫描
|
||||
</label>
|
||||
`, () => {
|
||||
const sel = document.querySelector('input[name="migMode"]:checked');
|
||||
return { migrate: sel && sel.value === 'migrate' };
|
||||
});
|
||||
if (!choice) return;
|
||||
migrate = choice.migrate;
|
||||
|
||||
const res = await window.api.library.setDir(dest, migrate);
|
||||
if (!res.ok) { await confirmModal('切换失败', res.error || '未知错误'); return; }
|
||||
await refreshLibraryDir();
|
||||
if (window.Library) window.Library.markDirty();
|
||||
};
|
||||
|
||||
$('libDirOpenBtn').onclick = async () => {
|
||||
const r = await window.api.library.getDir();
|
||||
if (r.ok && r.data) window.api.openPath(r.data.dir);
|
||||
};
|
||||
|
||||
async function refreshAskSave() {
|
||||
const r = await window.api.settings.get('askSavePath', false);
|
||||
$('askSaveChk').checked = !!(r.ok && r.data);
|
||||
}
|
||||
$('askSaveChk').onchange = () => window.api.settings.set('askSavePath', $('askSaveChk').checked);
|
||||
|
||||
refreshLibraryDir();
|
||||
refreshAskSave();
|
||||
|
||||
async function refreshProxy() {
|
||||
const r = await window.api.proxy.get();
|
||||
if (r.ok) $('proxyInput').value = r.data || '';
|
||||
}
|
||||
$('proxySaveBtn').onclick = async () => {
|
||||
await window.api.proxy.set($('proxyInput').value.trim());
|
||||
$('proxySaveBtn').textContent = '已保存 ✓';
|
||||
const r = await window.api.proxy.set($('proxyInput').value.trim());
|
||||
$('proxySaveBtn').textContent = r.ok ? '已保存 ✓' : '保存失败';
|
||||
$('proxySaveBtn').title = r.ok ? '' : (r.error || '代理地址无效');
|
||||
setTimeout(() => { $('proxySaveBtn').textContent = '保存'; }, 1500);
|
||||
};
|
||||
refreshProxy();
|
||||
|
||||
async function refreshSemanticKeyStatus() {
|
||||
const r = await window.api.semanticScholar.keyStatus();
|
||||
const status = r.ok && r.data ? r.data : { configured: false, persistent: false };
|
||||
$('semanticKeyStatus').textContent = status.configured
|
||||
? (status.persistent ? '已配置(由系统安全存储加密)' : '已配置(仅本次运行)')
|
||||
: '未配置(匿名请求容易被限流)';
|
||||
$('semanticKeyClearBtn').classList.toggle('hidden', !status.configured);
|
||||
}
|
||||
|
||||
$('semanticKeySaveBtn').onclick = async () => {
|
||||
const key = $('semanticKeyInput').value.trim();
|
||||
if (!key) {
|
||||
$('semanticKeySaveBtn').textContent = '请输入 Key';
|
||||
setTimeout(() => { $('semanticKeySaveBtn').textContent = '保存'; }, 1500);
|
||||
return;
|
||||
}
|
||||
const r = await window.api.semanticScholar.setKey(key);
|
||||
$('semanticKeyInput').value = '';
|
||||
$('semanticKeySaveBtn').textContent = r.ok ? '已保存 ✓' : '保存失败';
|
||||
$('semanticKeySaveBtn').title = r.ok ? '' : (r.error || '保存失败');
|
||||
await refreshSemanticKeyStatus();
|
||||
setTimeout(() => { $('semanticKeySaveBtn').textContent = '保存'; }, 1500);
|
||||
};
|
||||
|
||||
$('semanticKeyClearBtn').onclick = async () => {
|
||||
const ok = await confirmModal('清除 API Key', '确定清除 Semantic Scholar API Key 吗?');
|
||||
if (!ok) return;
|
||||
await window.api.semanticScholar.clearKey();
|
||||
await refreshSemanticKeyStatus();
|
||||
};
|
||||
|
||||
refreshSemanticKeyStatus();
|
||||
|
||||
async function runUpdateCheck(silent) {
|
||||
const statusEl = $('updateStatus');
|
||||
const btn = $('checkUpdateBtn');
|
||||
if (!silent) {
|
||||
btn.disabled = true;
|
||||
statusEl.textContent = '正在检查...';
|
||||
}
|
||||
const res = await window.api.checkUpdate();
|
||||
if (!silent) btn.disabled = false;
|
||||
if (!res.ok) {
|
||||
if (!silent) statusEl.textContent = '检查失败:' + res.error;
|
||||
return;
|
||||
}
|
||||
const { latest, hasUpdate, url } = res.data;
|
||||
if (hasUpdate) {
|
||||
statusEl.textContent = `发现新版本 ${latest}`;
|
||||
const ok = await openModal(
|
||||
'发现新版本',
|
||||
`<p>检测到新版本 <b>${escapeHtml(latest)}</b>,是否前往 GitHub Releases 下载?</p>`
|
||||
);
|
||||
if (ok) window.api.openExternal(url);
|
||||
} else if (!silent) {
|
||||
statusEl.textContent = '已是最新版本';
|
||||
}
|
||||
}
|
||||
|
||||
window.api.getVersion().then((r) => {
|
||||
if (r && r.ok) $('appVersion').textContent = 'v' + r.data;
|
||||
});
|
||||
|
||||
const autoCheckEl = $('autoCheckUpdate');
|
||||
window.api.settings.get('autoCheckUpdate', false).then((r) => {
|
||||
autoCheckEl.checked = !!(r.ok && r.data);
|
||||
if (autoCheckEl.checked) runUpdateCheck(true);
|
||||
});
|
||||
autoCheckEl.onchange = () => window.api.settings.set('autoCheckUpdate', autoCheckEl.checked);
|
||||
|
||||
$('checkUpdateBtn').onclick = () => runUpdateCheck(false);
|
||||
|
||||
switchTab('library');
|
||||
|
||||
Reference in New Issue
Block a user