|
41 | 41 | </style> |
42 | 42 | </head> |
43 | 43 | <body> |
44 | | - <nav class="navbar bg-body-tertiary navbar-expand-lg sticky-top"> |
| 44 | + <nav class="navbar bg-body-secondary navbar-expand-lg sticky-top"> |
45 | 45 | <div class="container-fluid"> |
46 | 46 | <a class="navbar-brand" href="#"> |
47 | 47 | <img src="../static/favicon.svg" alt="Stable Diffusion 模型管理器" height="26"> |
48 | 48 | </a> |
49 | | - <button class="btn btn-outline-secondary ms-auto" data-bs-toggle="modal" data-bs-target="#settingsModal"> |
50 | | - <i class="bi bi-gear"></i> |
51 | | - <span class="ms-1">设置</span> |
52 | | - </button> |
53 | | - <button class="btn btn-outline-success ms-3" id="nsfwToggle"> |
| 49 | + |
| 50 | + <button class="btn btn-outline-success ms-auto" id="nsfwToggle"> |
54 | 51 | <i class="bi bi-eye-slash-fill" id="nsfwIcon"></i> |
55 | 52 | <span class="ms-1" id="nsfwText">NSFW 已关闭</span> |
56 | 53 | </button> |
| 54 | + |
| 55 | + <button class="btn btn-outline-secondary ms-3" data-bs-toggle="modal" data-bs-target="#settingsModal"> |
| 56 | + <i class="bi bi-gear"></i> |
| 57 | + <span class="ms-1">设置</span> |
| 58 | + </button> |
| 59 | + |
57 | 60 | <button class="btn btn-outline ms-3" id="darkModeToggle"> |
58 | 61 | <i class="bi bi-sun-fill" id="lightIcon"></i> |
59 | 62 | <i class="bi bi-moon-stars-fill d-none" id="darkIcon"></i> |
60 | 63 | </button> |
| 64 | + <button class="btn btn-outline-secondary ms-3 d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#filterSidebar"> |
| 65 | + <i class="bi bi-funnel-fill"></i> |
| 66 | + </button> |
61 | 67 | </div> |
62 | 68 | </nav> |
63 | 69 |
|
@@ -88,48 +94,92 @@ <h5 class="modal-title" id="settingsModalLabel">设置</h5> |
88 | 94 | </div> |
89 | 95 | </div> |
90 | 96 |
|
91 | | - <div class="container mt-4"> |
92 | | - |
93 | | - <div class="alert alert-danger d-none" id="error" role="alert"></div> |
94 | | - <div class="text-center d-none" id="loading"> |
95 | | - <div class="progress mb-2" style="height: 20px;"> |
96 | | - <div class="progress-bar progress-bar-striped progress-bar-animated" |
97 | | - role="progressbar" |
98 | | - style="width: 0%" |
99 | | - aria-valuenow="0" |
100 | | - aria-valuemin="0" |
101 | | - aria-valuemax="100">0%</div> |
102 | | - </div> |
103 | | - <div class="text-muted" id="progressMessage"></div> |
104 | | - </div> |
| 97 | + <div class="container-fluid"> |
| 98 | + <div class="row"> |
| 99 | + <!-- 主内容区 --> |
| 100 | + <div class="col"> |
| 101 | + <div class="container mt-4"> |
| 102 | + <div class="alert alert-danger d-none" id="error" role="alert"></div> |
| 103 | + <div class="text-center d-none" id="loading"> |
| 104 | + <div class="progress mb-2" style="height: 20px;"> |
| 105 | + <div class="progress-bar progress-bar-striped progress-bar-animated" |
| 106 | + role="progressbar" |
| 107 | + style="width: 0%" |
| 108 | + aria-valuenow="0" |
| 109 | + aria-valuemin="0" |
| 110 | + aria-valuemax="100">0%</div> |
| 111 | + </div> |
| 112 | + <div class="text-muted" id="progressMessage"></div> |
| 113 | + </div> |
105 | 114 |
|
106 | | - <!-- 筛选按钮组 --> |
107 | | - <div class="d-flex flex-wrap gap-2 mb-3"> |
108 | | - <!-- 基础模型筛选标题 --> |
109 | | - <div class="d-flex align-items-center me-2"> |
110 | | - <small class="text-muted">基础模型:</small> |
111 | | - </div> |
112 | | - <!-- 基础模型筛选按钮 --> |
113 | | - <div class="d-flex flex-wrap gap-2 me-4" id="baseModelFilters"> |
114 | | - <button type="button" class="btn btn-outline-secondary" data-filter="baseModel" data-value=""> |
115 | | - 全部 |
116 | | - </button> |
| 115 | + <!-- 模型列表 --> |
| 116 | + <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5 g-3 pb-5" id="modelsList"> |
| 117 | + </div> |
| 118 | + </div> |
117 | 119 | </div> |
118 | 120 |
|
119 | | - <!-- 类型筛选标题 --> |
120 | | - <div class="d-flex align-items-center me-2"> |
121 | | - <small class="text-muted">类型:</small> |
122 | | - </div> |
123 | | - <!-- 类型筛选按钮 --> |
124 | | - <div class="d-flex flex-wrap gap-2" id="typeFilters"> |
125 | | - <button type="button" class="btn btn-outline-secondary" data-filter="type" data-value=""> |
126 | | - 全部 |
127 | | - </button> |
| 121 | + <!-- 侧边栏 --> |
| 122 | + <div class="col-auto border-start bg-body-tertiary d-none d-lg-block" style="width: 25%;"> |
| 123 | + <div class="d-flex flex-column position-sticky p-3" style="top: 56px; max-height: calc(100vh - 56px); overflow-y: auto;"> |
| 124 | + <div class="d-flex align-items-center pb-3 mb-3 border-bottom"> |
| 125 | + <span class="fs-5 fw-semibold">筛选器</span> |
| 126 | + </div> |
| 127 | + <!-- 基础模型筛选 --> |
| 128 | + <div class="mb-3"> |
| 129 | + <div class="nav flex-column"> |
| 130 | + <div class="nav-item"> |
| 131 | + <span class="nav-link text-secondary small mb-2">基础模型</span> |
| 132 | + <div class="d-flex flex-wrap gap-2" id="baseModelFilters"> |
| 133 | + |
| 134 | + </div> |
| 135 | + </div> |
| 136 | + </div> |
| 137 | + </div> |
| 138 | + |
| 139 | + <!-- 类型筛选 --> |
| 140 | + <div class="mb-3"> |
| 141 | + <div class="nav flex-column"> |
| 142 | + <div class="nav-item"> |
| 143 | + <span class="nav-link text-secondary small mb-2">类型</span> |
| 144 | + <div class="d-flex flex-wrap gap-2" id="typeFilters"> |
| 145 | + |
| 146 | + </div> |
| 147 | + </div> |
| 148 | + </div> |
| 149 | + </div> |
| 150 | + </div> |
128 | 151 | </div> |
129 | 152 | </div> |
| 153 | + </div> |
130 | 154 |
|
131 | | - <!-- 模型列表 --> |
132 | | - <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5 g-3 pb-5" id="modelsList"> |
| 155 | + <!-- 画布外侧边栏 --> |
| 156 | + <div class="offcanvas offcanvas-end d-lg-none" tabindex="-1" id="filterSidebar" aria-labelledby="filterSidebarLabel"> |
| 157 | + <div class="offcanvas-header"> |
| 158 | + <h5 class="offcanvas-title" id="filterSidebarLabel">筛选器</h5> |
| 159 | + <button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#filterSidebar" aria-label="Close"></button> |
| 160 | + </div> |
| 161 | + <div class="offcanvas-body"> |
| 162 | + <!-- 基础模型筛选 --> |
| 163 | + <div class="mb-3"> |
| 164 | + <div class="nav flex-column"> |
| 165 | + <div class="nav-item"> |
| 166 | + <span class="nav-link text-secondary small mb-2">基础模型</span> |
| 167 | + <div class="d-flex flex-wrap gap-2" id="baseModelFiltersMobile"> |
| 168 | + </div> |
| 169 | + </div> |
| 170 | + </div> |
| 171 | + </div> |
| 172 | + |
| 173 | + <!-- 类型筛选 --> |
| 174 | + <div class="mb-3"> |
| 175 | + <div class="nav flex-column"> |
| 176 | + <div class="nav-item"> |
| 177 | + <span class="nav-link text-secondary small mb-2">类型</span> |
| 178 | + <div class="d-flex flex-wrap gap-2" id="typeFiltersMobile"> |
| 179 | + </div> |
| 180 | + </div> |
| 181 | + </div> |
| 182 | + </div> |
133 | 183 | </div> |
134 | 184 | </div> |
135 | 185 |
|
@@ -390,39 +440,48 @@ <h5 class="card-title mb-0">${model.name}</h5> |
390 | 440 | // 获取所有基础模型 |
391 | 441 | const baseModels = new Set(models.map(model => model.baseModel || '未知')); |
392 | 442 | const baseModelGroup = document.getElementById('baseModelFilters'); |
393 | | - baseModelGroup.innerHTML = ` |
394 | | - <button type="button" class="btn btn-outline-secondary" data-filter="baseModel" data-value=""> |
| 443 | + const baseModelGroupMobile = document.getElementById('baseModelFiltersMobile'); |
| 444 | + const filterHtml = ` |
| 445 | + <button type="button" class="btn btn-outline-secondary btn-sm" data-filter="baseModel" data-value=""> |
395 | 446 | 全部 |
396 | 447 | </button> |
397 | 448 | `; |
| 449 | + baseModelGroup.innerHTML = filterHtml; |
| 450 | + baseModelGroupMobile.innerHTML = filterHtml; |
398 | 451 |
|
399 | 452 | baseModels.forEach(baseModel => { |
400 | | - baseModelGroup.innerHTML += ` |
401 | | - <button type="button" class="btn btn-outline-secondary" data-filter="baseModel" data-value="${baseModel}"> |
| 453 | + const buttonHtml = ` |
| 454 | + <button type="button" class="btn btn-outline-secondary btn-sm" data-filter="baseModel" data-value="${baseModel}"> |
402 | 455 | ${baseModel} |
403 | 456 | </button> |
404 | 457 | `; |
| 458 | + baseModelGroup.innerHTML += buttonHtml; |
| 459 | + baseModelGroupMobile.innerHTML += buttonHtml; |
405 | 460 | }); |
406 | 461 |
|
407 | 462 | // 获取所有类型 |
408 | 463 | const types = new Set(models.map(model => model.type || '未知')); |
409 | 464 | const typeGroup = document.getElementById('typeFilters'); |
| 465 | + const typeGroupMobile = document.getElementById('typeFiltersMobile'); |
410 | 466 | typeGroup.innerHTML = ` |
411 | | - <button type="button" class="btn btn-outline-secondary" data-filter="type" data-value=""> |
| 467 | + <button type="button" class="btn btn-outline-secondary btn-sm" data-filter="type" data-value=""> |
412 | 468 | 全部 |
413 | 469 | </button> |
414 | 470 | `; |
| 471 | + typeGroupMobile.innerHTML = typeGroup.innerHTML; |
415 | 472 |
|
416 | 473 | types.forEach(type => { |
417 | | - typeGroup.innerHTML += ` |
418 | | - <button type="button" class="btn btn-outline-secondary" data-filter="type" data-value="${type}"> |
| 474 | + const buttonHtml = ` |
| 475 | + <button type="button" class="btn btn-outline-secondary btn-sm" data-filter="type" data-value="${type}"> |
419 | 476 | ${type} |
420 | 477 | </button> |
421 | 478 | `; |
| 479 | + typeGroup.innerHTML += buttonHtml; |
| 480 | + typeGroupMobile.innerHTML += buttonHtml; |
422 | 481 | }); |
423 | 482 |
|
424 | 483 | // 重新添加事件监听 |
425 | | - document.querySelectorAll('#baseModelFilters button, #typeFilters button').forEach(button => { |
| 484 | + document.querySelectorAll('#baseModelFilters button, #typeFilters button, #baseModelFiltersMobile button, #typeFiltersMobile button').forEach(button => { |
426 | 485 | button.addEventListener('click', (e) => { |
427 | 486 | const filter = e.target.dataset.filter; |
428 | 487 | const value = e.target.dataset.value; |
|
0 commit comments