This commit is contained in:
@@ -5,6 +5,14 @@ import { ArrowLeftOutlined, ClockCircleOutlined, UserOutlined, BookOutlined, For
|
||||
import { getVCCourseDetail, createOrder, nativePay, queryOrderStatus } from '../api';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkMath from 'remark-math';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import styles from './VCCourseDetail.module.less';
|
||||
import CodeBlock from '../components/CodeBlock';
|
||||
|
||||
const { Title, Paragraph } = Typography;
|
||||
|
||||
@@ -28,6 +36,34 @@ const VCCourseDetail = () => {
|
||||
// 优先从 URL 获取,如果没有则从 localStorage 获取
|
||||
const refCode = searchParams.get('ref') || localStorage.getItem('ref_code');
|
||||
|
||||
const markdownComponents = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
code({node, inline, className, children, ...props}) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return !inline && match ? (
|
||||
<CodeBlock
|
||||
language={match[1]}
|
||||
{...props}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</CodeBlock>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
img({node, ...props}) {
|
||||
return (
|
||||
<img
|
||||
{...props}
|
||||
style={{ maxHeight: 400, borderRadius: 8, maxWidth: '100%', margin: '10px 0' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDetail = async () => {
|
||||
try {
|
||||
@@ -262,8 +298,14 @@ const VCCourseDetail = () => {
|
||||
{course.content && (
|
||||
<div style={{ marginTop: 40 }}>
|
||||
<Title level={3} style={{ color: '#fff', marginBottom: 20 }}>课程大纲与详情</Title>
|
||||
<div style={{ color: '#ccc', lineHeight: '1.8', fontSize: '16px', whiteSpace: 'pre-line' }}>
|
||||
{course.content}
|
||||
<div className={styles['markdown-body']}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkMath, remarkGfm]}
|
||||
rehypePlugins={[rehypeKatex, rehypeRaw]}
|
||||
components={markdownComponents}
|
||||
>
|
||||
{course.content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
109
frontend/src/pages/VCCourseDetail.module.less
Normal file
109
frontend/src/pages/VCCourseDetail.module.less
Normal file
@@ -0,0 +1,109 @@
|
||||
.markdown-body {
|
||||
color: #ddd;
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #fff;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h1 { font-size: 2em; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 0.3em; }
|
||||
h2 { font-size: 1.5em; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 0.3em; }
|
||||
h3 { font-size: 1.25em; }
|
||||
h4 { font-size: 1em; }
|
||||
h5 { font-size: 0.875em; }
|
||||
h6 { font-size: 0.85em; color: #888; }
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
li {
|
||||
word-wrap: break-all;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 16px;
|
||||
padding: 0 1em;
|
||||
color: #8b949e;
|
||||
border-left: 0.25em solid #30363d;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
|
||||
thead {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color: transparent;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
&:nth-child(2n) {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
/* Ensure text color is readable */
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
td {
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
/* Inline Code */
|
||||
code:not([class*="language-"]) {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(110, 118, 129, 0.4);
|
||||
border-radius: 6px;
|
||||
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
|
||||
}
|
||||
|
||||
/* Images */
|
||||
img {
|
||||
max-width: 100%;
|
||||
box-sizing: content-box;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ const VCCourses = () => {
|
||||
VC <span style={{ color: '#00f0ff' }}>CODING COURSES</span>
|
||||
</Title>
|
||||
<Paragraph style={{ color: '#aaa', fontSize: 18, maxWidth: 600, margin: '0 auto' }}>
|
||||
探索 VB Coding 软件与硬件课程,开启您的编程之旅。
|
||||
探索 VC Coding 软件与硬件课程,开启您的编程之旅。
|
||||
</Paragraph>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user