feat: 新增应用全局常量 APP,消除硬编码散落
- 新增 src/shared/app.ts,定义应用元信息(name、title、subtitle、description、version) - 后端 3 处硬编码改为引用 APP.name - 前端 3 处硬编码改为引用 APP.title/APP.description - localStorage key 从 my-app.theme.preference 改为 theme.preference - 构建脚本可执行文件名改为引用 APP.name - 更新 README.md 和 DEVELOPMENT.md 文档 - 新增 openspec/specs/app-constants/spec.md 规范文档
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { APP } from "../shared/app";
|
||||
|
||||
export interface ServerConfig {
|
||||
host: string;
|
||||
port: number;
|
||||
@@ -43,7 +45,7 @@ export function parseRuntimeArgs(argv: string[] = Bun.argv.slice(2)): { configPa
|
||||
if (argv.length === 0) return {};
|
||||
const firstArg = argv[0];
|
||||
if (firstArg === "--help" || firstArg === "-h") {
|
||||
console.log("用法: my-app [config.yaml]");
|
||||
console.log(`用法: ${APP.name} [config.yaml]`);
|
||||
console.log(" config.yaml 可选 YAML 配置文件路径(不存在时使用默认配置)");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { ApiErrorResponse, HealthResponse, RuntimeMode } from "../shared/api";
|
||||
|
||||
import { APP } from "../shared/app";
|
||||
|
||||
export function createApiError(error: string, status: number): ApiErrorResponse {
|
||||
return { error, status };
|
||||
}
|
||||
@@ -18,7 +20,7 @@ export function createHeaders(mode: RuntimeMode, init: HeadersInit): Headers {
|
||||
export function createHealthResponse(): HealthResponse {
|
||||
return {
|
||||
ok: true,
|
||||
service: "my-app",
|
||||
service: APP.name,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { RuntimeMode } from "../shared/api";
|
||||
import type { ServerConfig } from "./config";
|
||||
import type { StaticAssets } from "./static";
|
||||
|
||||
import { APP } from "../shared/app";
|
||||
import { createApiError, jsonResponse } from "./helpers";
|
||||
import { handleHealth } from "./routes/health";
|
||||
import { serveStaticAsset } from "./static";
|
||||
@@ -32,7 +33,7 @@ export function startServer(options: StartServerOptions) {
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`my-app listening on ${server.url}`);
|
||||
console.log(`${APP.name} listening on ${server.url}`);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
7
src/shared/app.ts
Normal file
7
src/shared/app.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const APP = {
|
||||
description: "基于 Bun + React + TDesign 的全栈开发框架",
|
||||
name: "my-app",
|
||||
subtitle: "Bun 全栈应用",
|
||||
title: "My App",
|
||||
version: "0.1.0",
|
||||
} as const;
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useEffect } from "react";
|
||||
import { Layout, Menu, RadioGroup, Space } from "tdesign-react";
|
||||
|
||||
import type { HealthResponse } from "../shared/api";
|
||||
|
||||
import { APP } from "../shared/app";
|
||||
import { type ThemePreference, useThemePreference } from "./hooks/use-theme-preference";
|
||||
|
||||
const { Content, Header } = Layout;
|
||||
@@ -21,6 +23,11 @@ export function App() {
|
||||
staleTime: 5000,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = APP.title;
|
||||
document.querySelector('meta[name="description"]')?.setAttribute("content", APP.description);
|
||||
}, []);
|
||||
|
||||
const handleThemeChange = (value: ThemePreference) => {
|
||||
setThemePreference(value);
|
||||
};
|
||||
@@ -31,7 +38,7 @@ export function App() {
|
||||
<Menu.HeadMenu
|
||||
logo={
|
||||
<span className="dashboard-brand">
|
||||
<span className="dashboard-logo">my-app</span>
|
||||
<span className="dashboard-logo">{APP.title}</span>
|
||||
</span>
|
||||
}
|
||||
operations={
|
||||
@@ -50,7 +57,7 @@ export function App() {
|
||||
<Content>
|
||||
<div className="dashboard-content">
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<h2>欢迎使用 my-app</h2>
|
||||
<h2>欢迎使用 {APP.title}</h2>
|
||||
<p>在此构建你的应用。以下是 /health API 的返回数据(前后端联调示例):</p>
|
||||
{health && <pre className="health-response">{JSON.stringify(health, null, 2)}</pre>}
|
||||
</Space>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
|
||||
export type EffectiveTheme = "dark" | "light";
|
||||
export type ThemePreference = "dark" | "light" | "system";
|
||||
|
||||
export const THEME_PREFERENCE_STORAGE_KEY = "my-app.theme.preference";
|
||||
export const THEME_PREFERENCE_STORAGE_KEY = "theme.preference";
|
||||
export const THEME_MEDIA_QUERY = "(prefers-color-scheme: dark)";
|
||||
|
||||
export function applyInitialThemePreference() {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="my-app" />
|
||||
<title>my-app</title>
|
||||
<meta name="description" content="" />
|
||||
<title>App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user