feat: add SharePanel component for social sharing with QR code support
Some checks failed
docker-images / resolve-build-targets (push) Successful in 7s
ui-regression / playwright-regression (push) Failing after 13m47s
docker-images / build-and-push (push) Failing after 7s
docker-images / submit-indexnow (push) Has been skipped

- Implemented SharePanel component in `SharePanel.astro` for sharing content on social media platforms.
- Integrated QR code generation for WeChat sharing using the `qrcode` library.
- Added localization support for English and Chinese languages.
- Created utility functions in `seo.ts` for building article summaries and FAQs.
- Introduced API routes for serving IndexNow key and generating full LLM catalog and summaries.
- Enhanced SEO capabilities with structured data for articles and pages.
This commit is contained in:
2026-04-02 14:15:21 +08:00
parent a516be2e91
commit 3628a46ed1
53 changed files with 4390 additions and 91 deletions

View File

@@ -315,6 +315,10 @@ test('后台可完成媒体库上传/元数据/替换/删除,并执行设置
await page.getByRole('link', { name: '设置' }).click()
await page.getByTestId('site-settings-site-name').fill('InitCool Deep Regression')
await page.getByTestId('site-settings-popup-title').fill('订阅深回归')
await page
.locator('label', { hasText: '开启文章页微信扫码分享' })
.locator('input[type="checkbox"]')
.check()
await page.getByTestId('site-settings-save').click()
await page.getByTestId('site-settings-reindex').click()
await page.getByTestId('site-settings-test-provider').click()
@@ -324,6 +328,7 @@ test('后台可完成媒体库上传/元数据/替换/删除,并执行设置
state = await getDebugState(request)
expect(state.site_settings.site_name).toBe('InitCool Deep Regression')
expect(state.site_settings.subscription_popup_title).toBe('订阅深回归')
expect(state.site_settings.seo_wechat_share_qr_enabled).toBe(true)
expect(state.site_settings.ai_chunks_count).toBeGreaterThan(128)
})

View File

@@ -1,6 +1,6 @@
import { expect, test } from '@playwright/test'
import { getDebugState, resetMockState } from './helpers'
import { getDebugState, patchAdminSiteSettings, resetMockState } from './helpers'
test.beforeEach(async ({ request }) => {
await resetMockState(request)
@@ -100,6 +100,7 @@ test('友链申请与订阅确认/偏好/退订链路可用', async ({ page, req
await expect(page.locator('[data-subscribe-status]')).toContainText('订阅')
await page.locator('[data-subscription-popup-open]').click()
await expect(page.locator('[data-subscription-popup-panel]')).toBeVisible()
await page.locator('[data-subscription-popup-form] input[name="displayName"]').fill('弹窗订阅用户')
await page.locator('[data-subscription-popup-email]').fill('playwright-subscriber@example.com')
await page.locator('[data-subscription-popup-form] button[type="submit"]').click()
@@ -128,3 +129,49 @@ test('友链申请与订阅确认/偏好/退订链路可用', async ({ page, req
await page.getByRole('button', { name: '确认退订' }).click()
await expect(page.locator('[data-unsubscribe-status]')).toContainText('成功退订')
})
test('GEO 分享面板、AI 摘要块与 llms 入口可用', async ({ page, request }) => {
await patchAdminSiteSettings(request, {
seoWechatShareQrEnabled: true,
})
await page.goto('/')
await expect(page.locator('head link[rel="alternate"][href$="/llms.txt"]')).toHaveCount(1)
await expect(page.locator('head link[rel="alternate"][href$="/llms-full.txt"]')).toHaveCount(1)
await expect(page.getByRole('heading', { name: '给 AI 看的站点摘要' })).toBeVisible()
await expect(page.getByRole('button', { name: '微信扫码' }).first()).toBeVisible()
await page.goto('/about')
await expect(page.getByRole('heading', { name: '给 AI 看的身份摘要' })).toBeVisible()
await expect(page.getByText('身份主页')).toBeVisible()
await page.goto('/articles')
await expect(page.getByRole('heading', { name: '给 AI 看的归档摘要' })).toBeVisible()
await page.goto('/reviews')
await expect(page.getByRole('heading', { name: '给 AI 看的评测摘要' })).toBeVisible()
await page.goto('/ask')
await expect(page.getByRole('heading', { name: '给 AI 看的问答页摘要' })).toBeVisible()
await page.goto('/friends')
await expect(page.getByRole('heading', { name: '给 AI 看的友链网络摘要' })).toBeVisible()
await page.goto('/articles/playwright-regression-workflow')
await page.getByRole('button', { name: '微信扫码' }).first().click()
await expect(page.locator('[data-article-wechat-qr-modal]')).toHaveAttribute('aria-hidden', 'false')
await expect(page.getByRole('heading', { name: '微信扫码分享' })).toBeVisible()
await expect(page.locator('[data-article-qr-download]')).toBeVisible()
await page.locator('[data-article-wechat-qr-close]').first().click()
await expect(page.locator('[data-article-wechat-qr-modal]')).toHaveAttribute('aria-hidden', 'true')
await page.goto('/categories/frontend-engineering')
await expect(page.getByRole('button', { name: '复制摘要' })).toBeVisible()
await page.goto('/tags/playwright')
await expect(page.getByRole('button', { name: '分享摘要' })).toBeVisible()
await page.goto('/reviews/1')
await expect(page.getByRole('button', { name: '复制摘要' })).toBeVisible()
})

View File

@@ -19,6 +19,20 @@ export async function getDebugState(request: APIRequestContext) {
return response.json()
}
export async function patchAdminSiteSettings(
request: APIRequestContext,
payload: Record<string, unknown>,
) {
const response = await request.patch(`${MOCK_BASE_URL}/api/admin/site-settings`, {
headers: {
cookie: `${ADMIN_COOKIE.name}=${ADMIN_COOKIE.value}`,
},
data: payload,
})
expect(response.ok()).toBeTruthy()
return response.json()
}
export async function loginAdmin(page: Page) {
await page.goto('/login')
await page.getByLabel('用户名').fill('admin')