ui: 侧边栏按“记录/工具/分析/系统”重排并分组分隔;精简设置/登录/记账页冗余文案(修正旧架构遗留说明);键盘焦点态

This commit is contained in:
root
2026-10-17 11:07:04 +08:00
parent 08b0317d35
commit a1f2a45623
10 changed files with 69 additions and 70 deletions
+29 -16
View File
@@ -110,15 +110,15 @@ function onGlobalKey(e: KeyboardEvent) {
}
const VIEWS = [
{ key: 'calendar', name: '日历', icon: 'calendar' },
{ key: 'today', name: '详情', icon: 'edit' },
{ key: 'stats', name: '统计', icon: 'chart' },
{ key: 'report', name: '总结', icon: 'export' },
{ key: 'ledger', name: '记账', icon: 'wallet' },
{ key: 'memo', name: '备忘录', icon: 'memo' },
{ key: 'vault', name: '密码库', icon: 'lock' },
{ key: 'settings', name: '设置', icon: 'settings' },
{ key: 'admin', name: '用户管理', icon: 'shield' }
{ key: 'calendar', name: '日历', icon: 'calendar', group: '记录' },
{ key: 'today', name: '详情', icon: 'edit', group: '记录' },
{ key: 'ledger', name: '记账', icon: 'wallet', group: '工具' },
{ key: 'memo', name: '备忘录', icon: 'memo', group: '工具' },
{ key: 'stats', name: '统计', icon: 'chart', group: '分析' },
{ key: 'report', name: '总结', icon: 'export', group: '分析' },
{ key: 'vault', name: '密码库', icon: 'lock', group: '系统' },
{ key: 'settings', name: '设置', icon: 'settings', group: '系统' },
{ key: 'admin', name: '用户管理', icon: 'shield', group: '系统' }
]
function readRoute(): string {
@@ -233,13 +233,15 @@ onMounted(async () => {
<LoginView v-if="!session" @login="onLogin" />
<div v-else class="app">
<nav class="side">
<button
v-for="v in shownViews" :key="v.key"
class="nav-btn" :class="{ active: route === v.key }"
:data-tip="v.name" @click="nav(v.key)"
>
<Icon :name="v.icon" :size="18" />
</button>
<template v-for="(v, i) in shownViews" :key="v.key">
<div v-if="i > 0 && v.group !== shownViews[i - 1].group" class="nav-sep"></div>
<button
class="nav-btn" :class="{ active: route === v.key }"
:data-tip="v.name" @click="nav(v.key)"
>
<Icon :name="v.icon" :size="18" />
</button>
</template>
</nav>
<div class="main">
<header class="topbar">
@@ -420,6 +422,17 @@ onMounted(async () => {
transition: opacity 0.15s;
}
.nav-btn:hover::after { opacity: 1; }
.nav-sep {
width: 26px;
height: 1px;
flex: none;
background: var(--border);
margin: 5px 0;
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.topbar {
+1 -1
View File
@@ -320,7 +320,7 @@ async function confirmDelete() {
</table>
</div>
<div class="muted vault-note num">筛选结果 {{ filtered.length }} 条 / 本月共 {{ rows.length }} 条</div>
<div class="muted vault-note num">本月 {{ rows.length }} 条 · 当前筛选 {{ filtered.length }} 条</div>
<TransactionDialog
:open="dialogOpen"
+6 -20
View File
@@ -13,8 +13,7 @@ import { downloadCsv } from '../lib/csv'
import { hhmm, parseDateStr, todayStr } from '../lib/date'
import { computeDay } from '../domain/attendance'
import { changeLoginPassword, deleteCurrentUser, getSession } from '../db/auth'
import { setSyncBaseOverride } from '../lib/server'
import { ensureCloudBinding, getSyncBase, getSyncLastTime, isSyncConfigured, syncWithStoredConfig, syncLastError, type SyncResult } from '../sync/engine'
import { ensureCloudBinding, getSyncLastTime, isSyncConfigured, syncWithStoredConfig, syncLastError, type SyncResult } from '../sync/engine'
import { getCloudCreds } from '../db/auth'
import { showToast } from '../lib/toast'
@@ -38,7 +37,6 @@ const newLoginPwd2 = ref('')
const accError = ref('')
const deleteAccConfirm = ref(false)
const appVersion = __APP_VERSION__
const syncBase = ref('')
const cloudUser = ref('')
const syncStatus = ref('')
const syncError = ref('')
@@ -47,7 +45,6 @@ const syncing = ref(false)
const lastSync = ref('')
async function loadSyncInfo() {
syncBase.value = await getSyncBase()
cloudUser.value = getCloudCreds()?.username || ''
lastSync.value = await getSyncLastTime()
autoSyncErr.value = syncLastError.value
@@ -55,11 +52,6 @@ async function loadSyncInfo() {
watch(syncLastError, v => { autoSyncErr.value = v })
async function saveSyncConfig() {
await setSyncBaseOverride(syncBase.value.trim())
showToast('同步地址已保存')
}
async function rebindCloud() {
syncError.value = ''
try {
@@ -76,7 +68,6 @@ async function doSyncNow() {
autoSyncErr.value = syncLastError.value
syncing.value = true
try {
await saveSyncConfig()
const result: SyncResult = await syncWithStoredConfig()
lastSync.value = result.at
syncStatus.value = '推送 ' + result.pushed + ' 条 · 拉取 ' + result.pulled + ' 条 · 删除 ' + result.removed + ' 条'
@@ -263,7 +254,7 @@ async function doImport() {
<div class="settings">
<div class="card section">
<div class="section-title">账号与隐私</div>
<div class="muted acc-line">版本 v{{ appVersion }} · 当前用户:{{ username }} · 每位用户独立数据库隔离存储于本机 · 登录密码仅保存 PBKDF2 哈希</div>
<div class="muted acc-line">v{{ appVersion }} · 当前账号 {{ username }} · 登录密码仅存 PBKDF2 哈希</div>
<div class="ai-grid">
<div class="rule-item">
<label>旧登录密码</label>
@@ -352,15 +343,10 @@ async function doImport() {
<div class="card section">
<div class="section-title">多端数据同步</div>
<div class="muted acc-line">云端账号:{{ cloudUser || '未绑定' }} · 数据仅保存在服务器,本机只是实时缓存(变更 ~3 秒推上云端 + 其他端秒级自动刷新);密码库同步密文,主密码永不出本机</div>
<div class="rule-item">
<label>同步服务地址(默认即可)</label>
<input class="input" v-model="syncBase" placeholder="https://daywk.linyux.cn" />
</div>
<div class="muted acc-line">云端账号:{{ cloudUser || '未绑定' }} · 数据存于服务器,其他端变更秒级自动刷新</div>
<div class="rule-save">
<button class="btn btn-sm" @click="saveSyncConfig">保存地址</button>
<button class="btn btn-primary btn-sm" :disabled="syncing" @click="doSyncNow">{{ syncing ? '同步中' : '立即同步' }}</button>
<button class="btn btn-sm" @click="rebindCloud">重建云端绑定</button>
<button class="btn btn-sm" @click="rebindCloud">重新校验连接</button>
</div>
<div v-if="syncStatus" class="muted acc-line">上次同步:{{ lastSync || '--' }} · {{ syncStatus }}</div>
<div v-if="syncError" class="form-error">{{ syncError }}</div>
@@ -385,7 +371,7 @@ async function doImport() {
</div>
<div class="rule-save">
<button class="btn btn-primary btn-sm" @click="saveAi">保存 AI 配置</button>
<span class="muted loc-note">配置仅保存在本机浏览器;总结页可用 AI 润色草稿,需服务端允许浏览器跨域</span>
<span class="muted loc-note">配置仅保存在本机;总结页「AI 润色」需此处先配置</span>
</div>
</div>
@@ -402,7 +388,7 @@ async function doImport() {
/>
</div>
<div v-if="importError" class="import-error">{{ importError }}</div>
<div class="muted backup-note">数据保存在浏览器 IndexedDB 中,建议定期导出 JSON 备份。</div>
<div class="muted backup-note">数据保存在云端服务器;导出的 JSON 备份用于跨设备迁移或灾后恢复。</div>
</div>
<ConfirmDialog