diff --git a/web/static/css/components/content.css b/web/static/css/components/content.css
index 7525c2a..9240597 100644
--- a/web/static/css/components/content.css
+++ b/web/static/css/components/content.css
@@ -332,6 +332,18 @@
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s ease;
+ min-width: 100px;
+ height: 32px;
+ appearance: none;
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right 8px center;
+ background-size: 16px;
+ padding-right: 28px;
+}
+
+.per-page-select:hover {
+ border-color: var(--border-dark);
}
.per-page-select:focus {
@@ -340,6 +352,60 @@
box-shadow: 0 0 0 2px var(--accent-100);
}
+/* 卡片布局控制 */
+.cards-layout-controls {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.cards-per-row-select {
+ padding: 4px 8px;
+ border: 1px solid var(--border-medium);
+ border-radius: var(--radius-sm);
+ font-size: 14px;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ min-width: 80px;
+ height: 32px;
+ appearance: none;
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right 8px center;
+ background-size: 16px;
+ padding-right: 28px;
+}
+
+.cards-per-row-select:hover {
+ border-color: var(--border-dark);
+}
+
+.cards-per-row-select:focus {
+ outline: none;
+ border-color: var(--accent-500);
+ box-shadow: 0 0 0 2px var(--accent-100);
+}
+
+/* 黑暗模式下的select样式 */
+[data-theme="dark"] .cards-per-row-select,
+[data-theme="dark"] .per-page-select {
+ background-color: var(--bg-secondary);
+ border-color: var(--border-dark);
+}
+
+[data-theme="dark"] .cards-per-row-select:hover,
+[data-theme="dark"] .per-page-select:hover {
+ border-color: var(--border-light);
+}
+
+[data-theme="dark"] .cards-per-row-select:focus,
+[data-theme="dark"] .per-page-select:focus {
+ border-color: var(--accent-500);
+ box-shadow: 0 0 0 2px var(--accent-900);
+}
+
/* 主内容区域 */
.content-main {
flex: 1;
@@ -430,9 +496,30 @@
/* 卡片视图 */
.data-cards {
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
padding: 16px;
+ transition: all 0.3s ease;
+}
+
+/* 动态列数样式 */
+.data-cards[data-columns="1"] {
+ grid-template-columns: 1fr;
+}
+
+.data-cards[data-columns="2"] {
+ grid-template-columns: repeat(2, 1fr);
+}
+
+.data-cards[data-columns="3"] {
+ grid-template-columns: repeat(3, 1fr);
+}
+
+.data-cards[data-columns="4"] {
+ grid-template-columns: repeat(4, 1fr);
+}
+
+.data-cards[data-columns="6"] {
+ grid-template-columns: repeat(6, 1fr);
}
.data-card {
@@ -519,8 +606,15 @@
padding: 16px;
}
- .data-cards {
+ .data-cards[data-columns="1"],
+ .data-cards[data-columns="2"],
+ .data-cards[data-columns="3"],
+ .data-cards[data-columns="4"],
+ .data-cards[data-columns="6"] {
grid-template-columns: 1fr;
+ }
+
+ .data-cards {
padding: 12px;
}
@@ -531,6 +625,26 @@
}
}
+@media (max-width: 480px) {
+ .header-tools {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 8px;
+ }
+
+ .view-controls,
+ .export-dropdown,
+ .cards-layout-controls {
+ width: 100%;
+ }
+
+ .export-button,
+ .cards-per-row-select {
+ width: 100%;
+ justify-content: center;
+ }
+}
+
@media (max-width: 480px) {
.header-tools {
flex-direction: column;
diff --git a/web/static/js/app.js b/web/static/js/app.js
index f571683..10af151 100644
--- a/web/static/js/app.js
+++ b/web/static/js/app.js
@@ -295,6 +295,12 @@ class AppState {
// 切换数据展示方式
this.switchDataDisplay(mode);
+
+ // 显示/隐藏卡片布局控制
+ const cardsLayoutControls = document.getElementById('cardsLayoutControls');
+ if (cardsLayoutControls) {
+ cardsLayoutControls.style.display = mode === 'cards' ? 'flex' : 'none';
+ }
}
switchDataDisplay(mode) {
@@ -404,6 +410,15 @@ class AppState {
window.location.href = `/?${params.toString()}`;
}
+ // 卡片布局控制
+ changeCardsPerRow(columns) {
+ const cardsContainer = document.querySelector('.data-cards');
+ if (cardsContainer) {
+ cardsContainer.setAttribute('data-columns', columns);
+ localStorage.setItem('cardsPerRow', columns);
+ }
+ }
+
// 键盘快捷键
setupKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
@@ -613,6 +628,15 @@ class AppState {
perPageSelect.value = savedPerPage;
}
}
+
+ // 恢复卡片布局设置
+ const savedCardsPerRow = localStorage.getItem('cardsPerRow') || '2';
+ this.changeCardsPerRow(savedCardsPerRow);
+
+ const cardsPerRowSelect = document.querySelector('.cards-per-row-select');
+ if (cardsPerRowSelect) {
+ cardsPerRowSelect.value = savedCardsPerRow;
+ }
}
openSettings() {
@@ -647,6 +671,7 @@ window.setViewMode = (mode) => app.setViewMode(mode);
window.toggleExportMenu = () => app.toggleExportMenu();
window.exportData = (format) => app.exportData(format);
window.changePerPage = (perPage) => app.changePerPage(perPage);
+window.changeCardsPerRow = (columns) => app.changeCardsPerRow(columns);
window.showDetail = (id) => app.showDetail(id);
window.closeModal = () => app.closeModal();
window.openSettings = () => app.openSettings();
diff --git a/web/templates/list.html b/web/templates/list.html
index 4614734..5d14722 100644
--- a/web/templates/list.html
+++ b/web/templates/list.html
@@ -167,6 +167,19 @@