ui: 手机端布局重设计——底部带文字标签栏(5个主入口)+更多弹层、全屏搜索、打卡日历底部抽屉、弹窗贴底、记账卡片行、密码库横滑表格
This commit is contained in:
+26
File diff suppressed because one or more lines are too long
-26
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,2 +1,2 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-DCXp8Rvi.js","./index-BztxiTek.js","./index-CkA9n_Ns.css"])))=>i.map(i=>d[i]);
|
||||
import{r as s,_ as i}from"./index-BztxiTek.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-DCXp8Rvi.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-XtLEyZTQ.js","./index-BMAz2lN3.js","./index-DUPSAEmu.css"])))=>i.map(i=>d[i]);
|
||||
import{r as s,_ as i}from"./index-BMAz2lN3.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-XtLEyZTQ.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
|
||||
+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="apple-touch-icon" href="./icons/icon-192.png" />
|
||||
<title>实习工作日志</title>
|
||||
<script type="module" crossorigin src="./assets/index-BztxiTek.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-CkA9n_Ns.css">
|
||||
<script type="module" crossorigin src="./assets/index-BMAz2lN3.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DUPSAEmu.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
+261
-30
@@ -42,6 +42,8 @@ async function onLogin(s: Session) {
|
||||
}
|
||||
}
|
||||
async function doLogout() {
|
||||
mobileSearch.value = false
|
||||
moreOpen.value = false
|
||||
stopRealtime()
|
||||
if (syncTimer !== null) { window.clearInterval(syncTimer); syncTimer = null }
|
||||
await logoutUser()
|
||||
@@ -126,6 +128,22 @@ window.addEventListener('hashchange', () => { route.value = readRoute() })
|
||||
function nav(key: string) { location.hash = '#/' + key }
|
||||
const current = computed(() => VIEWS.find(v => v.key === route.value) || VIEWS[0])
|
||||
|
||||
// 手机端底部标签页:4 个主功能 + 「更多」弹层(统计/总结/密码库/设置/退出)
|
||||
const TAB_KEYS = ['calendar', 'today', 'ledger', 'memo']
|
||||
const TABS = VIEWS.filter(v => TAB_KEYS.includes(v.key))
|
||||
const MORE_VIEWS = VIEWS.filter(v => !TAB_KEYS.includes(v.key))
|
||||
const isMoreActive = computed(() => MORE_VIEWS.some(v => v.key === route.value))
|
||||
const moreOpen = ref(false)
|
||||
function navFromSheet(key: string) { moreOpen.value = false; nav(key) }
|
||||
|
||||
// 手机端全屏搜索
|
||||
const mobileSearch = ref(false)
|
||||
const msInput = ref<HTMLInputElement | null>(null)
|
||||
function openMobileSearch() { mobileSearch.value = true; window.setTimeout(() => msInput.value?.focus(), 60) }
|
||||
function pickLog(item: LogItem & { date: string }) { openLog(item); mobileSearch.value = false }
|
||||
function pickMemo(m: Memo) { openMemo(m); mobileSearch.value = false }
|
||||
|
||||
|
||||
// ---- 工具栏打卡 ----
|
||||
const today = ref(todayStr())
|
||||
const att = ref<Attendance | undefined>(undefined)
|
||||
@@ -212,6 +230,9 @@ onMounted(async () => {
|
||||
<div class="main">
|
||||
<header class="topbar">
|
||||
<span class="title">{{ current.name }}</span>
|
||||
<button class="mob-search-btn" title="搜索" @click="openMobileSearch">
|
||||
<Icon name="search" :size="16" />
|
||||
</button>
|
||||
<div class="gsearch">
|
||||
<Icon name="search" :size="14" class="gs-icon" />
|
||||
<input
|
||||
@@ -242,8 +263,8 @@ onMounted(async () => {
|
||||
<span class="dot" style="background: var(--warn)"></span>
|
||||
填写本周周记
|
||||
</button>
|
||||
<span class="user-chip num">{{ session?.username }}</span>
|
||||
<button class="btn btn-sm" @click="doLogout">退出</button>
|
||||
<span class="user-chip num mobile-hide">{{ session?.username }}</span>
|
||||
<button class="btn btn-sm mobile-hide" @click="doLogout">退出</button>
|
||||
<button class="btn btn-primary btn-sm" :disabled="clockDone" @click="doClock">
|
||||
<Icon :name="clockDone ? 'check-circle' : 'clock'" :size="14" />
|
||||
<span class="num">{{ clockLabel }}</span>
|
||||
@@ -262,6 +283,72 @@ onMounted(async () => {
|
||||
<SettingsView v-else />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 手机端底部标签栏 -->
|
||||
<nav class="tabbar">
|
||||
<button
|
||||
v-for="v in TABS" :key="v.key"
|
||||
class="tab-btn" :class="{ active: route === v.key }"
|
||||
@click="nav(v.key)"
|
||||
>
|
||||
<Icon :name="v.icon" :size="20" />
|
||||
<span>{{ v.name }}</span>
|
||||
</button>
|
||||
<button class="tab-btn" :class="{ active: isMoreActive }" @click="moreOpen = true">
|
||||
<Icon name="more" :size="20" />
|
||||
<span>更多</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- 手机端「更多」底部弹层 -->
|
||||
<div v-if="moreOpen" class="sheet-mask" @click="moreOpen = false">
|
||||
<div class="more-sheet" @click.stop>
|
||||
<div class="sheet-user num">{{ session?.username }}</div>
|
||||
<button v-for="v in MORE_VIEWS" :key="v.key" class="sheet-row" @click="navFromSheet(v.key)">
|
||||
<Icon :name="v.icon" :size="18" />
|
||||
<span class="sheet-name">{{ v.name }}</span>
|
||||
<Icon name="chevron-right" :size="14" class="sheet-go" />
|
||||
</button>
|
||||
<div class="sheet-divider"></div>
|
||||
<button class="sheet-row sheet-danger" @click="moreOpen = false; doLogout()">
|
||||
<Icon name="close" :size="18" />
|
||||
<span class="sheet-name">退出登录</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 手机端全屏搜索 -->
|
||||
<div v-if="mobileSearch" class="msw-mask" @click.self="mobileSearch = false">
|
||||
<div class="msw">
|
||||
<div class="msw-bar">
|
||||
<Icon name="search" :size="16" class="msw-icon" />
|
||||
<input
|
||||
ref="msInput" class="msw-input" v-model="searchQ"
|
||||
placeholder="搜索工作记录与备忘录"
|
||||
@input="onSearchInput" @keydown.esc="mobileSearch = false"
|
||||
/>
|
||||
<button class="icon-btn" @click="clearSearch(); mobileSearch = false">
|
||||
<Icon name="close" :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="msw-results">
|
||||
<template v-if="hasResults">
|
||||
<div v-if="results.logs.length" class="gs-group muted">工作记录</div>
|
||||
<div v-for="r in results.logs" :key="'l' + r.id" class="msw-item" @mousedown.prevent="pickLog(r)">
|
||||
<span class="tag">{{ categoryLabel(r.category) }}</span>
|
||||
<span class="gs-text">{{ r.content }}</span>
|
||||
<span class="muted num gs-date">{{ r.date }}</span>
|
||||
</div>
|
||||
<div v-if="results.memos.length" class="gs-group muted">备忘录</div>
|
||||
<div v-for="m in results.memos" :key="'m' + m.id" class="msw-item" @mousedown.prevent="pickMemo(m)">
|
||||
<span class="gs-text">{{ m.title || '无标题' }}</span>
|
||||
<span class="muted gs-sub">{{ m.content }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="searchQ.trim()" class="gs-empty muted">无匹配结果</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast-wrap">
|
||||
<transition-group name="toast">
|
||||
@@ -419,38 +506,182 @@ onMounted(async () => {
|
||||
.toast-enter-from, .toast-leave-to { opacity: 0; transform: translateY(-4px); }
|
||||
.toast-enter-active, .toast-leave-active { transition: all 0.15s ease; }
|
||||
|
||||
/* ---- 新增的手机端组件样式(桌面端隐藏) ---- */
|
||||
.mob-search-btn {
|
||||
display: none;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-2);
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.mobile-hide { display: initial; }
|
||||
|
||||
.tabbar {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 30;
|
||||
background: var(--bg-page);
|
||||
border-top: 1px solid var(--border);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.tab-btn {
|
||||
flex: 1;
|
||||
height: calc(52px - 6px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-3);
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
}
|
||||
.tab-btn.active { color: var(--accent); }
|
||||
|
||||
.sheet-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 70;
|
||||
background: rgba(29, 33, 41, 0.35);
|
||||
}
|
||||
.more-sheet {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--bg-page);
|
||||
border-radius: 14px 14px 0 0;
|
||||
padding: 10px 14px calc(14px + env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.sheet-user {
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
padding: 4px 6px 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.sheet-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
height: 46px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-1);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-sm);
|
||||
text-align: left;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.sheet-row:hover, .sheet-row:active { background: var(--bg-hover); }
|
||||
.sheet-name { flex: 1; }
|
||||
.sheet-go { color: var(--text-3); }
|
||||
.sheet-divider { height: 1px; background: var(--border); margin: 6px 0; }
|
||||
.sheet-danger { color: var(--danger); }
|
||||
|
||||
.msw-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 70;
|
||||
background: var(--bg-page);
|
||||
}
|
||||
.msw {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.msw-bar {
|
||||
height: 52px;
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.msw-icon { color: var(--text-3); }
|
||||
.msw-input {
|
||||
flex: 1;
|
||||
height: 34px;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
background: transparent;
|
||||
color: var(--text-1);
|
||||
}
|
||||
.msw-results {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 10px 4px calc(20px + env(safe-area-inset-bottom));
|
||||
}
|
||||
.msw-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 10px;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
.msw-item:hover, .msw-item:active { background: var(--bg-hover); }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.app { flex-direction: column; height: 100dvh; }
|
||||
.side {
|
||||
order: 2;
|
||||
width: 100%;
|
||||
height: calc(54px + env(safe-area-inset-bottom));
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
border-right: none;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 0;
|
||||
}
|
||||
.main { order: 1; flex: 1; min-height: 0; }
|
||||
.nav-btn { width: 44px; height: 42px; }
|
||||
.nav-btn::after { display: none; }
|
||||
/* 布局骨架:顶栏一行 + 内容 + 底部标签栏 */
|
||||
.app { display: block; height: 100dvh; }
|
||||
.side { display: none; }
|
||||
.main { display: flex; flex-direction: column; height: 100dvh; min-width: 0; }
|
||||
|
||||
.topbar {
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
min-height: 52px;
|
||||
padding: 8px 12px;
|
||||
row-gap: 8px;
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
padding: 0 10px;
|
||||
gap: 6px;
|
||||
}
|
||||
.topbar > .flex1 { display: none; }
|
||||
.gsearch { order: 10; width: 100%; max-width: 100%; }
|
||||
.gs-input { height: 34px; }
|
||||
.gs-panel { top: 100%; }
|
||||
.topbar-actions { margin-left: auto; }
|
||||
.user-chip { max-width: 72px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.title { font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.gsearch { display: none; }
|
||||
.gsearch .gs-panel { display: none; }
|
||||
.mob-search-btn { display: flex; }
|
||||
.topbar-actions { margin-left: auto; gap: 6px; }
|
||||
.mobile-hide { display: none !important; }
|
||||
.week-tip { height: 28px; max-width: 118px; padding: 0 8px; white-space: nowrap; overflow: hidden; }
|
||||
.topbar-actions .btn { padding: 0 10px; }
|
||||
.topbar-actions .btn .num { font-size: 12px; }
|
||||
|
||||
.content-inner { padding: 12px; }
|
||||
.toast-wrap { top: 8px; right: 12px; left: 12px; align-items: flex-end; }
|
||||
.tabbar { display: flex; }
|
||||
.content { flex: 1; min-height: 0; }
|
||||
.content-inner {
|
||||
max-width: none;
|
||||
padding: 12px 12px calc(66px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 卡片与表单:统一左右贴边、内距收紧 */
|
||||
.card { border-radius: var(--radius-md); }
|
||||
input.input, select.select, textarea.textarea { width: 100%; box-sizing: border-box; }
|
||||
.btn { touch-action: manipulation; }
|
||||
|
||||
/* 打卡按钮更醒目 */
|
||||
.topbar-actions .btn-primary {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
/* 提示信息对齐左上,避免遮挡底部标签栏 */
|
||||
.toast-wrap {
|
||||
top: 6px;
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,5 +22,6 @@ export const ICONS: Record<string, string> = {
|
||||
settings: svg('<circle cx="12" cy="12" r="3"/><path d="M12 4.5v2.2M12 17.3v2.2M4.5 12h2.2M17.3 12h2.2M6.8 6.8l1.6 1.6M15.6 15.6l1.6 1.6M17.2 6.8l-1.6 1.6M8.4 15.6l-1.6 1.6"/>'),
|
||||
memo: svg('<path d="M6.5 3.5h7.5L18.5 8v11a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1v-15a1 1 0 0 1 1-1z"/><path d="M14 3.5V8h4.5M9.5 12h5M9.5 15.5h5"/>'),
|
||||
lock: svg('<rect x="5.5" y="10.5" width="13" height="9.5" rx="1.5"/><path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5"/>'),
|
||||
wallet: svg('<rect x="3.5" y="6" width="17" height="12.5" rx="2"/><path d="M3.5 10h17"/><circle cx="16.4" cy="14.2" r="1.1"/>')
|
||||
wallet: svg('<rect x="3.5" y="6" width="17" height="12.5" rx="2"/><path d="M3.5 10h17"/><circle cx="16.4" cy="14.2" r="1.1"/>'),
|
||||
more: svg('<circle cx="5.4" cy="12" r="1.7"/><circle cx="12" cy="12" r="1.7"/><circle cx="18.6" cy="12" r="1.7"/>')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './styles/tokens.css'
|
||||
import './styles/mobile.css'
|
||||
import { showFatalError } from './lib/errOverlay'
|
||||
import { bootstrapSession, openUserDatabase } from './db/auth'
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/* ============================================================
|
||||
手机端(<=720px)全局适配:作用于各视图与弹窗组件
|
||||
(App.vue 的壳层样式是 scoped,跨组件规则统一放这里,便于集中管理)
|
||||
============================================================ */
|
||||
|
||||
/* 通用的白底卡片在窄屏贴边 */
|
||||
@media (max-width: 720px) {
|
||||
.card { border-radius: 12px; }
|
||||
|
||||
input.input, select.select, textarea.textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.btn { touch-action: manipulation; }
|
||||
|
||||
/* ---------- 弹窗:窄屏一体布局,覆盖组件内固定宽度 ---------- */
|
||||
.dialog {
|
||||
width: calc(100vw - 20px) !important;
|
||||
max-width: calc(100vw - 20px) !important;
|
||||
max-height: 86vh;
|
||||
max-height: 86dvh;
|
||||
overflow: auto;
|
||||
}
|
||||
.dialog-body { padding: 14px !important; }
|
||||
.form-grid { grid-template-columns: 1fr !important; }
|
||||
.dialog-btns { gap: 8px; }
|
||||
.dialog-btns .btn { flex: 1; }
|
||||
|
||||
/* ---------- 日历当日抽屉:改为底部抽屉 ---------- */
|
||||
.drawer {
|
||||
top: auto !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: min(86vh, 86dvh) !important;
|
||||
border-left: none !important;
|
||||
border-top: 1px solid var(--border);
|
||||
border-radius: 14px 14px 0 0 !important;
|
||||
}
|
||||
.drawer-enter-active, .drawer-leave-active { transition: transform 0.2s ease, opacity 0.2s ease; }
|
||||
.drawer-enter-from, .drawer-leave-to { transform: translateY(28px); opacity: 0; }
|
||||
.drawer-head { border-radius: 14px 14px 0 0; }
|
||||
.drawer-body { padding: 12px !important; }
|
||||
|
||||
/* ---------- 记账表格:卡片行(带行内标签),手机上不横滚 ---------- */
|
||||
.tx-table { min-width: 0 !important; }
|
||||
.tx-table thead { display: none !important; }
|
||||
.tx-table tr { display: block; padding: 10px 12px; border-bottom: 1px solid var(--border); background: transparent; }
|
||||
.tx-table tbody tr:last-child { border-bottom: none; }
|
||||
.tx-table td {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 3px 0 !important;
|
||||
border: none !important;
|
||||
white-space: normal !important;
|
||||
}
|
||||
.tx-table td::before {
|
||||
content: attr(data-label);
|
||||
color: var(--text-3);
|
||||
font-size: 12px;
|
||||
flex: none;
|
||||
}
|
||||
.tx-table td.ops-cell::before { content: none; }
|
||||
.tx-table td.ops-cell { justify-content: flex-end; }
|
||||
.tx-table td.empty { justify-content: center; }
|
||||
.tx-table td.empty::before { content: none !important; }
|
||||
|
||||
/* ---------- 密码库等其余宽表:横向滚动,不再挤压折行 ---------- */
|
||||
.table-card, .tx-card { overflow-x: auto; }
|
||||
.cred-table { min-width: 560px !important; }
|
||||
.th-ops { width: auto !important; }
|
||||
.vault-toolbar { flex-wrap: wrap; row-gap: 8px; }
|
||||
.vault-toolbar .search-wrap { width: 100%; max-width: none !important; }
|
||||
.vault-toolbar .flex1 { display: none; }
|
||||
.vault-toolbar .btn { flex: none; }
|
||||
|
||||
/* ---------- 日历格子收窄 ---------- */
|
||||
.cal-toolbar { margin-bottom: 8px; }
|
||||
.cal-cell { min-height: 54px !important; padding: 4px 5px !important; }
|
||||
.cal-marks { margin-top: 3px !important; height: 12px !important; gap: 3px !important; }
|
||||
|
||||
/* ---------- 各种桌面习惯型小间距收敛 ---------- */
|
||||
.section, .sec { padding: 12px 12px !important; }
|
||||
.rule-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
@@ -293,16 +293,16 @@ async function confirmDelete() {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="r in filtered" :key="r.id">
|
||||
<td class="num">{{ r.date }}</td>
|
||||
<td><span class="tag">{{ r.category }}</span></td>
|
||||
<td class="num" :class="r.type === 'income' ? 'amt-income' : 'amt-expense'">{{ amountText(r) }}</td>
|
||||
<td class="td-note">{{ r.note || '--' }}</td>
|
||||
<td>
|
||||
<td class="num" data-label="日期">{{ r.date }}</td>
|
||||
<td data-label="分类"><span class="tag">{{ r.category }}</span></td>
|
||||
<td class="num" data-label="金额" :class="r.type === 'income' ? 'amt-income' : 'amt-expense'">{{ amountText(r) }}</td>
|
||||
<td class="td-note" data-label="备注">{{ r.note || '--' }}</td>
|
||||
<td data-label="报销">
|
||||
<span v-if="r.type !== 'expense' || r.reimbursable !== 1" class="muted">--</span>
|
||||
<span v-else-if="r.reimbursed === 1" class="tx-done">已报销</span>
|
||||
<span v-else class="tx-pending">待报销</span>
|
||||
</td>
|
||||
<td>
|
||||
<td class="ops-cell">
|
||||
<div class="ops">
|
||||
<button
|
||||
v-if="r.type === 'expense' && r.reimbursable === 1 && r.reimbursed !== 1"
|
||||
|
||||
@@ -271,7 +271,7 @@ async function doChangePwd() {
|
||||
</div>
|
||||
</td>
|
||||
<td class="td-remark">{{ row.remark || '--' }}</td>
|
||||
<td>
|
||||
<td class="td-ops">
|
||||
<div class="ops">
|
||||
<button class="btn btn-xs" @click="openEdit(row)">编辑</button>
|
||||
<button class="btn btn-xs" @click="deleteTarget = row">删除</button>
|
||||
@@ -284,7 +284,7 @@ async function doChangePwd() {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="muted vault-note num">共 {{ rows.length }} 条账号记录 · 密码以 AES-256-GCM 加密存储于本机</div>
|
||||
<div class="muted vault-note num">共 {{ rows.length }} 条账号记录 · 密码以 AES-256-GCM 加密,服务器仅存密文</div>
|
||||
</template>
|
||||
|
||||
<CredentialDialog
|
||||
|
||||
Reference in New Issue
Block a user