diff --git a/src/main/resources/templates/browse.html b/src/main/resources/templates/browse.html
index 1ab1aee..76126bb 100644
--- a/src/main/resources/templates/browse.html
+++ b/src/main/resources/templates/browse.html
@@ -14,23 +14,27 @@
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-left: env(safe-area-inset-left, 0px);
--safe-right: env(safe-area-inset-right, 0px);
- --nav-height: 52px;
+ --nav-height: 56px;
--color-primary: #6366f1;
- --color-bg: #f1f5f9;
+ --color-primary-light: #818cf8;
+ --color-primary-dark: #4f46e5;
+ --color-bg: #f8fafc;
--color-surface: #ffffff;
--color-text: #1e293b;
--color-text-secondary: #64748b;
--color-text-muted: #94a3b8;
--color-border: #e2e8f0;
- --radius-lg: 20px;
+ --radius-xl: 24px;
+ --radius-lg: 18px;
--radius-md: 14px;
--radius-sm: 10px;
--radius-full: 999px;
- --shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
+ --shadow-md: 0 4px 12px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
--font-xs: 13px;
--font-sm: 14px;
- --font-base: 17px;
- --font-lg: 19px;
+ --font-base: 16px;
+ --font-lg: 18px;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
@@ -43,20 +47,30 @@
/* ==================== 导航 ==================== */
.navbar {
- background: var(--color-surface); border-bottom: 1px solid var(--color-border);
+ background: rgba(255,255,255,0.78);
+ border-bottom: 1px solid rgba(226,232,240,0.6);
height: var(--nav-height); padding: 0 var(--safe-left) 0 var(--safe-right);
display: flex; justify-content: space-between; align-items: center;
position: sticky; top: 0; z-index: 100;
- backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
- background: rgba(255,255,255,0.85);
+ backdrop-filter: blur(24px) saturate(1.4);
+ -webkit-backdrop-filter: blur(24px) saturate(1.4);
}
- .navbar .brand { font-size: 17px; font-weight: 700; color: var(--color-text); letter-spacing: -0.3px; padding-left: 20px; }
- .navbar .nav-links { padding-right: 16px; display: flex; gap: 6px; }
+ .navbar .brand {
+ font-size: 18px; font-weight: 800; color: var(--color-text);
+ letter-spacing: -0.4px; padding-left: 20px;
+ display: flex; align-items: center; gap: 6px;
+ }
+ .navbar .brand-dot {
+ width: 8px; height: 8px; border-radius: 50%;
+ background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));
+ }
+ .navbar .nav-links { padding-right: 16px; display: flex; gap: 4px; }
.navbar .nav-links a {
color: var(--color-text-secondary); text-decoration: none; font-size: var(--font-sm);
- padding: 8px 14px; border-radius: var(--radius-sm); transition: all 0.2s; font-weight: 500; white-space: nowrap;
+ padding: 8px 14px; border-radius: var(--radius-sm); transition: all 0.2s;
+ font-weight: 500; white-space: nowrap;
}
- .navbar .nav-links a:hover, .navbar .nav-links a:active { background: #f1f5f9; color: var(--color-primary); }
+ .navbar .nav-links a:hover { background: rgba(99,102,241,0.06); color: var(--color-primary); }
/* ==================== 页面容器 ==================== */
.page-container {
@@ -64,87 +78,118 @@
padding: 16px 12px calc(24px + var(--safe-bottom));
}
- /* ==================== 筛选栏 — 紧凑 ==================== */
+ /* ==================== 筛选栏 ==================== */
.filter-bar {
- background: var(--color-surface); border-radius: var(--radius-md);
- padding: 12px 14px; box-shadow: var(--shadow-sm); margin-bottom: 12px;
+ background: var(--color-surface); border-radius: var(--radius-xl);
+ padding: 18px 18px 14px;
+ box-shadow: var(--shadow-md);
+ border: 1px solid rgba(226,232,240,0.5);
+ margin-bottom: 14px;
+ animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
- .filter-section { margin-bottom: 10px; }
- .filter-section:last-of-type { margin-bottom: 10px; }
+
+ .filter-section { margin-bottom: 14px; }
+ .filter-section:last-of-type { margin-bottom: 14px; }
.filter-section-title {
font-size: 11px; font-weight: 700; color: var(--color-text-muted);
- text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 4px;
+ text-transform: uppercase; letter-spacing: 0.8px; margin-bottom: 8px;
+ padding-left: 2px;
}
/* 日期输入 */
.date-row {
- display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; margin-bottom: 2px;
+ display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px;
}
.date-row input[type="date"] {
border: 1.5px solid var(--color-border); border-radius: var(--radius-sm);
- font-size: var(--font-sm); padding: 0 10px; transition: border-color 0.2s;
- background: #f8fafc; width: 100%; min-width: 0; height: 40px; min-height: 40px;
- -webkit-appearance: none; line-height: 40px;
+ font-size: var(--font-sm); padding: 0 12px;
+ transition: all 0.2s; background: #f8fafc;
+ width: 100%; min-width: 0; height: 42px; min-height: 42px;
+ -webkit-appearance: none; line-height: 42px; color: var(--color-text);
}
.date-row input[type="date"]:focus {
border-color: var(--color-primary); outline: none;
- box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
+ background: white;
+ box-shadow: 0 0 0 3px rgba(99,102,241,0.08);
}
/* Chip 多选标签组 */
.chip-group {
- display: flex; flex-wrap: wrap; gap: 5px;
- max-height: 88px; overflow-y: auto; -webkit-overflow-scrolling: touch;
+ display: flex; flex-wrap: wrap; gap: 6px;
+ max-height: 96px; overflow-y: auto; -webkit-overflow-scrolling: touch;
padding: 2px 0;
}
+ .chip-group::-webkit-scrollbar { width: 3px; }
+ .chip-group::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 3px; }
+
.chip-group .chip {
- display: inline-flex; align-items: center; gap: 3px;
- padding: 4px 10px; border-radius: var(--radius-full);
+ display: inline-flex; align-items: center; gap: 4px;
+ padding: 6px 12px; border-radius: var(--radius-full);
border: 1.5px solid var(--color-border); font-size: var(--font-xs);
cursor: pointer; user-select: none; -webkit-user-select: none;
- transition: all 0.15s; background: #fafbfc; color: var(--color-text-secondary);
- white-space: nowrap; min-height: 30px;
+ transition: all 0.2s ease; background: #fafbfc;
+ color: var(--color-text-secondary); white-space: nowrap; min-height: 32px;
+ font-weight: 500;
}
- .chip-group .chip:active { transform: scale(0.96); }
+ .chip-group .chip:active { transform: scale(0.95); }
.chip-group .chip input[type="checkbox"] {
width: 14px; height: 14px; accent-color: var(--color-primary); margin: 0; flex-shrink: 0;
}
.chip-group .chip.selected {
- background: #eef2ff; border-color: var(--color-primary); color: var(--color-primary); font-weight: 600;
- }
- .chip-group .chip-count {
- font-size: 10px; color: var(--color-text-muted); margin-left: auto;
+ background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
+ border-color: var(--color-primary); color: var(--color-primary); font-weight: 600;
+ box-shadow: 0 2px 8px rgba(99,102,241,0.1);
}
/* 按钮行 */
- .btn-group-row {
- display: flex; gap: 8px; margin-top: 12px;
- }
+ .btn-group-row { display: flex; gap: 10px; margin-top: 16px; }
.btn-reset {
background: var(--color-surface); color: var(--color-text-secondary);
- border: 1.5px solid var(--color-border); padding: 10px 18px; border-radius: var(--radius-sm);
- font-weight: 500; font-size: var(--font-sm); transition: all 0.2s;
- display: flex; align-items: center; justify-content: center; gap: 4px; white-space: nowrap; cursor: pointer;
+ border: 1.5px solid var(--color-border); padding: 11px 20px; border-radius: var(--radius-sm);
+ font-weight: 600; font-size: var(--font-sm); transition: all 0.2s;
+ display: flex; align-items: center; justify-content: center; gap: 5px;
+ white-space: nowrap; cursor: pointer;
}
- .btn-reset:active { background: #f1f5f9; }
+ .btn-reset:hover { border-color: var(--color-text-muted); color: var(--color-text); }
+ .btn-reset:active { background: #f1f5f9; transform: scale(0.98); }
+
.btn-search {
- flex: 1; background: var(--color-primary); color: white; border: none;
- padding: 10px 20px; border-radius: var(--radius-sm); font-weight: 600; font-size: var(--font-sm);
- transition: background 0.2s, transform 0.1s; cursor: pointer;
- display: flex; align-items: center; justify-content: center; gap: 4px;
+ flex: 1; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
+ color: white; border: none; padding: 11px 20px; border-radius: var(--radius-sm);
+ font-weight: 700; font-size: var(--font-sm);
+ transition: all 0.2s; cursor: pointer;
+ display: flex; align-items: center; justify-content: center; gap: 5px;
+ box-shadow: 0 4px 14px rgba(99,102,241,0.25);
+ position: relative; overflow: hidden;
}
- .btn-search:active { background: #4f46e5; transform: scale(0.98); }
+ .btn-search::after {
+ content: ''; position: absolute; inset: 0;
+ background: linear-gradient(135deg, transparent 0%, rgba(255,255,255,0.12) 100%);
+ pointer-events: none;
+ }
+ .btn-search:hover { box-shadow: 0 6px 20px rgba(99,102,241,0.35); transform: translateY(-1px); }
+ .btn-search:active { transform: translateY(0) scale(0.98); box-shadow: 0 2px 8px rgba(99,102,241,0.25); }
/* ==================== 汇总栏 ==================== */
.summary-bar {
- background: var(--color-surface); border-radius: var(--radius-sm);
- padding: 10px 16px; margin-bottom: 10px; display: none;
+ background: var(--color-surface); border-radius: var(--radius-lg);
+ padding: 14px 18px; margin-bottom: 12px; display: none;
align-items: center; justify-content: space-between;
- box-shadow: var(--shadow-sm); animation: fadeInUp 0.3s ease;
+ box-shadow: var(--shadow-sm);
+ border: 1px solid rgba(226,232,240,0.5);
+ animation: fadeInUp 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.summary-bar.visible { display: flex; }
- .summary-amount { font-size: var(--font-base); font-weight: 700; color: var(--color-primary); }
- .summary-count { font-size: var(--font-xs); color: var(--color-text-secondary); font-weight: 500; }
+ .summary-amount {
+ font-size: var(--font-lg); font-weight: 800;
+ background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
+ background-clip: text;
+ }
+ .summary-count {
+ font-size: var(--font-xs); color: var(--color-text-secondary); font-weight: 500;
+ background: #f1f5f9; padding: 4px 10px; border-radius: var(--radius-full);
+ }
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(8px); }
@@ -153,40 +198,79 @@
/* ==================== 表格 ==================== */
.table-card {
- background: var(--color-surface); border-radius: var(--radius-md);
- overflow: hidden; box-shadow: var(--shadow-sm);
+ background: var(--color-surface); border-radius: var(--radius-xl);
+ overflow: hidden; box-shadow: var(--shadow-md);
+ border: 1px solid rgba(226,232,240,0.5);
+ animation: fadeInUp 0.45s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
- .table-card table { width: 100%; min-width: 500px; margin: 0; border-collapse: collapse; }
+ .table-card table { width: 100%; min-width: 520px; margin: 0; border-collapse: collapse; }
.table-card table thead th {
- background: #f8fafc; font-size: 11px; font-weight: 700; color: var(--color-text-secondary);
- text-transform: uppercase; letter-spacing: 0.4px; padding: 4px 6px;
- border-bottom: 2px solid var(--color-border); white-space: nowrap; position: sticky; top: 0;
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
+ font-size: 11px; font-weight: 700; color: var(--color-text-secondary);
+ text-transform: uppercase; letter-spacing: 0.6px; padding: 12px 14px;
+ border-bottom: 2px solid var(--color-border); white-space: nowrap;
+ position: sticky; top: 0;
}
- .table-card table tbody td {
- padding: 3px 6px; border-bottom: 1px solid #f1f5f9;
- font-size: var(--font-xs); color: var(--color-text); vertical-align: middle;
- }
- .table-card table tbody tr:last-child td { border-bottom: none; }
- .table-card table tbody tr:active { background: #f8fafc; }
- .category-badge {
- display: inline-block; background: #eef2ff; color: var(--color-primary);
- padding: 2px 8px; border-radius: var(--radius-full); font-size: 11px; font-weight: 600; white-space: nowrap;
- }
- .amount-cell { font-weight: 600; color: #ef4444; text-align: right; font-variant-numeric: tabular-nums; }
+ .table-card table thead th:first-child { padding-left: 18px; }
+ .table-card table thead th:last-child { padding-right: 18px; text-align: right; }
- .empty-msg { text-align: center; padding: 40px 20px; color: var(--color-text-muted); font-size: var(--font-sm); }
- .loading-bar { text-align: center; padding: 36px 20px; display: none; }
+ .table-card table tbody td {
+ padding: 12px 14px; border-bottom: 1px solid #f1f5f9;
+ font-size: var(--font-sm); color: var(--color-text); vertical-align: middle;
+ transition: background 0.15s;
+ }
+ .table-card table tbody td:first-child { padding-left: 18px; font-weight: 500; }
+ .table-card table tbody td:last-child { padding-right: 18px; }
+ .table-card table tbody tr:last-child td { border-bottom: none; }
+ .table-card table tbody tr:hover { background: #f8fafc; }
+ .table-card table tbody tr:active { background: #f1f5f9; }
+
+ .category-badge {
+ display: inline-block;
+ background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
+ color: var(--color-primary);
+ padding: 3px 10px; border-radius: var(--radius-full);
+ font-size: 11px; font-weight: 600; white-space: nowrap;
+ }
+ .amount-cell {
+ font-weight: 700; color: #ef4444; text-align: right;
+ font-variant-numeric: tabular-nums; font-size: var(--font-sm);
+ }
+
+ .empty-msg {
+ text-align: center; padding: 48px 20px; color: var(--color-text-muted);
+ font-size: var(--font-sm); display: flex; flex-direction: column;
+ align-items: center; gap: 8px;
+ }
+ .empty-msg i { font-size: 32px; opacity: 0.4; }
+
+ .loading-bar {
+ text-align: center; padding: 40px 20px; display: none;
+ }
.loading-bar.show { display: block; }
- .loading-bar .spinner-border { width: 28px; height: 28px; border-width: 3px; color: var(--color-primary); }
+ .loading-bar .spinner-border {
+ width: 28px; height: 28px; border-width: 3px; color: var(--color-primary);
+ }
+ .loading-bar p {
+ margin-top: 12px; color: var(--color-text-muted); font-size: var(--font-sm);
+ }
+
+ /* ==================== 响应式 ==================== */
+ @media (min-width: 768px) {
+ .page-container { padding: 24px 20px 32px; }
+ .filter-bar { padding: 20px 22px 16px; }
+ .table-card table thead th { padding: 14px 16px; }
+ .table-card table tbody td { padding: 14px 16px; }
+ }
@@ -217,7 +301,7 @@
-
+
@@ -261,7 +348,6 @@
// ==================== 初始化 Chip 选择器 ====================
document.addEventListener('DOMContentLoaded', loadFilterOptions);
- // 存储每个 filter 组的 chip 容器 ID 映射
const chipMap = {
person: 'chipPerson',
location: 'chipLocation',
@@ -283,7 +369,6 @@
const label = document.createElement('label');
label.className = 'chip';
label.innerHTML = ' ' + escHtml(v);
- // 点击 label 切换 selected 样式
label.addEventListener('change', function(e) {
if (e.target.checked) {
label.classList.add('selected');
@@ -331,8 +416,8 @@
renderResults(data);
} catch (err) {
console.error('查询失败:', err);
- emptyMsg.textContent = '查询失败,请重试';
- emptyMsg.style.display = 'block';
+ emptyMsg.innerHTML = '查询失败,请重试';
+ emptyMsg.style.display = 'flex';
} finally {
loadingBar.classList.remove('show');
}
@@ -352,8 +437,8 @@
if (!data.records || data.records.length === 0) {
resultTable.style.display = 'none';
- emptyMsg.textContent = '未找到匹配的事项记录';
- emptyMsg.style.display = 'block';
+ emptyMsg.innerHTML = '未找到匹配的事项记录';
+ emptyMsg.style.display = 'flex';
summaryBar.classList.remove('visible');
return;
}
@@ -381,7 +466,6 @@
}
function resetFilters() {
- // 取消所有 chip 勾选
['chipPerson', 'chipLocation', 'chipCategory'].forEach(id => {
const container = document.getElementById(id);
if (!container) return;
@@ -394,8 +478,9 @@
document.getElementById('filterEndTime').value = '';
document.getElementById('resultTable').style.display = 'none';
- document.getElementById('emptyMsg').textContent = '点击「查询」查看事项数据';
- document.getElementById('emptyMsg').style.display = 'block';
+ const emptyMsg = document.getElementById('emptyMsg');
+ emptyMsg.innerHTML = '点击「查询」查看事项数据';
+ emptyMsg.style.display = 'flex';
document.getElementById('summaryBar').classList.remove('visible');
}
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
index ee10711..181def3 100644
--- a/src/main/resources/templates/index.html
+++ b/src/main/resources/templates/index.html
@@ -14,25 +14,28 @@
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-left: env(safe-area-inset-left, 0px);
--safe-right: env(safe-area-inset-right, 0px);
- --mic-size: clamp(72px, 20vw, 92px);
+ --mic-size: clamp(72px, 20vw, 88px);
--mic-color: #6366f1;
+ --mic-color-light: #818cf8;
--mic-recording: #ef4444;
- --nav-height: 52px;
- --color-bg: #f1f5f9;
+ --nav-height: 56px;
+ --color-bg: #f8fafc;
--color-surface: #ffffff;
--color-text: #1e293b;
--color-text-secondary: #64748b;
--color-text-muted: #94a3b8;
--color-border: #e2e8f0;
- --radius-lg: 20px;
+ --radius-xl: 24px;
+ --radius-lg: 18px;
--radius-md: 14px;
--radius-sm: 10px;
--radius-full: 999px;
- --shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
- --font-xs: 14px;
- --font-sm: 15px;
- --font-base: 18px;
- --font-lg: 20px;
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
+ --shadow-md: 0 4px 12px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
+ --font-xs: 13px;
+ --font-sm: 14px;
+ --font-base: 16px;
+ --font-lg: 18px;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
@@ -45,101 +48,143 @@
/* ==================== 顶部导航 ==================== */
.navbar {
- background: rgba(255,255,255,0.85); border-bottom: 1px solid var(--color-border);
+ background: rgba(255,255,255,0.78);
+ border-bottom: 1px solid rgba(226,232,240,0.6);
height: var(--nav-height); padding: 0 var(--safe-left) 0 var(--safe-right);
display: flex; justify-content: space-between; align-items: center;
position: sticky; top: 0; z-index: 100;
- backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
+ backdrop-filter: blur(24px) saturate(1.4);
+ -webkit-backdrop-filter: blur(24px) saturate(1.4);
}
- .navbar .brand { font-size: 18px; font-weight: 700; color: var(--color-text); letter-spacing: -0.3px; padding-left: 20px; }
- .navbar .nav-links { padding-right: 16px; display: flex; gap: 6px; }
+ .navbar .brand {
+ font-size: 18px; font-weight: 800; color: var(--color-text);
+ letter-spacing: -0.4px; padding-left: 20px;
+ display: flex; align-items: center; gap: 6px;
+ }
+ .navbar .brand-dot {
+ width: 8px; height: 8px; border-radius: 50%;
+ background: linear-gradient(135deg, var(--mic-color), var(--mic-color-light));
+ }
+ .navbar .nav-links { padding-right: 16px; display: flex; gap: 4px; }
.navbar .nav-links a {
color: var(--color-text-secondary); text-decoration: none; font-size: var(--font-sm);
- padding: 8px 14px; border-radius: var(--radius-sm); transition: all 0.2s; font-weight: 500; white-space: nowrap;
+ padding: 8px 14px; border-radius: var(--radius-sm); transition: all 0.2s;
+ font-weight: 500; white-space: nowrap;
}
- .navbar .nav-links a:hover, .navbar .nav-links a:active { background: #f1f5f9; color: var(--mic-color); }
+ .navbar .nav-links a:hover { background: rgba(99,102,241,0.06); color: var(--mic-color); }
/* ==================== 通知栏 ==================== */
.notification {
position: fixed; top: calc(var(--nav-height) + 12px + var(--safe-top)); left: 50%;
transform: translateX(-50%) translateY(-8px); z-index: 9999;
- background: #10b981; color: white; padding: 10px 20px; border-radius: var(--radius-full);
+ background: var(--color-surface); color: #059669;
+ padding: 10px 20px; border-radius: var(--radius-full);
font-size: var(--font-sm); font-weight: 600;
- box-shadow: 0 4px 16px rgba(16, 185, 129, 0.35);
+ box-shadow: 0 8px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04);
+ border: 1px solid rgba(16,185,129,0.2);
opacity: 0; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease;
max-width: calc(100vw - 40px); text-align: center;
+ display: flex; align-items: center; gap: 8px;
}
.notification.show { opacity: 1; transform: translateX(-50%) translateY(0); }
- .notification.error { background: #ef4444; box-shadow: 0 4px 16px rgba(239, 68, 68, 0.35); }
+ .notification.error {
+ color: #dc2626; border-color: rgba(239,68,68,0.2);
+ }
/* ==================== 内容区 ==================== */
.content-area {
max-width: 640px; margin: 0 auto;
- padding: 10px 8px calc(var(--mic-size) + 20px + var(--safe-bottom) + 40px);
+ padding: 16px 12px calc(var(--mic-size) + 24px + var(--safe-bottom) + 48px);
}
/* ==================== 空状态 ==================== */
- .empty-state { text-align: center; padding: 40px 16px 24px; color: var(--color-text-muted); }
- .empty-state .empty-icon { font-size: 52px; margin-bottom: 14px; display: block; opacity: 0.6; }
- .empty-state .empty-title { font-size: var(--font-lg); font-weight: 600; color: var(--color-text); margin-bottom: 6px; }
- .empty-state .empty-desc { font-size: var(--font-sm); color: var(--color-text-muted); line-height: 1.4; }
+ .empty-state {
+ text-align: center; padding: 48px 20px 32px;
+ animation: fadeInUp 0.5s cubic-bezier(0.16, 1, 0.3, 1);
+ }
+ .empty-icon-wrap {
+ display: inline-flex; align-items: center; justify-content: center;
+ width: 80px; height: 80px; border-radius: 24px;
+ background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 50%, #ede9fe 100%);
+ box-shadow: 0 4px 16px rgba(99,102,241,0.12);
+ margin-bottom: 20px; font-size: 36px;
+ }
+ .empty-state .empty-title {
+ font-size: var(--font-lg); font-weight: 700; color: var(--color-text);
+ margin-bottom: 8px; letter-spacing: -0.3px;
+ }
+ .empty-state .empty-desc {
+ font-size: var(--font-sm); color: var(--color-text-muted); line-height: 1.6;
+ }
/* ==================== 查询结果 ==================== */
.result-card {
background: transparent; border-radius: 0;
padding: 0; box-shadow: none;
- display: none; animation: fadeInUp 0.35s ease;
+ display: none; animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.result-card.visible { display: block; }
/* 卡片列表 */
- .records-list {
- display: -webkit-flex; display: flex;
- -webkit-flex-direction: column; flex-direction: column;
- gap: 5px;
- }
+ .records-list { display: flex; flex-direction: column; gap: 8px; }
.record-card {
- border-radius: 12px; padding: 10px 14px;
- display: -webkit-flex; display: flex;
- -webkit-flex-direction: column; flex-direction: column;
- gap: 2px;
+ background: var(--color-surface); border-radius: var(--radius-lg);
+ padding: 14px 16px; display: flex; flex-direction: column; gap: 6px;
border-left: 4px solid #e2e8f0;
- box-shadow: 0 1px 2px rgba(0,0,0,0.03);
- -webkit-tap-highlight-color: rgba(0,0,0,0.05);
+ box-shadow: var(--shadow-sm);
+ transition: all 0.2s ease;
+ animation: cardSlideIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
}
- .record-card:active { opacity: 0.85; }
+ .record-card:nth-child(1) { animation-delay: 0s; }
+ .record-card:nth-child(2) { animation-delay: 0.05s; }
+ .record-card:nth-child(3) { animation-delay: 0.1s; }
+ .record-card:nth-child(4) { animation-delay: 0.15s; }
+ .record-card:nth-child(5) { animation-delay: 0.2s; }
+ @keyframes cardSlideIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+ .record-card:active { transform: scale(0.99); opacity: 0.9; }
+
.card-top {
- display: -webkit-flex; display: flex;
- -webkit-justify-content: space-between; justify-content: space-between;
- -webkit-align-items: baseline; align-items: baseline;
+ display: flex; justify-content: space-between; align-items: baseline;
+ }
+ .card-time {
+ font-size: 14px; color: var(--color-text-secondary); font-weight: 500;
+ display: flex; align-items: center; gap: 6px;
+ }
+ .card-time .cat-emoji { font-size: 18px; }
+ .card-amount {
+ font-size: 20px; font-weight: 800; color: #ef4444; white-space: nowrap;
+ font-variant-numeric: tabular-nums; letter-spacing: -0.3px;
}
- .card-time { font-size: 16px; color: #64748b; font-weight: 500; }
- .card-time .cat-emoji { margin-right: 4px; font-size: 18px; }
- .card-amount { font-size: 20px; font-weight: 700; color: #ef4444; white-space: nowrap; }
.card-detail {
- font-size: 15px; color: #334155; line-height: 1.5;
- display: -webkit-flex; display: flex;
- -webkit-flex-wrap: wrap; flex-wrap: wrap;
- gap: 4px 8px;
+ font-size: 14px; color: #334155; line-height: 1.5;
+ display: flex; flex-wrap: wrap; gap: 4px 10px;
}
.card-detail .meta-tag {
- font-size: 13px; color: #94a3b8; white-space: nowrap;
+ font-size: 12px; color: var(--color-text-muted);
+ background: #f1f5f9; padding: 2px 8px; border-radius: var(--radius-full);
}
/* 底部总结 */
.summary-footer {
- margin-top: 10px; padding: 14px 16px;
- background: #eef2ff; border-radius: 12px;
- text-align: center; font-size: 16px; font-weight: 600;
- color: #4f46e5; line-height: 1.5;
+ margin-top: 14px; padding: 16px 20px;
+ background: linear-gradient(135deg, #eef2ff 0%, #ede9fe 100%);
+ border: 1px solid rgba(99,102,241,0.1);
+ border-radius: var(--radius-lg);
+ text-align: center; font-size: 15px; font-weight: 600;
+ color: #4f46e5; line-height: 1.6;
+ animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both;
}
/* ==================== 聊天回复 ==================== */
.chat-reply {
- background: var(--color-surface); border-radius: var(--radius-sm);
- padding: 12px 12px; box-shadow: var(--shadow-sm);
- font-size: var(--font-sm); line-height: 1.6; color: var(--color-text);
- display: none; animation: fadeInUp 0.35s ease;
+ background: var(--color-surface); border-radius: var(--radius-lg);
+ padding: 16px 18px; box-shadow: var(--shadow-md);
+ font-size: var(--font-sm); line-height: 1.7; color: var(--color-text);
+ display: none; animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
+ border: 1px solid rgba(226,232,240,0.5);
}
.chat-reply.visible { display: block; }
.chat-reply p { margin: 0; }
@@ -152,61 +197,80 @@
/* ==================== 麦克风按钮区域 ==================== */
.mic-bottom-bar {
position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
- background: linear-gradient(180deg, transparent 0%, var(--color-bg) 50%, var(--color-bg) 100%);
- padding: 16px 0 calc(24px + var(--safe-bottom));
+ background: linear-gradient(180deg, transparent 0%, var(--color-bg) 40%, var(--color-bg) 100%);
+ padding: 20px 0 calc(28px + var(--safe-bottom));
display: flex; flex-direction: column; align-items: center; pointer-events: none;
}
.mic-container { position: relative; pointer-events: auto; }
.mic-btn {
width: var(--mic-size); height: var(--mic-size); border-radius: 50%;
- background: var(--mic-color); border: none; color: white;
- font-size: calc(var(--mic-size) * 0.4); cursor: pointer;
- box-shadow: 0 4px 24px rgba(99, 102, 241, 0.4);
- transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.2s ease;
+ background: linear-gradient(135deg, var(--mic-color) 0%, var(--mic-color-light) 100%);
+ border: none; color: white;
+ font-size: calc(var(--mic-size) * 0.38); cursor: pointer;
+ box-shadow:
+ 0 6px 24px rgba(99,102,241,0.35),
+ 0 2px 8px rgba(99,102,241,0.2),
+ inset 0 1px 0 rgba(255,255,255,0.2);
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
display: flex; align-items: center; justify-content: center;
- user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; position: relative;
+ user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
+ position: relative;
+ }
+ .mic-btn::before {
+ content: ''; position: absolute; inset: -6px; border-radius: 50%;
+ background: rgba(99,102,241,0.08); z-index: -1;
+ transition: all 0.3s ease;
+ }
+ .mic-btn:active {
+ transform: scale(0.92);
+ box-shadow: 0 3px 12px rgba(99,102,241,0.3), inset 0 1px 0 rgba(255,255,255,0.2);
}
- .mic-btn:active { transform: scale(0.94); box-shadow: 0 2px 12px rgba(99, 102, 241, 0.3); transition: transform 0.08s ease; }
.mic-btn.recording {
- background: var(--mic-recording); box-shadow: 0 4px 28px rgba(239, 68, 68, 0.5);
+ background: linear-gradient(135deg, var(--mic-recording) 0%, #f87171 100%);
+ box-shadow:
+ 0 6px 28px rgba(239,68,68,0.45),
+ 0 2px 8px rgba(239,68,68,0.25);
animation: micPulse 1.6s ease-in-out infinite;
}
+ .mic-btn.recording::before { background: rgba(239,68,68,0.1); }
@keyframes micPulse {
- 0%, 100% { box-shadow: 0 4px 28px rgba(239, 68, 68, 0.5); }
- 50% { box-shadow: 0 4px 28px rgba(239, 68, 68, 0.25), 0 0 0 16px rgba(239, 68, 68, 0.08); }
+ 0%, 100% { box-shadow: 0 6px 28px rgba(239,68,68,0.45), 0 2px 8px rgba(239,68,68,0.25); }
+ 50% { box-shadow: 0 6px 28px rgba(239,68,68,0.2), 0 0 0 14px rgba(239,68,68,0.06); }
}
- /* 底部状态行:提示文字 + 处理中指示器 */
+ /* 底部状态行 */
.mic-status-row {
- display: flex; align-items: center; gap: 10px; margin-top: 10px;
+ display: flex; align-items: center; gap: 10px; margin-top: 12px;
pointer-events: none; user-select: none; -webkit-user-select: none;
}
.mic-hint {
font-size: var(--font-xs); color: var(--color-text-muted); font-weight: 500;
}
- .mic-hint.recording-hint { color: var(--mic-recording); font-weight: 600; animation: hintBlink 1.2s ease-in-out infinite; }
+ .mic-hint.recording-hint {
+ color: var(--mic-recording); font-weight: 600;
+ animation: hintBlink 1.2s ease-in-out infinite;
+ }
@keyframes hintBlink {
- 0%, 100% { opacity: 1; } 50% { opacity: 0.5; }
+ 0%, 100% { opacity: 1; } 50% { opacity: 0.4; }
}
+
/* 处理中指示器 */
- .processing-indicator {
- display: none; align-items: center; gap: 4px;
- }
+ .processing-indicator { display: none; align-items: center; gap: 6px; }
.processing-indicator.show { display: flex; }
.processing-dot {
- width: 6px; height: 6px; border-radius: 50%; background: var(--mic-color);
+ width: 5px; height: 5px; border-radius: 50%; background: var(--mic-color);
animation: dotBounce 1.2s ease-in-out infinite;
}
.processing-dot:nth-child(2) { animation-delay: 0.15s; }
.processing-dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes dotBounce {
- 0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
+ 0%, 80%, 100% { transform: scale(0.5); opacity: 0.3; }
40% { transform: scale(1); opacity: 1; }
}
- .processing-text { font-size: var(--font-xs); color: var(--mic-color); font-weight: 500; }
+ .processing-text { font-size: var(--font-xs); color: var(--mic-color); font-weight: 600; }
@media (min-width: 768px) {
- .content-area { padding: 32px 20px 120px; }
+ .content-area { padding: 28px 20px 120px; }
.navbar .brand { padding-left: 24px; }
}
@@ -214,9 +278,9 @@
@@ -225,7 +289,7 @@
-
🎤
+
🎤
语音记账助手
按住下方按钮说话
录入事项或查询记录
@@ -252,21 +316,18 @@
// ==================== 麦克风权限预检 ====================
(async function checkMicPermission() {
try {
- // 先检查当前权限状态
if (navigator.permissions) {
const status = await navigator.permissions.query({ name: 'microphone' });
- if (status.state === 'granted') return; // 已有权限
+ if (status.state === 'granted') return;
if (status.state === 'denied') {
showNotification('麦克风权限已被拒绝,请在系统设置中开启', true);
return;
}
}
- // 请求权限
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getTracks().forEach(t => t.stop());
} catch (err) {
console.log('麦克风权限预检:', err.message);
- // 不强制弹通知,用户使用时还会再次触发权限请求
}
})();
@@ -379,7 +440,6 @@
hapticFeedback('warning');
return;
}
- // 显示底部处理中指示器(不阻塞操作)
processing.classList.add('show');
micHint.style.display = 'none';
await sendAudio(wavBlob);
@@ -552,7 +612,8 @@
let notifyTimer = null;
function showNotification(message, isError) {
if (notifyTimer) clearTimeout(notifyTimer);
- notification.textContent = message;
+ const icon = isError ? '✕' : '✓';
+ notification.innerHTML = '
' + icon + ' ' + escapeHtml(message);
notification.className = 'notification' + (isError ? ' error' : '');
void notification.offsetWidth;
notification.classList.add('show');
diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html
index 0d545c5..74adc77 100644
--- a/src/main/resources/templates/login.html
+++ b/src/main/resources/templates/login.html
@@ -7,21 +7,24 @@
登录 - TraceCD
+
-
-
📝
-
TraceCD
-
语音智能记账助手
+
+
+
+
-
+