Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Dependencies
node_modules

# Build outputs
build
dist

# Configuration files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
jspm_packages

# TypeScript v1 declaration files
typings

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn
.yarn-integrity
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Docker
Dockerfile*
docker-compose*
.dockerignore

# IDEs
.vscode
.idea
*.swp
*.swo

# OS generated files
.DS_Store
Thumbs.db

# Testing
coverage/
.nyc_output/

# Documentation
docs/
33 changes: 33 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Multi-stage Dockerfile for React application
# Stage 1: Build the application
FROM node:23-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Stage 2: Serve the application
FROM nginx:alpine

# Copy built application from builder stage to nginx static directory
COPY --from=builder /app/build /usr/share/nginx/html

# Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Expose port 80
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"]
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3.8'

services:
app:
build: .
ports:
- "80:80"
restart: unless-stopped
52 changes: 52 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Log format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;

# Server block
server {
listen 80;
server_name localhost;

# Serve static files directly
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
}
9 changes: 5 additions & 4 deletions src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Button, message, Tooltip } from 'antd';
import { Button, ButtonProps, message, Tooltip } from 'antd';
import React, { useState } from 'react';

import { CheckOutlined, CopyOutlined } from '@ant-design/icons';

interface CopyButtonProps {
value: string | number | bigint | undefined;
interface CopyButtonProps extends Omit<ButtonProps, 'value' | 'onClick'> {
value: string | number | bigint | readonly string[] | undefined;
}

const clipboard = (window.isSecureContext && navigator.clipboard) || {
Expand All @@ -25,7 +25,7 @@ const clipboard = (window.isSecureContext && navigator.clipboard) || {
}),
};

const CopyButton: React.FC<CopyButtonProps> = ({ value }) => {
const CopyButton: React.FC<CopyButtonProps> = ({ value, ...props }) => {
const [copied, setCopied] = useState(false);

function onClick() {
Expand Down Expand Up @@ -55,6 +55,7 @@ const CopyButton: React.FC<CopyButtonProps> = ({ value }) => {
style={{ border: 'none' }}
icon={copied ? <CheckOutlined /> : <CopyOutlined />}
onClick={onClick}
{...props}
/>
</Tooltip>
</>
Expand Down
59 changes: 43 additions & 16 deletions src/components/tools/JsonTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import React, { useState, useRef, useEffect } from 'react';
import { Button, Flex, Input, message, Select, Switch, Tabs, InputRef } from 'antd';
import { Splitter } from 'antd';
import ReactJson, { InteractionProps, OnSelectProps, ThemeKeys } from 'react-json-view';
import {
DownloadOutlined,
SortAscendingOutlined,
} from '@ant-design/icons';

interface JsonViewerProps {
initialData?: object;
Expand Down Expand Up @@ -64,15 +68,15 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
useEffect(() => {
if (!initialData) {
const sampleData = {
name: "Vyckey",
email: "vyckey@qq.com",
avatar: "https://vyckey.github.io/avatar.jpg",
website: "https://vyckey.github.io",
name: 'Vyckey',
email: 'vyckey@qq.com',
avatar: 'https://vyckey.github.io/avatar.jpg',
website: 'https://vyckey.github.io',
address: {
city: "Shanghai",
country: "China"
city: 'Shanghai',
country: 'China'
},
hobbies: ["reading", "coding"],
hobbies: ['reading', 'coding'],
isActive: true
};
setJsonValue(sampleData);
Expand Down Expand Up @@ -217,9 +221,29 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
// Handle selection if needed
};

const handleExport = () => {
try {
const obj = JSON.parse(textValue);
const blob = new Blob([JSON.stringify(obj, null, 2)], {
type: 'application/json',
});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'data.json';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} catch (e: unknown) {
const errorMessage = e instanceof Error ? e.message : String(e);
message.error(`JSON 格式错误: ${errorMessage}`);
}
};

return (
<>
<Flex gap="small" wrap align="center">
<Flex gap='small' wrap align='center'>
<Select
showSearch={true}
defaultValue={theme}
Expand All @@ -240,7 +264,7 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
onChange={(val: number) => handleFormat(val)}
/>
<Button onClick={handleCompress}>压缩</Button>
<Button type="primary" onClick={handleParse}>解析</Button>
<Button type='primary' onClick={handleParse}>解析</Button>
<Button onClick={handleStringify}>反解析</Button>
<Select
defaultValue={indentWidth}
Expand All @@ -250,9 +274,6 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
}))}
onChange={(val: number) => setIndentWidth(val)}
/>
<Button onClick={() => handleSortKeys(!isSortKeys)}>
{isSortKeys ? '取消' : ''}排序
</Button>
<Select
defaultValue={collapsed}
value={collapsed}
Expand All @@ -269,17 +290,23 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
onChange={(val: boolean | number) => setCollapsed(val)}
style={{ width: 120 }}
/>
<Button
icon={<SortAscendingOutlined />}
onClick={() => handleSortKeys(!isSortKeys)}>
{isSortKeys ? '取消' : ''}排序
</Button>
<div style={{ display: 'flex', alignItems: 'center' }}>
<span style={{ marginRight: 8 }}>编辑</span>
<Switch
checked={enableEditing}
onChange={setEnableEditing}
/>
</div>
<Button icon={<DownloadOutlined />} onClick={handleExport} >导出文件</Button>
</Flex>
<Splitter
style={{ minHeight: 600, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)', marginTop: 16 }}>
<Splitter.Panel defaultSize="40%" min="20%" max="70%">
<Splitter.Panel defaultSize='40%' min='20%' max='70%'>
<div style={{ display: 'flex', height: '100%' }}>
{/* Line numbers */}
<div
Expand All @@ -304,7 +331,7 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
<Input.TextArea
ref={textAreaRef}
value={textValue}
placeholder="请输入JSON文本"
placeholder='请输入JSON文本'
style={{
whiteSpace: 'nowrap',
overflowX: 'auto',
Expand All @@ -327,7 +354,7 @@ const JsonViewer: React.FC<JsonViewerProps> = ({ initialData }) => {
indentWidth={indentWidth}
sortKeys={isSortKeys}
collapsed={collapsed}
iconStyle="square"
iconStyle='square'
displayDataTypes={false}
style={{ textAlign: 'left', height: '100%' }}
onEdit={handleEdit}
Expand All @@ -347,7 +374,7 @@ export default function JsonTools() {
return (
<>
<Tabs
type="card"
type='card'
items={[
{
key: 'json_view',
Expand Down
Loading
Loading