Browse Source

优化显示

master
秦汉 1 week ago
parent
commit
7414b3e9bf
  1. 178
      AGENTS.md
  2. 2
      Build_God_Admin_Frontend/Frontend/.env.development
  3. 12
      Build_God_Api/Build_God_Api/DB/Character.cs
  4. 3
      Build_God_Api/Build_God_Api/Dto/CharacterDto.cs
  5. 15
      Build_God_Api/Build_God_Api/Services/CharacterService.cs
  6. 55
      Build_God_Api/Build_God_Api/Services/Game/CharacterAttributeCalculateService.cs
  7. 2
      Build_God_Api/Build_God_Api/Services/Game/TrainingService.cs
  8. 4
      Build_God_Api/Build_God_Api/Services/ScrapService.cs
  9. 2
      Build_God_Game/.env.development
  10. 2
      Build_God_Game/.env.production
  11. 41
      Build_God_Game/src/api/auth.ts
  12. 70
      Build_God_Game/src/api/character.ts
  13. 54
      Build_God_Game/src/api/dailyMission.ts
  14. 34
      Build_God_Game/src/api/index.ts
  15. 41
      Build_God_Game/src/api/scrap.ts
  16. 80
      Build_God_Game/src/views/CharacterView.vue
  17. 268
      Build_God_Game/src/views/DailyMissionView.vue
  18. 99
      Build_God_Game/src/views/GameView.vue
  19. 100
      Build_God_Game/src/views/TrainingView.vue
  20. 14
      Build_God_Game/vite.config.ts

178
AGENTS.md

@ -4,8 +4,9 @@ Guidelines for agentic coding agents working on this repository.
## Project Overview
- **Backend**: ASP.NET Core 8.0 Web API (C#)
- **Frontend**: Vue 3 + TypeScript + Vite + Element Plus
- **Backend**: ASP.NET Core 8.0 Web API (C#) with PostgreSQL and SqlSugar ORM
- **Admin Frontend**: Vue 3 + TypeScript + Vite + Element Plus (port 5173)
- **Game Frontend**: Vue 3 + TypeScript + Vite + Element Plus + TailwindCSS + Three.js (port 5174)
---
@ -14,16 +15,19 @@ Guidelines for agentic coding agents working on this repository.
### Backend (.NET API)
```bash
# Build the solution (from Build_God_Api directory)
dotnet build Build_God_Api/Build_God_Api.csproj
# Build the solution
dotnet build Build_God_Api/Build_God_Api/Build_God_Api.csproj
# Run the API
dotnet run --project Build_God_Api/Build_God_Api.csproj
# Run the API (launches on https://localhost:59447, http://localhost:59448)
dotnet run --project Build_God_Api/Build_God_Api/Build_God_Api.csproj
# Run with specific URL
dotnet run --urls "http://localhost:5091"
```
**Note**: No test framework or linting is currently configured for the backend.
### Frontend (Vue 3)
### Admin Frontend (Build_God_Admin_Frontend/Frontend)
```bash
# Install dependencies
@ -42,7 +46,23 @@ npm run type-check
npm run preview
```
**Note**: No test framework or ESLint is currently configured for the frontend.
### Game Frontend (Build_God_Game)
```bash
# Install dependencies
npm install
# Start development server (http://localhost:5174)
npm run dev
# Build for production
npm run build
# Type-check only
vue-tsc --build
```
**Note**: No test framework or ESLint is configured for either frontend.
---
@ -52,36 +72,43 @@ npm run preview
#### Conventions
- Use **file-scoped namespaces** (`namespace Build_God_Api.Controllers;`)
- Enable **nullable reference types**
- Enable **nullable reference types** (`<Nullable>enable</Nullable>`)
- Use **primary constructors** for dependency injection
- Use **async/await** for all I/O operations
- Use **region** sparingly - prefer natural code organization
#### Naming
- **Classes/Types**: PascalCase (`AccountController`, `AccountService`)
- **Methods/Properties**: PascalCase (`GetAccount`, `UserName`)
- **Local variables/Parameters**: camelCase (`accountId`, `emailAddress`)
- **Interfaces**: Prefix with `I` (`IAccountService`)
- **DTOs**: Postfix with `Cmd` for commands, `Dto` for responses
#### Project Structure
```
Build_God_Api/
Controllers/ # API endpoints
Services/ # Business logic (interface + implementation)
Services/Game/ # Game-specific services
DB/ # Database entities (extend BaseEntity)
Dto/ # Data transfer objects
Common/ # Utilities
Hubs/ # SignalR hubs
Build_God_Api/Build_God_Api/
Controllers/ # API endpoints
Services/ # Business logic interfaces
Services/Game/ # Game-specific services
DB/ # Database entities (extend BaseEntity)
Dto/ # Data transfer objects
Common/ # Utilities and helpers
Hubs/ # SignalR hubs
```
#### Error Handling
- Return `BadRequest("error message")` for validation errors
- Return `Ok(result)` for successful operations
- Use try-catch with `ILogger<T>` for error logging
- Use try-catch with `Console.WriteLine` for error logging
- Validate inputs at controller level using DataAnnotations
#### Route Conventions
- Use `[Route("api/god/[controller]")]`
- Use `[ApiController]` and HTTP method attributes
- Use `[ApiController]` and HTTP method attributes (`[HttpGet]`, `[HttpPost]`)
#### Database Entities (SqlSugar)
- All entities extend `BaseEntity` which provides `Id`, `CreatedOn`, `UpdatedOn`, `CreatedBy`, `UpdatedBy`
- Use `[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]` for auto-increment primary keys
---
@ -90,35 +117,57 @@ Build_God_Api/
#### Conventions
- Use **Composition API** with `<script setup lang="ts">`
- Use **path aliases**: `@/` maps to `src/`
- Admin: Plain CSS (`<style scoped lang="css">`)
- Game: Tailwind CSS utility classes
#### Naming
- **Components**: PascalCase (`LoginView.vue`, `Sidebar.vue`)
- **Variables/functions**: camelCase (`handleLogin`, `userName`)
- **Types/Interfaces**: PascalCase (`LoginRequest`, `User`)
- **Store names**: kebab-case in defineStore (`defineStore('auth', ...)`)
#### Project Structure
```
src/
api/ # API calls
components/ # Reusable components
views/ # Page components
stores/ # Pinia stores
router/ # Vue Router configuration
Admin Frontend (src/):
api/ # API modules
components/ # Reusable components
views/ # Page components (subdir: admin/)
stores/ # Pinia stores
router/ # Vue Router config
assets/ # Static assets
Game Frontend (src/):
api/ # API modules
components/ # Reusable components
composables/ # Vue composables
views/ # Page components
stores/ # Pinia stores
router/ # Vue Router config
```
#### Imports
- Use `@/` alias: `import { useAuthStore } from '@/stores/auth'`
- Group: external libs → internal imports → types
- Order: external libs → internal imports → types/interfaces
#### TypeScript Settings (tsconfig.app.json)
```json
{
"strict": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false
}
```
#### TypeScript
#### TypeScript Patterns
- Define types/interfaces for all API payloads
- Use `ref<T>` and `computed<T>` for reactive state
- Avoid `any` - use `unknown` or proper types
- Use `unknown` or proper types instead of `any`
#### Vue Component Pattern
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
@ -143,8 +192,59 @@ import { ref, computed } from 'vue'
export const useAuthStore = defineStore('auth', () => {
const token = ref('')
const isAuthenticated = computed(() => !!token.value)
return { token, isAuthenticated }
const login = async (credentials: LoginRequest): Promise<boolean> => {
// implementation
}
return { token, isAuthenticated, login }
})
```
#### API Module Pattern
All API modules share a centralized axios instance from `api/index.ts`.
**`src/api/index.ts`** - Shared axios instance (one per frontend):
```typescript
import axios from 'axios'
const instance = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:5091/api/god/',
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
})
instance.interceptors.request.use((config) => {
const token = localStorage.getItem('auth_token') // or sessionStorage for Admin
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
instance.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('auth_token')
window.location.href = '/login'
}
return Promise.reject(error.response?.data || error.message)
}
)
export default instance
```
**`src/api/{entity}.ts`** - Individual API modules (import from index):
```typescript
import http from './index'
export interface CharacterDto { ... }
export const characterApi = {
getList: (): Promise<CharacterDto[]> => {
return http.get('/character/list')
}
}
```
---
@ -157,7 +257,7 @@ export const useAuthStore = defineStore('auth', () => {
### Authentication
- JWT Bearer tokens
- Store in `localStorage` as `auth_token`
- Store in `localStorage` as `auth_token` (Game) or `sessionStorage` (Admin)
- Header: `Authorization: Bearer {token}`
### Key Endpoints
@ -169,9 +269,9 @@ export const useAuthStore = defineStore('auth', () => {
## 4. Development Workflow
1. **Start Backend**: `dotnet run --project Build_God_Api/Build_God_Api.csproj`
2. **Start Frontend**: `npm run dev` (in `Build_God_Admin_Frontend/Frontend/`)
3. **Access**: Frontend at `http://localhost:5173`
1. **Start Backend**: `dotnet run --project Build_God_Api/Build_God_Api/Build_God_Api.csproj`
2. **Start Admin Frontend**: `npm run dev` (in `Build_God_Admin_Frontend/Frontend/`)
3. **Start Game Frontend**: `npm run dev` (in `Build_God_Game/`)
---
@ -185,7 +285,7 @@ export const useAuthStore = defineStore('auth', () => {
5. Register service in `Program.cs`
### Frontend
1. Create API module in `src/api/`
1. Add API function in `src/api/{entity}.ts` (import `http` from `./index`)
2. Add Pinia store in `src/stores/` if needed
3. Create view component in `src/views/`
4. Add route in `src/router/index.ts`
@ -194,9 +294,9 @@ export const useAuthStore = defineStore('auth', () => {
## 6. Important Notes
- **Admin login**: `admin` / `love_god.123`
- **Admin credentials**: `admin` / `love_god.123`
- **Test account**: `Tom` / `123456` (email: 976802198@qq.com)
- Backend runs on port **5091**
- Frontend uses Vite proxy for API calls
- Backend runs on ports **59447** (HTTPS) and **59448** (HTTP)
- Admin Frontend uses sessionStorage; Game Frontend uses localStorage
- Always use `await` with async operations
- Run `npm run type-check` before committing
- Run type-check before committing

2
Build_God_Admin_Frontend/Frontend/.env.development

@ -1,2 +1,2 @@
# 开发环境配置
VITE_API_URL=http://localhost:5091/api/god/
VITE_API_URL=https://localhost:59447/api/god/

12
Build_God_Api/Build_God_Api/DB/Character.cs

@ -34,20 +34,10 @@ namespace Build_God_Api.DB
public decimal Money { get; set; }
/// <summary>
/// 最大生命值
/// </summary>
public decimal MaxHP { get; set; }
/// <summary>
/// 当前生命值
/// 当前生命值(实时跟踪)
/// </summary>
public decimal CurrentHP { get; set; }
/// <summary>
/// 攻击力
/// </summary>
public decimal Attack { get; set; }
/// <summary>
/// 突破成功的概率[0-100]
/// </summary>

3
Build_God_Api/Build_God_Api/Dto/CharacterDto.cs

@ -7,7 +7,10 @@
public string LevelName { get; set; }
public int LevelId { get; set; }
public decimal MaxHP { get; set; }
public decimal CurrentHP { get; set; }
public decimal Attack { get; set; }
public decimal Defend { get; set; }
public decimal CriticalRate { get; set; }
public string ProfessionName { get; set; }
public decimal Money { get; set; }
public decimal CurrentExp { get; set; }

15
Build_God_Api/Build_God_Api/Services/CharacterService.cs

@ -100,7 +100,7 @@ public interface ICharacterService
{
character.LevelId = nextLevelId;
character.BreakthroughRate = nextLevel.BaseBreakthroughRate;
await calculateService.CalculateAndUpdateAttributesAsync(character);
await calculateService.RecalculateMaxHPAsync(character);
return true;
}
@ -111,7 +111,7 @@ public interface ICharacterService
{
character.LevelId = nextLevelId;
character.BreakthroughRate = nextLevel.BaseBreakthroughRate;
await calculateService.CalculateAndUpdateAttributesAsync(character);
await calculateService.RecalculateMaxHPAsync(character);
return true;
}
else
@ -267,14 +267,19 @@ public async Task<Character?> GetCharacterByAccountId(int accountId)
nextLevelRequiredPillName = pill?.Name;
}
var attrs = await calculateService.CalculateAttributesAsync(c);
result.Add(new CharacterDto
{
Id = c.Id,
Name = c.Name,
LevelName = level?.Name ?? "未知",
LevelId = c.LevelId,
MaxHP = c.MaxHP,
Attack = c.Attack,
MaxHP = attrs.MaxHP,
CurrentHP = c.CurrentHP,
Attack = attrs.Attack,
Defend = attrs.Defend,
CriticalRate = attrs.CriticalRate,
ProfessionName = profession?.Name,
Money = c.Money,
CurrentExp = c.CurrentExp,
@ -328,9 +333,7 @@ public async Task<Character?> GetCharacterByAccountId(int accountId)
CurrentExp = 0,
LevelId = 1,
Money = 0,
MaxHP = 100,
CurrentHP = 100,
Attack = 0,
ProfessionId = character.ProfessionId,
SpiritFieldId = 0,
BreakthroughRate = 0,

55
Build_God_Api/Build_God_Api/Services/Game/CharacterAttributeCalculateService.cs

@ -1,4 +1,4 @@
using Build_God_Api.DB;
using Build_God_Api.DB;
using SqlSugar;
namespace Build_God_Api.Services.Game
@ -8,14 +8,31 @@ namespace Build_God_Api.Services.Game
/// </summary>
public interface ICharacterAttributeCalculateService
{
Task<bool> CalculateAndUpdateAttributesAsync(Character character);
Task<CharacterAttributes> CalculateAttributesAsync(Character character);
Task RecalculateMaxHPAsync(Character character);
}
public class CharacterAttributes
{
public decimal MaxHP { get; set; }
public decimal CurrentHP { get; set; }
public decimal Attack { get; set; }
public decimal Defend { get; set; }
public decimal CriticalRate { get; set; }
}
public class CharacterAttributeCalculateService(ISqlSugarClient context): ICharacterAttributeCalculateService
{
private readonly ISqlSugarClient _context = context;
public async Task<bool> CalculateAndUpdateAttributesAsync(Character character)
private decimal CalculateMaxHP(Character character, Profession? profession)
{
decimal healthRate = profession?.HealthRate ?? 1m;
return 100 + character.LevelId * character.CurrentExp * 5 * healthRate;
}
public async Task<CharacterAttributes> CalculateAttributesAsync(Character character)
{
Profession? profession = null;
if (character.ProfessionId.HasValue && character.ProfessionId > 0)
@ -23,21 +40,37 @@ namespace Build_God_Api.Services.Game
profession = await _context.Queryable<Profession>().FirstAsync(x => x.Id == character.ProfessionId);
}
// 无职业则默认系数为1
decimal attackRate = profession?.AttackRate ?? 1m;
decimal defendRate = profession?.DefendRate ?? 1m;
decimal healthRate = profession?.HealthRate ?? 1m;
decimal criticalRate = profession?.CriticalRate ?? 1m;
// 血量 = 基础值(100) + 等级 * 经验 * 5 * 生命系数
character.MaxHP = 100 + character.LevelId * character.CurrentExp * 5 * healthRate;
decimal maxHP = CalculateMaxHP(character, profession);
decimal attack = 10 + character.LevelId * character.CurrentExp * 2 * attackRate;
decimal defend = character.LevelId * defendRate * 0.5m;
decimal critRate = criticalRate * 0.1m;
// 攻击力 = 基础值(10) + 等级 * 经验 * 2 * 攻击系数
character.Attack = 10 + character.LevelId * character.CurrentExp * 2 * attackRate;
return new CharacterAttributes
{
MaxHP = maxHP,
CurrentHP = character.CurrentHP,
Attack = attack,
Defend = defend,
CriticalRate = critRate
};
}
await _context.Updateable(character).ExecuteCommandAsync();
public async Task RecalculateMaxHPAsync(Character character)
{
var profession = await _context.Queryable<Profession>().FirstAsync(x => x.Id == character.ProfessionId);
var newMaxHP = CalculateMaxHP(character, profession);
return true;
if (newMaxHP > 0)
{
decimal hpPercentage = character.CurrentHP / newMaxHP;
character.CurrentHP = hpPercentage * newMaxHP;
}
await _context.Updateable(character).ExecuteCommandAsync();
}
}
}

2
Build_God_Api/Build_God_Api/Services/Game/TrainingService.cs

@ -58,7 +58,7 @@ namespace Build_God_Api.Services.Game
var exp = (decimal)totalTime.TotalMinutes;
character.CurrentExp += exp;
character.TrainingOn = null;
await characterAttributeCalculateService.CalculateAndUpdateAttributesAsync(character);
await _context.Updateable(character).ExecuteCommandAsync();
return true;
}

4
Build_God_Api/Build_God_Api/Services/ScrapService.cs

@ -82,10 +82,6 @@ namespace Build_God_Api.Services
};
await db.Insertable(characterScrap).ExecuteCommandAsync();
character.Attack += selectedScrap.AttackBonus;
character.MaxHP += selectedScrap.HpBonus;
await db.Updateable(character).ExecuteCommandAsync();
return new ScrapScanResultDto
{
Scrap = MapToDto(selectedScrap),

2
Build_God_Game/.env.development

@ -0,0 +1,2 @@
# 开发环境配置
VITE_API_URL=https://localhost:59447/api/god/

2
Build_God_Game/.env.production

@ -0,0 +1,2 @@
# 生产环境配置
VITE_API_URL=https://api.example.com/api

41
Build_God_Game/src/api/auth.ts

@ -1,37 +1,4 @@
import axios from 'axios'
const API_BASE_URL = '/api'
const instance = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
instance.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('auth_token')
localStorage.removeItem('user')
window.location.href = '/login'
}
return Promise.reject(error.response?.data || error.message)
}
)
import http from './index'
export interface LoginRequest {
name: string
@ -50,12 +17,10 @@ export interface AuthResponse {
export const authApi = {
login: (data: LoginRequest): Promise<AuthResponse> => {
return instance.post('/account/login', data)
return http.post('/account/login', data)
},
register: (data: RegisterRequest): Promise<number> => {
return instance.post('/account/register', data)
return http.post('/account/register', data)
}
}
export default instance

70
Build_God_Game/src/api/character.ts

@ -1,46 +1,14 @@
import axios from 'axios'
const API_BASE_URL = '/api'
const API_GOD_URL = '/api/god'
const instance = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
instance.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('auth_token')
localStorage.removeItem('user')
window.location.href = '/login'
}
return Promise.reject(error.response?.data || error.message)
}
)
import http from './index'
export interface CharacterDto {
id: number
name: string
levelName: string
levelId: number
maxHP: number
currentHP: number
attack: number
defend: number
criticalRate: number
professionName?: string
money: number
currentExp: number
@ -56,7 +24,6 @@ export interface CharacterDto {
lastLogin: string
createdOn: string
}
export interface ProfessionDto {
id: number
name: string
@ -66,44 +33,33 @@ export interface ProfessionDto {
healthRate: number
criticalRate: number
}
export interface CreateCharacterRequest {
name: string
professionId: number
}
export const characterApi = {
getCharacterList: (): Promise<CharacterDto[]> => {
return instance.get('/character/list')
return http.get('/character/list')
},
createCharacter: (data: CreateCharacterRequest): Promise<boolean> => {
return instance.post('/character/register', data)
return http.post('/character/register', data)
},
deleteCharacter: (characterId: number): Promise<boolean> => {
return instance.delete(`/character/${characterId}`)
return http.delete(`/character/${characterId}`)
},
selectCharacter: (characterId: number): Promise<boolean> => {
return instance.post(`/character/select/${characterId}`)
return http.post(`/character/select/${characterId}`)
},
getProfessions: (): Promise<ProfessionDto[]> => {
return instance.get(`/profession/all`)
return http.get('/profession/all')
},
startTraining: (characterId: number): Promise<boolean> => {
return instance.post(`/character/${characterId}/training/start`)
return http.post(`/character/${characterId}/training/start`)
},
stopTraining: (characterId: number): Promise<number> => {
return instance.post(`/character/${characterId}/training/stop`)
return http.post(`/character/${characterId}/training/stop`)
},
breakthrough: (characterId: number): Promise<boolean> => {
return instance.post(`/character/${characterId}/breakthrough`)
return http.post(`/character/${characterId}/breakthrough`)
}
}
export default instance
}

54
Build_God_Game/src/api/dailyMission.ts

@ -1,56 +1,18 @@
import axios from 'axios'
const API_BASE_URL = '/api'
const instance = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
instance.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('auth_token')
localStorage.removeItem('user')
window.location.href = '/login'
}
return Promise.reject(error.response?.data || error.message)
}
)
import http from './index'
export const DailyMissionStatus = {
Pending: 0,
InProgress: 1,
Completed: 2,
Claimed: 3
} as const
export type DailyMissionStatus = typeof DailyMissionStatus[keyof typeof DailyMissionStatus]
export const RewardType = {
Pill: 1,
Equipment: 2,
Exp: 3,
Money: 4
} as const
export type RewardType = typeof RewardType[keyof typeof RewardType]
export interface MissionReward {
rewardType: RewardType
rewardTypeName: string
@ -58,7 +20,6 @@ export interface MissionReward {
itemName: string
count: number
}
export interface DailyMission {
id: number
characterId: number
@ -74,19 +35,14 @@ export interface DailyMission {
assignedDate: string
rewards: MissionReward[]
}
export const dailyMissionApi = {
getList: (): Promise<DailyMission[]> => {
return instance.get('/dailyMission/list')
return http.get('/dailyMission/list')
},
accept: (dailyMissionId: number): Promise<boolean> => {
return instance.post(`/dailyMission/${dailyMissionId}/accept`)
return http.post(`/dailyMission/${dailyMissionId}/accept`)
},
claim: (dailyMissionId: number): Promise<boolean> => {
return instance.post(`/dailyMission/${dailyMissionId}/claim`)
return http.post(`/dailyMission/${dailyMissionId}/claim`)
}
}
export default instance
}

34
Build_God_Game/src/api/index.ts

@ -0,0 +1,34 @@
import axios from 'axios'
const instance = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:5091/api/god/',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
instance.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('auth_token')
localStorage.removeItem('user')
window.location.href = '/login'
}
return Promise.reject(error.response?.data || error.message)
}
)
export default instance

41
Build_God_Game/src/api/scrap.ts

@ -1,35 +1,4 @@
import axios from 'axios'
const instance = axios.create({
baseURL: '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
instance.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('auth_token')
localStorage.removeItem('user')
window.location.href = '/login'
}
return Promise.reject(error.response?.data || error.message)
}
)
import http from './index'
export interface ScrapDto {
id: number
@ -61,16 +30,14 @@ export interface ScrapHistoryDto {
export const scrapApi = {
getScrapList: (): Promise<ScrapDto[]> => {
return instance.get('/scrap/list')
return http.get('/scrap/list')
},
scanScrap: (characterId: number): Promise<ScrapScanResultDto> => {
return instance.post('/scrap/scan', { characterId })
return http.post('/scrap/scan', { characterId })
},
getScrapHistory: (characterId: number): Promise<ScrapHistoryDto[]> => {
return instance.get(`/scrap/history/${characterId}`)
return http.get(`/scrap/history/${characterId}`)
}
}
export default instance

80
Build_God_Game/src/views/CharacterView.vue

@ -3,6 +3,7 @@ import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useCharacterStore } from '@/stores/character'
import { useAuthStore } from '@/stores/auth'
import { ElProgress } from 'element-plus'
import Particles from '@/components/Particles/Particles.vue'
import GlareHover from '@/components/GlareHover/GlareHover.vue'
@ -97,6 +98,11 @@ const formatDate = (dateStr: string) => {
const date = new Date(dateStr)
return date.toLocaleDateString('zh-CN')
}
const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
if (!nextLevelMinExp) return 0
return Math.min(100, (currentExp / nextLevelMinExp) * 100)
}
</script>
<template>
@ -128,20 +134,52 @@ const formatDate = (dateStr: string) => {
<div class="character-stats">
<div class="stat-item">
<span class="stat-label">生命</span>
<span class="stat-value">{{ char.maxHP }}</span>
<span class="stat-value">{{ Math.floor(char.maxHP) }}</span>
</div>
<div class="stat-item">
<span class="stat-label">攻击</span>
<span class="stat-value">{{ char.attack }}</span>
<span class="stat-value">{{ Math.floor(char.attack) }}</span>
</div>
<div class="stat-item">
<span class="stat-label">灵石</span>
<span class="stat-value">{{ char.money }}</span>
<span class="stat-label">防御</span>
<span class="stat-value">{{ Math.floor(char.defend) }}</span>
</div>
<div class="stat-item">
<span class="stat-label">暴击</span>
<span class="stat-value">{{ Math.floor(char.criticalRate * 100) }}%</span>
</div>
</div>
<div class="character-second-stats">
<div v-if="char.professionName" class="stat-item">
<span class="stat-label">职业</span>
<span class="stat-value">{{ char.professionName }}</span>
</div>
<div class="stat-item">
<span class="stat-label">灵石</span>
<span class="stat-value">{{ char.money }}</span>
</div>
</div>
<div class="character-extended-stats">
<div class="stat-item exp-item">
<span class="stat-label">经验</span>
<el-progress
:percentage="getExpProgress(char.currentExp, char.nextLevelMinExp)"
color="#ff8844"
:show-text="false"
:stroke-width="4"
/>
<span class="stat-value-small">{{ Math.floor(char.currentExp) }}/{{ char.nextLevelMinExp }}</span>
</div>
<div class="stat-item">
<span class="stat-label">修炼</span>
<span class="stat-value">{{ char.trainingExpRate }}x</span>
</div>
<div class="stat-item">
<span class="stat-label">突破</span>
<span class="stat-value">{{ char.breakthroughRate }}%</span>
</div>
</div>
<div class="character-meta">
@ -383,7 +421,14 @@ const formatDate = (dateStr: string) => {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-bottom: 12px;
margin-bottom: 8px;
}
.character-second-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin-bottom: 8px;
}
.stat-item {
@ -413,6 +458,31 @@ const formatDate = (dateStr: string) => {
font-size: 0.75rem;
}
.character-extended-stats {
display: flex;
justify-content: space-between;
gap: 8px;
padding-top: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.05);
margin-top: 8px;
}
.character-extended-stats .stat-item {
flex: 1;
text-align: center;
}
.exp-item {
flex: 2 !important;
}
.stat-value-small {
display: block;
color: #666666;
font-size: 0.65rem;
margin-top: 2px;
}
.delete-btn {
position: absolute;
top: 12px;

268
Build_God_Game/src/views/DailyMissionView.vue

@ -2,6 +2,7 @@
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { dailyMissionApi, type DailyMission, DailyMissionStatus, RewardType } from '@/api/dailyMission'
import Particles from '@/components/Particles/Particles.vue'
const router = useRouter()
@ -143,157 +144,128 @@ const todayMissions = computed(() => activeMissions.value.filter(m => !m.isFromY
<template>
<div class="daily-mission-page">
<div class="page-header">
<span class="back-btn" @click="handleGoBack"> 返回</span>
<span class="title">每日任务</span>
<span class="placeholder"></span>
</div>
<div v-if="showMessage" class="toast-message">
{{ message }}
</div>
<Particles :particle-count="60" :particle-colors="['#ffffff', '#aaaaaa']" class="particles-bg" />
<div v-if="loading" class="loading">
加载中...
</div>
<div class="page-container">
<div v-else class="missions-content">
<div v-if="yesterdayMissions.length > 0" class="mission-section yesterday-section">
<div class="section-header">
<span class="section-title">昨日任务</span>
<span class="section-badge highlight">待领取</span>
</div>
<div class="mission-list">
<div
v-for="mission in yesterdayMissions"
:key="mission.id"
class="mission-card highlight"
>
<div class="mission-header">
<span class="mission-name">{{ mission.missionTitle }}</span>
<span class="mission-status" :class="getStatusClass(mission.status)">
{{ getStatusText(mission.status) }}
</span>
</div>
<div class="mission-desc">{{ mission.missionDescription }}</div>
<div class="mission-time">
<span v-if="mission.status === DailyMissionStatus.InProgress">
剩余时间: {{ remainingTimes[mission.id] || formatRemainingTime(mission.endTime) }}
</span>
<span v-else-if="mission.status === DailyMissionStatus.Completed">
已完成请领取奖励
</span>
<span v-else>
挂机时间: {{ mission.spendTimeMinutes }}分钟
</span>
</div>
<div class="mission-rewards">
<span class="rewards-label">奖励:</span>
<span
v-for="(reward, idx) in mission.rewards"
:key="idx"
class="reward-item"
>
{{ getRewardIcon(reward.rewardType) }}{{ reward.itemName || reward.rewardTypeName }}×{{ reward.count }}
</span>
</div>
<div class="mission-actions">
<button
v-if="canAccept(mission)"
class="action-btn accept-btn"
@click="handleAccept(mission)"
>
接取任务
</button>
<button
v-if="canClaim(mission)"
class="action-btn claim-btn"
@click="handleClaim(mission)"
>
领取奖励
</button>
<button
v-if="mission.status === DailyMissionStatus.InProgress"
class="action-btn disabled-btn"
disabled
>
挂机中...
</button>
<div class="page-header">
<span class="back-btn" @click="handleGoBack"> 返回</span>
<span class="title">每日任务</span>
<span class="placeholder"></span>
</div>
<div v-if="showMessage" class="toast-message">
{{ message }}
</div>
<div v-if="loading" class="loading">
加载中...
</div>
<div v-else class="missions-content">
<div v-if="yesterdayMissions.length > 0" class="mission-section yesterday-section">
<div class="section-header">
<span class="section-title">昨日任务</span>
<span class="section-badge highlight">待领取</span>
</div>
<div class="mission-list">
<div v-for="mission in yesterdayMissions" :key="mission.id" class="mission-card highlight">
<div class="mission-header">
<span class="mission-name">{{ mission.missionTitle }}</span>
<span class="mission-status" :class="getStatusClass(mission.status)">
{{ getStatusText(mission.status) }}
</span>
</div>
<div class="mission-desc">{{ mission.missionDescription }}</div>
<div class="mission-time">
<span v-if="mission.status === DailyMissionStatus.InProgress">
剩余时间: {{ remainingTimes[mission.id] || formatRemainingTime(mission.endTime) }}
</span>
<span v-else-if="mission.status === DailyMissionStatus.Completed">
已完成请领取奖励
</span>
<span v-else>
挂机时间: {{ mission.spendTimeMinutes }}分钟
</span>
</div>
<div class="mission-rewards">
<span class="rewards-label">奖励:</span>
<span v-for="(reward, idx) in mission.rewards" :key="idx" class="reward-item">
{{ getRewardIcon(reward.rewardType) }}{{ reward.itemName || reward.rewardTypeName }}×{{ reward.count
}}
</span>
</div>
<div class="mission-actions">
<button v-if="canAccept(mission)" class="action-btn accept-btn" @click="handleAccept(mission)">
接取任务
</button>
<button v-if="canClaim(mission)" class="action-btn claim-btn" @click="handleClaim(mission)">
领取奖励
</button>
<button v-if="mission.status === DailyMissionStatus.InProgress" class="action-btn disabled-btn"
disabled>
挂机中...
</button>
</div>
</div>
</div>
</div>
</div>
<div v-if="todayMissions.length > 0" class="mission-section">
<div class="section-header">
<span class="section-title">今日任务</span>
<span class="section-badge">{{ todayMissions.filter(m => m.status === DailyMissionStatus.Claimed).length }}/{{ todayMissions.length }}</span>
</div>
<div class="mission-list">
<div
v-for="mission in todayMissions"
:key="mission.id"
class="mission-card"
>
<div class="mission-header">
<span class="mission-name">{{ mission.missionTitle }}</span>
<span class="mission-status" :class="getStatusClass(mission.status)">
{{ getStatusText(mission.status) }}
</span>
</div>
<div class="mission-desc">{{ mission.missionDescription }}</div>
<div class="mission-time">
<span v-if="mission.status === DailyMissionStatus.InProgress">
剩余时间: {{ remainingTimes[mission.id] || formatRemainingTime(mission.endTime) }}
</span>
<span v-else-if="mission.status === DailyMissionStatus.Completed">
已完成请领取奖励
</span>
<span v-else>
挂机时间: {{ mission.spendTimeMinutes }}分钟
</span>
</div>
<div class="mission-rewards">
<span class="rewards-label">奖励:</span>
<span
v-for="(reward, idx) in mission.rewards"
:key="idx"
class="reward-item"
>
{{ getRewardIcon(reward.rewardType) }}{{ reward.itemName || reward.rewardTypeName }}×{{ reward.count }}
</span>
</div>
<div class="mission-actions">
<button
v-if="canAccept(mission)"
class="action-btn accept-btn"
@click="handleAccept(mission)"
>
接取任务
</button>
<button
v-if="canClaim(mission)"
class="action-btn claim-btn"
@click="handleClaim(mission)"
>
领取奖励
</button>
<button
v-if="mission.status === DailyMissionStatus.InProgress"
class="action-btn disabled-btn"
disabled
>
挂机中...
</button>
<div v-if="todayMissions.length > 0" class="mission-section">
<div class="section-header">
<span class="section-title">今日任务</span>
<span class="section-badge">{{todayMissions.filter(m => m.status === DailyMissionStatus.Claimed).length}}/{{
todayMissions.length }}</span>
</div>
<div class="mission-list">
<div v-for="mission in todayMissions" :key="mission.id" class="mission-card">
<div class="mission-header">
<span class="mission-name">{{ mission.missionTitle }}</span>
<span class="mission-status" :class="getStatusClass(mission.status)">
{{ getStatusText(mission.status) }}
</span>
</div>
<div class="mission-desc">{{ mission.missionDescription }}</div>
<div class="mission-time">
<span v-if="mission.status === DailyMissionStatus.InProgress">
剩余时间: {{ remainingTimes[mission.id] || formatRemainingTime(mission.endTime) }}
</span>
<span v-else-if="mission.status === DailyMissionStatus.Completed">
已完成请领取奖励
</span>
<span v-else>
挂机时间: {{ mission.spendTimeMinutes }}分钟
</span>
</div>
<div class="mission-rewards">
<span class="rewards-label">奖励:</span>
<span v-for="(reward, idx) in mission.rewards" :key="idx" class="reward-item">
{{ getRewardIcon(reward.rewardType) }}{{ reward.itemName || reward.rewardTypeName }}×{{ reward.count
}}
</span>
</div>
<div class="mission-actions">
<button v-if="canAccept(mission)" class="action-btn accept-btn" @click="handleAccept(mission)">
接取任务
</button>
<button v-if="canClaim(mission)" class="action-btn claim-btn" @click="handleClaim(mission)">
领取奖励
</button>
<button v-if="mission.status === DailyMissionStatus.InProgress" class="action-btn disabled-btn"
disabled>
挂机中...
</button>
</div>
</div>
</div>
</div>
</div>
<div v-if="activeMissions.length === 0" class="empty-state">
<div class="empty-icon">📋</div>
<div class="empty-text">今日暂无任务</div>
<div class="empty-hint">请明日再来</div>
<div v-if="activeMissions.length === 0" class="empty-state">
<div class="empty-icon">📋</div>
<div class="empty-text">今日暂无任务</div>
<div class="empty-hint">请明日再来</div>
</div>
</div>
</div>
</div>
@ -315,6 +287,21 @@ const todayMissions = computed(() => activeMissions.value.filter(m => !m.isFromY
margin-bottom: 20px;
}
.page-container {
max-width: 480px;
margin: 0 auto;
position: relative;
z-index: 10;
}
.particles-bg {
position: fixed !important;
top: 0;
left: 0;
width: 100% !important;
height: 100% !important;
}
.back-btn {
color: #666666;
font-size: 0.9rem;
@ -355,6 +342,7 @@ const todayMissions = computed(() => activeMissions.value.filter(m => !m.isFromY
opacity: 0;
transform: translateX(-50%) translateY(-10px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);

99
Build_God_Game/src/views/GameView.vue

@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElProgress } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
import { useCharacterStore } from '@/stores/character'
import { useRouter } from 'vue-router'
@ -33,8 +34,8 @@ const showBreakthroughMessage = ref(false)
const menuItems = computed(() => [
{ label: '任务', icon: '🗺️' },
{ label: '战斗', icon: '⚔️' },
{ label: '背包', icon: '🎒' },
// { label: '', icon: '' },
// { label: '', icon: '🎒' },
{ label: '角色', icon: '👤' },
{ label: isTraining.value ? '打坐中' : '打坐', icon: isTraining.value ? '🔥' : '🧘', isTraining: isTraining.value },
{ label: '捡垃圾', icon: '🗑️' },
@ -72,12 +73,8 @@ const handleBreakthrough = async () => {
<template>
<div class="game-page">
<Particles
:particle-count="100"
:particle-colors="['#ffffff', '#cccccc']"
class="particles-bg"
/>
<Particles :particle-count="100" :particle-colors="['#ffffff', '#cccccc']" class="particles-bg" />
<div class="game-container">
<div class="character-header" @click="handleSwitchCharacter">
<div class="character-info">
@ -86,31 +83,22 @@ const handleBreakthrough = async () => {
</div>
<span class="switch-btn">切换角色</span>
</div>
<h1 class="welcome-text">欢迎来到CUPOWER</h1>
<div class="menu-grid">
<GlareHover
v-for="item in menuItems"
:key="item.label"
width="100%"
height="120px"
:background="item.isTraining ? 'rgba(255,136,68,0.1)' : 'rgba(255,255,255,0.02)'"
border-radius="16px"
<GlareHover v-for="item in menuItems" :key="item.label" width="100%" height="120px"
:background="item.isTraining ? 'rgba(255,136,68,0.1)' : 'rgba(255,255,255,0.02)'" border-radius="16px"
:border-color="item.isTraining ? 'rgba(255,136,68,0.3)' : 'rgba(255,255,255,0.08)'"
:glare-color="item.isTraining ? '#ff8844' : '#ffffff'"
:glare-opacity="0.1"
class="menu-card"
:class="{ 'training-active': item.isTraining }"
@click="navigateTo(item)"
>
:glare-color="item.isTraining ? '#ff8844' : '#ffffff'" :glare-opacity="0.1" class="menu-card"
:class="{ 'training-active': item.isTraining }" @click="navigateTo(item)">
<div class="menu-content">
<span class="menu-icon">{{ item.icon }}</span>
<span class="menu-label">{{ item.label }}</span>
</div>
</GlareHover>
</div>
<div v-if="nextLevelName" class="breakthrough-section">
<div class="breakthrough-header">
<span class="breakthrough-title">境界突破</span>
@ -123,25 +111,20 @@ const handleBreakthrough = async () => {
</div>
<div class="exp-info">
<span class="exp-text">{{ currentExp }} / {{ nextLevelMinExp }} 经验</span>
<div class="exp-bar">
<div class="exp-fill" :style="{ width: expProgress + '%' }"></div>
</div>
<el-progress :percentage="Math.floor(expProgress)" color="#ff8844" :show-text="false" :stroke-width="8" />
<span class="exp-percent">{{ Math.floor(expProgress) }}%</span>
</div>
<div class="rate-info">
<span class="rate-label">突破成功率</span>
<span class="rate-value">{{ breakthroughRate }}%</span>
<el-progress :percentage="breakthroughRate" color="#22c55e" :show-text="false" :stroke-width="8" />
<span class="exp-percent">{{ breakthroughRate + `%` }}</span>
</div>
<div v-if="hasPillCost" class="pill-cost-info">
<span class="pill-cost-label">突破消耗</span>
<span class="pill-cost-value">{{ requiredPillName }} ×{{ requiredPillQuantity }}</span>
</div>
<button
class="breakthrough-btn"
:class="{ 'can-breakthrough': canBreakthrough }"
:disabled="!canBreakthrough"
@click="handleBreakthrough"
>
<button class="breakthrough-btn" :class="{ 'can-breakthrough': canBreakthrough }" :disabled="!canBreakthrough"
@click="handleBreakthrough">
{{ canBreakthrough ? '突破' : '未满足突破条件' }}
</button>
</div>
@ -150,18 +133,10 @@ const handleBreakthrough = async () => {
<div v-if="showBreakthroughMessage" class="toast-message">
{{ breakthroughMessage }}
</div>
<GlareHover
width="200px"
height="44px"
background="transparent"
border-radius="22px"
border-color="rgba(255,255,255,0.1)"
glare-color="#ffffff"
:glare-opacity="0.1"
class="logout-button"
@click="handleLogout"
>
<GlareHover width="200px" height="44px" background="transparent" border-radius="22px"
border-color="rgba(255,255,255,0.1)" glare-color="#ffffff" :glare-opacity="0.1" class="logout-button"
@click="handleLogout">
<span class="logout-text">退出登录</span>
</GlareHover>
@ -341,20 +316,6 @@ const handleBreakthrough = async () => {
text-align: center;
}
.exp-bar {
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
overflow: hidden;
}
.exp-fill {
height: 100%;
background: linear-gradient(90deg, #ff8844, #ff6644);
border-radius: 4px;
transition: width 0.3s ease;
}
.exp-percent {
color: #888888;
font-size: 0.75rem;
@ -363,22 +324,14 @@ const handleBreakthrough = async () => {
.rate-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.02);
border-radius: 8px;
flex-direction: column;
gap: 6px;
}
.rate-label {
color: #666666;
font-size: 0.85rem;
}
.rate-value {
color: #ff8844;
font-size: 0.9rem;
font-weight: 500;
font-size: 0.75rem;
white-space: nowrap;
}
.pill-cost-info {
@ -389,7 +342,6 @@ const handleBreakthrough = async () => {
background: rgba(255, 68, 68, 0.1);
border: 1px solid rgba(255, 68, 68, 0.2);
border-radius: 8px;
margin-top: 10px;
}
.pill-cost-label {
@ -450,6 +402,7 @@ const handleBreakthrough = async () => {
opacity: 0;
transform: translateX(-50%) translateY(-10px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);

100
Build_God_Game/src/views/TrainingView.vue

@ -2,6 +2,7 @@
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useCharacterStore } from '@/stores/character'
import Particles from '@/components/Particles/Particles.vue'
const router = useRouter()
const characterStore = useCharacterStore()
@ -82,54 +83,52 @@ const handleStopTraining = async () => {
<template>
<div class="training-page">
<div class="training-header">
<span class="back-btn" @click="handleGoBack"> 返回</span>
<span class="title">打坐</span>
<span class="placeholder"></span>
</div>
<div v-if="showMessage" class="toast-message">
{{ message }}
</div>
<Particles :particle-count="60" :particle-colors="['#ffffff', '#aaaaaa']" class="particles-bg" />
<div class="training-content">
<div class="status-section">
<div class="status-icon" :class="{ training: isTraining }">
{{ isTraining ? '🔥' : '💤' }}
</div>
<div class="status-text">
{{ isTraining ? '正在打坐中...' : '未开始打坐' }}
</div>
<div class="page-container">
<div class="training-header">
<span class="back-btn" @click="handleGoBack"> 返回</span>
<span class="title">打坐</span>
<span class="placeholder"></span>
</div>
<div class="info-section">
<div class="info-item">
<span class="label">已打坐</span>
<span class="value">{{ Math.floor(elapsedSeconds / 60) }}{{ elapsedSeconds % 60 }}</span>
<div v-if="showMessage" class="toast-message">
{{ message }}
</div>
<div class="training-content">
<div class="status-section">
<div class="status-icon" :class="{ training: isTraining }">
{{ isTraining ? '🔥' : '💤' }}
</div>
<div class="status-text">
{{ isTraining ? '正在打坐中...' : '未开始打坐' }}
</div>
</div>
<div class="info-item">
<span class="label">预计获得</span>
<span class="value highlight">{{ expectedExp }} 经验</span>
<div class="info-section">
<div class="info-item">
<span class="label">已打坐</span>
<span class="value">{{ Math.floor(elapsedSeconds / 60) }}{{ elapsedSeconds % 60 }}</span>
</div>
<div class="info-item">
<span class="label">预计获得</span>
<span class="value highlight">{{ expectedExp }} 经验</span>
</div>
</div>
</div>
<div class="action-section">
<button
v-if="!isTraining"
class="action-btn start-btn"
@click="handleStartTraining"
>
开始打坐
</button>
<button
v-else
class="action-btn stop-btn"
@click="handleStopTraining"
>
停止打坐
</button>
<div class="action-section">
<button v-if="!isTraining" class="action-btn start-btn" @click="handleStartTraining">
开始打坐
</button>
<button v-else class="action-btn stop-btn" @click="handleStopTraining">
停止打坐
</button>
</div>
</div>
</div>
</div>
</template>
@ -150,6 +149,21 @@ const handleStopTraining = async () => {
margin-bottom: 40px;
}
.page-container {
max-width: 480px;
margin: 0 auto;
position: relative;
z-index: 10;
}
.particles-bg {
position: fixed !important;
top: 0;
left: 0;
width: 100% !important;
height: 100% !important;
}
.back-btn {
color: #666666;
font-size: 0.9rem;
@ -197,9 +211,12 @@ const handleStopTraining = async () => {
}
@keyframes pulse {
0%, 100% {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
@ -210,7 +227,7 @@ const handleStopTraining = async () => {
font-size: 1rem;
}
.status-icon.training + .status-text {
.status-icon.training+.status-text {
color: #ff8844;
}
@ -302,6 +319,7 @@ const handleStopTraining = async () => {
opacity: 0;
transform: translateX(-50%) translateY(-10px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);

14
Build_God_Game/vite.config.ts

@ -12,19 +12,5 @@ export default defineConfig({
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
port: 5174,
proxy: {
'/api': {
target: 'http://localhost:5091',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/api/god')
},
'/hubs': {
target: 'http://localhost:5091',
changeOrigin: true
}
}
}
})

Loading…
Cancel
Save