非常好, 让我们将代码转换为 TypeScript, 并添加适当的类型定义。以下是主要文件的 TypeScript 版本:
src/index.ts:
import { Elysia } from "elysia";
import { html } from "@elysiajs/html";
import { staticPlugin } from "@elysiajs/static";
import { layout } from "./views/layout";
import { forecast } from "./views/forecast";
import { themesList, themeEditor } from "./views/themes";
import { getWeatherForecast, WeatherData } from "./weather";
import { getThemes, getTheme, saveTheme, Theme } from "./themes";
const app = new Elysia()
.use(html())
.use(staticPlugin())
.get("/", ({ html }) => html(layout(forecast())))
.get("/forecast", async ({ query, html }) => {
const city = query.city as string;
const days = parseInt(query.days as string, 10);
if (city && days) {
const weatherData = await getWeatherForecast(city, days);
return html(forecast(city, days, weatherData));
}
return html(forecast());
})
.get("/themes", async ({ html }) => {
const themes = await getThemes();
return html(layout(themesList(themes)));
})
.get("/themes/edit/:name?", async ({ params, html }) => {
const theme = params.name ? await getTheme(params.name) : null;
return html(layout(themeEditor(theme)));
})
.post("/themes/save", async ({ body, html }) => {
const theme = body as Theme;
await saveTheme(theme);
const themes = await getThemes();
return html(themesList(themes));
})
.get("/apply-theme/:name", async ({ params, html }) => {
const theme = await getTheme(params.name as string);
if (theme) {
return html(layout(forecast(), theme.content));
}
return html(layout(forecast()));
})
.get("/uno.css", async () => {
const { createGenerator } = await import('unocss');
const { default: presetUno } = await import('@unocss/preset-uno');
const { default: presetIcons } = await import('@unocss/preset-icons');
const uno = createGenerator({
presets: [
presetUno(),
presetIcons({
scale: 1.2,
warn: true,
}),
],
});
const { css } = await uno.generate('');
return new Response(css, {
headers: {
'content-type': 'text/css',
},
});
})
.listen(3000);
console.log(`Server is running at http://localhost:${app.server?.port}`);src/views/layout.ts:
export const layout = (content: string, customStyles: string = '') => `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather Forecast</title>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
<link href="/uno.css" rel="stylesheet">
<link href="/public/styles.css" rel="stylesheet">
<style>${customStyles}</style>
</head>
<body class="bg-gray-100 text-gray-800">
<div class="container mx-auto p-4">
<h1 class="text-3xl font-bold mb-4 flex items-center"><i class="i-carbon-cloud mr-2"></i>Weather Forecast</h1>
<nav class="mb-4 flex">
<a href="/" class="mr-4 flex items-center"><i class="i-carbon-home mr-1"></i>Home</a>
<a href="/themes" class="mr-4 flex items-center"><i class="i-carbon-list mr-1"></i>Themes</a>
</nav>
${content}
</div>
</body>
</html>
`;src/views/forecast.ts:
import { WeatherData } from "../weather";
const getWeatherIcon = (condition: string): string => {
switch (condition.toLowerCase()) {
case 'sunny':
return 'i-carbon-sun';
case 'cloudy':
return 'i-carbon-cloud';
case 'rainy':
return 'i-carbon-rain';
default:
return 'i-carbon-cloud';
}
};
export const forecast = (city?: string, days?: number, weatherData?: WeatherData[]): string => `
<div class="mb-4">
<form hx-get="/forecast" hx-target="#forecast" class="mb-4">
<input type="text" name="city" placeholder="Enter city" required class="p-2 border rounded mr-2">
<input type="number" name="days" placeholder="Number of days" required min="1" max="7" class="p-2 border rounded mr-2">
<button type="submit" class="p-2 bg-blue-500 text-white rounded flex items-center">
<i class="i-carbon-search mr-1"></i>Get Forecast
</button>
</form>
<div id="forecast">
${weatherData ? `
<h2 class="text-2xl mb-2">Weather Forecast for ${city} (${days} days)</h2>
<ul>
${weatherData.map(day => `
<li class="mb-2 flex items-center">
<i class="${getWeatherIcon(day.condition)} mr-2"></i>
${day.date}: ${day.temperature}°C, ${day.condition}
</li>
`).join('')}
</ul>
` : ''}
</div>
</div>
`;src/views/themes.ts:
import { Theme } from "../themes";
export const themesList = (themes: Theme[]): string => `
<div class="mb-4">
<h2 class="text-2xl mb-2 flex items-center"><i class="i-carbon-list mr-2"></i>Themes</h2>
<ul>
${themes.map(theme => `
<li class="mb-2 flex items-center">
<i class="i-carbon-document mr-2"></i>${theme.name}
<button hx-get="/apply-theme/${theme.name}" hx-target="body" class="ml-2 p-1 bg-blue-500 text-white rounded flex items-center">
<i class="i-carbon-checkmark mr-1"></i>Apply
</button>
<a href="/themes/edit/${theme.name}" class="ml-2 p-1 bg-green-500 text-white rounded flex items-center">
<i class="i-carbon-edit mr-1"></i>Edit
</a>
</li>
`).join('')}
</ul>
<a href="/themes/edit" class="mt-4 inline-block p-2 bg-purple-500 text-white rounded flex items-center">
<i class="i-carbon-add mr-1"></i>Create New Theme
</a>
</div>
`;
export const themeEditor = (theme: Theme | null): string => `
<div class="mb-4">
<h2 class="text-2xl mb-2 flex items-center">
<i class="i-carbon-edit mr-2"></i>${theme ? 'Edit Theme' : 'Create New Theme'}
</h2>
<form hx-post="/themes/save" hx-swap="outerHTML">
<div class="mb-2">
<label for="name" class="block flex items-center"><i class="i-carbon-text-font mr-1"></i>Theme Name:</label>
<input type="text" id="name" name="name" value="${theme?.name || ''}" required class="w-full p-2 border rounded">
</div>
<div class="mb-2">
<label for="content" class="block flex items-center"><i class="i-carbon-code mr-1"></i>Theme Content:</label>
<textarea id="content" name="content" rows="10" required class="w-full p-2 border rounded">${theme?.content || ''}</textarea>
</div>
<button type="submit" class="p-2 bg-blue-500 text-white rounded flex items-center">
<i class="i-carbon-save mr-1"></i>Save Theme
</button>
</form>
</div>
`;src/weather.ts:
export interface WeatherData {
date: string;
temperature: number;
condition: string;
}
export async function getWeatherForecast(city: string, days: number): Promise<WeatherData[]> {
// 模拟API调用
return new Promise((resolve) => {
setTimeout(() => {
const forecast: WeatherData[] = Array.from({ length: days }, (_, i) => ({
date: new Date(Date.now() + i * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
temperature: Math.round(Math.random() * 30),
condition: ['Sunny', 'Cloudy', 'Rainy'][Math.floor(Math.random() * 3)],
}));
resolve(forecast);
}, 500);
});
}src/themes.ts:
export interface Theme {
name: string;
content: string;
}
let themes: Theme[] = [
{ name: 'Default', content: '' },
{ name: 'Dark', content: 'body { background-color: #333; color: #fff; }' },
];
export async function getThemes(): Promise<Theme[]> {
return themes;
}
export async function getTheme(name: string): Promise<Theme | undefined> {
return themes.find(theme => theme.name === name);
}
export async function saveTheme(theme: Theme): Promise<void> {
const index = themes.findIndex(t => t.name === theme.name);
if (index !== -1) {
themes[index] = theme;
} else {
themes.push(theme);
}
}- 创建
uno.config.ts:
import { defineConfig, presetUno, presetIcons } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetIcons({
scale: 1.2,
warn: true,
}),
],
safelist: 'i-carbon-home i-carbon-list i-carbon-edit i-carbon-add i-carbon-checkmark i-carbon-close i-carbon-sun i-carbon-cloud i-carbon-rain'.split(' '),
})这些 TypeScript 文件提供了类型安全和更好的代码组织。确保更新你的 tsconfig.json 文件以包含适当的编译器选项,例如:
{
"compilerOptions": {
"target": "ES 2020",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}最后, 更新你的 package.json 脚本以使用 TypeScript:
{
"scripts": {
"dev": "bun run --watch src/index.ts"
}
}这样, 你就可以使用 bun run dev 来启动 TypeScript 版本的应用了。这个版本提供了更好的类型安全性和代码提示, 有助于减少错误并提高开发效率。