.
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
# Community Post Bilingual Pairing Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Display translated community posts as vertically interleaved original/translation sentence pairs instead of separate long text blocks.
|
||||
|
||||
**Architecture:** Keep the change local to the community post detail page. Reuse the news detail page's newline-first, sentence-second splitting rule, derive a bilingual view model with a computed property, and render it through a dedicated template branch without changing API contracts.
|
||||
|
||||
**Tech Stack:** uni-app, Vue 3 `<script setup>`, TypeScript, SCSS
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add the bilingual sentence view model
|
||||
|
||||
**Files:**
|
||||
- Modify: `uniapp/src/packages_community/pages/post_detail.vue`
|
||||
|
||||
- [ ] **Step 1: Upgrade the local text splitter**
|
||||
|
||||
Replace the newline-only `splitContentLines` helper with the article-detail behavior: normalize line endings, prefer explicit non-empty lines, and otherwise split the single line by Chinese or English sentence punctuation.
|
||||
|
||||
```ts
|
||||
const splitContentLines = (value: unknown) => {
|
||||
const normalized = String(value || '')
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n')
|
||||
.trim()
|
||||
if (!normalized) return []
|
||||
|
||||
const manualLines = normalized
|
||||
.split('\n')
|
||||
.map((line) => line.replace(/\s+/g, ' ').trim())
|
||||
.filter(Boolean)
|
||||
|
||||
if (manualLines.length > 1) return manualLines
|
||||
|
||||
const sentenceLines = normalized
|
||||
.replace(/\s+/g, ' ')
|
||||
.match(/[^。!?.!?]+[。!?.!?]?/g)
|
||||
?.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
return sentenceLines?.length ? sentenceLines : [normalized]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Derive paired original and translated blocks**
|
||||
|
||||
Add computed original lines and a computed array whose length is the maximum of both sides. Filter only fully empty rows so an unmatched trailing sentence remains visible.
|
||||
|
||||
```ts
|
||||
const originalLines = computed(() => splitContentLines(post.value?.content))
|
||||
const translatedLines = computed(() => splitContentLines(translatedText.value))
|
||||
const bilingualBlocks = computed(() => {
|
||||
const size = Math.max(originalLines.value.length, translatedLines.value.length)
|
||||
return Array.from({ length: size }, (_, index) => ({
|
||||
original: originalLines.value[index] || '',
|
||||
translated: translatedLines.value[index] || ''
|
||||
})).filter((block) => block.original || block.translated)
|
||||
})
|
||||
const hasBilingualContent = computed(() => originalLines.value.length > 0 && translatedLines.value.length > 0)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Make expansion evaluate the complete bilingual body**
|
||||
|
||||
Use the combined bilingual text length and group count when bilingual content exists; keep the existing summary rule for ordinary posts.
|
||||
|
||||
```ts
|
||||
const previewCanExpand = computed(() => {
|
||||
if (hasBilingualContent.value) {
|
||||
const contentLength = bilingualBlocks.value.reduce(
|
||||
(total, block) => total + block.original.length + block.translated.length,
|
||||
0
|
||||
)
|
||||
return contentLength > 180 || bilingualBlocks.value.length > 3
|
||||
}
|
||||
|
||||
const summary = postPreview.value.summary
|
||||
return summary.length > 90 || summary.split('\n').length > 3
|
||||
})
|
||||
```
|
||||
|
||||
### Task 2: Render one original sentence followed by one translation
|
||||
|
||||
**Files:**
|
||||
- Modify: `uniapp/src/packages_community/pages/post_detail.vue`
|
||||
|
||||
- [ ] **Step 1: Add the bilingual template branch**
|
||||
|
||||
Place this branch before the ordinary title/summary content. The branch replaces both the artificial title and the former standalone translation card.
|
||||
|
||||
```vue
|
||||
<view v-if="hasBilingualContent" class="detail-post-card__bilingual"
|
||||
:class="{ 'detail-post-card__bilingual--collapsed': previewCanExpand && !previewExpanded }">
|
||||
<view v-for="(block, index) in bilingualBlocks" :key="`bilingual-${index}`"
|
||||
class="detail-post-card__bilingual-pair">
|
||||
<text v-if="block.original" class="detail-post-card__bilingual-original">{{ block.original }}</text>
|
||||
<text v-if="block.translated" class="detail-post-card__bilingual-translated">{{ block.translated }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<text v-if="postPreview.title" class="detail-post-card__title">{{ postPreview.title }}</text>
|
||||
|
||||
<view v-if="postPreview.summary" class="detail-post-card__summary-wrap">
|
||||
<text class="detail-post-card__summary"
|
||||
:class="{ 'detail-post-card__summary--collapsed': previewCanExpand && !previewExpanded }">
|
||||
{{ postPreview.summary }}
|
||||
</text>
|
||||
<text v-if="previewCanExpand" class="detail-post-card__expand"
|
||||
@tap="previewExpanded = !previewExpanded">
|
||||
{{ previewExpanded ? '收起' : '展开' }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<text v-if="hasBilingualContent && previewCanExpand" class="detail-post-card__expand detail-post-card__expand--bilingual"
|
||||
@tap="previewExpanded = !previewExpanded">
|
||||
{{ previewExpanded ? '收起' : '展开' }}
|
||||
</text>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Remove the obsolete standalone translation block**
|
||||
|
||||
Delete the old `detail-post-card__translation`, `detail-post-card__translation-label`, and `detail-post-card__translation-text` template markup because translation is now rendered inside each bilingual pair.
|
||||
|
||||
### Task 3: Style the bilingual reading hierarchy
|
||||
|
||||
**Files:**
|
||||
- Modify: `uniapp/src/packages_community/pages/post_detail.vue`
|
||||
|
||||
- [ ] **Step 1: Add bilingual layout styles**
|
||||
|
||||
Use a single-column, mobile-first hierarchy. Keep the original dominant and the translation visually subordinate without wrapping every pair in a heavy card.
|
||||
|
||||
```scss
|
||||
&__bilingual {
|
||||
margin-top: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
&--collapsed {
|
||||
max-height: 430rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&__bilingual-pair {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
&__bilingual-original {
|
||||
color: #20242b;
|
||||
font-size: 29rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.72;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&__bilingual-translated {
|
||||
color: #647187;
|
||||
font-size: 27rpx;
|
||||
line-height: 1.72;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&__expand--bilingual {
|
||||
display: block;
|
||||
margin-top: 12rpx;
|
||||
text-align: right;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Remove unused translation-card styles**
|
||||
|
||||
Delete the old `&__translation`, `&__translation-label`, and `&__translation-text` rules so the stylesheet matches the template.
|
||||
|
||||
### Task 4: Review and commit the source change
|
||||
|
||||
**Files:**
|
||||
- Review: `uniapp/src/packages_community/pages/post_detail.vue`
|
||||
|
||||
- [ ] **Step 1: Inspect the scoped diff**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
git diff -- uniapp/src/packages_community/pages/post_detail.vue
|
||||
```
|
||||
|
||||
Expected: only the bilingual template, computed pairing logic, expansion rule, and related styles change.
|
||||
|
||||
- [ ] **Step 2: Check whitespace and conflict markers**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
git diff --check -- uniapp/src/packages_community/pages/post_detail.vue
|
||||
rg -n "<<<<<<<|=======|>>>>>>>" uniapp/src/packages_community/pages/post_detail.vue
|
||||
if ($LASTEXITCODE -eq 0) { throw 'Conflict markers found' }
|
||||
if ($LASTEXITCODE -gt 1) { throw 'Conflict marker scan failed' }
|
||||
```
|
||||
|
||||
Expected: `git diff --check` exits successfully and `rg` returns no matches.
|
||||
|
||||
- [ ] **Step 3: Review logic against edge cases**
|
||||
|
||||
Confirm from the diff that:
|
||||
|
||||
- bilingual mode requires both original content and translated content;
|
||||
- unmatched trailing sentences remain rendered;
|
||||
- no API or paid-content logic changed;
|
||||
- normal posts retain the old title/summary branch;
|
||||
- old translation-card selectors have no remaining references.
|
||||
|
||||
- [ ] **Step 4: Commit only the intended files**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
git add -- docs/superpowers/plans/2026-08-02-community-post-bilingual-pairing.md uniapp/src/packages_community/pages/post_detail.vue
|
||||
git commit -m "feat: pair community post translations"
|
||||
```
|
||||
|
||||
Expected: one commit containing the plan and the uni-app page source. Do not include existing `.playwright-mcp` or modified shared docs.
|
||||
|
||||
## Verification boundary
|
||||
|
||||
The repository `AGENTS.md` prohibits automatically running builds or tests unless the user explicitly requests them. This plan therefore stops at scoped static diff checks and code review; suggested optional verification is `npm run build:h5` plus browser inspection of post `23361` after separate authorization.
|
||||
@@ -13,6 +13,43 @@
|
||||
|
||||
## 一、已完成事项
|
||||
|
||||
### 99. 社区标签图标管理与用户端展示
|
||||
|
||||
- 状态:代码已修改,待页面配置验收 - 时间:2026-08-02
|
||||
|
||||
完成内容:
|
||||
- 社区标签管理页新增图标上传、预览和编辑回显,复用管理端现有素材选择器。
|
||||
- 后端补充 `la_community_tag.icon` 字段校验及图片地址存取规范化,管理端和用户端接口统一返回可访问的完整图片地址。
|
||||
- 社区用户端话题栏优先展示后台上传的标签图标;未配置图标时继续使用原有默认图标,兼容存量标签。
|
||||
|
||||
涉及模块:
|
||||
- `admin/src/views/community/tag/index.vue`
|
||||
- `server/app/adminapi/validate/community/CommunityTagValidate.php`
|
||||
- `server/app/common/model/community/CommunityTag.php`
|
||||
- `uniapp/src/pages/community/community.vue`
|
||||
|
||||
### 98. 社区付费帖子详情页视觉与解锁逻辑优化
|
||||
|
||||
- 状态:代码已修改并同步测试服,H5 页面已复核 - 时间:2026-08-02
|
||||
|
||||
完成内容:
|
||||
- 按帖子详情设计稿重构顶部导航、作者内容卡、付费标签、正文标题与免费摘要、积分解锁卡、互动统计、评论头像列表和底部输入栏。
|
||||
- 详情页复用社区列表的两阶段购买流程,先以 `confirm=0` 探测购买状态,用户确认后以 `confirm=1` 扣除积分;成功后直接更新全文、锁定状态、购买人数和积分余额,不再重新请求详情导致浏览量重复增加。
|
||||
- 解锁卡新增所需积分、当前余额、积分明细入口和永久可读提示;余额请求失败显示 `--`,未登录时引导登录。
|
||||
- 后端 `/api/community/postDetail` 改为按 `free_content_len` 返回 20~500 字的免费预览,异常时默认 100 字,不再硬编码 20 字。
|
||||
- 修复付费特朗普帖子可能通过 `translated_content` 泄露全文的问题:锁定状态不生成或返回译文,解锁后继续沿用原翻译链路。
|
||||
- 增加帖子详情和评论加载失败的可重试状态、点赞进行中保护、非负计数处理及评论头像失效回退。
|
||||
|
||||
涉及模块:
|
||||
- `server/app/api/controller/CommunityController.php`
|
||||
- `uniapp/src/packages_community/pages/post_detail.vue`
|
||||
- `docs/superpowers/specs/2026-08-02-community-paid-post-detail-ui-design.md`
|
||||
- `docs/superpowers/plans/2026-08-02-community-paid-post-detail-refresh.md`
|
||||
|
||||
验证说明:
|
||||
- 已在 `http://localhost:5178/packages_community/pages/post_detail?id=8` 使用 430×932 H5 视口复核,页面无横向溢出,部署后免费预览已按配置长度生效。
|
||||
- 按当前任务边界未执行 H5 构建或自动化测试。
|
||||
|
||||
### 95. 首页频道栏横向滚动与等宽间距
|
||||
|
||||
- 状态:代码已修改,待页面验证 - 时间:2026-08-02
|
||||
@@ -27,6 +64,38 @@
|
||||
验证说明:
|
||||
- 已完成模板与样式静态检查,未执行 H5 构建或浏览器验证。
|
||||
|
||||
### 97. 首页今日焦点按频道匹配赛事
|
||||
|
||||
- 状态:代码已修改,待页面验证 - 时间:2026-08-02
|
||||
|
||||
完成内容:
|
||||
- 首页“今日焦点”在切换资讯频道时同步刷新赛事,频道名称直接匹配赛事 `league_name`,例如 NBA、CBA、英超、世界杯等。
|
||||
- 匹配优先级调整为当前赛事的进行中比赛、当前赛事的未开始比赛、其他热门比赛;热门比赛不足时继续从其他进行中和未开始比赛补齐,避免焦点区域空白。
|
||||
- 全站兜底比赛结果增加 60 秒缓存,并通过请求序号防止快速切换频道时旧请求覆盖新频道结果。
|
||||
|
||||
涉及模块:
|
||||
- `uniapp/src/pages/index/index.vue`
|
||||
|
||||
验证说明:
|
||||
- 已完成代码静态检查,按当前任务要求未执行 H5 构建或部署。
|
||||
|
||||
### 96. 社区广场路径与视觉重构
|
||||
|
||||
- 状态:代码已修改,待 H5 发布验收 - 时间:2026-08-02
|
||||
|
||||
完成内容:
|
||||
- 社区 Tab 的规范入口由 `/pages/empty/empty` 更正为 `/pages/community/community`;原路径和旧分包入口均保留自动跳转,避免旧链接或后台旧配置中断访问。
|
||||
- 将社区页改为搜索、积分入口、频道导航、动态话题胶囊和卡片式讨论流布局,并重构帖子卡片的信息层级、付费解锁区、互动栏与图片展示。
|
||||
- 继续复用现有社区帖子、标签、积分、点赞、付费解锁、发布、分享和 AI 分析接口,未变更后端契约。
|
||||
|
||||
涉及模块:
|
||||
- `uniapp/src/pages/community/community.vue`
|
||||
- `uniapp/src/packages_community/components/post-card.vue`
|
||||
- `uniapp/src/pages.json`
|
||||
- `uniapp/src/components/tabbar/tabbar.vue`
|
||||
- `uniapp/src/packages_community/pages/publish.vue`
|
||||
- `uniapp/src/api/community.ts`
|
||||
|
||||
### 94. 统一开发编排接入 crawler
|
||||
|
||||
- 状态:已完成 - 时间:2026-08-01
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
| 赛事详情页 | `uniapp/src/pages/match_detail/match_detail.vue` | 🚧 部分完成 | 2026-05-29 优化顶部赛事摘要头部,补充联赛、对阵、状态与直播入口信息 |
|
||||
| 世界杯专题页(排名导航) | `uniapp/src/packages_match/pages/worldcup.vue` | 🚧 部分完成 | 2026-05-20 已按设计稿拆分排名导航、积分榜、球员榜组件,后续可继续细化表格视觉与新闻页样式 |
|
||||
| 首页 | `uniapp/src/pages/index/index.vue` | 🚧 部分完成 | 2026-07-31 已按当前设计图重构为“今日焦点赛程、主推大图、最新资讯”三段式首页;2026-08-02 频道栏改为可横向滑动,频道项固定宽度并统一间距;频道仍复用后台配置分类,待实际 H5 页面验收后确认最终状态 |
|
||||
| 社区广场 | `uniapp/src/pages/community/community.vue` | 🚧 部分完成 | 2026-08-02 已按优化设计稿重构为社区信息流;入口路径调整为 `/pages/community/community`,旧 `/pages/empty/empty` 与旧分包入口会自动跳转,待 H5 发布后验收实际效果 |
|
||||
| 社区帖子详情 | `uniapp/src/packages_community/pages/post_detail.vue` | 🚧 部分完成 | 2026-08-02 已按付费帖子详情设计稿重构作者内容卡、免费摘要、积分解锁卡、互动评论区和底部输入栏;430px H5 页面已复核,App/小程序与正式构建待验收 |
|
||||
| 新闻详情 | `uniapp/src/pages/news_detail/news_detail.vue` | 待核对 | 富文本与图片渲染已存在相关实现 |
|
||||
| 个人中心 | 待补充 | 待核对 | 需核对实际页面路径 |
|
||||
| AI 分析 | 待补充 | 待核对 | 需核对实际页面路径 |
|
||||
|
||||
Reference in New Issue
Block a user