Просмотр исходного кода

📝 feat(SettingsAnnouncements/SettingsFAQ): update placeholders & success texts, add Markdown/HTML hint

• SettingsAnnouncements.js
  – Placeholder now states “Supports Markdown/HTML” for both small & expanded editors
  – Success/alert messages unified to use Chinese quotation marks

• SettingsFAQ.js
  – Answer textarea placeholder updated with Markdown/HTML support note
  – Unified success/alert messages punctuation

These tweaks clarify rich-text support and keep UI copy consistent.
t0ng7u 8 месяцев назад
Родитель
Сommit
ea379e1d0e

+ 25 - 19
web/src/components/layout/NoticeModal.js

@@ -113,25 +113,31 @@ const NoticeModal = ({ visible, onClose, isMobile, defaultTab = 'inApp', unreadK
     return (
       <div className="max-h-[55vh] overflow-y-auto pr-2 card-content-scroll">
         <Timeline mode="alternate">
-          {processedAnnouncements.map((item, idx) => (
-            <Timeline.Item
-              key={idx}
-              type={item.type}
-              time={item.time}
-              className={item.isUnread ? '' : ''}
-            >
-              <div>
-                {item.isUnread ? (
-                  <span className="shine-text">
-                    {item.content}
-                  </span>
-                ) : (
-                  item.content
-                )}
-                {item.extra && <div className="text-xs text-gray-500">{item.extra}</div>}
-              </div>
-            </Timeline.Item>
-          ))}
+          {processedAnnouncements.map((item, idx) => {
+            const htmlContent = marked.parse(item.content || '');
+            const htmlExtra = item.extra ? marked.parse(item.extra) : '';
+            return (
+              <Timeline.Item
+                key={idx}
+                type={item.type}
+                time={item.time}
+                className={item.isUnread ? '' : ''}
+              >
+                <div>
+                  <div
+                    className={item.isUnread ? 'shine-text' : ''}
+                    dangerouslySetInnerHTML={{ __html: htmlContent }}
+                  />
+                  {item.extra && (
+                    <div
+                      className="text-xs text-gray-500"
+                      dangerouslySetInnerHTML={{ __html: htmlExtra }}
+                    />
+                  )}
+                </div>
+              </Timeline.Item>
+            );
+          })}
         </Timeline>
       </div>
     );

+ 2 - 0
web/src/i18n/locales/en.json

@@ -1615,6 +1615,7 @@
   "编辑公告": "Edit Notice",
   "公告内容": "Notice Content",
   "请输入公告内容": "Please enter the notice content",
+  "请输入公告内容(支持 Markdown/HTML)": "Please enter the notice content (supports Markdown/HTML)",
   "发布日期": "Publish Date",
   "请选择发布日期": "Please select the publish date",
   "发布时间": "Publish Time",
@@ -1630,6 +1631,7 @@
   "请输入问题标题": "Please enter the question title",
   "回答内容": "Answer Content",
   "请输入回答内容": "Please enter the answer content",
+  "请输入回答内容(支持 Markdown/HTML)": "Please enter the answer content (supports Markdown/HTML)",
   "确定要删除此问答吗?": "Are you sure you want to delete this FAQ?",
   "系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)": "System notice management, you can publish system notices and important messages (maximum 100, display latest 20 on the front end)",
   "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "FAQ management, providing answers to common questions for users (maximum 50, display latest 20 on the front end)",

+ 25 - 5
web/src/pages/Detail/index.js

@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useRef, useState, useMemo, useCallback }
 import { initVChartSemiTheme } from '@visactor/vchart-semi-theme';
 import { useNavigate } from 'react-router-dom';
 import { Wallet, Activity, Zap, Gauge, PieChart, Server, Bell, HelpCircle } from 'lucide-react';
+import { marked } from 'marked';
 
 import {
   Card,
@@ -1267,10 +1268,27 @@ const Detail = (props) => {
                       onScroll={() => handleCardScroll(announcementScrollRef, setShowAnnouncementScrollHint)}
                     >
                       {announcementData.length > 0 ? (
-                        <Timeline
-                          mode="alternate"
-                          dataSource={announcementData}
-                        />
+                        <Timeline mode="alternate">
+                          {announcementData.map((item, idx) => (
+                            <Timeline.Item
+                              key={idx}
+                              type={item.type || 'default'}
+                              time={item.time}
+                            >
+                              <div>
+                                <div
+                                  dangerouslySetInnerHTML={{ __html: marked.parse(item.content || '') }}
+                                />
+                                {item.extra && (
+                                  <div
+                                    className="text-xs text-gray-500"
+                                    dangerouslySetInnerHTML={{ __html: marked.parse(item.extra) }}
+                                  />
+                                )}
+                              </div>
+                            </Timeline.Item>
+                          ))}
+                        </Timeline>
                       ) : (
                         <div className="flex justify-center items-center py-8">
                           <Empty
@@ -1321,7 +1339,9 @@ const Detail = (props) => {
                               header={item.question}
                               itemKey={index.toString()}
                             >
-                              <p>{item.answer}</p>
+                              <div
+                                dangerouslySetInnerHTML={{ __html: marked.parse(item.answer || '') }}
+                              />
                             </Collapse.Panel>
                           ))}
                         </Collapse>

+ 2 - 2
web/src/pages/Setting/Dashboard/SettingsAnnouncements.js

@@ -494,7 +494,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
           <Form.TextArea
             field='content'
             label={t('公告内容')}
-            placeholder={t('请输入公告内容')}
+            placeholder={t('请输入公告内容(支持 Markdown/HTML)')}
             maxCount={500}
             rows={3}
             rules={[{ required: true, message: t('请输入公告内容') }]}
@@ -571,7 +571,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
       >
         <TextArea
           value={announcementForm.content}
-          placeholder={t('请输入公告内容')}
+          placeholder={t('请输入公告内容(支持 Markdown/HTML)')}
           maxCount={500}
           rows={15}
           style={{ width: '100%' }}

+ 1 - 1
web/src/pages/Setting/Dashboard/SettingsFAQ.js

@@ -424,7 +424,7 @@ const SettingsFAQ = ({ options, refresh }) => {
           <Form.TextArea
             field='answer'
             label={t('回答内容')}
-            placeholder={t('请输入回答内容')}
+            placeholder={t('请输入回答内容(支持 Markdown/HTML)')}
             maxCount={1000}
             rows={6}
             rules={[{ required: true, message: t('请输入回答内容') }]}