|
@@ -5,6 +5,8 @@ import type { ColumnsType } from "antd/es/table";
|
|
|
const API_BASE_URL =
|
|
const API_BASE_URL =
|
|
|
import.meta.env.VITE_API_BASE_URL ?? "/demand/api/v1";
|
|
import.meta.env.VITE_API_BASE_URL ?? "/demand/api/v1";
|
|
|
|
|
|
|
|
|
|
+const BODY_PREVIEW_LENGTH = 160;
|
|
|
|
|
+
|
|
|
const getResolvedApiBaseUrl = () => {
|
|
const getResolvedApiBaseUrl = () => {
|
|
|
if (API_BASE_URL.startsWith("http://") || API_BASE_URL.startsWith("https://")) {
|
|
if (API_BASE_URL.startsWith("http://") || API_BASE_URL.startsWith("https://")) {
|
|
|
return API_BASE_URL;
|
|
return API_BASE_URL;
|
|
@@ -154,6 +156,7 @@ export default function HotContentSourcePage({
|
|
|
const [loading, setLoading] = useState(true);
|
|
const [loading, setLoading] = useState(true);
|
|
|
const [error, setError] = useState("");
|
|
const [error, setError] = useState("");
|
|
|
const [detail, setDetail] = useState<HotContentSourceDetail | null>(null);
|
|
const [detail, setDetail] = useState<HotContentSourceDetail | null>(null);
|
|
|
|
|
+ const [bodyExpanded, setBodyExpanded] = useState(false);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
let cancelled = false;
|
|
let cancelled = false;
|
|
@@ -256,13 +259,22 @@ export default function HotContentSourcePage({
|
|
|
|
|
|
|
|
const record = detail?.record;
|
|
const record = detail?.record;
|
|
|
const syncLog = detail?.sync_log;
|
|
const syncLog = detail?.sync_log;
|
|
|
- const bodyPreview = useMemo(() => {
|
|
|
|
|
- const text = (record?.article_body ?? "").replace(/\s+/g, " ").trim();
|
|
|
|
|
- if (!text) {
|
|
|
|
|
- return "暂无正文";
|
|
|
|
|
- }
|
|
|
|
|
- return text.length > 160 ? `${text.slice(0, 160)}…` : text;
|
|
|
|
|
- }, [record?.article_body]);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const articleBodyText = useMemo(
|
|
|
|
|
+ () => (record?.article_body ?? "").replace(/\s+/g, " ").trim(),
|
|
|
|
|
+ [record?.article_body],
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ setBodyExpanded(false);
|
|
|
|
|
+ }, [record?.id]);
|
|
|
|
|
+
|
|
|
|
|
+ const bodyTruncated = articleBodyText.length > BODY_PREVIEW_LENGTH;
|
|
|
|
|
+ const displayBody = !articleBodyText
|
|
|
|
|
+ ? "暂无正文"
|
|
|
|
|
+ : bodyExpanded || !bodyTruncated
|
|
|
|
|
+ ? articleBodyText
|
|
|
|
|
+ : `${articleBodyText.slice(0, BODY_PREVIEW_LENGTH)}…`;
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div className="page hot-source-page">
|
|
<div className="page hot-source-page">
|
|
@@ -333,7 +345,25 @@ export default function HotContentSourcePage({
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div className="hot-source-record-subtitle">{record.article_title || "-"}</div>
|
|
<div className="hot-source-record-subtitle">{record.article_title || "-"}</div>
|
|
|
- <div className="hot-source-record-preview">{bodyPreview}</div>
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ className={
|
|
|
|
|
+ bodyExpanded
|
|
|
|
|
+ ? "hot-source-record-preview hot-source-record-preview--expanded"
|
|
|
|
|
+ : "hot-source-record-preview"
|
|
|
|
|
+ }
|
|
|
|
|
+ >
|
|
|
|
|
+ {displayBody}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {bodyTruncated ? (
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type="link"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ className="hot-source-body-toggle"
|
|
|
|
|
+ onClick={() => setBodyExpanded((value) => !value)}
|
|
|
|
|
+ >
|
|
|
|
|
+ {bodyExpanded ? "收起正文" : "展开全文"}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ ) : null}
|
|
|
<Typography.Title level={5} className="hot-source-section-label">
|
|
<Typography.Title level={5} className="hot-source-section-label">
|
|
|
贡献匹配摘要
|
|
贡献匹配摘要
|
|
|
</Typography.Title>
|
|
</Typography.Title>
|