Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b9c7d3e0d | |||
| b2fea5df8e | |||
| a447bffa77 | |||
| 4d009a0195 | |||
| 7a0a74bfea | |||
| fd537af916 | |||
| e7f4fc3374 | |||
|
92703d4985
|
|||
| 0113ece426 | |||
| 37adfddf3f |
@@ -2,13 +2,13 @@ name: Upload Release Assets
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created] # 仅当创建 Release 之后触发
|
types: [created] # Trigger only after a Release is created
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-upload:
|
build-and-upload:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# 必须授予权限以允许 Action 修改 Release
|
# Must grant permission to allow the Action to modify the Release
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
@@ -16,15 +16,26 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Create archive
|
- name: Setup Node
|
||||||
run: |
|
uses: actions/setup-node@v4
|
||||||
TAG_NAME=${{ github.event.release.tag_name }}
|
with:
|
||||||
tar -czvf "website-dist-${TAG_NAME}.tar.gz" --exclude=".git*" --exclude=".github*" .
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build release archive
|
||||||
|
run: pnpm build:tar
|
||||||
|
|
||||||
- name: Upload Release Asset
|
- name: Upload Release Asset
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
# 上传刚才生成的 tar.gz 文件
|
files: dist.tar.gz
|
||||||
files: dist-${{ github.event.release.tag_name }}.tar.gz
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
Vendored
+18
-21
@@ -1,22 +1,19 @@
|
|||||||
{
|
{
|
||||||
// Use IntelliSense to learn about possible attributes.
|
"version": "0.2.0",
|
||||||
// Hover to view descriptions of existing attributes.
|
"configurations": [
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
{
|
||||||
"version": "0.2.0",
|
"type": "node-terminal",
|
||||||
"configurations": [
|
"request": "launch",
|
||||||
{
|
"name": "pnpm dev",
|
||||||
"type": "node-terminal",
|
"command": "pnpm dev",
|
||||||
"request": "launch",
|
"cwd": "${workspaceFolder}"
|
||||||
"name": "pnpm dev",
|
},
|
||||||
"command": "pnpm dev",
|
{
|
||||||
"cwd": "${workspaceFolder}"
|
"type": "node-terminal",
|
||||||
},
|
"request": "launch",
|
||||||
{
|
"name": "build",
|
||||||
"type": "node-terminal",
|
"command": "pnpm build",
|
||||||
"request": "launch",
|
"cwd": "${workspaceFolder}"
|
||||||
"name": "build",
|
}
|
||||||
"command": "pnpm build",
|
]
|
||||||
"cwd": "${workspaceFolder}"
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install # Install dependencies (pnpm required)
|
||||||
|
pnpm dev # Start Vite dev server
|
||||||
|
pnpm build # TypeScript check + Vite production build
|
||||||
|
pnpm build:tar # Build + tar.gz archive (used by CI)
|
||||||
|
pnpm preview # Preview production build locally
|
||||||
|
pnpm lint # ESLint
|
||||||
|
```
|
||||||
|
|
||||||
|
No test suite exists in this project.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
This is a Chinese-language SPA for browsing and managing Delta Force guide ("《三角洲》指南"). The frontend talks to a Spring Boot backend via REST APIs.
|
||||||
|
|
||||||
|
**App shell** (`src/main.tsx`): React 19 + React Router 7 + Redux Toolkit + Ant Design 6 + Tailwind CSS 4. Wraps the router in Redux `Provider` → `PersistGate` (Redux Persist) → Ant Design `StyleProvider`/`ConfigProvider` (locale `zh_CN`).
|
||||||
|
|
||||||
|
**Routing** (`src/router/index.tsx`): Two layout groups:
|
||||||
|
- `HeroLayout` (nav header + footer) for `/`, `/firearms`, `/mod-codes`
|
||||||
|
- `EmptyLayout` (minimal) for `/login`
|
||||||
|
All page components are lazy-loaded via `createBrowserRouter` + `lazy()`.
|
||||||
|
|
||||||
|
**State** (`src/store/`): Redux Toolkit with two slices — `auth` (current user) and `firearms` (paginated firearm list). State is persisted to `localStorage` or `sessionStorage` based on the `VITE_REDUX_STORAGE` env var. Use typed hooks from `src/hooks/store.ts` (`useAppDispatch`, `useAppSelector`).
|
||||||
|
|
||||||
|
**API layer** (`src/api/`): Axios instance (`src/shared/web-client/`) with base URL from `VITE_API_BASE_URL`, 10s timeout, and credentials. API modules: `FirearmApi`, `ModificationApi`, `TagApi`, `AuthApi`.
|
||||||
|
|
||||||
|
**Pages**:
|
||||||
|
- `FirearmsPage` — paginated card grid with type filter, create/edit modals (admin-only), delete with popconfirm
|
||||||
|
- `ModCodesPage` — paginated list with tag multi-select and firearmId query param filter, create/edit modals with nested accessory/tuning form lists
|
||||||
|
- `LoginPage` — simple username/password form, dispatches `setCurrentUser` on success
|
||||||
|
|
||||||
|
**Shared form components**: `FirearmForm` and `ModificationForm` are reused by both create and edit modals. `ModificationForm` fetches all firearms for its weapon selector and supports a `lockFirearmId` prop that disables the selector (used when navigating from a specific firearm).
|
||||||
|
|
||||||
|
**Type system** (`src/types/`): `Firearm` with weapon stats, `Modification` with nested `Accessory[]` → `Tuning[]`, `Page<T>` for paginated API responses, `User` for auth.
|
||||||
|
|
||||||
|
**Vite config**: Alias `@` → `./src`. Plugins: React, Tailwind CSS 4, port checker. Build uses rolldown with manual chunk splitting for React, Redux, Ant Design, React Router, and rc-* packages.
|
||||||
|
|
||||||
|
**Styling**: Tailwind CSS 4 with CSS layers (`theme, base, antd, components, utilities`). Responsive grid for mod code cards (1→2→3→4 columns). Prettier: 100 print width, no semicolons, double quotes, trailing commas ES5.
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
```
|
||||||
|
VITE_API_BASE_URL=/api # Backend API base URL
|
||||||
|
VITE_REDUX_STORAGE=local # "local" or "session" for Redux persistence
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing conventions
|
||||||
|
|
||||||
|
- User-facing copy, documentation, and code comments in British English
|
||||||
|
- Commit messages use `chore:` prefix for dependency updates (per Dependabot config)
|
||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/onixbyte.svg" />
|
<link rel="icon" type="image/svg+xml" href="/onixbyte.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>三角洲行动改枪码库</title>
|
<title>《三角洲》指南</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useDispatch, useSelector } from "react-redux"
|
import { useDispatch, useSelector } from "react-redux"
|
||||||
import type { AppDispatch, RootState } from "./index"
|
import type { AppDispatch, RootState } from "@/store"
|
||||||
|
|
||||||
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
|
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
|
||||||
export const useAppSelector = useSelector.withTypes<RootState>()
|
export const useAppSelector = useSelector.withTypes<RootState>()
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
LoginOutlined,
|
LoginOutlined,
|
||||||
} from "@ant-design/icons"
|
} from "@ant-design/icons"
|
||||||
import { AuthApi } from "@/api"
|
import { AuthApi } from "@/api"
|
||||||
import { useAppDispatch, useAppSelector } from "@/store/hooks"
|
import { useAppDispatch, useAppSelector } from "@/hooks/store"
|
||||||
import { clearCurrentUser } from "@/store/auth-slice"
|
import { clearCurrentUser } from "@/store/auth-slice"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
@@ -100,18 +100,18 @@ export default function HeroLayout() {
|
|||||||
{isDropdownOpen && (
|
{isDropdownOpen && (
|
||||||
<div className="absolute top-full left-2/3 -translate-x-1/2 w-18 bg-gray-950 border border-gray-700 rounded-lg shadow-xl py-1 z-20 opacity-60">
|
<div className="absolute top-full left-2/3 -translate-x-1/2 w-18 bg-gray-950 border border-gray-700 rounded-lg shadow-xl py-1 z-20 opacity-60">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zihluwang/frontend-repo"
|
href="https://github.com/zihluwang/delta-force-guide-web"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="block px-4 py-2 text-sm text-gray-300 hover:bg-gray-800 hover:text-white transition-colors">
|
className="block px-4 py-2 text-sm text-gray-300 hover:bg-gray-800 hover:text-white transition-colors">
|
||||||
前端
|
Web
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zihluwang/backend-repo"
|
href="https://github.com/zihluwang/delta-force-guide-server"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="block px-4 py-2 text-sm text-gray-300 hover:bg-gray-800 hover:text-white transition-colors">
|
className="block px-4 py-2 text-sm text-gray-300 hover:bg-gray-800 hover:text-white transition-colors">
|
||||||
后端
|
Server
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -153,7 +153,7 @@ export default function HeroLayout() {
|
|||||||
<Link
|
<Link
|
||||||
to="/login"
|
to="/login"
|
||||||
className="flex items-center gap-1.5 text-gray-400 hover:text-white transition-colors duration-200">
|
className="flex items-center gap-1.5 text-gray-400 hover:text-white transition-colors duration-200">
|
||||||
<LoginOutlined className="text-lg opacity-80" />
|
<LoginOutlined className="text-lg opacity-80" />
|
||||||
登录
|
登录
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from "react"
|
|||||||
import { useNavigate } from "react-router-dom"
|
import { useNavigate } from "react-router-dom"
|
||||||
import { App, Button, Card, Form, Input, Typography } from "antd"
|
import { App, Button, Card, Form, Input, Typography } from "antd"
|
||||||
import { AuthApi } from "@/api"
|
import { AuthApi } from "@/api"
|
||||||
import { useAppDispatch } from "@/store/hooks"
|
import { useAppDispatch } from "@/hooks/store"
|
||||||
import { setCurrentUser } from "@/store/auth-slice"
|
import { setCurrentUser } from "@/store/auth-slice"
|
||||||
import { LoginRequest } from "@/types"
|
import { LoginRequest } from "@/types"
|
||||||
|
|
||||||
@@ -29,11 +29,11 @@ export default function LoginPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-100 px-4 py-10 sm:py-16">
|
<div className="min-h-screen bg-gray-100 px-4 py-10 sm:py-16">
|
||||||
<div className="mx-auto max-w-md">
|
<div className="mx-auto max-w-md">
|
||||||
<Card bordered={false} className="shadow-sm">
|
<Card variant="borderless" className="shadow-sm">
|
||||||
<Typography.Title level={3} className="!mb-2 text-center">
|
<Typography.Title level={3} className="mb-2! text-center">
|
||||||
登录
|
登录
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Typography.Paragraph className="!mb-6 text-center !text-gray-500">
|
<Typography.Paragraph className="mb-6! text-center text-gray-500!">
|
||||||
使用你的帐号登录后即可继续操作
|
使用你的帐号登录后即可继续操作
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export default function LoginPage() {
|
|||||||
<Input.Password autoComplete="current-password" placeholder="请输入密码" />
|
<Input.Password autoComplete="current-password" placeholder="请输入密码" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item className="!mb-0">
|
<Form.Item className="mb-0!">
|
||||||
<Button type="primary" htmlType="submit" loading={loading} block>
|
<Button type="primary" htmlType="submit" loading={loading} block>
|
||||||
登录
|
登录
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user