From 5cd954ccad8bee3e4bd3f4ac219bc4de9a839955 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Sat, 11 Apr 2026 11:17:27 +0000 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E8=8B=B1=E6=96=87=E6=90=9C?= =?UTF-8?q?=E7=B4=A2+=E5=85=A8=E9=83=A8=E9=85=8D=E6=96=B9=E7=BF=BB?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 搜索框支持英文:自动匹配配方英文名和精油英文名 - 292条配方全部翻译英文名(本地+线上同步) - 输入英文时搜索范围包含en_name和精油英文名 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/views/RecipeSearch.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/RecipeSearch.vue b/frontend/src/views/RecipeSearch.vue index 6f142f2..1cac3b2 100644 --- a/frontend/src/views/RecipeSearch.vue +++ b/frontend/src/views/RecipeSearch.vue @@ -175,6 +175,7 @@ import { useUiStore } from '../stores/ui' import { api } from '../composables/useApi' import RecipeCard from '../components/RecipeCard.vue' import RecipeDetailOverlay from '../components/RecipeDetailOverlay.vue' +import { oilEn } from '../composables/useOilTranslation' const auth = useAuthStore() const oils = useOilsStore() @@ -313,11 +314,14 @@ function expandQuery(q) { const exactResults = computed(() => { if (!searchQuery.value.trim()) return [] const q = searchQuery.value.trim().toLowerCase() + const isEn = /^[a-zA-Z\s]+$/.test(q) return recipeStore.recipes.filter(r => { const nameMatch = r.name.toLowerCase().includes(q) + const enNameMatch = isEn && (r.en_name || '').toLowerCase().includes(q) + const oilEnMatch = isEn && r.ingredients.some(ing => (oilEn(ing.oil) || '').toLowerCase().includes(q)) const visibleTags = auth.canEdit ? (r.tags || []) : (r.tags || []).filter(t => !EDITOR_ONLY_TAGS.includes(t)) const tagMatch = visibleTags.some(t => t.toLowerCase().includes(q)) - return nameMatch || tagMatch + return nameMatch || enNameMatch || oilEnMatch || tagMatch }).sort((a, b) => a.name.localeCompare(b.name, 'zh')) }) -- 2.49.1 From 6448c24caf9740a9c488076cdbddd99d58e725a2 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Sat, 11 Apr 2026 16:34:52 +0000 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=E5=95=86=E4=B8=9A=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=AE=A1=E6=A0=B8=E6=98=BE=E7=A4=BA=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E7=9A=84=E8=AF=81=E6=98=8E=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 审核列表显示缩略图(60x60),点击查看大图 - 全屏遮罩预览,点击关闭 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/views/UserManagement.vue | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frontend/src/views/UserManagement.vue b/frontend/src/views/UserManagement.vue index 52de97a..1676f63 100644 --- a/frontend/src/views/UserManagement.vue +++ b/frontend/src/views/UserManagement.vue @@ -31,6 +31,7 @@ {{ group.latest.display_name || group.latest.username }} 商户名:{{ group.latest.business_name }} {{ { pending: '待审核', approved: '已通过', rejected: '已拒绝' }[group.effectiveStatus] }} +
@@ -128,6 +134,7 @@ const users = ref([]) const searchQuery = ref('') const filterRole = ref('') const translations = ref([]) +const showDocFull = ref(null) const businessApps = ref([]) import { reactive } from 'vue' @@ -443,6 +450,10 @@ onMounted(() => { } .biz-reject-reason { color: #c62828; font-size: 11px; } .biz-time { color: #bbb; font-size: 11px; margin-left: auto; } +.biz-doc-preview { width: 60px; height: 60px; object-fit: cover; border-radius: 6px; cursor: pointer; border: 1px solid #e5e4e7; margin-top: 6px; } +.biz-doc-preview:hover { border-color: #7ec6a4; } +.doc-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 1000; display: flex; align-items: center; justify-content: center; cursor: pointer; } +.doc-full-img { max-width: 90vw; max-height: 90vh; border-radius: 10px; } .btn-approve { background: #4a9d7e; -- 2.49.1 From 026ff18e92b482751a4ff3ace505afc7379f0bcf Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Sat, 11 Apr 2026 16:41:34 +0000 Subject: [PATCH 3/5] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E8=8B=B1?= =?UTF-8?q?=E6=96=87=E6=90=9C=E7=B4=A2=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=EF=BC=885=E4=B8=AA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - oilEn翻译验证(已知/未知精油) - 英文查询检测 - 英文匹配精油名和配方en_name 全部通过: 209 unit + 36 e2e Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/__tests__/newFeatures.test.js | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/frontend/src/__tests__/newFeatures.test.js b/frontend/src/__tests__/newFeatures.test.js index 0150bae..8b50b8c 100644 --- a/frontend/src/__tests__/newFeatures.test.js +++ b/frontend/src/__tests__/newFeatures.test.js @@ -85,3 +85,51 @@ describe('EDITOR_ONLY_TAGS', () => { expect(EDITOR_ONLY_TAGS).toContain('已审核') }) }) + +// --------------------------------------------------------------------------- +// English search +// --------------------------------------------------------------------------- +describe('English search matching', () => { + const { oilEn } = require('../composables/useOilTranslation') + + it('oilEn returns English name for known oils', () => { + expect(oilEn('薰衣草')).toBe('Lavender') + expect(oilEn('茶树')).toBe('Tea Tree') + expect(oilEn('乳香')).toBe('Frankincense') + }) + + it('oilEn returns empty for unknown oils', () => { + expect(oilEn('不存在的油')).toBeFalsy() + }) + + it('English query detection', () => { + const isEn = (q) => /^[a-zA-Z\s]+$/.test(q) + expect(isEn('lavender')).toBe(true) + expect(isEn('Tea Tree')).toBe(true) + expect(isEn('薰衣草')).toBe(false) + expect(isEn('lav3')).toBe(false) + }) + + it('English matches oil name in recipe', () => { + const recipe = { + name: '助眠配方', + en_name: 'Sleep Aid Blend', + ingredients: [{ oil: '薰衣草', drops: 3 }], + tags: [] + } + const q = 'lavender' + const isEn = /^[a-zA-Z\s]+$/.test(q) + const enNameMatch = isEn && (recipe.en_name || '').toLowerCase().includes(q) + const oilEnMatch = isEn && recipe.ingredients.some(ing => (oilEn(ing.oil) || '').toLowerCase().includes(q)) + expect(oilEnMatch).toBe(true) + expect(enNameMatch).toBe(false) + }) + + it('English matches recipe en_name', () => { + const recipe = { name: '助眠', en_name: 'Sleep Aid Blend', ingredients: [], tags: [] } + const q = 'sleep' + const isEn = /^[a-zA-Z\s]+$/.test(q) + const enNameMatch = isEn && (recipe.en_name || '').toLowerCase().includes(q) + expect(enNameMatch).toBe(true) + }) +}) -- 2.49.1 From 87e24773aa5a0fdfe46d90ac844dbf5a4aafff2b Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Sat, 11 Apr 2026 17:12:30 +0000 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20CI=E5=A2=9E=E5=8A=A0requestTimeout?= =?UTF-8?q?=E5=92=8CresponseTimeout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 49dfb19..6a7d608 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -68,7 +68,7 @@ jobs: cypress/e2e/category-modules.cy.js,\ cypress/e2e/notification-flow.cy.js,\ cypress/e2e/registration-flow.cy.js\ - " --config "video=false,defaultCommandTimeout=5000,pageLoadTimeout=10000,baseUrl=http://localhost:$FE_PORT" + " --config "video=false,defaultCommandTimeout=5000,pageLoadTimeout=10000,requestTimeout=5000,responseTimeout=10000,baseUrl=http://localhost:$FE_PORT" EXIT_CODE=$? # Cleanup -- 2.49.1 From e605da786a67738ab21a454270a4122bcce8ebe1 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Sat, 11 Apr 2026 17:23:57 +0000 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20CI=E7=94=A8timeout=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E5=BC=BA=E5=88=B63=E5=88=86=E9=92=9F=E8=B6=85=E6=97=B6+?= =?UTF-8?q?=E6=B8=85=E7=90=86Cypress=E8=BF=9B=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/test.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 6a7d608..43a4911 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -58,9 +58,9 @@ jobs: exit 1 fi - # Run core cypress specs with timeouts + # Run core cypress specs with hard 3-minute timeout cd frontend - npx cypress run --spec "\ + timeout 180 npx cypress run --spec "\ cypress/e2e/recipe-detail.cy.js,\ cypress/e2e/oil-reference.cy.js,\ cypress/e2e/oil-data-integrity.cy.js,\ @@ -73,7 +73,12 @@ jobs: # Cleanup kill $BE_PID $FE_PID 2>/dev/null + pkill -f "Cypress" 2>/dev/null || true rm -f "$DB_FILE" + if [ $EXIT_CODE -eq 124 ]; then + echo "ERROR: Cypress timed out after 3 minutes" + exit 1 + fi exit $EXIT_CODE build-check: -- 2.49.1