优化添加背景页面右边栏清除背景后的状态

This commit is contained in:
jingrow 2026-01-21 22:23:27 +08:00
parent 887f4efcb1
commit e544ec301e

View File

@ -27,7 +27,7 @@ favorite-colors-grid<template>
v-for="(shade, index) in colorShades" v-for="(shade, index) in colorShades"
:key="index" :key="index"
class="shade-item" class="shade-item"
:class="{ 'active': shade === backgroundColor }" :class="{ 'active': backgroundColor && shade === backgroundColor }"
:style="{ backgroundColor: shade }" :style="{ backgroundColor: shade }"
@click="selectShade(shade)" @click="selectShade(shade)"
:title="shade" :title="shade"
@ -208,7 +208,7 @@ favorite-colors-grid<template>
v-model="backgroundColor" v-model="backgroundColor"
type="text" type="text"
class="sidebar-hex-input" class="sidebar-hex-input"
placeholder="#FFFFFF" placeholder=""
@input="onHexInputChange" @input="onHexInputChange"
@blur="onHexInputBlur" @blur="onHexInputBlur"
/> />
@ -294,7 +294,15 @@ favorite-colors-grid<template>
<n-tab-pane name="color" :tab="t('Color')"> <n-tab-pane name="color" :tab="t('Color')">
<div class="color-picker-section"> <div class="color-picker-section">
<div class="color-picker-container"> <div class="color-picker-container">
<!-- Show checkerboard when background is cleared -->
<div
v-if="!backgroundColor"
class="sidebar-color-picker-empty"
@click="backgroundColor = '#FFFFFF'"
>
</div>
<input <input
v-else
v-model="backgroundColor" v-model="backgroundColor"
type="color" type="color"
class="sidebar-color-picker" class="sidebar-color-picker"
@ -305,7 +313,7 @@ favorite-colors-grid<template>
v-model="backgroundColor" v-model="backgroundColor"
type="text" type="text"
class="sidebar-hex-input" class="sidebar-hex-input"
placeholder="#FFFFFF" placeholder=""
@input="onHexInputChange" @input="onHexInputChange"
@blur="onHexInputBlur" @blur="onHexInputBlur"
/> />
@ -313,7 +321,7 @@ favorite-colors-grid<template>
class="add-favorite-btn" class="add-favorite-btn"
@click="addToFavorites" @click="addToFavorites"
:title="t('Add to favorites')" :title="t('Add to favorites')"
:disabled="favoriteColors.length >= MAX_FAVORITE_COLORS" :disabled="!backgroundColor || favoriteColors.length >= MAX_FAVORITE_COLORS"
> >
<i class="fa fa-star"></i> <i class="fa fa-star"></i>
</button> </button>
@ -403,7 +411,7 @@ favorite-colors-grid<template>
<n-spin :show="pexelsLoading"> <n-spin :show="pexelsLoading">
<div v-if="pexelsImages.length > 0" class="background-images-grid"> <div v-if="pexelsImages.length > 0" class="background-images-grid">
<div <div
v-for="image in pexelsImages" v-for="image in displayedPexelsImages"
:key="image.id" :key="image.id"
class="background-image-item" class="background-image-item"
@click="applyBackgroundImage(image.src.medium)" @click="applyBackgroundImage(image.src.medium)"
@ -411,7 +419,16 @@ favorite-colors-grid<template>
<img :src="image.src.tiny" :alt="image.alt" /> <img :src="image.src.tiny" :alt="image.alt" />
</div> </div>
</div> </div>
<n-empty v-else :description="t('No images found')" size="small" /> <div v-if="showLoadMoreButton" class="load-more-container">
<button
type="button"
class="load-more-btn"
@click="loadMorePexelsImages"
>
{{ t('Show More') }}
</button>
</div>
<n-empty v-else-if="pexelsImages.length === 0" :description="t('No images found')" size="small" />
</n-spin> </n-spin>
</div> </div>
</n-tab-pane> </n-tab-pane>
@ -429,7 +446,15 @@ favorite-colors-grid<template>
<div class="color-picker-section"> <div class="color-picker-section">
<h4 class="section-title">{{ t('Background Color') }}</h4> <h4 class="section-title">{{ t('Background Color') }}</h4>
<div class="color-picker-container"> <div class="color-picker-container">
<!-- Show checkerboard when background is cleared -->
<div
v-if="!backgroundColor"
class="sidebar-color-picker-empty"
@click="backgroundColor = '#FFFFFF'"
>
</div>
<input <input
v-else
v-model="backgroundColor" v-model="backgroundColor"
type="color" type="color"
class="sidebar-color-picker" class="sidebar-color-picker"
@ -440,7 +465,7 @@ favorite-colors-grid<template>
v-model="backgroundColor" v-model="backgroundColor"
type="text" type="text"
class="sidebar-hex-input" class="sidebar-hex-input"
placeholder="#FFFFFF" placeholder=""
@input="onHexInputChange" @input="onHexInputChange"
@blur="onHexInputBlur" @blur="onHexInputBlur"
/> />
@ -448,7 +473,7 @@ favorite-colors-grid<template>
class="add-favorite-btn" class="add-favorite-btn"
@click="addToFavorites" @click="addToFavorites"
:title="t('Add to favorites')" :title="t('Add to favorites')"
:disabled="favoriteColors.length >= MAX_FAVORITE_COLORS" :disabled="!backgroundColor || favoriteColors.length >= MAX_FAVORITE_COLORS"
> >
<i class="fa fa-star"></i> <i class="fa fa-star"></i>
</button> </button>
@ -552,7 +577,7 @@ favorite-colors-grid<template>
<button <button
type="button" type="button"
class="load-more-btn" class="load-more-btn"
@click="showAllPexelsImages = true" @click="loadMorePexelsImages"
> >
{{ t('Show More') }} {{ t('Show More') }}
</button> </button>
@ -693,14 +718,23 @@ const fetchPexelsImages = async () => {
params: { params: {
query: pexelsQuery.value, query: pexelsQuery.value,
page: pexelsPage.value, page: pexelsPage.value,
per_page: 20, per_page: PEXELS_PER_PAGE,
orientation: 'landscape' orientation: 'landscape'
}, },
headers: { headers: {
Authorization: PEXELS_API_KEY Authorization: PEXELS_API_KEY
} }
}) })
pexelsImages.value = response.data.photos || [] const newPhotos = response.data.photos || []
// Append new photos to existing list
if (pexelsPage.value === 1) {
pexelsImages.value = newPhotos
} else {
pexelsImages.value = [...pexelsImages.value, ...newPhotos]
}
PEXELS_TOTAL_FETCHED.value = pexelsImages.value.length
} catch (error: any) { } catch (error: any) {
console.error('Failed to fetch Pexels images:', error) console.error('Failed to fetch Pexels images:', error)
// Don't show error message if API key is not configured // Don't show error message if API key is not configured
@ -1044,25 +1078,35 @@ const pexelsLoading = ref(false)
const pexelsPage = ref(1) const pexelsPage = ref(1)
const pexelsQuery = ref('nature background') const pexelsQuery = ref('nature background')
const PEXELS_API_KEY = __PEXELS_API_KEY__ || '' const PEXELS_API_KEY = __PEXELS_API_KEY__ || ''
const showAllPexelsImages = ref(false) const pexelsDisplayCount = ref(12)
const PEXELS_INITIAL_DISPLAY = 12 const PEXELS_PER_PAGE = 12
const PEXELS_TOTAL_FETCHED = ref(0)
// Custom background images // Custom background images
const customBackgrounds = ref<string[]>([]) const customBackgrounds = ref<string[]>([])
// Computed: Display limited or all Pexels images // Computed: Display limited Pexels images
const displayedPexelsImages = computed(() => { const displayedPexelsImages = computed(() => {
if (showAllPexelsImages.value || pexelsImages.value.length <= PEXELS_INITIAL_DISPLAY) { return pexelsImages.value.slice(0, pexelsDisplayCount.value)
return pexelsImages.value
}
return pexelsImages.value.slice(0, PEXELS_INITIAL_DISPLAY)
}) })
// Show "Load More" button condition // Show "Load More" button condition
const showLoadMoreButton = computed(() => { const showLoadMoreButton = computed(() => {
return !showAllPexelsImages.value && pexelsImages.value.length > PEXELS_INITIAL_DISPLAY return pexelsImages.value.length > pexelsDisplayCount.value
}) })
// Load more Pexels images
const loadMorePexelsImages = async () => {
// Increase display count by 12
pexelsDisplayCount.value += PEXELS_PER_PAGE
// If we need to fetch more images from API
if (pexelsDisplayCount.value > pexelsImages.value.length) {
pexelsPage.value++
await fetchPexelsImages()
}
}
// Load custom backgrounds from localStorage // Load custom backgrounds from localStorage
const loadCustomBackgrounds = () => { const loadCustomBackgrounds = () => {
try { try {
@ -1788,7 +1832,7 @@ const clearBackground = async () => {
// Create new fabric canvas WITHOUT background // Create new fabric canvas WITHOUT background
fabricCanvas = new Canvas(canvasRef.value, { fabricCanvas = new Canvas(canvasRef.value, {
selection: false, selection: false,
backgroundColor: 'transparent' backgroundColor: null
}) })
// Load the original uploaded image (already has transparency) // Load the original uploaded image (already has transparency)
@ -1817,8 +1861,8 @@ const clearBackground = async () => {
scaleY: 1 scaleY: 1
}) })
// Ensure no background color // Ensure no background color (null for transparent)
fabricCanvas.backgroundColor = '' fabricCanvas.backgroundColor = null
fabricCanvas.add(img) fabricCanvas.add(img)
fabricCanvas.centerObject(img) fabricCanvas.centerObject(img)
@ -1837,8 +1881,8 @@ const clearBackground = async () => {
resultImage.value = dataUrl resultImage.value = dataUrl
await cacheResultImage(dataUrl) await cacheResultImage(dataUrl)
// Reset background color to default // Clear hex input by setting empty string (placeholder will show)
backgroundColor.value = '#FFFFFF' backgroundColor.value = ''
// Update or create history item // Update or create history item
if (currentHistoryIndex.value === -1) { if (currentHistoryIndex.value === -1) {
@ -2673,7 +2717,8 @@ const removeHistoryItem = (index: number) => {
.n-tabs-pane-wrapper { .n-tabs-pane-wrapper {
flex: 1; flex: 1;
overflow: hidden; overflow-y: auto;
overflow-x: hidden;
} }
} }
} }
@ -2913,6 +2958,32 @@ const removeHistoryItem = (index: number) => {
} }
} }
.sidebar-color-picker-empty {
width: 100%;
height: 60px;
border: 2px solid #e5e7eb;
border-radius: 8px;
cursor: pointer;
position: relative;
display: flex;
align-items: center;
justify-content: center;
/* Checkerboard pattern for transparent background */
background-image:
linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #f1f5f9 75%),
linear-gradient(-45deg, transparent 75%, #f1f5f9 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
background-color: #ffffff;
transition: all 0.2s ease;
&:hover {
border-color: #1fc76f;
}
}
.sidebar-hex-input { .sidebar-hex-input {
flex: 1; flex: 1;
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
@ -3074,7 +3145,7 @@ const removeHistoryItem = (index: number) => {
border: 2px solid #e5e7eb; border: 2px solid #e5e7eb;
transition: all 0.15s ease; transition: all 0.15s ease;
position: relative; position: relative;
overflow: hidden; overflow: visible;
background: #f3f4f6; background: #f3f4f6;
img { img {
@ -3096,8 +3167,8 @@ const removeHistoryItem = (index: number) => {
.remove-bg-btn { .remove-bg-btn {
position: absolute; position: absolute;
top: 4px; top: -8px;
right: 4px; right: -8px;
width: 20px; width: 20px;
height: 20px; height: 20px;
border-radius: 999px; border-radius: 999px;
@ -3108,7 +3179,7 @@ const removeHistoryItem = (index: number) => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
opacity: 0; opacity: 0;
transform: scale(0.8); transform: scale(0.9);
transition: all 0.2s ease; transition: all 0.2s ease;
cursor: pointer; cursor: pointer;
font-size: 0; font-size: 0;