Browse Source

fix(frontend): 文章详情页封面图和正文图片拼接 API_BASE_URL,修复未登录时相对路径图片加载失败

- coverImage 通过 getImageUrl 拼接绝对 URL
- content 富文本内 /uploads/ 相对图片路径替换为完整 URL
- 后端 FileStorageService 存储相对路径,前端渲染时需拼接 baseURL
Xiaogang Liao 3 months ago
parent
commit
dd83d73c3c
1 changed files with 15 additions and 0 deletions
  1. 15 0
      cfc-frontend/pages/article-center/article-detail.vue

+ 15 - 0
cfc-frontend/pages/article-center/article-detail.vue

@@ -82,6 +82,7 @@
 import { getArticleDetail, reportReadingTime, getShareQrCode, completeArticleRead, generateQuiz, submitQuiz } from '@/utils/api.js'
 import { getArticleDetail, reportReadingTime, getShareQrCode, completeArticleRead, generateQuiz, submitQuiz } from '@/utils/api.js'
 import shareMixin from '../../components/share-mixin.js'
 import shareMixin from '../../components/share-mixin.js'
 import ContentSharePoster from '@/components/ContentSharePoster.vue'
 import ContentSharePoster from '@/components/ContentSharePoster.vue'
+import config from '@/config.js'
 
 
 export default {
 export default {
   mixins: [shareMixin],
   mixins: [shareMixin],
@@ -116,6 +117,8 @@ export default {
         var res = await getArticleDetail({ id: id })
         var res = await getArticleDetail({ id: id })
         if (res.code === 200 && res.data) {
         if (res.code === 200 && res.data) {
           this.article = res.data
           this.article = res.data
+          this.article.coverImage = this.getImageUrl(this.article.coverImage)
+          this.article.content = this.resolveContentUrls(this.article.content)
           this.setShareInfo('推荐阅读: ' + (res.data.title || ''), '/pages/article-center/article-detail?id=' + id)
           this.setShareInfo('推荐阅读: ' + (res.data.title || ''), '/pages/article-center/article-detail?id=' + id)
           this.startReadingTimer()
           this.startReadingTimer()
         } else { this.error = true; this.errorMsg = '文章不存在或无权限查看' }
         } else { this.error = true; this.errorMsg = '文章不存在或无权限查看' }
@@ -123,6 +126,18 @@ export default {
       finally { this.loading = false }
       finally { this.loading = false }
     },
     },
     formatDate(d) { return d ? d.slice(0, 10) : '' },
     formatDate(d) { return d ? d.slice(0, 10) : '' },
+    getImageUrl: function(path) {
+      if (!path) return ''
+      if (path.indexOf('http://') === 0 || path.indexOf('https://') === 0) return path
+      return config.API_BASE_URL + path
+    },
+    resolveContentUrls: function(html) {
+      if (!html) return html
+      // 将富文本内容中的相对图片路径(/uploads/...)拼接为绝对 URL
+      return html.replace(/(src|href)=["']\/(uploads\/[^"']+)/g, function(match, attr, path) {
+        return attr + '="' + config.API_BASE_URL + '/' + path + '"'
+      })
+    },
     parseDimensions: function(article) {
     parseDimensions: function(article) {
       var dimMap = { body: { code: 'body', color: '#FF8C42', name: '身' }, mind: { code: 'mind', color: '#FF6B9D', name: '心' }, wisdom: { code: 'wisdom', color: '#6366F1', name: '智' }, action: { code: 'action', color: '#10B981', name: '行' }, wealth: { code: 'wealth', color: '#F59E0B', name: '富' } }
       var dimMap = { body: { code: 'body', color: '#FF8C42', name: '身' }, mind: { code: 'mind', color: '#FF6B9D', name: '心' }, wisdom: { code: 'wisdom', color: '#6366F1', name: '智' }, action: { code: 'action', color: '#10B981', name: '行' }, wealth: { code: 'wealth', color: '#F59E0B', name: '富' } }
       var raw = article && article.relatedDimensions ? article.relatedDimensions.split(',').map(function(s) { return s.trim().toLowerCase() }) : []
       var raw = article && article.relatedDimensions ? article.relatedDimensions.split(',').map(function(s) { return s.trim().toLowerCase() }) : []