markdown1
This commit is contained in:
101
.github/workflows/deploy.yml
vendored
101
.github/workflows/deploy.yml
vendored
@@ -1,101 +0,0 @@
|
|||||||
# name: Deploy to Server
|
|
||||||
|
|
||||||
# on:
|
|
||||||
# push:
|
|
||||||
# branches:
|
|
||||||
# - main
|
|
||||||
|
|
||||||
# jobs:
|
|
||||||
# build-and-deploy:
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# steps:
|
|
||||||
# - name: Checkout code
|
|
||||||
# uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# - name: Build Docker Images
|
|
||||||
# run: |
|
|
||||||
# echo "Building Backend Image..."
|
|
||||||
# docker build -t market-backend:latest ./backend
|
|
||||||
|
|
||||||
# echo "Building Frontend Image..."
|
|
||||||
# # 注意:这里我们传入了服务器的 IP 地址作为 API URL
|
|
||||||
# # 如果你的后端端口不是 8000,请修改这里
|
|
||||||
# docker build -t market-frontend:latest \
|
|
||||||
# --build-arg VITE_API_URL=http://47.101.218.42:8000/api \
|
|
||||||
# ./frontend
|
|
||||||
|
|
||||||
# - name: Save Docker Images
|
|
||||||
# run: |
|
|
||||||
# echo "Saving images to tarball..."
|
|
||||||
# docker save market-backend:latest market-frontend:latest | gzip > market-images.tar.gz
|
|
||||||
|
|
||||||
# - name: Generate Production Compose File
|
|
||||||
# run: |
|
|
||||||
# # 生成生产环境专用的 docker-compose.prod.yml
|
|
||||||
# # 1. 使用构建好的镜像 (image) 替代构建指令 (build)
|
|
||||||
# # 2. 移除代码挂载 (volumes),确保使用镜像内的代码
|
|
||||||
# cat > docker-compose.prod.yml <<EOF
|
|
||||||
# services:
|
|
||||||
# backend:
|
|
||||||
# image: market-backend:latest
|
|
||||||
# command: sh -c "python manage.py collectstatic --noinput && python manage.py migrate && gunicorn --bind 0.0.0.0:8000 --access-logfile - --error-logfile - config.wsgi:application"
|
|
||||||
# ports:
|
|
||||||
# - "8000:8000"
|
|
||||||
# environment:
|
|
||||||
# - DB_NAME=\${DB_NAME:-market}
|
|
||||||
# - DB_USER=\${DB_USER:-market}
|
|
||||||
# - DB_PASSWORD=\${DB_PASSWORD:-123market}
|
|
||||||
# - DB_HOST=\${DB_HOST:-6.6.6.66}
|
|
||||||
# - DB_PORT=\${DB_PORT:-5432}
|
|
||||||
# # 如果需要持久化媒体文件,请取消下面的注释并在服务器上创建相应目录
|
|
||||||
# # volumes:
|
|
||||||
# # - ./media:/app/media
|
|
||||||
|
|
||||||
# frontend:
|
|
||||||
# image: market-frontend:latest
|
|
||||||
# ports:
|
|
||||||
# - "15173:15173"
|
|
||||||
# environment:
|
|
||||||
# - VITE_API_URL=http://47.101.218.42:8000/api
|
|
||||||
# depends_on:
|
|
||||||
# - backend
|
|
||||||
# EOF
|
|
||||||
|
|
||||||
# - name: Ensure target directory exists
|
|
||||||
# uses: appleboy/ssh-action@v1.0.3
|
|
||||||
# with:
|
|
||||||
# host: 47.101.218.42
|
|
||||||
# username: ecs-user
|
|
||||||
# password: 123quant-speed
|
|
||||||
# script: mkdir -p ~/data/dev/market_page
|
|
||||||
|
|
||||||
# - name: Copy files to server
|
|
||||||
# uses: appleboy/scp-action@v0.1.7
|
|
||||||
# with:
|
|
||||||
# host: 47.101.218.42
|
|
||||||
# username: ecs-user
|
|
||||||
# password: 123quant-speed
|
|
||||||
# source: "market-images.tar.gz,docker-compose.prod.yml"
|
|
||||||
# target: "~/data/dev/market_page"
|
|
||||||
|
|
||||||
# - name: Deploy on Server
|
|
||||||
# uses: appleboy/ssh-action@v1.0.3
|
|
||||||
# with:
|
|
||||||
# host: 47.101.218.42
|
|
||||||
# username: ecs-user
|
|
||||||
# password: 123quant-speed
|
|
||||||
# script: |
|
|
||||||
# cd ~/data/dev/market_page
|
|
||||||
|
|
||||||
# echo "1. Loading Docker images (this may take a while)..."
|
|
||||||
# gunzip -c market-images.tar.gz | sudo docker load
|
|
||||||
|
|
||||||
# echo "2. Restarting services..."
|
|
||||||
# # 使用 -f 指定生产环境配置文件
|
|
||||||
# echo "123quant-speed" | sudo -S docker compose -f docker-compose.prod.yml down
|
|
||||||
# echo "123quant-speed" | sudo -S docker compose -f docker-compose.prod.yml up -d
|
|
||||||
|
|
||||||
# echo "3. Cleaning up..."
|
|
||||||
# rm market-images.tar.gz
|
|
||||||
|
|
||||||
# echo "Deployment successful!"
|
|
||||||
@@ -342,3 +342,70 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Markdown Table Styles */
|
||||||
|
.richText table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 16px 0;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.richText th,
|
||||||
|
.richText td {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid #434343;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.richText th {
|
||||||
|
background-color: #1f1f1f;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.richText tr:nth-child(even) {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.richText tr:hover {
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code Block Styles */
|
||||||
|
.codeBlockWrapper {
|
||||||
|
position: relative;
|
||||||
|
margin: 16px 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover .copyButton {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
z-index: 10;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
opacity: 0; /* Hidden by default, shown on hover */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { motion, useScroll, useTransform } from 'framer-motion';
|
import { motion, useScroll, useTransform } from 'framer-motion';
|
||||||
import { ArrowLeftOutlined, ShareAltOutlined, CalendarOutlined, ClockCircleOutlined, EnvironmentOutlined, UserOutlined, UploadOutlined, PayCircleOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, ShareAltOutlined, CalendarOutlined, ClockCircleOutlined, EnvironmentOutlined, UserOutlined, UploadOutlined, PayCircleOutlined, CopyOutlined, CheckOutlined } from '@ant-design/icons';
|
||||||
import confetti from 'canvas-confetti';
|
import confetti from 'canvas-confetti';
|
||||||
import { message, Spin, Button, Result, Modal, Form, Input, Select, Radio, Checkbox, Upload } from 'antd';
|
import { message, Spin, Button, Result, Modal, Form, Input, Select, Radio, Checkbox, Upload } from 'antd';
|
||||||
import { getActivityDetail, signUpActivity, queryOrderStatus } from '../../api';
|
import { getActivityDetail, signUpActivity, queryOrderStatus } from '../../api';
|
||||||
@@ -18,6 +18,43 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|||||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
|
|
||||||
|
const CodeBlock = ({ inline, className, children, ...props }) => {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
|
const codeString = String(children).replace(/\n$/, '');
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard.writeText(codeString);
|
||||||
|
setCopied(true);
|
||||||
|
message.success('代码已复制');
|
||||||
|
setTimeout(() => setCopied(false), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return !inline && match ? (
|
||||||
|
<div className={styles.codeBlockWrapper}>
|
||||||
|
<div
|
||||||
|
className={styles.copyButton}
|
||||||
|
onClick={handleCopy}
|
||||||
|
>
|
||||||
|
{copied ? <CheckOutlined /> : <CopyOutlined />}
|
||||||
|
<span style={{ marginLeft: 4 }}>{copied ? '已复制' : '复制'}</span>
|
||||||
|
</div>
|
||||||
|
<SyntaxHighlighter
|
||||||
|
style={vscDarkPlus}
|
||||||
|
language={match[1]}
|
||||||
|
PreTag="div"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{codeString}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<code className={className} {...props}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const ActivityDetail = () => {
|
const ActivityDetail = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -410,23 +447,7 @@ const ActivityDetail = () => {
|
|||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
rehypePlugins={[rehypeRaw]}
|
rehypePlugins={[rehypeRaw]}
|
||||||
components={{
|
components={{
|
||||||
code({inline, className, children, ...props}) {
|
code: CodeBlock
|
||||||
const match = /language-(\w+)/.exec(className || '')
|
|
||||||
return !inline && match ? (
|
|
||||||
<SyntaxHighlighter
|
|
||||||
style={vscDarkPlus}
|
|
||||||
language={match[1]}
|
|
||||||
PreTag="div"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{String(children).replace(/\n$/, '')}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
) : (
|
|
||||||
<code className={className} {...props}>
|
|
||||||
{children}
|
|
||||||
</code>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{activity.description || activity.content || '暂无详情描述'}
|
{activity.description || activity.content || '暂无详情描述'}
|
||||||
|
|||||||
Reference in New Issue
Block a user