fix: 上传图片保持比例、QR正方形裁剪、Logo左下角、日期靠右
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 14s
Test / e2e-test (push) Failing after 1m26s
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 14s
Test / e2e-test (push) Failing after 1m26s
上传图片: - object-fit: contain 保持原比例,不压扁不拉长 - QR上传检测是否正方形,非正方形提示并自动裁剪中心区域 配方卡片: - Logo位置改为左下角 (left:24px) - 制作日期靠右对齐 (text-align:right) 品牌预览: - Logo位置改为左下角 (left:16px) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1170,8 +1170,7 @@ async function saveRecipe() {
|
|||||||
.card-logo {
|
.card-logo {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 60px;
|
bottom: 60px;
|
||||||
left: 50%;
|
left: 24px;
|
||||||
transform: translateX(-50%);
|
|
||||||
height: 60px;
|
height: 60px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -1300,7 +1299,7 @@ async function saveRecipe() {
|
|||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
text-align: center;
|
text-align: right;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--text-light, #9a8570);
|
color: var(--text-light, #9a8570);
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
|
|||||||
@@ -462,11 +462,56 @@ function compressImage(base64, maxSize = 500000) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crop image to square from center
|
||||||
|
function cropToSquare(base64) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => {
|
||||||
|
const size = Math.min(img.width, img.height)
|
||||||
|
const x = (img.width - size) / 2
|
||||||
|
const y = (img.height - size) / 2
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = size
|
||||||
|
canvas.height = size
|
||||||
|
canvas.getContext('2d').drawImage(img, x, y, size, size, 0, 0, size, size)
|
||||||
|
resolve(canvas.toDataURL('image/png'))
|
||||||
|
}
|
||||||
|
img.onerror = () => resolve(base64)
|
||||||
|
img.src = base64
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if image is roughly square
|
||||||
|
function checkSquare(base64) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => {
|
||||||
|
const ratio = img.width / img.height
|
||||||
|
resolve(ratio > 0.85 && ratio < 1.15) // within 15% of square
|
||||||
|
}
|
||||||
|
img.onerror = () => resolve(true)
|
||||||
|
img.src = base64
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function handleUpload(type, event) {
|
async function handleUpload(type, event) {
|
||||||
const file = event.target.files[0]
|
const file = event.target.files[0]
|
||||||
if (!file) return
|
if (!file) return
|
||||||
try {
|
try {
|
||||||
let base64 = await readFileAsBase64(file)
|
let base64 = await readFileAsBase64(file)
|
||||||
|
|
||||||
|
// QR: check if square, offer to crop
|
||||||
|
if (type === 'qr') {
|
||||||
|
const isSquare = await checkSquare(base64)
|
||||||
|
if (!isSquare) {
|
||||||
|
const { showConfirm: confirm } = await import('../composables/useDialog')
|
||||||
|
const ok = await confirm('二维码图片不是正方形,是否自动裁剪为正方形?\n(取中心区域)')
|
||||||
|
if (ok) {
|
||||||
|
base64 = await cropToSquare(base64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const maxSize = type === 'bg' ? 1000000 : 500000
|
const maxSize = type === 'bg' ? 1000000 : 500000
|
||||||
base64 = await compressImage(base64, maxSize)
|
base64 = await compressImage(base64, maxSize)
|
||||||
const fieldMap = { logo: 'brand_logo', bg: 'brand_bg', qr: 'qr_code' }
|
const fieldMap = { logo: 'brand_logo', bg: 'brand_bg', qr: 'qr_code' }
|
||||||
@@ -958,7 +1003,7 @@ async function applyBusiness() {
|
|||||||
transition: border-color 0.15s;
|
transition: border-color 0.15s;
|
||||||
}
|
}
|
||||||
.upload-box:hover { border-color: var(--sage, #7a9e7e); }
|
.upload-box:hover { border-color: var(--sage, #7a9e7e); }
|
||||||
.upload-box-img { width: 100%; height: 100%; object-fit: cover; }
|
.upload-box-img { width: 100%; height: 100%; object-fit: contain; }
|
||||||
.upload-box-hint { font-size: 12px; color: var(--text-light, #9a8570); }
|
.upload-box-hint { font-size: 12px; color: var(--text-light, #9a8570); }
|
||||||
.btn-clear {
|
.btn-clear {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
@@ -1019,8 +1064,7 @@ async function applyBusiness() {
|
|||||||
.card-preview-logo {
|
.card-preview-logo {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
left: 50%;
|
left: 16px;
|
||||||
transform: translateX(-50%);
|
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: auto;
|
width: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
|||||||
Reference in New Issue
Block a user