ui: 侧边栏按“记录/工具/分析/系统”重排并分组分隔;精简设置/登录/记账页冗余文案(修正旧架构遗留说明);键盘焦点态
This commit is contained in:
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-DtE1VpZK.js","./index-D6jOKt8Z.js","./index-2EGKD3r-.css"])))=>i.map(i=>d[i]);
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-Tgz3gT_A.js","./index-D8vVtKDY.js","./index-DxgZcHRE.css"])))=>i.map(i=>d[i]);
|
||||||
import{r as s,_ as i}from"./index-D6jOKt8Z.js";var t;(function(e){e.Documents="DOCUMENTS",e.Data="DATA",e.Library="LIBRARY",e.Cache="CACHE",e.External="EXTERNAL",e.ExternalStorage="EXTERNAL_STORAGE"})(t||(t={}));var a;(function(e){e.UTF8="utf8",e.ASCII="ascii",e.UTF16="utf16"})(a||(a={}));const E=s("Filesystem",{web:()=>i(()=>import("./web-DtE1VpZK.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
|
import{r as s,_ as i}from"./index-D8vVtKDY.js";var t;(function(e){e.Documents="DOCUMENTS",e.Data="DATA",e.Library="LIBRARY",e.Cache="CACHE",e.External="EXTERNAL",e.ExternalStorage="EXTERNAL_STORAGE"})(t||(t={}));var a;(function(e){e.UTF8="utf8",e.ASCII="ascii",e.UTF16="utf16"})(a||(a={}));const E=s("Filesystem",{web:()=>i(()=>import("./web-Tgz3gT_A.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
|
||||||
-26
File diff suppressed because one or more lines are too long
+26
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -8,8 +8,8 @@
|
|||||||
<link rel="icon" href="./icons/icon.svg" type="image/svg+xml" />
|
<link rel="icon" href="./icons/icon.svg" type="image/svg+xml" />
|
||||||
<link rel="apple-touch-icon" href="./icons/icon-192.png" />
|
<link rel="apple-touch-icon" href="./icons/icon-192.png" />
|
||||||
<title>实习工作日志</title>
|
<title>实习工作日志</title>
|
||||||
<script type="module" crossorigin src="./assets/index-D6jOKt8Z.js"></script>
|
<script type="module" crossorigin src="./assets/index-D8vVtKDY.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-2EGKD3r-.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-DxgZcHRE.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "intern-log",
|
"name": "intern-log",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+23
-10
@@ -110,15 +110,15 @@ function onGlobalKey(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const VIEWS = [
|
const VIEWS = [
|
||||||
{ key: 'calendar', name: '日历', icon: 'calendar' },
|
{ key: 'calendar', name: '日历', icon: 'calendar', group: '记录' },
|
||||||
{ key: 'today', name: '详情', icon: 'edit' },
|
{ key: 'today', name: '详情', icon: 'edit', group: '记录' },
|
||||||
{ key: 'stats', name: '统计', icon: 'chart' },
|
{ key: 'ledger', name: '记账', icon: 'wallet', group: '工具' },
|
||||||
{ key: 'report', name: '总结', icon: 'export' },
|
{ key: 'memo', name: '备忘录', icon: 'memo', group: '工具' },
|
||||||
{ key: 'ledger', name: '记账', icon: 'wallet' },
|
{ key: 'stats', name: '统计', icon: 'chart', group: '分析' },
|
||||||
{ key: 'memo', name: '备忘录', icon: 'memo' },
|
{ key: 'report', name: '总结', icon: 'export', group: '分析' },
|
||||||
{ key: 'vault', name: '密码库', icon: 'lock' },
|
{ key: 'vault', name: '密码库', icon: 'lock', group: '系统' },
|
||||||
{ key: 'settings', name: '设置', icon: 'settings' },
|
{ key: 'settings', name: '设置', icon: 'settings', group: '系统' },
|
||||||
{ key: 'admin', name: '用户管理', icon: 'shield' }
|
{ key: 'admin', name: '用户管理', icon: 'shield', group: '系统' }
|
||||||
]
|
]
|
||||||
|
|
||||||
function readRoute(): string {
|
function readRoute(): string {
|
||||||
@@ -233,13 +233,15 @@ onMounted(async () => {
|
|||||||
<LoginView v-if="!session" @login="onLogin" />
|
<LoginView v-if="!session" @login="onLogin" />
|
||||||
<div v-else class="app">
|
<div v-else class="app">
|
||||||
<nav class="side">
|
<nav class="side">
|
||||||
|
<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
|
<button
|
||||||
v-for="v in shownViews" :key="v.key"
|
|
||||||
class="nav-btn" :class="{ active: route === v.key }"
|
class="nav-btn" :class="{ active: route === v.key }"
|
||||||
:data-tip="v.name" @click="nav(v.key)"
|
:data-tip="v.name" @click="nav(v.key)"
|
||||||
>
|
>
|
||||||
<Icon :name="v.icon" :size="18" />
|
<Icon :name="v.icon" :size="18" />
|
||||||
</button>
|
</button>
|
||||||
|
</template>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
@@ -420,6 +422,17 @@ onMounted(async () => {
|
|||||||
transition: opacity 0.15s;
|
transition: opacity 0.15s;
|
||||||
}
|
}
|
||||||
.nav-btn:hover::after { opacity: 1; }
|
.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; }
|
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
||||||
.topbar {
|
.topbar {
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ async function confirmDelete() {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="muted vault-note num">筛选结果 {{ filtered.length }} 条 / 本月共 {{ rows.length }} 条</div>
|
<div class="muted vault-note num">本月 {{ rows.length }} 条 · 当前筛选 {{ filtered.length }} 条</div>
|
||||||
|
|
||||||
<TransactionDialog
|
<TransactionDialog
|
||||||
:open="dialogOpen"
|
:open="dialogOpen"
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import { downloadCsv } from '../lib/csv'
|
|||||||
import { hhmm, parseDateStr, todayStr } from '../lib/date'
|
import { hhmm, parseDateStr, todayStr } from '../lib/date'
|
||||||
import { computeDay } from '../domain/attendance'
|
import { computeDay } from '../domain/attendance'
|
||||||
import { changeLoginPassword, deleteCurrentUser, getSession } from '../db/auth'
|
import { changeLoginPassword, deleteCurrentUser, getSession } from '../db/auth'
|
||||||
import { setSyncBaseOverride } from '../lib/server'
|
import { ensureCloudBinding, getSyncLastTime, isSyncConfigured, syncWithStoredConfig, syncLastError, type SyncResult } from '../sync/engine'
|
||||||
import { ensureCloudBinding, getSyncBase, getSyncLastTime, isSyncConfigured, syncWithStoredConfig, syncLastError, type SyncResult } from '../sync/engine'
|
|
||||||
import { getCloudCreds } from '../db/auth'
|
import { getCloudCreds } from '../db/auth'
|
||||||
import { showToast } from '../lib/toast'
|
import { showToast } from '../lib/toast'
|
||||||
|
|
||||||
@@ -38,7 +37,6 @@ const newLoginPwd2 = ref('')
|
|||||||
const accError = ref('')
|
const accError = ref('')
|
||||||
const deleteAccConfirm = ref(false)
|
const deleteAccConfirm = ref(false)
|
||||||
const appVersion = __APP_VERSION__
|
const appVersion = __APP_VERSION__
|
||||||
const syncBase = ref('')
|
|
||||||
const cloudUser = ref('')
|
const cloudUser = ref('')
|
||||||
const syncStatus = ref('')
|
const syncStatus = ref('')
|
||||||
const syncError = ref('')
|
const syncError = ref('')
|
||||||
@@ -47,7 +45,6 @@ const syncing = ref(false)
|
|||||||
const lastSync = ref('')
|
const lastSync = ref('')
|
||||||
|
|
||||||
async function loadSyncInfo() {
|
async function loadSyncInfo() {
|
||||||
syncBase.value = await getSyncBase()
|
|
||||||
cloudUser.value = getCloudCreds()?.username || ''
|
cloudUser.value = getCloudCreds()?.username || ''
|
||||||
lastSync.value = await getSyncLastTime()
|
lastSync.value = await getSyncLastTime()
|
||||||
autoSyncErr.value = syncLastError.value
|
autoSyncErr.value = syncLastError.value
|
||||||
@@ -55,11 +52,6 @@ async function loadSyncInfo() {
|
|||||||
watch(syncLastError, v => { autoSyncErr.value = v })
|
watch(syncLastError, v => { autoSyncErr.value = v })
|
||||||
|
|
||||||
|
|
||||||
async function saveSyncConfig() {
|
|
||||||
await setSyncBaseOverride(syncBase.value.trim())
|
|
||||||
showToast('同步地址已保存')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function rebindCloud() {
|
async function rebindCloud() {
|
||||||
syncError.value = ''
|
syncError.value = ''
|
||||||
try {
|
try {
|
||||||
@@ -76,7 +68,6 @@ async function doSyncNow() {
|
|||||||
autoSyncErr.value = syncLastError.value
|
autoSyncErr.value = syncLastError.value
|
||||||
syncing.value = true
|
syncing.value = true
|
||||||
try {
|
try {
|
||||||
await saveSyncConfig()
|
|
||||||
const result: SyncResult = await syncWithStoredConfig()
|
const result: SyncResult = await syncWithStoredConfig()
|
||||||
lastSync.value = result.at
|
lastSync.value = result.at
|
||||||
syncStatus.value = '推送 ' + result.pushed + ' 条 · 拉取 ' + result.pulled + ' 条 · 删除 ' + result.removed + ' 条'
|
syncStatus.value = '推送 ' + result.pushed + ' 条 · 拉取 ' + result.pulled + ' 条 · 删除 ' + result.removed + ' 条'
|
||||||
@@ -263,7 +254,7 @@ async function doImport() {
|
|||||||
<div class="settings">
|
<div class="settings">
|
||||||
<div class="card section">
|
<div class="card section">
|
||||||
<div class="section-title">账号与隐私</div>
|
<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="ai-grid">
|
||||||
<div class="rule-item">
|
<div class="rule-item">
|
||||||
<label>旧登录密码</label>
|
<label>旧登录密码</label>
|
||||||
@@ -352,15 +343,10 @@ async function doImport() {
|
|||||||
|
|
||||||
<div class="card section">
|
<div class="card section">
|
||||||
<div class="section-title">多端数据同步</div>
|
<div class="section-title">多端数据同步</div>
|
||||||
<div class="muted acc-line">云端账号:{{ cloudUser || '未绑定' }} · 数据仅保存在服务器,本机只是实时缓存(变更 ~3 秒推上云端 + 其他端秒级自动刷新);密码库同步密文,主密码永不出本机</div>
|
<div class="muted acc-line">云端账号:{{ cloudUser || '未绑定' }} · 数据存于服务器,其他端变更秒级自动刷新</div>
|
||||||
<div class="rule-item">
|
|
||||||
<label>同步服务地址(默认即可)</label>
|
|
||||||
<input class="input" v-model="syncBase" placeholder="https://daywk.linyux.cn" />
|
|
||||||
</div>
|
|
||||||
<div class="rule-save">
|
<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-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>
|
||||||
<div v-if="syncStatus" class="muted acc-line">上次同步:{{ lastSync || '--' }} · {{ syncStatus }}</div>
|
<div v-if="syncStatus" class="muted acc-line">上次同步:{{ lastSync || '--' }} · {{ syncStatus }}</div>
|
||||||
<div v-if="syncError" class="form-error">{{ syncError }}</div>
|
<div v-if="syncError" class="form-error">{{ syncError }}</div>
|
||||||
@@ -385,7 +371,7 @@ async function doImport() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="rule-save">
|
<div class="rule-save">
|
||||||
<button class="btn btn-primary btn-sm" @click="saveAi">保存 AI 配置</button>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -402,7 +388,7 @@ async function doImport() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="importError" class="import-error">{{ importError }}</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>
|
</div>
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|||||||
Reference in New Issue
Block a user