clean up RemoveBackground component and optimize code
This commit is contained in:
parent
2a47e6a705
commit
6475c947c0
@ -11,7 +11,6 @@
|
||||
<p>{{ t('Drop image anywhere to remove background') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文件输入框,始终存在 -->
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
@ -22,7 +21,6 @@
|
||||
|
||||
<div class="page-header">
|
||||
<h2>{{ t('Remove Background') }}</h2>
|
||||
<!-- 工具栏图标 -->
|
||||
<div v-if="uploadedImage" class="toolbar-actions">
|
||||
<button
|
||||
v-if="resultImage"
|
||||
@ -44,7 +42,6 @@
|
||||
|
||||
<div class="page-content">
|
||||
<div class="tool-container">
|
||||
<!-- 上传区域 -->
|
||||
<div class="upload-section">
|
||||
<div
|
||||
v-if="!uploadedImage"
|
||||
@ -62,12 +59,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图片预览和处理区域 -->
|
||||
<div v-else class="preview-section">
|
||||
<!-- 单窗口对比视图 -->
|
||||
<div class="comparison-view" v-if="resultImage">
|
||||
<div class="comparison-container" ref="comparisonContainerRef">
|
||||
<!-- 原图(显示左侧部分) -->
|
||||
<div
|
||||
class="comparison-image original-image"
|
||||
:style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }"
|
||||
@ -80,7 +74,6 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 去背景后的图片(显示右侧部分) -->
|
||||
<div
|
||||
class="comparison-image result-image"
|
||||
:style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }"
|
||||
@ -93,7 +86,6 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 拖动竖线 -->
|
||||
<div
|
||||
class="split-line"
|
||||
:class="{ 'dragging': isDraggingSplitLine }"
|
||||
@ -107,7 +99,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 处理中或未处理状态 -->
|
||||
<div v-else class="single-image-view">
|
||||
<div class="image-wrapper">
|
||||
<img :src="uploadedImageUrl" alt="Original" />
|
||||
@ -120,10 +111,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部历史记录栏 -->
|
||||
<div v-if="historyList.length > 0 || uploadedImage" class="history-bar">
|
||||
<div class="history-scroll-container">
|
||||
<!-- 添加新图片按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
class="history-item add-button"
|
||||
@ -133,7 +122,6 @@
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
|
||||
<!-- 历史记录缩略图列表 -->
|
||||
<div
|
||||
v-for="(item, index) in historyList"
|
||||
:key="item.id"
|
||||
@ -153,7 +141,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前正在处理的图片(如果还没有添加到历史记录) -->
|
||||
<div
|
||||
v-if="uploadedImage && resultImage && currentHistoryIndex === -1"
|
||||
class="history-item active"
|
||||
@ -180,45 +167,37 @@ import { useAuthStore } from '@/shared/stores/auth'
|
||||
const message = useMessage()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
// 历史记录项类型
|
||||
interface HistoryItem {
|
||||
id: string
|
||||
originalImageUrl: string
|
||||
originalImageFile: File | null
|
||||
resultImage: string // Base64编码的结果图片
|
||||
resultImage: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
// 文件相关
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null)
|
||||
const uploadedImage = ref<File | null>(null)
|
||||
const uploadedImageUrl = ref<string>('')
|
||||
const resultImage = ref<string>('') // Base64编码的结果图片
|
||||
const resultImage = ref<string>('')
|
||||
const resultImageUrl = computed(() => {
|
||||
if (!resultImage.value) return ''
|
||||
// 如果已经是data URL格式,直接返回
|
||||
if (resultImage.value.startsWith('data:')) {
|
||||
return resultImage.value
|
||||
}
|
||||
// 否则添加data URL前缀
|
||||
return `data:image/png;base64,${resultImage.value}`
|
||||
})
|
||||
|
||||
// 历史记录
|
||||
const historyList = ref<HistoryItem[]>([])
|
||||
const currentHistoryIndex = ref<number>(-1) // -1 表示当前正在处理的图片,>=0 表示历史记录索引
|
||||
|
||||
// 状态
|
||||
const currentHistoryIndex = ref<number>(-1)
|
||||
const isDragging = ref(false)
|
||||
const dragCounter = ref(0)
|
||||
const processing = ref(false)
|
||||
const splitPosition = ref(0) // 分割线位置(百分比),0表示最左边,默认只显示结果图
|
||||
const splitPosition = ref(0)
|
||||
const comparisonContainerRef = ref<HTMLElement | null>(null)
|
||||
const originalImageRef = ref<HTMLImageElement | null>(null)
|
||||
const resultImageRef = ref<HTMLImageElement | null>(null)
|
||||
const isDraggingSplitLine = ref(false)
|
||||
|
||||
// 调整容器大小以匹配图片实际显示尺寸
|
||||
const adjustContainerSize = () => {
|
||||
const container = comparisonContainerRef.value
|
||||
if (!container) return
|
||||
@ -226,19 +205,16 @@ const adjustContainerSize = () => {
|
||||
const img = originalImageRef.value || resultImageRef.value
|
||||
if (!img) return
|
||||
|
||||
// 获取父元素 preview-section 的实际可用尺寸
|
||||
const previewSection = container.closest('.preview-section') as HTMLElement
|
||||
if (!previewSection) return
|
||||
|
||||
const previewRect = previewSection.getBoundingClientRect()
|
||||
// 考虑 padding (12px * 2 = 24px)
|
||||
const padding = 24
|
||||
const maxAvailableWidth = Math.max(0, previewRect.width - padding)
|
||||
const maxAvailableHeight = Math.max(0, previewRect.height - padding)
|
||||
|
||||
if (maxAvailableWidth <= 0 || maxAvailableHeight <= 0) return
|
||||
|
||||
// 等待图片加载完成
|
||||
if (!img.complete) {
|
||||
img.addEventListener('load', adjustContainerSize, { once: true })
|
||||
return
|
||||
@ -247,24 +223,18 @@ const adjustContainerSize = () => {
|
||||
const { naturalWidth, naturalHeight } = img
|
||||
if (naturalWidth === 0 || naturalHeight === 0) return
|
||||
|
||||
// 计算缩放比例,保持宽高比且不超过父元素可用空间
|
||||
const scale = Math.min(
|
||||
maxAvailableWidth / naturalWidth,
|
||||
maxAvailableHeight / naturalHeight,
|
||||
1 // 不放大
|
||||
1
|
||||
)
|
||||
|
||||
// 设置容器大小为计算出的尺寸
|
||||
container.style.width = `${naturalWidth * scale}px`
|
||||
container.style.height = `${naturalHeight * scale}px`
|
||||
}
|
||||
|
||||
// 监听图片加载完成
|
||||
watch([uploadedImageUrl, resultImageUrl], () => {
|
||||
adjustContainerSize()
|
||||
}, { immediate: true })
|
||||
watch([uploadedImageUrl, resultImageUrl], adjustContainerSize, { immediate: true })
|
||||
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
adjustContainerSize()
|
||||
}
|
||||
@ -277,16 +247,13 @@ onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
// 触发文件选择
|
||||
const triggerFileInput = () => {
|
||||
if (fileInputRef.value) {
|
||||
// 重置文件输入框的值,确保每次点击都能触发 change 事件
|
||||
fileInputRef.value.value = ''
|
||||
fileInputRef.value.click()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件选择
|
||||
const handleFileSelect = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement
|
||||
const file = target.files?.[0]
|
||||
@ -295,14 +262,12 @@ const handleFileSelect = (event: Event) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 判断拖拽是否包含文件
|
||||
const hasFiles = (event: DragEvent) => {
|
||||
const types = event.dataTransfer?.types
|
||||
if (!types) return false
|
||||
return Array.from(types).includes('Files')
|
||||
}
|
||||
|
||||
// 处理拖拽
|
||||
const handleDragEnter = (event: DragEvent) => {
|
||||
if (!hasFiles(event)) return
|
||||
dragCounter.value += 1
|
||||
@ -336,16 +301,13 @@ const handleDrop = (event: DragEvent) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件
|
||||
const processFile = (file: File) => {
|
||||
// 验证文件类型
|
||||
const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
|
||||
if (!validTypes.includes(file.type)) {
|
||||
message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件大小(最大10MB)
|
||||
const maxSize = 10 * 1024 * 1024
|
||||
if (file.size > maxSize) {
|
||||
message.warning(t('Image size exceeds 10MB limit'))
|
||||
@ -354,20 +316,17 @@ const processFile = (file: File) => {
|
||||
|
||||
uploadedImage.value = file
|
||||
resultImage.value = ''
|
||||
splitPosition.value = 0 // 重置分割线位置到最左边,默认显示完整结果图
|
||||
currentHistoryIndex.value = -1 // 重置为当前处理状态
|
||||
splitPosition.value = 0
|
||||
currentHistoryIndex.value = -1
|
||||
|
||||
// 创建预览URL
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
uploadedImageUrl.value = e.target?.result as string
|
||||
// 上传后自动触发去背景
|
||||
handleRemoveBackground()
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
// 重置上传
|
||||
const resetUpload = () => {
|
||||
uploadedImage.value = null
|
||||
uploadedImageUrl.value = ''
|
||||
@ -377,43 +336,33 @@ const resetUpload = () => {
|
||||
if (fileInputRef.value) {
|
||||
fileInputRef.value.value = ''
|
||||
}
|
||||
// 重置容器大小
|
||||
if (comparisonContainerRef.value) {
|
||||
comparisonContainerRef.value.style.width = ''
|
||||
comparisonContainerRef.value.style.height = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 选择历史记录项
|
||||
const selectHistoryItem = (index: number) => {
|
||||
if (index < 0 || index >= historyList.value.length) return
|
||||
|
||||
const item = historyList.value[index]
|
||||
currentHistoryIndex.value = index
|
||||
|
||||
// 恢复原图和结果图
|
||||
uploadedImageUrl.value = item.originalImageUrl
|
||||
resultImage.value = item.resultImage
|
||||
splitPosition.value = 0
|
||||
|
||||
// 恢复文件对象(如果存在)
|
||||
uploadedImage.value = item.originalImageFile
|
||||
}
|
||||
|
||||
// 获取历史记录缩略图URL
|
||||
const getHistoryThumbnailUrl = (item: HistoryItem): string => {
|
||||
// 优先显示结果图(去背景后的图片)
|
||||
if (item.resultImage) {
|
||||
if (item.resultImage.startsWith('data:')) {
|
||||
return item.resultImage
|
||||
}
|
||||
return `data:image/png;base64,${item.resultImage}`
|
||||
}
|
||||
// 如果没有结果图,显示原图
|
||||
return item.originalImageUrl
|
||||
}
|
||||
|
||||
// 拖动竖线处理
|
||||
const handleSplitLineMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
isDraggingSplitLine.value = true
|
||||
@ -437,17 +386,12 @@ const handleSplitLineMouseDown = (e: MouseEvent) => {
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
|
||||
// 处理去背景
|
||||
const handleRemoveBackground = async () => {
|
||||
// 检查用户是否已登录
|
||||
if (!authStore.isLoggedIn) {
|
||||
message.error(t('Please login first to use this feature'))
|
||||
return
|
||||
}
|
||||
|
||||
// 与应用市场保持一致,不检查 cookie,直接调用 API
|
||||
// 后端 whitelist 会处理认证,如果没有 cookie 会返回 401
|
||||
|
||||
if (!uploadedImage.value) {
|
||||
message.warning(t('Please upload an image first'))
|
||||
return
|
||||
@ -457,7 +401,6 @@ const handleRemoveBackground = async () => {
|
||||
resultImage.value = ''
|
||||
|
||||
try {
|
||||
// 将文件转换为base64
|
||||
const reader = new FileReader()
|
||||
const base64Promise = new Promise<string>((resolve, reject) => {
|
||||
reader.onload = (e) => {
|
||||
@ -469,7 +412,6 @@ const handleRemoveBackground = async () => {
|
||||
reader.readAsDataURL(uploadedImage.value)
|
||||
const base64Data = await base64Promise
|
||||
|
||||
// 调用去背景API,需要认证时使用 withCredentials 发送 session cookie
|
||||
const response = await axios.post(
|
||||
'/jingrow.tools.tools.remove_background',
|
||||
{
|
||||
@ -477,25 +419,20 @@ const handleRemoveBackground = async () => {
|
||||
},
|
||||
{
|
||||
headers: get_session_api_headers(),
|
||||
withCredentials: true, // 发送 session cookie 以进行认证
|
||||
timeout: 180000 // 3分钟超时
|
||||
withCredentials: true,
|
||||
timeout: 180000
|
||||
}
|
||||
)
|
||||
|
||||
// whitelist 返回格式: {success: true, data: function_result}
|
||||
// function_result 格式: {success: true/false, data: [...], ...}
|
||||
if (response.data?.success && response.data?.data) {
|
||||
const result = response.data.data
|
||||
|
||||
// 检查函数返回的 success 字段
|
||||
if (result.success) {
|
||||
// 检查 data 字段(数组格式)
|
||||
if (result.data && Array.isArray(result.data) && result.data.length > 0) {
|
||||
const firstResult = result.data[0]
|
||||
if (firstResult.success && firstResult.image_content) {
|
||||
resultImage.value = firstResult.image_content
|
||||
|
||||
// 将结果添加到历史记录(如果当前是新的处理,不是从历史记录中选择的)
|
||||
if (currentHistoryIndex.value === -1 && uploadedImage.value && uploadedImageUrl.value) {
|
||||
const historyItem: HistoryItem = {
|
||||
id: `history-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
@ -507,7 +444,6 @@ const handleRemoveBackground = async () => {
|
||||
historyList.value.unshift(historyItem)
|
||||
currentHistoryIndex.value = 0
|
||||
} else if (currentHistoryIndex.value >= 0) {
|
||||
// 如果是从历史记录中选择的,更新该历史记录项的结果
|
||||
historyList.value[currentHistoryIndex.value].resultImage = firstResult.image_content
|
||||
}
|
||||
|
||||
@ -519,18 +455,15 @@ const handleRemoveBackground = async () => {
|
||||
message.error(t('No image data returned'))
|
||||
}
|
||||
} else {
|
||||
// 函数返回失败
|
||||
message.error(result.error || t('Failed to remove background'))
|
||||
}
|
||||
} else {
|
||||
// whitelist 包装失败
|
||||
message.error(response.data?.error || response.data?.message || t('Failed to remove background'))
|
||||
}
|
||||
} catch (error: any) {
|
||||
let errorMessage = t('Failed to remove background')
|
||||
|
||||
if (error.response) {
|
||||
// 服务器返回了错误响应
|
||||
if (error.response.data) {
|
||||
errorMessage = error.response.data.error ||
|
||||
error.response.data.detail ||
|
||||
@ -543,10 +476,8 @@ const handleRemoveBackground = async () => {
|
||||
errorMessage = t('Authentication failed. Please login again.')
|
||||
}
|
||||
} else if (error.request) {
|
||||
// 请求已发出但没有收到响应
|
||||
errorMessage = t('Network error. Please check your connection.')
|
||||
} else {
|
||||
// 其他错误
|
||||
errorMessage = error.message || errorMessage
|
||||
}
|
||||
|
||||
@ -556,17 +487,14 @@ const handleRemoveBackground = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 下载结果
|
||||
const handleDownload = () => {
|
||||
if (!resultImage.value) return
|
||||
|
||||
try {
|
||||
// 提取 base64 数据
|
||||
const base64Data = resultImage.value.includes(',')
|
||||
? resultImage.value.split(',')[1]
|
||||
: resultImage.value
|
||||
|
||||
// 将 base64 转换为二进制数据
|
||||
const byteCharacters = atob(base64Data)
|
||||
const byteNumbers = new Array(byteCharacters.length)
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
@ -574,10 +502,6 @@ const handleDownload = () => {
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers)
|
||||
const blob = new Blob([byteArray], { type: 'image/png' })
|
||||
|
||||
// 使用 blob URL 创建下载链接
|
||||
// 注意:在 HTTP 环境下,浏览器可能会显示安全警告,这是正常的,不影响下载功能
|
||||
// 要消除警告,需要将网站升级到 HTTPS
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
@ -586,7 +510,6 @@ const handleDownload = () => {
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
|
||||
// 延迟清理,确保下载开始后再清理
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
@ -622,13 +545,12 @@ const removeHistoryItem = (index: number) => {
|
||||
<style scoped lang="scss">
|
||||
.remove-background-page {
|
||||
position: relative;
|
||||
width: calc(100% + 40px); /* 抵消父容器左右 padding */
|
||||
height: calc(100vh - 64px - 40px); /* 100vh - header高度(64px) - content-wrapper上下padding(40px) */
|
||||
margin: -20px; /* 抵消父容器 content-wrapper 的 padding */
|
||||
width: calc(100% + 40px);
|
||||
height: calc(100vh - 64px - 40px);
|
||||
margin: -20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.global-drag-overlay {
|
||||
@ -742,9 +664,7 @@ const removeHistoryItem = (index: number) => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
background: white;
|
||||
padding: 12px 36px;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
@ -766,7 +686,6 @@ const removeHistoryItem = (index: number) => {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 工具栏图标 */
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
@ -801,55 +720,6 @@ const removeHistoryItem = (index: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.page-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, #1fc76f 0%, #16a085 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(31, 199, 111, 0.25);
|
||||
|
||||
i {
|
||||
font-size: 28px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.tool-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@ -858,7 +728,6 @@ h1 {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 上传区域 */
|
||||
.upload-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@ -872,7 +741,6 @@ h1 {
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
/* 当显示图片时,移除 padding 和边框 */
|
||||
.upload-section:has(.preview-section) {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
@ -951,7 +819,6 @@ h1 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 预览区域 */
|
||||
.preview-section {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
@ -961,10 +828,9 @@ h1 {
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
padding: 12px; /* 用于计算可用空间 */
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
|
||||
.preview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@ -992,7 +858,6 @@ h1 {
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 对比视图 */
|
||||
.comparison-view {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -1005,8 +870,6 @@ h1 {
|
||||
.comparison-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
/* 容器大小由 JavaScript 动态设置,匹配图片实际显示尺寸 */
|
||||
/* 同时设置 max-width 和 max-height 作为安全限制,确保不超过父元素 */
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
@ -1021,7 +884,6 @@ h1 {
|
||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
||||
}
|
||||
|
||||
/* 所有对比图片容器的公共样式 */
|
||||
.comparison-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -1044,15 +906,12 @@ h1 {
|
||||
|
||||
.original-image {
|
||||
z-index: 1;
|
||||
/* clip-path 通过内联样式动态设置,用于裁剪显示区域 */
|
||||
}
|
||||
|
||||
.result-image {
|
||||
z-index: 2;
|
||||
/* clip-path 通过内联样式动态设置,用于裁剪显示区域 */
|
||||
}
|
||||
|
||||
/* 拖动竖线 */
|
||||
.split-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -1097,7 +956,6 @@ h1 {
|
||||
}
|
||||
}
|
||||
|
||||
/* 单图视图 */
|
||||
.single-image-view {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -1134,24 +992,6 @@ h1 {
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
color: #cbd5e1;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.processing-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -1188,13 +1028,11 @@ h1 {
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部历史记录栏 */
|
||||
.history-bar {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
background: transparent;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@ -1355,8 +1193,6 @@ h1 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.page-header {
|
||||
padding: 10px 12px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user