将文本翻译成日语
This commit is contained in:
43
package-lock.json
generated
43
package-lock.json
generated
@@ -25,6 +25,8 @@
|
|||||||
"@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
|
"@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
|
||||||
"@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
|
"@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
|
||||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
|
"@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
|
||||||
|
"js-md5": "^0.8.3",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"sass": "^1.77.4",
|
"sass": "^1.77.4",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
@@ -5722,6 +5724,15 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/charenc": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
@@ -5987,6 +5998,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypt": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-font-size-keywords": {
|
"node_modules/css-font-size-keywords": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz",
|
||||||
@@ -7318,6 +7338,12 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-buffer": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.16.1",
|
"version": "2.16.1",
|
||||||
"resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
|
"resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||||
@@ -8256,6 +8282,12 @@
|
|||||||
"integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==",
|
"integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/js-md5": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -8653,6 +8685,17 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/md5": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"charenc": "0.0.2",
|
||||||
|
"crypt": "0.0.2",
|
||||||
|
"is-buffer": "~1.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
"@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
|
"@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
|
||||||
"@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
|
"@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
|
||||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
|
"@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
|
||||||
|
"js-md5": "^0.8.3",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"sass": "^1.77.4",
|
"sass": "^1.77.4",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
|
|||||||
@@ -4,17 +4,20 @@
|
|||||||
<main>
|
<main>
|
||||||
<!-- 评论头部信息 -->
|
<!-- 评论头部信息 -->
|
||||||
<view class="commenthead">
|
<view class="commenthead">
|
||||||
<text class="commentcount">{{ comments.length }}条评论</text>
|
<text class="commentcount">{{ comments.length }}件のコメント</text>
|
||||||
<view class="headswitch">
|
<view class="headswitch">
|
||||||
<text class="inact" @tap="handleOpenApp">默认</text>
|
<text class="inact" @tap="handleOpenApp">デフォルト</text>
|
||||||
<view class="act" @tap="handleOpenApp">最新</view>
|
<view class="act" @tap="handleOpenApp">最新</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 评论主体 -->
|
<!-- 评论主体 -->
|
||||||
|
<!-- 翻译加载中状态 -->
|
||||||
|
<view v-if="isTranslating" class="translating">
|
||||||
|
<text>コメント読み込み中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view v-if="comments.list.length > 0">
|
<view v-else-if="comments.list.length > 0">
|
||||||
|
|
||||||
<view v-for="commentItem in comments.list" :key="commentItem.id" class="commentdetail" @tap="onDelegateTap">
|
<view v-for="commentItem in comments.list" :key="commentItem.id" class="commentdetail" @tap="onDelegateTap">
|
||||||
<!-- 头像 -->
|
<!-- 头像 -->
|
||||||
<view class="commentdetailleft">
|
<view class="commentdetailleft">
|
||||||
@@ -32,8 +35,8 @@
|
|||||||
<uni-dateformat v-if="commentItem.createdAt"
|
<uni-dateformat v-if="commentItem.createdAt"
|
||||||
:date="Date.parse(commentItem.createdAt.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
|
:date="Date.parse(commentItem.createdAt.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
|
||||||
class="date-text" />
|
class="date-text" />
|
||||||
<text v-else class="date-text">未知时间</text>
|
<text v-else class="date-text">不明な時間</text>
|
||||||
<text class="replytext" @tap.stop="handleOpenApp">回复</text>
|
<text class="replytext" @tap.stop="handleOpenApp">返信</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview"></view>
|
||||||
@@ -55,8 +58,8 @@
|
|||||||
<view class="date-reply">
|
<view class="date-reply">
|
||||||
<uni-dateformat v-if="child.date" :date="Date.parse(child.date.replace(/-/g, '/'))"
|
<uni-dateformat v-if="child.date" :date="Date.parse(child.date.replace(/-/g, '/'))"
|
||||||
:threshold="[0, 0]" format="yyyy-MM-dd" class="date-text" />
|
:threshold="[0, 0]" format="yyyy-MM-dd" class="date-text" />
|
||||||
<text v-else class="date-text">未知时间</text>
|
<text v-else class="date-text">不明な時間</text>
|
||||||
<text class="replytext" @tap.stop="handleOpenApp">回复</text>
|
<text class="replytext" @tap.stop="handleOpenApp">返信</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="commentlike">
|
<view class="commentlike">
|
||||||
@@ -70,7 +73,7 @@
|
|||||||
<view v-if="commentItem.reply.length" class="expandcomment">
|
<view v-if="commentItem.reply.length" class="expandcomment">
|
||||||
<view style="width:20px;height:1px;background:rgba(65,60,67,.2)"></view>
|
<view style="width:20px;height:1px;background:rgba(65,60,67,.2)"></view>
|
||||||
<text class="expandcommenttext" :data-cid="commentItem.id">
|
<text class="expandcommenttext" :data-cid="commentItem.id">
|
||||||
{{ commentItem.showChild ? '收起' : `展开${commentItem.reply.length}条回复` }}
|
{{ commentItem.showChild ? '折りたたむ' : `${commentItem.reply.length}件の返信を表示` }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -80,10 +83,9 @@
|
|||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
||||||
<view v-else class="nocomments">
|
<view v-else class="nocomments">
|
||||||
<image src="/static/imgs/empty-img/b-empty-img@3x.webp" mode="aspectFit" alt="暂无评论"></image>
|
<image src="/static/imgs/empty-img/b-empty-img@3x.webp" mode="aspectFit" alt="暂无评论"></image>
|
||||||
<text class="nocommentstext">空空如也~</text>
|
<text class="nocommentstext">コメントはまだありません</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 占位视图 -->
|
<!-- 占位视图 -->
|
||||||
@@ -99,12 +101,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { getCommentList, getUserImg } from '@/api/api.js'
|
import { getCommentList, getUserImg } from '@/api/api.js'
|
||||||
import { useCommonStore } from '@/stores/common.js'
|
import { useCommonStore } from '@/stores/common.js'
|
||||||
import Findmore from '@/pages/findmore/findmore.vue'
|
import Findmore from '@/pages/findmore/findmore.vue'
|
||||||
import Intereact from '@/pages/intereact/intereact.vue'
|
import Intereact from '@/pages/intereact/intereact.vue'
|
||||||
|
// 引入翻译函数
|
||||||
|
import { translateZhToJa } from '@/utils/translate.js';
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
|
|
||||||
@@ -120,6 +124,9 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 翻译加载状态
|
||||||
|
const isTranslating = ref(false);
|
||||||
|
|
||||||
// 处理评论点击事件
|
// 处理评论点击事件
|
||||||
function onDelegateTap(e) {
|
function onDelegateTap(e) {
|
||||||
const cid = e.target?.dataset?.cid
|
const cid = e.target?.dataset?.cid
|
||||||
@@ -139,11 +146,11 @@ function formatCount(count) {
|
|||||||
return common.formatCount(count)
|
return common.formatCount(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad(() => {
|
onLoad(async () => { // 标记为异步函数
|
||||||
const params = {
|
const params = {
|
||||||
postId: props.postid
|
postId: props.postid
|
||||||
}
|
}
|
||||||
getCommentList(params).then(res => {
|
getCommentList(params).then(async (res) => { // 标记为异步函数
|
||||||
try {
|
try {
|
||||||
// 检查响应状态
|
// 检查响应状态
|
||||||
if (res.statusCode === 200 && res.data) {
|
if (res.statusCode === 200 && res.data) {
|
||||||
@@ -151,10 +158,32 @@ onLoad(() => {
|
|||||||
comments.list = res.data.list || []
|
comments.list = res.data.list || []
|
||||||
comments.length = res.data.total || 0
|
comments.length = res.data.total || 0
|
||||||
|
|
||||||
// 处理每条评论
|
// 构建父子关系映射对象
|
||||||
comments.list.forEach(comment => {
|
const commentMap = {};
|
||||||
processComment(comment)
|
comments.list.forEach(comment => {
|
||||||
})
|
commentMap[comment.id] = comment;
|
||||||
|
comment.children = [];
|
||||||
|
});
|
||||||
|
comments.list.forEach(comment => {
|
||||||
|
if (comment.parentId) {
|
||||||
|
const parent = commentMap[comment.parentId];
|
||||||
|
if (parent) {
|
||||||
|
parent.children.push(comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 开始翻译,显示加载状态
|
||||||
|
isTranslating.value = true;
|
||||||
|
|
||||||
|
// 等待所有评论翻译完成(异步处理)
|
||||||
|
await Promise.all(
|
||||||
|
comments.list.map(comment => processComment(comment))
|
||||||
|
);
|
||||||
|
|
||||||
|
// 翻译完成,隐藏加载状态
|
||||||
|
isTranslating.value = false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`请求失败: ${res.statusCode}`)
|
throw new Error(`请求失败: ${res.statusCode}`)
|
||||||
}
|
}
|
||||||
@@ -164,6 +193,8 @@ onLoad(() => {
|
|||||||
title: '加载评论失败',
|
title: '加载评论失败',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
})
|
})
|
||||||
|
// 异常时隐藏加载状态
|
||||||
|
isTranslating.value = false;
|
||||||
// 设置默认空数据
|
// 设置默认空数据
|
||||||
comments.list = []
|
comments.list = []
|
||||||
comments.length = 0
|
comments.length = 0
|
||||||
@@ -174,6 +205,8 @@ onLoad(() => {
|
|||||||
title: '网络连接异常',
|
title: '网络连接异常',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
})
|
})
|
||||||
|
// 异常时隐藏加载状态
|
||||||
|
isTranslating.value = false;
|
||||||
// 设置默认空数据
|
// 设置默认空数据
|
||||||
comments.list = []
|
comments.list = []
|
||||||
comments.length = 0
|
comments.length = 0
|
||||||
@@ -181,64 +214,79 @@ onLoad(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理单条评论数据
|
* 处理单条评论数据(异步,支持翻译)
|
||||||
* @param {Object} comment - 评论对象
|
* @param {Object} comment - 评论对象
|
||||||
*/
|
*/
|
||||||
function processComment(comment) {
|
async function processComment(comment) {
|
||||||
// 确保comment对象有必要的属性
|
// 确保comment对象有必要的属性
|
||||||
if (!comment.children || comment.children.length === 0) {
|
if (!comment.children || comment.children.length === 0) {
|
||||||
comment.showChild = false
|
comment.showChild = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保user对象存在
|
// 确保user对象存在
|
||||||
if (!comment.user) {
|
if (!comment.user) {
|
||||||
comment.user = {}
|
comment.user = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置评论基本属性
|
// 设置评论基本属性
|
||||||
comment.content = comment.content || ''
|
comment.content = comment.content || '';
|
||||||
comment.likeCount = comment.likeCount || 0
|
comment.likeCount = comment.likeCount || 0;
|
||||||
comment.userName = comment.user.nickName || '匿名用户'
|
comment.createdAt = comment.createdAt || comment.date || '';
|
||||||
comment.createdAt = comment.createdAt || comment.date || ''
|
|
||||||
|
|
||||||
// 生成renderNodes用于富文本渲染
|
// 翻译用户名和评论内容(并行处理,提升效率)
|
||||||
comment.renderNodes = common.contentToRenderNodes(comment.content, comment.atUsers)
|
const [translatedUserName, translatedContent] = await Promise.all([
|
||||||
|
translateZhToJa(comment.user.nickName || '匿名用户'),
|
||||||
|
translateZhToJa(comment.content)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 更新为翻译后的日语
|
||||||
|
comment.userName = translatedUserName;
|
||||||
|
// 生成日语内容的renderNodes
|
||||||
|
comment.renderNodes = common.contentToRenderNodes(translatedContent, comment.atUsers);
|
||||||
|
|
||||||
// 处理用户头像
|
// 处理用户头像
|
||||||
handleCommentAvatar(comment)
|
handleCommentAvatar(comment);
|
||||||
|
|
||||||
// 处理子评论
|
// 处理子评论(异步)
|
||||||
if (comment.reply && comment.reply.length > 0) {
|
if (comment.reply && comment.reply.length > 0) {
|
||||||
// 将reply赋值给children,确保模板和代码一致
|
// 将reply赋值给children,确保模板和代码一致
|
||||||
comment.children = comment.reply
|
comment.children = comment.reply;
|
||||||
|
|
||||||
comment.children.forEach(childComment => {
|
// 并行翻译所有子评论
|
||||||
processChildComment(childComment)
|
await Promise.all(
|
||||||
})
|
comment.children.map(childComment => processChildComment(childComment))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理子评论数据
|
* 处理子评论数据(异步,支持翻译)
|
||||||
* @param {Object} childComment - 子评论对象
|
* @param {Object} childComment - 子评论对象
|
||||||
*/
|
*/
|
||||||
function processChildComment(childComment) {
|
async function processChildComment(childComment) {
|
||||||
// 确保user对象存在
|
// 确保user对象存在
|
||||||
if (!childComment.user) {
|
if (!childComment.user) {
|
||||||
childComment.user = {}
|
childComment.user = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置子评论基本属性
|
// 设置子评论基本属性
|
||||||
childComment.content = childComment.content || ''
|
childComment.content = childComment.content || '';
|
||||||
childComment.likeCount = childComment.likeCount || 0
|
childComment.likeCount = childComment.likeCount || 0;
|
||||||
childComment.userName = childComment.user.nickName || '匿名用户'
|
childComment.date = childComment.createdAt || childComment.date || '';
|
||||||
childComment.date = childComment.createdAt || childComment.date || ''
|
|
||||||
|
// 翻译子评论的用户名和内容
|
||||||
|
const [translatedUserName, translatedContent] = await Promise.all([
|
||||||
|
translateZhToJa(childComment.user.nickName || '匿名ユーザー'),
|
||||||
|
translateZhToJa(childComment.content)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 更新为翻译后的日语
|
||||||
|
childComment.userName = translatedUserName;
|
||||||
|
// 生成日语内容的renderNodes
|
||||||
|
childComment.renderNodes = common.contentToRenderNodes(translatedContent, childComment.atUsers);
|
||||||
|
|
||||||
// 处理子评论头像
|
// 处理子评论头像
|
||||||
handleCommentAvatar(childComment)
|
handleCommentAvatar(childComment);
|
||||||
|
|
||||||
// 为子评论生成renderNodes
|
|
||||||
childComment.renderNodes = common.contentToRenderNodes(childComment.content, childComment.atUsers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,8 +326,6 @@ function handleCommentAvatar(comment) {
|
|||||||
comment.userAvatar = ''
|
comment.userAvatar = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -309,8 +355,9 @@ main {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 29px;
|
height: 32px;
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentcount {
|
.commentcount {
|
||||||
@@ -329,36 +376,36 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.headswitch {
|
.headswitch {
|
||||||
width: 102px;
|
min-width: 120px;
|
||||||
height: 100%;
|
width: auto;
|
||||||
|
height: 28px;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-around;
|
justify-content: space-between;
|
||||||
border-radius: 24px;
|
border-radius: 14px;
|
||||||
padding: 2px;
|
padding: 2px 3px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: #f0eef1;
|
background-color: #f0eef1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headswitch .inact,
|
.headswitch .inact,
|
||||||
.act {
|
.act {
|
||||||
width: 48px;
|
min-width: 54px;
|
||||||
height: 23px;
|
padding: 0 8px;
|
||||||
|
height: 24px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-stretch: normal;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
letter-spacing: normal;
|
|
||||||
color: #110c13;
|
color: #110c13;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headswitch .inact {
|
.headswitch .inact {
|
||||||
border-radius: 24px;
|
border-radius: 12px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,4 +601,12 @@ main {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: #918e93;
|
color: #918e93;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 翻译加载状态样式 */
|
||||||
|
.translating {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #918e93;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="findmore" @tap="common.openapp">
|
<view class="findmore" @tap="common.openapp">
|
||||||
<text class="openapptext">APP查看更多</text>
|
<text class="openapptext">APP でさらに表示</text>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -15,19 +15,22 @@ const common = useCommonStore()
|
|||||||
bottom: 88px;
|
bottom: 88px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: 177px;
|
min-width: 177px;
|
||||||
|
width: auto;
|
||||||
|
padding: 0 20px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
border-radius: 43px;
|
border-radius: 43px;
|
||||||
background-image: linear-gradient(97deg, #7c45ed 1%, #7c68ef 20%, #7bd8f8 92%);
|
background-image: linear-gradient(97deg, #7c45ed 1%, #7c68ef 20%, #7bd8f8 92%);
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 52px;
|
line-height: 52px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openapptext {
|
.openapptext {
|
||||||
width: 96px;
|
|
||||||
height: 22px;
|
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-stretch: normal;
|
font-stretch: normal;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<view class="head">
|
<view class="head">
|
||||||
<image src="/static/imgs/h5logo/h5logo@3x.webp" mode="aspectFit" class="applogo" alt="官网logo" />
|
<image src="/static/imgs/h5logo/h5logo@3x.webp" mode="aspectFit" class="applogo" alt="官网logo" />
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview"></view>
|
||||||
<view class="download" @tap="common.download">下载应用</view>
|
<view class="download" @tap="common.download">アプリをダウンロード</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -39,7 +39,8 @@ const common = useCommonStore()
|
|||||||
}
|
}
|
||||||
|
|
||||||
.download {
|
.download {
|
||||||
width: 97px;
|
min-width: 120px;
|
||||||
|
padding: 0 16px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
border-radius: 29px;
|
border-radius: 29px;
|
||||||
background-image: linear-gradient(156deg, #7c45ed -1%, #7c68ef 19%, #7bd8f8 97%);
|
background-image: linear-gradient(156deg, #7c45ed -1%, #7c68ef 19%, #7bd8f8 97%);
|
||||||
@@ -49,5 +50,6 @@ const common = useCommonStore()
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
<view class="interaction">
|
<view class="interaction">
|
||||||
<view class="editarea" @tap="handleOpenApp">
|
<view class="editarea" @tap="handleOpenApp">
|
||||||
<image src="/static/imgs/editicon/icon@2x.webp" mode="aspectFit" class="editicon" alt="编辑标签"></image>
|
<image src="/static/imgs/editicon/icon@2x.webp" mode="aspectFit" class="editicon" alt="编辑标签"></image>
|
||||||
<text class="edittext">快来互动吧…</text>
|
<text class="edittext">さあ、交流しましょう…</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview small"></view>
|
||||||
<view class="collection" @tap="handleOpenApp">
|
<view class="collection" @tap="handleOpenApp">
|
||||||
<image src="@/static/imgs/staricon/icon@3x.webp" mode="aspectFit" class="collectionicon" alt="收藏标签"></image>
|
<image src="@/static/imgs/staricon/icon@3x.webp" mode="aspectFit" class="collectionicon" alt="收藏标签"></image>
|
||||||
<text class="collectioncount">{{ formatCount(collectsum) }}</text>
|
<text class="collectioncount">{{ formatCount(collectsum) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview small"></view>
|
||||||
<view class="like" @tap="handleOpenApp">
|
<view class="like" @tap="handleOpenApp">
|
||||||
<image src="@/static/imgs/likeicon/icon@3x.webp" mode="aspectFit" class="likeicon" alt="点赞标签"></image>
|
<image src="@/static/imgs/likeicon/icon@3x.webp" mode="aspectFit" class="likeicon" alt="点赞标签"></image>
|
||||||
<text class="likecount">{{ formatCount(countLike) }}</text>
|
<text class="likecount">{{ formatCount(countLike) }}</text>
|
||||||
@@ -37,11 +37,9 @@ function handleOpenApp() {
|
|||||||
common.openapp()
|
common.openapp()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化数字显示
|
|
||||||
function formatCount(count) {
|
function formatCount(count) {
|
||||||
return common.formatCount(count)
|
return common.formatCount(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -50,13 +48,9 @@ function formatCount(count) {
|
|||||||
pointer-events: none
|
pointer-events: none
|
||||||
}
|
}
|
||||||
|
|
||||||
.interaction {
|
.spacerview.small {
|
||||||
width: 100%;
|
flex: 0.3;
|
||||||
max-width: 430px;
|
min-width: 8px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: auto auto 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.interaction {
|
.interaction {
|
||||||
@@ -66,19 +60,21 @@ function formatCount(count) {
|
|||||||
background-color: #faf9fb;
|
background-color: #faf9fb;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 11.5px 16px;
|
padding: 11.5px 12px;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: auto auto 0;
|
margin: 15px auto 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editarea {
|
.editarea {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
padding: 0 20px;
|
padding: 0 16px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -94,16 +90,10 @@ function formatCount(count) {
|
|||||||
|
|
||||||
.edittext {
|
.edittext {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-grow: 0;
|
font-size: 13px;
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-stretch: normal;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: 40px;
|
|
||||||
letter-spacing: normal;
|
|
||||||
text-align: left;
|
|
||||||
color: #918e93;
|
color: #918e93;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collection,
|
.collection,
|
||||||
@@ -112,30 +102,17 @@ function formatCount(count) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collectionicon,
|
.collectionicon,
|
||||||
.likeicon {
|
.likeicon {
|
||||||
width: 24px;
|
width: 22px;
|
||||||
height: 24px;
|
height: 22px;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.collectioncount,
|
.collectioncount,
|
||||||
.likecount {
|
.likecount {
|
||||||
height: 17px;
|
font-size: 13px;
|
||||||
font-family: 'SFPro';
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-stretch: normal;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: normal;
|
|
||||||
letter-spacing: normal;
|
|
||||||
text-align: left;
|
|
||||||
color: #000;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -7,6 +7,15 @@
|
|||||||
<section class="content">
|
<section class="content">
|
||||||
<!-- 新闻区域 -->
|
<!-- 新闻区域 -->
|
||||||
<section class="news">
|
<section class="news">
|
||||||
|
<!-- 翻译加载状态 -->
|
||||||
|
<view v-if="isTranslating" class="translating">
|
||||||
|
<text>加载新闻内容中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 翻译错误提示 -->
|
||||||
|
<view v-if="translationError" class="translation-error">
|
||||||
|
<text>翻译服务暂时不可用,将显示原文</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 轮播图 -->
|
<!-- 轮播图 -->
|
||||||
<swiper v-if="post.imgs && post.imgs.length > 0" :indicator-dots="false" :autoplay="false" :interval="3000"
|
<swiper v-if="post.imgs && post.imgs.length > 0" :indicator-dots="false" :autoplay="false" :interval="3000"
|
||||||
@@ -37,21 +46,21 @@
|
|||||||
|
|
||||||
<!-- 新闻标题 -->
|
<!-- 新闻标题 -->
|
||||||
<view v-if="post.title" class="newstitle">
|
<view v-if="post.title" class="newstitle">
|
||||||
<text class="title">{{ post.title }}</text>
|
<text class="title">{{ post.translatedTitle || post.title }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 新闻信息 -->
|
<!-- 新闻信息 -->
|
||||||
<view v-if="post.copywriting && post.date" class="newsbottom">
|
<view v-if="(post.copywriting || post.translatedContent) && post.date" class="newsbottom">
|
||||||
<text class="copywriting">{{ post.copywriting }}</text>
|
<text class="copywriting">{{ post.translatedContent || post.copywriting }}</text>
|
||||||
<view class="meta-info">
|
<view class="meta-info">
|
||||||
<text v-if="post.source" class="source">{{ post.source }}</text>
|
<text v-if="post.source" class="source">{{ post.translatedSource || post.source }}</text>
|
||||||
<text v-if="post.source" class="date-text">·</text>
|
<text v-if="post.source" class="date-text">·</text>
|
||||||
<view class="datetextview">
|
<view class="datetextview">
|
||||||
<text class="date-text">{{ common.formatDate(post.time) }}</text>
|
<text class="date-text">{{ common.formatDate(post.time) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview"></view>
|
||||||
<view class="toseeall" @tap="handleInteraction">
|
<view class="toseeall" @tap="handleInteraction">
|
||||||
<text class="toseealltext">查看全文</text>
|
<text class="toseealltext">全文を表示</text>
|
||||||
<image src="@/static/imgs/arrowrightup/arrowrightup@3x.png" class="arrowrightupicon" mode="aspectFit"
|
<image src="@/static/imgs/arrowrightup/arrowrightup@3x.png" class="arrowrightupicon" mode="aspectFit"
|
||||||
alt="查看全文图标" />
|
alt="查看全文图标" />
|
||||||
</view>
|
</view>
|
||||||
@@ -88,7 +97,8 @@ import Head from '@/pages/head/head.vue'
|
|||||||
import Comments from '@/pages/comments/comments.vue'
|
import Comments from '@/pages/comments/comments.vue'
|
||||||
import Findmore from '@/pages/findmore/findmore.vue'
|
import Findmore from '@/pages/findmore/findmore.vue'
|
||||||
import { getPostList, getPostLImage, getPostVideo } from '../api/api.js'
|
import { getPostList, getPostLImage, getPostVideo } from '../api/api.js'
|
||||||
// import Intereact from '@/pages/intereact/intereact.vue'
|
// 引入与评论组件共享的翻译函数和全局缓存
|
||||||
|
import { translateZhToJa, translationCache } from '@/utils/translate.js';
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
|
|
||||||
@@ -100,6 +110,9 @@ const onChange = e => current.value = e.detail.current
|
|||||||
const post = ref({})
|
const post = ref({})
|
||||||
// 资源加载状态
|
// 资源加载状态
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
// 翻译状态管理
|
||||||
|
const isTranslating = ref(false);
|
||||||
|
const translationError = ref(false);
|
||||||
|
|
||||||
// 图片加载成功处理
|
// 图片加载成功处理
|
||||||
const onImageLoad = (index) => {
|
const onImageLoad = (index) => {
|
||||||
@@ -118,21 +131,73 @@ const onImageError = (index) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量翻译新闻内容
|
||||||
|
* @param {Object} newsData - 原始新闻数据
|
||||||
|
*/
|
||||||
|
async function translateNewsContent(newsData) {
|
||||||
|
// 收集需要翻译的文本
|
||||||
|
const translateTasks = [];
|
||||||
|
|
||||||
|
// 标题翻译
|
||||||
|
if (newsData.title && !translationCache[newsData.title]) {
|
||||||
|
translateTasks.push({
|
||||||
|
key: 'translatedTitle',
|
||||||
|
text: newsData.title
|
||||||
|
});
|
||||||
|
} else if (newsData.title) {
|
||||||
|
newsData.translatedTitle = translationCache[newsData.title];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容翻译
|
||||||
|
if (newsData.copywriting && !translationCache[newsData.copywriting]) {
|
||||||
|
translateTasks.push({
|
||||||
|
key: 'translatedContent',
|
||||||
|
text: newsData.copywriting
|
||||||
|
});
|
||||||
|
} else if (newsData.copywriting) {
|
||||||
|
newsData.translatedContent = translationCache[newsData.copywriting];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 来源翻译
|
||||||
|
if (newsData.source && !translationCache[newsData.source]) {
|
||||||
|
translateTasks.push({
|
||||||
|
key: 'translatedSource',
|
||||||
|
text: newsData.source
|
||||||
|
});
|
||||||
|
} else if (newsData.source) {
|
||||||
|
newsData.translatedSource = translationCache[newsData.source];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行批量翻译
|
||||||
|
if (translateTasks.length > 0) {
|
||||||
|
await Promise.all(
|
||||||
|
translateTasks.map(task =>
|
||||||
|
translateZhToJa(task.text).then(result => {
|
||||||
|
newsData[task.key] = result;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newsData;
|
||||||
|
}
|
||||||
|
|
||||||
const handleInteraction = () => {
|
const handleInteraction = () => {
|
||||||
common.openapp();
|
common.openapp();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件挂载时获取数据
|
// 组件挂载时获取数据
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
const params = {
|
const params = {
|
||||||
newsFilter: 'news_only'
|
newsFilter: 'news_only'
|
||||||
}
|
}
|
||||||
|
|
||||||
getPostList(params).then(res => {
|
getPostList(params).then(async (res) => { // 标记为异步函数
|
||||||
try {
|
try {
|
||||||
const data = res.data.data
|
const data = res.data.data
|
||||||
// console.log(data)
|
|
||||||
|
// 处理新闻相关字段
|
||||||
// 处理新闻相关字段 - 根据实际API返回字段优化
|
|
||||||
data.id = data.id || ''
|
data.id = data.id || ''
|
||||||
data.title = data.newsTitle || data.title || ''
|
data.title = data.newsTitle || data.title || ''
|
||||||
data.source = data.newsSource || data.source || ''
|
data.source = data.newsSource || data.source || ''
|
||||||
@@ -142,6 +207,11 @@ onLoad(() => {
|
|||||||
data.collectsum = data.collectsum || 0
|
data.collectsum = data.collectsum || 0
|
||||||
data.comments = data.comments || []
|
data.comments = data.comments || []
|
||||||
|
|
||||||
|
// 翻译新闻内容
|
||||||
|
isTranslating.value = true;
|
||||||
|
await translateNewsContent(data);
|
||||||
|
isTranslating.value = false;
|
||||||
|
|
||||||
const mediaPromises = []
|
const mediaPromises = []
|
||||||
|
|
||||||
// 处理图片资源
|
// 处理图片资源
|
||||||
@@ -149,15 +219,14 @@ onLoad(() => {
|
|||||||
data.images.forEach(image => {
|
data.images.forEach(image => {
|
||||||
mediaPromises.push(getPostLImage(image.original_url).then(imageRes => {
|
mediaPromises.push(getPostLImage(image.original_url).then(imageRes => {
|
||||||
if (imageRes.statusCode === 200 && imageRes.data) {
|
if (imageRes.statusCode === 200 && imageRes.data) {
|
||||||
// 将arraybuffer转换为base64
|
|
||||||
const base64 = uni.arrayBufferToBase64(imageRes.data)
|
const base64 = uni.arrayBufferToBase64(imageRes.data)
|
||||||
const imageUrl = 'data:image/webp;base64,' + base64
|
const imageUrl = 'data:image/webp;base64,' + base64
|
||||||
return {
|
return {
|
||||||
type: 'img',
|
type: 'img',
|
||||||
src: imageUrl,
|
src: imageUrl,
|
||||||
original_url: image.original_url,
|
original_url: image.original_url,
|
||||||
loading: true, // 初始加载状态
|
loading: true,
|
||||||
error: false // 错误状态
|
error: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -172,15 +241,14 @@ onLoad(() => {
|
|||||||
if (data.video && data.video.original_url) {
|
if (data.video && data.video.original_url) {
|
||||||
mediaPromises.push(getPostVideo(data.video.original_url).then(videoRes => {
|
mediaPromises.push(getPostVideo(data.video.original_url).then(videoRes => {
|
||||||
if (videoRes.statusCode === 200 && videoRes.data) {
|
if (videoRes.statusCode === 200 && videoRes.data) {
|
||||||
// 将arraybuffer转换为base64
|
|
||||||
const base64 = uni.arrayBufferToBase64(videoRes.data)
|
const base64 = uni.arrayBufferToBase64(videoRes.data)
|
||||||
const videoUrl = 'data:video/mp4;base64,' + base64
|
const videoUrl = 'data:video/mp4;base64,' + base64
|
||||||
return {
|
return {
|
||||||
type: 'video',
|
type: 'video',
|
||||||
src: videoUrl,
|
src: videoUrl,
|
||||||
original_url: data.video.original_url,
|
original_url: data.video.original_url,
|
||||||
loading: true, // 初始加载状态
|
loading: true,
|
||||||
error: false // 错误状态
|
error: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -193,15 +261,14 @@ onLoad(() => {
|
|||||||
// 统一等待所有媒体资源加载完成
|
// 统一等待所有媒体资源加载完成
|
||||||
if (mediaPromises.length > 0) {
|
if (mediaPromises.length > 0) {
|
||||||
Promise.all(mediaPromises).then(mediaItems => {
|
Promise.all(mediaPromises).then(mediaItems => {
|
||||||
// 过滤掉null值,将有效的媒体资源合并到imgs数组中
|
|
||||||
data.imgs = mediaItems.filter(item => item !== null)
|
data.imgs = mediaItems.filter(item => item !== null)
|
||||||
post.value = data
|
post.value = data
|
||||||
loading.value = true
|
loading.value = true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 如果没有媒体资源,设置空数组
|
|
||||||
data.imgs = []
|
data.imgs = []
|
||||||
post.value = data
|
post.value = data
|
||||||
|
loading.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -216,14 +283,19 @@ onLoad(() => {
|
|||||||
console.error(errorType, error)
|
console.error(errorType, error)
|
||||||
post.value = {
|
post.value = {
|
||||||
title: title,
|
title: title,
|
||||||
|
translatedTitle: translationCache[title] || title, // 错误标题也翻译
|
||||||
source: '系统',
|
source: '系统',
|
||||||
|
translatedSource: translationCache['系统'] || '系统',
|
||||||
copywriting: message,
|
copywriting: message,
|
||||||
|
translatedContent: translationCache[message] || message, // 错误信息也翻译
|
||||||
date: new Date().toISOString(),
|
date: new Date().toISOString(),
|
||||||
countLike: 0,
|
countLike: 0,
|
||||||
collectsum: 0,
|
collectsum: 0,
|
||||||
comments: [],
|
comments: [],
|
||||||
imgs: []
|
imgs: []
|
||||||
}
|
}
|
||||||
|
loading.value = true
|
||||||
|
isTranslating.value = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -231,11 +303,29 @@ onLoad(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 翻译相关样式 */
|
||||||
|
.translating {
|
||||||
|
text-align: center;
|
||||||
|
padding: 15rpx;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #918e93;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-error {
|
||||||
|
text-align: center;
|
||||||
|
padding: 15rpx;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ff6b6b;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
background-color: #fff5f5;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
page,
|
page,
|
||||||
.page {
|
.page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 430px;
|
max-width: 430px;
|
||||||
/* height: 100vh; */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -435,4 +525,4 @@ swiper-item {
|
|||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -11,10 +11,11 @@
|
|||||||
<image v-if="post.user && post.user.userImg" :src="post.user.userImg" mode="aspectFill" class="userimg"
|
<image v-if="post.user && post.user.userImg" :src="post.user.userImg" mode="aspectFill" class="userimg"
|
||||||
alt="用户头像" />
|
alt="用户头像" />
|
||||||
<image v-else src="/static/imgs/default-avatar.png" mode="aspectFill" class="userimg" alt="默认头像" />
|
<image v-else src="/static/imgs/default-avatar.png" mode="aspectFill" class="userimg" alt="默认头像" />
|
||||||
<text class="username">{{ post.user ? post.user.nickName : '未知用户' }}</text>
|
<!-- 用户名翻译 -->
|
||||||
|
<text class="username">{{ post.translatedUserName || post.user.nickName }}</text>
|
||||||
<button class="follow" @tap="common.openapp">
|
<button class="follow" @tap="common.openapp">
|
||||||
<uni-icons v-if="post.isfollow" type="checkmarkempty" size="20" color="#333"></uni-icons>
|
<uni-icons v-if="post.isfollow" type="checkmarkempty" size="20" color="#333"></uni-icons>
|
||||||
<text v-else>关注</text>
|
<text v-else>フォロー</text>
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -27,20 +28,10 @@
|
|||||||
@load="onImageLoad(i)" @error="onImageError(i)" />
|
@load="onImageLoad(i)" @error="onImageError(i)" />
|
||||||
<video v-else-if="item.type === 'video'" id="videoid" :src="item.src" class="swiper-item"
|
<video v-else-if="item.type === 'video'" id="videoid" :src="item.src" class="swiper-item"
|
||||||
:controls="false" @tap="pausevideo" object-fit="contain" alt="动态视频内容" />
|
:controls="false" @tap="pausevideo" object-fit="contain" alt="动态视频内容" />
|
||||||
<!-- 播放按钮 - 使用浏览器复制的样式 -->
|
<!-- 播放按钮 -->
|
||||||
<view v-if="!isPlaying" class="uni-video-cover" @tap="pausevideo">
|
<view v-if="!isPlaying" class="uni-video-cover" @tap="pausevideo">
|
||||||
<view class="uni-video-cover-play-button uni-video-icon"></view>
|
<view class="uni-video-cover-play-button uni-video-icon"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- <cover-view class="swiper-mask" @tap="common.openapp">
|
|
||||||
</cover-view> -->
|
|
||||||
<!-- 图片加载状态 -->
|
|
||||||
<!-- <view v-if="item.loading" class="image-loading">
|
|
||||||
<text>图片加载中...</text>
|
|
||||||
</view>
|
|
||||||
<view v-if="item.error" class="image-error">
|
|
||||||
<text>图片加载失败</text>
|
|
||||||
</view> -->
|
|
||||||
</view>
|
</view>
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
@@ -51,8 +42,9 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部文案 -->
|
<!-- 底部文案 -->
|
||||||
<view v-if="post.copywriting && post.date" class="momentbottom">
|
<view v-if="(post.copywriting || post.translatedContent) && post.date" class="momentbottom">
|
||||||
<text class="copywriting">{{ post.copywriting }}</text>
|
<!-- 内容翻译 -->
|
||||||
|
<text class="copywriting">{{ post.translatedContent || post.copywriting }}</text>
|
||||||
<uni-dateformat :date="Date.parse(post.date.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
|
<uni-dateformat :date="Date.parse(post.date.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
|
||||||
class="date-text" />
|
class="date-text" />
|
||||||
</view>
|
</view>
|
||||||
@@ -64,16 +56,13 @@
|
|||||||
<!-- 评论区域 + 互动区域 -->
|
<!-- 评论区域 + 互动区域 -->
|
||||||
<Comments :postid="post.id" />
|
<Comments :postid="post.id" />
|
||||||
|
|
||||||
<!-- 互动区域 -->
|
|
||||||
<!-- <Intereact :collectsum="post.collectsum" :likesum="post.likesum" /> -->
|
|
||||||
|
|
||||||
<!-- Findmore -->
|
<!-- Findmore -->
|
||||||
<Findmore />
|
<Findmore />
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else class="loading-container">
|
<view v-else class="loading-container">
|
||||||
<text>页面加载中...</text>
|
<text>ページ読み込み中...</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
@@ -86,20 +75,27 @@ import Head from '@/pages/head/head.vue'
|
|||||||
import Comments from '@/pages/comments/comments.vue'
|
import Comments from '@/pages/comments/comments.vue'
|
||||||
import Findmore from '@/pages/findmore/findmore.vue'
|
import Findmore from '@/pages/findmore/findmore.vue'
|
||||||
import { getPostList, getPostLImage, getPostVideo, getUserImg } from '../api/api.js'
|
import { getPostList, getPostLImage, getPostVideo, getUserImg } from '../api/api.js'
|
||||||
|
// 引入翻译工具
|
||||||
|
import { translateZhToJa } from '@/utils/translate.js';
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
|
|
||||||
// 当前 dot
|
// 当前轮播图索引
|
||||||
const current = ref(0)
|
const current = ref(0)
|
||||||
const onChange = e => current.value = e.detail.current
|
const onChange = e => current.value = e.detail.current
|
||||||
|
|
||||||
// 动态数据
|
// 动态数据 - 初始化翻译相关字段
|
||||||
const post = ref({})
|
const post = ref({
|
||||||
|
translatedContent: '', // 内容翻译结果
|
||||||
|
translatedUserName: '' // 用户名翻译结果
|
||||||
|
});
|
||||||
|
|
||||||
// 播放状态
|
// 播放状态
|
||||||
const isPlaying = ref(true) // 默认播放状态
|
const isPlaying = ref(true)
|
||||||
// 资源加载状态
|
// 页面加载状态
|
||||||
const finishloading = ref(false)
|
const finishloading = ref(false)
|
||||||
|
|
||||||
// 图片加载成功处理
|
// 图片加载处理
|
||||||
const onImageLoad = (index) => {
|
const onImageLoad = (index) => {
|
||||||
if (post.value.imgs && post.value.imgs[index]) {
|
if (post.value.imgs && post.value.imgs[index]) {
|
||||||
post.value.imgs[index].loading = false
|
post.value.imgs[index].loading = false
|
||||||
@@ -107,7 +103,6 @@ const onImageLoad = (index) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图片加载失败处理
|
|
||||||
const onImageError = (index) => {
|
const onImageError = (index) => {
|
||||||
if (post.value.imgs && post.value.imgs[index]) {
|
if (post.value.imgs && post.value.imgs[index]) {
|
||||||
post.value.imgs[index].loading = false
|
post.value.imgs[index].loading = false
|
||||||
@@ -116,13 +111,10 @@ const onImageError = (index) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//暂停/播放
|
// 视频播放/暂停控制
|
||||||
const pausevideo = () => {
|
const pausevideo = () => {
|
||||||
// 获取VideoContext实例
|
|
||||||
const videoCtx = uni.createVideoContext('videoid', getCurrentInstance());
|
const videoCtx = uni.createVideoContext('videoid', getCurrentInstance());
|
||||||
// 检查视频状态并切换播放/暂停
|
|
||||||
if (videoCtx) {
|
if (videoCtx) {
|
||||||
// 使用一个状态变量来跟踪播放状态
|
|
||||||
if (isPlaying.value) {
|
if (isPlaying.value) {
|
||||||
videoCtx.pause()
|
videoCtx.pause()
|
||||||
isPlaying.value = false
|
isPlaying.value = false
|
||||||
@@ -133,16 +125,14 @@ const pausevideo = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组件挂载时获取数据并翻译
|
||||||
// 组件挂载时获取数据
|
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
const params = ''
|
const params = ''
|
||||||
getPostList(params).then(res => {
|
getPostList(params).then(async (res) => {
|
||||||
try {
|
try {
|
||||||
// console.log(res.data.data)
|
|
||||||
const data = res.data.data
|
const data = res.data.data
|
||||||
|
|
||||||
// 处理帖子相关字段 - 根据实际API返回字段优化
|
// 处理帖子相关字段
|
||||||
data.id = data.id || ''
|
data.id = data.id || ''
|
||||||
data.title = data.newsTitle || data.title || ''
|
data.title = data.newsTitle || data.title || ''
|
||||||
data.source = data.newsSource || data.source || ''
|
data.source = data.newsSource || data.source || ''
|
||||||
@@ -150,9 +140,17 @@ onLoad(() => {
|
|||||||
data.date = data.time || data.date || ''
|
data.date = data.time || data.date || ''
|
||||||
data.countLike = data.likeCount || data.countLike || 0
|
data.countLike = data.likeCount || data.countLike || 0
|
||||||
data.collectsum = data.collectsum || 0
|
data.collectsum = data.collectsum || 0
|
||||||
// data.user.nickName = data.user.nickName || ''
|
|
||||||
// data.user.userImg = data.user.avatar || data.userImg || ''
|
// 仅翻译后端返回的特定内容
|
||||||
// data.comments = data.comments || []
|
// 1. 翻译用户名
|
||||||
|
if (data.user && data.user.nickName) {
|
||||||
|
data.translatedUserName = await translateZhToJa(data.user.nickName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 翻译内容
|
||||||
|
if (data.copywriting) {
|
||||||
|
data.translatedContent = await translateZhToJa(data.copywriting);
|
||||||
|
}
|
||||||
|
|
||||||
const mediaPromises = []
|
const mediaPromises = []
|
||||||
|
|
||||||
@@ -161,15 +159,14 @@ onLoad(() => {
|
|||||||
data.imgs.forEach(image => {
|
data.imgs.forEach(image => {
|
||||||
mediaPromises.push(getPostLImage(image.original_url).then(imageRes => {
|
mediaPromises.push(getPostLImage(image.original_url).then(imageRes => {
|
||||||
if (imageRes.statusCode === 200 && imageRes.data) {
|
if (imageRes.statusCode === 200 && imageRes.data) {
|
||||||
// 将arraybuffer转换为base64
|
|
||||||
const base64 = uni.arrayBufferToBase64(imageRes.data)
|
const base64 = uni.arrayBufferToBase64(imageRes.data)
|
||||||
const imageUrl = 'data:image/webp;base64,' + base64
|
const imageUrl = 'data:image/webp;base64,' + base64
|
||||||
return {
|
return {
|
||||||
type: 'img',
|
type: 'img',
|
||||||
src: imageUrl,
|
src: imageUrl,
|
||||||
original_url: image.original_url,
|
original_url: image.original_url,
|
||||||
loading: true, // 初始加载状态
|
loading: true,
|
||||||
error: false // 错误状态
|
error: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -183,20 +180,19 @@ onLoad(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//处理视频资源
|
// 处理视频资源
|
||||||
if (data.videos && Array.isArray(data.videos)) {
|
if (data.videos && Array.isArray(data.videos)) {
|
||||||
data.videos.forEach(video => {
|
data.videos.forEach(video => {
|
||||||
mediaPromises.push(getPostVideo(video.original_url).then(videoRes => {
|
mediaPromises.push(getPostVideo(video.original_url).then(videoRes => {
|
||||||
if (videoRes.statusCode === 200 && videoRes.data) {
|
if (videoRes.statusCode === 200 && videoRes.data) {
|
||||||
// 将arraybuffer转换为base64
|
|
||||||
const base64 = uni.arrayBufferToBase64(videoRes.data)
|
const base64 = uni.arrayBufferToBase64(videoRes.data)
|
||||||
const videoUrl = 'data:video/mp4;base64,' + base64
|
const videoUrl = 'data:video/mp4;base64,' + base64
|
||||||
return {
|
return {
|
||||||
type: 'video',
|
type: 'video',
|
||||||
src: videoUrl,
|
src: videoUrl,
|
||||||
original_url: video.original_url,
|
original_url: video.original_url,
|
||||||
loading: true, // 初始加载状态
|
loading: true,
|
||||||
error: false // 错误状态
|
error: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -210,14 +206,12 @@ onLoad(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//处理用户信息
|
// 处理用户信息
|
||||||
if (data.user && data.user.constructor === Object) {
|
if (data.user && data.user.constructor === Object) {
|
||||||
// 如果有头像路径,则获取头像资源
|
|
||||||
const avatarPath = data.user.avatar || data.userImg
|
const avatarPath = data.user.avatar || data.userImg
|
||||||
if (avatarPath) {
|
if (avatarPath) {
|
||||||
mediaPromises.push(getUserImg(avatarPath).then(userImgRes => {
|
mediaPromises.push(getUserImg(avatarPath).then(userImgRes => {
|
||||||
if (userImgRes.statusCode === 200 && userImgRes.data) {
|
if (userImgRes.statusCode === 200 && userImgRes.data) {
|
||||||
// 将arraybuffer转换为base64
|
|
||||||
const base64 = uni.arrayBufferToBase64(userImgRes.data)
|
const base64 = uni.arrayBufferToBase64(userImgRes.data)
|
||||||
const userImgUrl = 'data:image/webp;base64,' + base64
|
const userImgUrl = 'data:image/webp;base64,' + base64
|
||||||
return {
|
return {
|
||||||
@@ -235,60 +229,51 @@ onLoad(() => {
|
|||||||
|
|
||||||
data.user = {
|
data.user = {
|
||||||
nickName: data.user.nickName || '',
|
nickName: data.user.nickName || '',
|
||||||
userImg: avatarPath || '' // 先使用原始路径,后续会替换为base64
|
userImg: avatarPath || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一等待所有媒体资源加载完成
|
// 等待所有媒体资源加载完成
|
||||||
if (mediaPromises.length > 0) {
|
if (mediaPromises.length > 0) {
|
||||||
Promise.all(mediaPromises).then(mediaItems => {
|
Promise.all(mediaPromises).then(mediaItems => {
|
||||||
// 过滤掉null值,将有效的媒体资源合并到imgs数组中
|
|
||||||
const validMediaItems = mediaItems.filter(item => item !== null)
|
const validMediaItems = mediaItems.filter(item => item !== null)
|
||||||
|
|
||||||
// 分离用户头像和其他媒体资源
|
|
||||||
const userImgItem = validMediaItems.find(item => item.type === 'userImg')
|
const userImgItem = validMediaItems.find(item => item.type === 'userImg')
|
||||||
const otherMediaItems = validMediaItems.filter(item => item.type !== 'userImg')
|
const otherMediaItems = validMediaItems.filter(item => item.type !== 'userImg')
|
||||||
|
|
||||||
// 如果有用户头像资源,更新用户头像
|
|
||||||
if (userImgItem) {
|
if (userImgItem) {
|
||||||
data.user.userImg = userImgItem.src
|
data.user.userImg = userImgItem.src
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置其他媒体资源
|
|
||||||
data.imgs = otherMediaItems
|
data.imgs = otherMediaItems
|
||||||
|
|
||||||
// post.value = data
|
|
||||||
console.log(data)
|
|
||||||
Object.assign(post.value, data)
|
Object.assign(post.value, data)
|
||||||
finishloading.value = true
|
finishloading.value = true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 如果没有媒体资源,设置空数组
|
|
||||||
data.imgs = []
|
data.imgs = []
|
||||||
post.value = data
|
post.value = data
|
||||||
|
finishloading.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError('数据处理错误:', error, '数据加载失败', '抱歉,新闻内容加载失败,请稍后重试')
|
handleError(error)
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
handleError('网络请求错误:', error, '网络错误', '网络连接异常,请检查网络设置')
|
handleError(error)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 统一的错误处理函数
|
// 错误处理函数
|
||||||
const handleError = (errorType, error, title, message) => {
|
const handleError = (error) => {
|
||||||
console.error(errorType, error)
|
console.error('数据处理错误:', error)
|
||||||
post.value = {
|
post.value = {
|
||||||
title: title,
|
user: { nickName: '不明ユーザー', userImg: '' },
|
||||||
source: '系统',
|
translatedUserName: '不明ユーザー',
|
||||||
copywriting: message,
|
copywriting: 'コンテンツを読み込めませんでした',
|
||||||
|
translatedContent: 'コンテンツを読み込めませんでした',
|
||||||
date: new Date().toISOString(),
|
date: new Date().toISOString(),
|
||||||
countLike: 0,
|
|
||||||
collectsum: 0,
|
|
||||||
comments: [],
|
|
||||||
imgs: []
|
imgs: []
|
||||||
}
|
}
|
||||||
|
finishloading.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -296,6 +281,7 @@ onLoad(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 保持原有样式不变 */
|
||||||
page,
|
page,
|
||||||
.page {
|
.page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -304,19 +290,16 @@ page,
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
/* position: relative; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
/* overflow: hidden; */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.moment {
|
.moment {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/* flex-shrink: 0; */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -345,7 +328,7 @@ page,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.follow {
|
.follow {
|
||||||
width: 60px;
|
width: 84px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -433,19 +416,6 @@ swiper-item {
|
|||||||
content: '\ea24';
|
content: '\ea24';
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-video-cover {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dots-bar {
|
.dots-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -498,4 +468,4 @@ swiper-item {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
<!-- <Findmore /> -->
|
<!-- <Findmore /> -->
|
||||||
|
|
||||||
<view v-else class="loading-container">
|
<view v-else class="loading-container">
|
||||||
<text>页面加载中...</text>
|
<text>ページ読み込み中...</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
@@ -101,6 +101,8 @@ import Head from '@/pages/head/head.vue'
|
|||||||
import Comments from '@/pages/comments/comments.vue'
|
import Comments from '@/pages/comments/comments.vue'
|
||||||
import Intereact from '@/pages/intereact/intereact.vue'
|
import Intereact from '@/pages/intereact/intereact.vue'
|
||||||
import { getPostList, getPostVideo, getUserImg } from '@/api/api.js'
|
import { getPostList, getPostVideo, getUserImg } from '@/api/api.js'
|
||||||
|
// 导入翻译工具
|
||||||
|
import { translateZhToJa, translationCache } from '@/utils/translate.js'
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
const formatCount = common.formatCount
|
const formatCount = common.formatCount
|
||||||
@@ -113,7 +115,6 @@ const finishloading = ref(false)
|
|||||||
|
|
||||||
// 折叠展开状态
|
// 折叠展开状态
|
||||||
const isExpanded = ref(false)
|
const isExpanded = ref(false)
|
||||||
// const videoRef = ref(null)
|
|
||||||
// 静音状态
|
// 静音状态
|
||||||
const isMuted = ref(false)
|
const isMuted = ref(false)
|
||||||
// 播放状态
|
// 播放状态
|
||||||
@@ -268,20 +269,28 @@ onLoad(() => {
|
|||||||
videoData.userImg = avatarResult.url
|
videoData.userImg = avatarResult.url
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新视频数据 - 沿用data对象
|
// 准备需要翻译的字段
|
||||||
data.userName = data.user?.nickName || '匿名用户'
|
const userName = data.user?.nickName || '匿名用户'
|
||||||
data.date = data.time || data.date || ''
|
const copywriting = data.textContent || ''
|
||||||
data.copywriting = data.textContent || ''
|
|
||||||
data.likesum = data.likeCount || 0
|
// 并行翻译需要翻译的内容
|
||||||
data.commentsum = data.commentCount || 0
|
Promise.all([
|
||||||
data.sharesum = data.shareCount || 0
|
translateZhToJa(userName),
|
||||||
// 标记资源加载完成
|
translateZhToJa(copywriting)
|
||||||
finishloading.value = true
|
]).then(([translatedUserName, translatedCopywriting]) => {
|
||||||
|
// 更新视频数据 - 使用翻译后的内容
|
||||||
|
data.userName = translatedUserName
|
||||||
|
data.date = data.time || data.date || ''
|
||||||
|
data.copywriting = translatedCopywriting
|
||||||
|
data.likesum = data.likeCount || 0
|
||||||
|
data.commentsum = data.commentCount || 0
|
||||||
|
data.sharesum = data.shareCount || 0
|
||||||
|
// 标记资源加载完成
|
||||||
|
finishloading.value = true
|
||||||
|
|
||||||
|
// 最后用data覆盖整个videoData
|
||||||
|
Object.assign(videoData, data)
|
||||||
// 最后用data覆盖整个videoData
|
})
|
||||||
Object.assign(videoData, data)
|
|
||||||
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('资源加载失败:', error)
|
console.error('资源加载失败:', error)
|
||||||
|
|||||||
119
src/utils/translate.js
Normal file
119
src/utils/translate.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// src/utils/translate.js
|
||||||
|
import md5 from 'md5';
|
||||||
|
|
||||||
|
// 你的APP ID和密钥
|
||||||
|
const APP_ID = '20251202002510803';
|
||||||
|
const API_KEY = 'xcSyTk2wV8mt57tg_RJm';
|
||||||
|
|
||||||
|
// 并发控制:百度免费版支持每秒2-3次,设置最大并发数为3
|
||||||
|
const MAX_CONCURRENT = 3;
|
||||||
|
let currentConcurrent = 0;
|
||||||
|
|
||||||
|
// 全局翻译缓存(跨组件共享,避免重复翻译)
|
||||||
|
export const translationCache = {};
|
||||||
|
|
||||||
|
// 敏感词列表(可根据实际情况扩展)
|
||||||
|
const SENSITIVE_WORDS = [
|
||||||
|
'大纪元', // 已确认的敏感词
|
||||||
|
// 可添加其他敏感词
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤文本中的敏感词
|
||||||
|
* @param {string} text 原始文本
|
||||||
|
* @returns {Object} { filteredText: 过滤后文本, hasSensitive: 是否包含敏感词, sensitiveWords: 包含的敏感词列表 }
|
||||||
|
*/
|
||||||
|
const filterSensitiveWords = (text) => {
|
||||||
|
if (!text) {
|
||||||
|
return { filteredText: '', hasSensitive: false, sensitiveWords: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteredText = text;
|
||||||
|
const foundSensitiveWords = [];
|
||||||
|
|
||||||
|
// 检测并替换敏感词
|
||||||
|
SENSITIVE_WORDS.forEach(word => {
|
||||||
|
const regex = new RegExp(word, 'gi');
|
||||||
|
if (regex.test(filteredText)) {
|
||||||
|
foundSensitiveWords.push(word);
|
||||||
|
// 用***替换敏感词
|
||||||
|
filteredText = filteredText.replace(regex, '***');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
filteredText,
|
||||||
|
hasSensitive: foundSensitiveWords.length > 0,
|
||||||
|
sensitiveWords: foundSensitiveWords
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带并发控制和敏感词屏蔽的翻译函数
|
||||||
|
* @param {string} text - 待翻译文本
|
||||||
|
* @returns {Promise<string>} 翻译结果(敏感词已被***替换)
|
||||||
|
*/
|
||||||
|
export const translateZhToJa = async (text) => {
|
||||||
|
// 空文本直接返回
|
||||||
|
if (!text || text.trim() === '') return text.trim();
|
||||||
|
|
||||||
|
// 优先使用缓存(全局缓存,同一文本只翻译一次)
|
||||||
|
if (translationCache[text]) {
|
||||||
|
return translationCache[text];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤敏感词
|
||||||
|
const { filteredText, hasSensitive, sensitiveWords } = filterSensitiveWords(text);
|
||||||
|
|
||||||
|
// 如果有敏感词,在控制台提示
|
||||||
|
if (hasSensitive) {
|
||||||
|
console.log(`检测到敏感词: ${sensitiveWords.join(', ')},已自动屏蔽`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 并发控制:等待当前并发数低于最大值
|
||||||
|
while (currentConcurrent >= MAX_CONCURRENT) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
currentConcurrent++;
|
||||||
|
const salt = Math.random().toString(36).substring(2, 10);
|
||||||
|
// 正确签名规则:APP_ID + 过滤后的文本 + salt + API_KEY
|
||||||
|
const sign = md5(APP_ID + filteredText + salt + API_KEY);
|
||||||
|
|
||||||
|
const url = `/baidu-translate/api/trans/vip/translate?` +
|
||||||
|
`q=${encodeURIComponent(filteredText)}&` +
|
||||||
|
`from=auto&` +
|
||||||
|
`to=jp&` +
|
||||||
|
`appid=${APP_ID}&` +
|
||||||
|
`salt=${salt}&` +
|
||||||
|
`sign=${sign}`;
|
||||||
|
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`请求失败,状态码: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.error_code) {
|
||||||
|
console.warn(`百度翻译API错误: ${result.error_code},信息: ${result.error_msg}`);
|
||||||
|
// 错误时返回过滤后的原文(包含***)
|
||||||
|
translationCache[text] = filteredText;
|
||||||
|
return filteredText;
|
||||||
|
}
|
||||||
|
|
||||||
|
const translatedText = result.trans_result?.[0]?.dst?.trim() || filteredText;
|
||||||
|
// 缓存翻译结果
|
||||||
|
translationCache[text] = translatedText;
|
||||||
|
return translatedText;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`【翻译失败】原文:${text},原因:`, error.message);
|
||||||
|
// 失败时返回过滤后的原文(包含***)
|
||||||
|
translationCache[text] = filteredText;
|
||||||
|
return filteredText;
|
||||||
|
} finally {
|
||||||
|
currentConcurrent--;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -8,9 +8,14 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://192.168.1.7:8088', // 请替换为你的实际后端服务器地址
|
target: 'http://192.168.1.111:58080', // 请替换为你的实际后端服务器地址
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, '/api')
|
rewrite: (path) => path.replace(/^\/api/, '/api')
|
||||||
|
},
|
||||||
|
'/baidu-translate': {
|
||||||
|
target: 'https://fanyi-api.baidu.com', // 百度翻译API的基础地址
|
||||||
|
changeOrigin: true, // 修改请求头中的Origin,模拟同源请求
|
||||||
|
rewrite: (path) => path.replace(/^\/baidu-translate/, '') // 移除代理前缀
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user