mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
✨ Feature(custom): support adjust grid size in gallery page
ISSUES CLOSED: #419
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
"@piclist/i18n": "^2.0.0",
|
||||
"@piclist/store": "^3.0.0",
|
||||
"@smithy/node-http-handler": "^4.4.7",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"ali-oss": "^6.23.0",
|
||||
"axios": "^1.13.2",
|
||||
"chalk": "^5.6.2",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"dateRange": "Date Range",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"gridSize": "Grid Size",
|
||||
"gridView": "Grid",
|
||||
"haveDuplicate": "There are naming duplicates among the selected images, do you want to continue?",
|
||||
"hideFilters": "Filters",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"dateRange": "日期范围",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"gridSize": "网格大小",
|
||||
"gridView": "网格",
|
||||
"haveDuplicate": "已选中的图片中有命名重复, 是否继续?",
|
||||
"hideFilters": "过滤器",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"dateRange": "日期範圍",
|
||||
"delete": "刪除",
|
||||
"edit": "編輯",
|
||||
"gridSize": "網格大小",
|
||||
"gridView": "網格",
|
||||
"haveDuplicate": "已選中的圖片中有命名重複, 是否繼續?",
|
||||
"hideFilters": "過濾器",
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="grid-size-control">
|
||||
<GridIcon :size="14" />
|
||||
<input
|
||||
v-model.number="userGridColumns"
|
||||
type="range"
|
||||
min="1"
|
||||
max="15"
|
||||
step="1"
|
||||
class="grid-slider"
|
||||
:title="t('pages.gallery.gridSize')"
|
||||
/>
|
||||
</div>
|
||||
<div class="sync-delete-toggle">
|
||||
<span class="toggle-label">{{ t('pages.gallery.isAlwaysForceReload') }}</span>
|
||||
<label class="custom-switch">
|
||||
@@ -195,7 +207,7 @@
|
||||
:items="filterList"
|
||||
:item-height="itemHeight"
|
||||
:grid-items="4"
|
||||
:grid-breakpoints="gridBreakpoints"
|
||||
:grid-breakpoints="effectiveGridBreakpoints"
|
||||
key-field="key"
|
||||
:page-mode="true"
|
||||
:buffer-factor="0.5"
|
||||
@@ -468,6 +480,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import {
|
||||
CheckSquareIcon,
|
||||
ChevronDownIcon,
|
||||
@@ -564,18 +577,15 @@ const dateRangeEnd = ref('')
|
||||
const picBedDropdownOpen = ref(false)
|
||||
const sortDropdownOpen = ref(false)
|
||||
const showFormatInfo = ref(false)
|
||||
const viewMode = ref<'list' | 'grid'>('grid')
|
||||
const viewMode = useStorage<'list' | 'grid'>('galleryViewMode', 'grid')
|
||||
const componentKey = ref(0)
|
||||
const currentSortField = ref<'name' | 'time' | 'ext' | 'check'>('name')
|
||||
const userGridColumns = useStorage<number>('galleryGridColumns', 4)
|
||||
const itemHeight = 300
|
||||
const gridBreakpoints = [
|
||||
{ min: 0, cols: 1 },
|
||||
{ min: 380, cols: 2 },
|
||||
{ min: 768, cols: 3 },
|
||||
{ min: 1024, cols: 4 },
|
||||
{ min: 1280, cols: 6 },
|
||||
{ min: 1536, cols: 7 },
|
||||
]
|
||||
|
||||
const effectiveGridBreakpoints = computed(() => {
|
||||
return [{ min: 0, cols: userGridColumns.value }]
|
||||
})
|
||||
|
||||
const imageLoadStates = reactive<Record<string, boolean>>({})
|
||||
const imageErrorStates = reactive<Record<string, boolean>>({})
|
||||
@@ -961,7 +971,6 @@ function handleImageTouchEnd(event: TouchEvent) {
|
||||
|
||||
function toggleViewMode() {
|
||||
viewMode.value = viewMode.value === 'grid' ? 'list' : 'grid'
|
||||
localStorage.setItem('galleryViewMode', viewMode.value)
|
||||
}
|
||||
|
||||
function getViewModeIcon() {
|
||||
@@ -982,7 +991,6 @@ onBeforeRouteUpdate((to, from) => {
|
||||
})
|
||||
|
||||
async function initConf() {
|
||||
viewMode.value = (localStorage.getItem('galleryViewMode') as 'list' | 'grid') || 'grid'
|
||||
pasteStyle.value = (await getConfig(configPaths.settings.pasteStyle)) || IPasteStyle.MARKDOWN
|
||||
useShortUrl.value = (await getConfig(configPaths.settings.useShortUrl))
|
||||
? t('pages.gallery.shortUrl')
|
||||
@@ -1104,6 +1112,14 @@ watch(filterList, () => {
|
||||
clearChoosedList()
|
||||
})
|
||||
|
||||
watch(userGridColumns, _ => {
|
||||
nextTick(() => {
|
||||
if (virtualScrollerRef.value) {
|
||||
virtualScrollerRef.value.refresh()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let searchURLDebounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
|
||||
@@ -719,7 +719,6 @@
|
||||
<div class="form-group">
|
||||
<input v-model="customLink.value" type="text" class="form-input" :placeholder="''" />
|
||||
</div>
|
||||
<small> </small>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="btn btn-secondary" @click="cancelCustomLink">
|
||||
|
||||
@@ -181,6 +181,78 @@ input:checked + .switch-slider::before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* Grid Size Control */
|
||||
.grid-size-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.375rem 0.5rem;
|
||||
background: var(--color-surface-elevated);
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.grid-size-control .control-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.grid-slider {
|
||||
border-radius: 2px;
|
||||
width: 70px;
|
||||
height: 4px;
|
||||
background: var(--color-border);
|
||||
outline: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid-slider::-webkit-slider-thumb {
|
||||
border-radius: 50%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: var(--color-blue-common);
|
||||
transition: var(--transition-fast);
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid-slider::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 0 0 3px rgb(56 114 250 / 20%);
|
||||
}
|
||||
|
||||
.grid-slider::-moz-range-thumb {
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: var(--color-blue-common);
|
||||
transition: var(--transition-fast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid-slider::-moz-range-thumb:hover {
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 0 0 3px rgb(56 114 250 / 20%);
|
||||
}
|
||||
|
||||
.grid-value {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.125rem 0.375rem;
|
||||
min-width: 20px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
background: var(--color-blue-common);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Filter Card */
|
||||
.filter-card {
|
||||
border-radius: var(--radius-lg);
|
||||
|
||||
24
yarn.lock
24
yarn.lock
@@ -4461,6 +4461,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.3.58.tgz#7e8cdafee25c75d6eb18f530b93ac52edff53c03"
|
||||
integrity sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==
|
||||
|
||||
"@types/web-bluetooth@^0.0.21":
|
||||
version "0.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz#525433c784aed9b457aaa0ee3d92aeb71f346b63"
|
||||
integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==
|
||||
|
||||
"@types/write-file-atomic@^4.0.3":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/write-file-atomic/-/write-file-atomic-4.0.3.tgz#bda169b8369022e2c87028671fa4b742c08d98c9"
|
||||
@@ -4934,6 +4939,25 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.26.tgz#1e02ef2d64aced818cd31d81ce5175711dc90a9f"
|
||||
integrity sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==
|
||||
|
||||
"@vueuse/core@^14.1.0":
|
||||
version "14.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-14.1.0.tgz#274e98e591a505333b7dfb2bcaf7b4530a10b9c9"
|
||||
integrity sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==
|
||||
dependencies:
|
||||
"@types/web-bluetooth" "^0.0.21"
|
||||
"@vueuse/metadata" "14.1.0"
|
||||
"@vueuse/shared" "14.1.0"
|
||||
|
||||
"@vueuse/metadata@14.1.0":
|
||||
version "14.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-14.1.0.tgz#70fc2e94775e4a07369f11f86f6f0a465b04a381"
|
||||
integrity sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==
|
||||
|
||||
"@vueuse/shared@14.1.0":
|
||||
version "14.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-14.1.0.tgz#49b2face86a9c0c52e20eaf4c732a0223276c11f"
|
||||
integrity sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==
|
||||
|
||||
"@xmldom/xmldom@^0.8.3":
|
||||
version "0.8.6"
|
||||
resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz#8a1524eb5bd5e965c1e3735476f0262469f71440"
|
||||
|
||||
Reference in New Issue
Block a user