# Protein_synthesis
**Repository Path**: JLLAYUN/protein_synthesis
## Basic Information
- **Project Name**: Protein_synthesis
- **Description**: 关于蛋白质某一合成过程,使用微信小程序
- **Primary Language**: CSS
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-10-29
- **Last Updated**: 2026-02-28
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Coze Mini Program
这是一个基于 [Taro 4](https://docs.taro.zone/docs/) + [Nest.js](https://nestjs.com/) 的前后端分离项目,由扣子编程 CLI 创建。
## 技术栈
- **整体框架**: Taro 4.1.9
- **语言**: TypeScript 5.4.5
- **渲染**: React 18.0.0
- **样式**: TailwindCSS 4.1.18
- **Tailwind 适配层**: weapp-tailwindcss 4.9.2
- **状态管理**: Zustand 5.0.9
- **图标库**: lucide-react-taro latest
- **工程化**: Vite 4.2.0
- **包管理**: pnpm
- **运行时**: Node.js >= 18
- **服务端**: NestJS 10.4.15
- **数据库 ORM**: Drizzle ORM 0.45.1
- **类型校验**: Zod 4.3.5
## 项目结构
```
├── .cozeproj/ # Coze 平台配置
│ └── scripts/ # 构建和运行脚本
├── config/ # Taro 构建配置
│ ├── index.ts # 主配置文件
│ ├── dev.ts # 开发环境配置
│ └── prod.ts # 生产环境配置
├── server/ # NestJS 后端服务
│ └── src/
│ ├── main.ts # 服务入口
│ ├── app.module.ts # 根模块
│ ├── app.controller.ts # 应用控制器
│ └── app.service.ts # 应用服务
├── src/ # 前端源码
│ ├── pages/ # 页面组件
│ ├── presets/ # 框架预置逻辑(无需读取,如无必要不改动)
│ ├── utils/ # 工具函数
│ ├── network.ts # 封装好的网络请求工具
│ ├── app.ts # 应用入口
│ ├── app.config.ts # 应用配置
│ └── app.css # 全局样式
├── types/ # TypeScript 类型定义
├── key/ # 小程序密钥(CI 上传用)
├── .env.local # 环境变量
└── project.config.json # 微信小程序项目配置
```
## 快速开始
### 安装依赖
```bash
pnpm install
```
### 本地开发
同时启动 H5 前端和 NestJS 后端:
```bash
pnpm dev
```
- 前端地址:http://localhost:5000
- 后端地址:http://localhost:3000
单独启动:
```bash
pnpm dev:web # 仅 H5 前端
pnpm dev:weapp # 仅微信小程序
pnpm dev:server # 仅后端服务
```
### 构建
```bash
pnpm build # 构建所有(H5 + 小程序 + 后端)
pnpm build:web # 仅构建 H5,输出到 dist-web
pnpm build:weapp # 仅构建微信小程序,输出到 dist
pnpm build:server # 仅构建后端
```
### 预览小程序
```bash
pnpm preview:weapp # 构建并生成预览小程序二维码
```
## 前端核心开发规范
### 新建页面流程
1. 在 \`src/pages/\` 下创建页面目录
2. 创建 \`index.tsx\`(页面组件)
3. 创建 \`index.config.ts\`(页面配置)
4. 创建 \`index.css\`(页面样式,可选)
5. 在 \`src/app.config.ts\` 的 \`pages\` 数组中注册页面路径
或使用 Taro 脚手架命令:
```bash
pnpm new # 交互式创建页面/组件
```
### 常用 Taro 组件
引入方式
```typescript
import { Text } from '@tarojs/components'
```
- 基础组件
- Text
- Icon
- Progress
- RichText
- 表单组件
- Button
- Checkbox
- CheckboxGroup
- Editor
- Form
- Input
- Label
- Picker
- PickerView
- PickerViewColumn
- Radio
- RadioGroup
- Slider
- Switch
- Textarea
- 导航组件
- FunctionalPageNavigator
- NavigationBar
- Navigator
- TabItem
- Tabs
- 媒体组件
- Camera
- Image
- Video
- 视图容器
- ScrollView
- Swiper
- SwiperItem
- View
### 路径别名
项目配置了 `@/*` 路径别名指向 `src/*`:
```typescript
import { SomeComponent } from '@/components/SomeComponent'
import { useUserStore } from '@/stores/user'
```
### 代码模板
#### 页面组件 (TypeScript + React)
```tsx
// src/pages/example/index.tsx
import { View, Text } from '@tarojs/components'
import { useLoad, useDidShow } from '@tarojs/taro'
import type { FC } from 'react'
import './index.css'
const ExamplePage: FC = () => {
useLoad(() => {
console.log('Page loaded.')
})
useDidShow(() => {
console.log('Page showed.')
})
return (
Hello Taro!
)
}
export default ExamplePage
```
#### 页面配置
```typescript
// src/pages/example/index.config.ts
import { definePageConfig } from '@tarojs/taro'
export default definePageConfig({
navigationBarTitleText: '示例页面',
enablePullDownRefresh: true,
backgroundTextStyle: 'dark',
})
```
#### 应用配置
```typescript
// src/app.config.ts
import { defineAppConfig } from '@tarojs/taro'
export default defineAppConfig({
pages: [
'pages/index/index',
'pages/example/index',
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'App',
navigationBarTextStyle: 'black',
},
// TabBar 配置 (可选)
// tabBar: {
// list: [
// { pagePath: 'pages/index/index', text: '首页' },
// ],
// },
})
```
### 发送请求
**IMPORTANT: 禁止直接使用 Taro.request、Taro.uploadFile、Taro.downloadFile,使用 Network.request、Network.uploadFile、Network.downloadFile 替代。**
Network 是对 Taro.request、Taro.uploadFile、Taro.downloadFile 的封装,自动添加项目域名前缀,参数与 Taro 一致。
✅ 正确使用方式
```typescript
import { Network } from '@/network'
// GET 请求
const data = await Network.request({
url: '/api/hello'
})
// POST 请求
const result = await Network.request({
url: '/api/user/login',
method: 'POST',
data: { username, password }
})
// 文件上传
await Network.uploadFile({
url: '/api/upload',
filePath: tempFilePath,
name: 'file'
})
// 文件下载
await Network.downloadFile({
url: '/api/download/file.pdf'
})
```
❌ 错误用法
```typescript
import Taro from '@tarojs/taro'
// ❌ 会导致自动域名拼接无法生效,除非是特殊指定域名
const data = await Network.request({
url: 'http://localhost/api/hello'
})
// ❌ 不要直接使用 Taro.request
await Taro.request({ url: '/api/hello' })
// ❌ 不要直接使用 Taro.uploadFile
await Taro.uploadFile({ url: '/api/upload', filePath, name: 'file' })
```
### Zustand 状态管理
```typescript
// src/stores/user.ts
import { create } from 'zustand'
interface UserState {
userInfo: UserInfo | null
token: string
setUserInfo: (info: UserInfo) => void
setToken: (token: string) => void
logout: () => void
}
interface UserInfo {
id: string
name: string
avatar: string
}
export const useUserStore = create((set) => ({
userInfo: null,
token: '',
setUserInfo: (info) => set({ userInfo: info }),
setToken: (token) => set({ token }),
logout: () => set({ userInfo: null, token: '' }),
}))
```
### Taro 生命周期 Hooks
```typescript
import {
useLoad, // 页面加载 (onLoad)
useReady, // 页面初次渲染完成 (onReady)
useDidShow, // 页面显示 (onShow)
useDidHide, // 页面隐藏 (onHide)
usePullDownRefresh, // 下拉刷新 (onPullDownRefresh)
useReachBottom, // 触底加载 (onReachBottom)
useShareAppMessage, // 分享 (onShareAppMessage)
useRouter, // 获取路由参数
} from '@tarojs/taro'
```
### 路由导航
```typescript
import Taro from '@tarojs/taro'
// 保留当前页面,跳转到新页面
Taro.navigateTo({ url: '/pages/detail/index?id=1' })
// 关闭当前页面,跳转到新页面
Taro.redirectTo({ url: '/pages/detail/index' })
// 跳转到 tabBar 页面
Taro.switchTab({ url: '/pages/index/index' })
// 返回上一页
Taro.navigateBack({ delta: 1 })
// 获取路由参数
const router = useRouter()
const { id } = router.params
```
### 图标使用 (lucide-react-taro)
**IMPORTANT: 禁止使用 lucide-react,必须使用 lucide-react-taro 替代。**
lucide-react-taro 是 Lucide 图标库的 Taro 适配版本,专为小程序环境优化,API 与 lucide-react 一致:
```tsx
import { View } from '@tarojs/components'
import { House, Settings, User, Search, Camera, Zap } from 'lucide-react-taro'
const IconDemo = () => {
return (
{/* 基本用法 */}
{/* 自定义尺寸和颜色 */}
{/* 自定义描边宽度 */}
{/* 绝对描边宽度(描边不随 size 缩放) */}
{/* 组合使用 */}
)
}
```
常用属性:
- `size` - 图标大小(默认 24)
- `color` - 图标颜色(默认 currentColor,小程序中建议显式设置)
- `strokeWidth` - 线条粗细(默认 2)
- `absoluteStrokeWidth` - 绝对描边宽度,启用后描边不随 size 缩放
- `className` / `style` - 自定义样式
更多图标请访问:https://lucide.dev/icons
### TabBar 图标生成 (CLI 工具)
**IMPORTANT: 微信小程序的 TabBar 不支持 base64 或 SVG 图片,必须使用本地 PNG 文件。**
lucide-react-taro 提供了 CLI 工具来生成 TabBar 所需的 PNG 图标:
```bash
# 生成带选中状态的图标
npx taro-lucide-tabbar House Settings User -c "#999999" -a "#1890ff"
# 指定输出目录和尺寸
npx taro-lucide-tabbar House Settings User -c "#999999" -a "#1890ff" -o ./src/assets/tabbar -s 81
```
CLI 参数:
- `--color, -c` (默认 #000000): 图标颜色
- `--active-color, -a`: 选中状态颜色
- `--size, -s` (默认 81): 图标尺寸
- `--output, -o` (默认 ./tabbar-icons): 输出目录
- `--stroke-width` (默认 2): 描边宽度
在 `app.config.ts` 中使用生成的图标:
```typescript
export default defineAppConfig({
tabBar: {
color: '#999999',
selectedColor: '#1890ff',
backgroundColor: '#ffffff',
borderStyle: 'black',
list: [
{
pagePath: 'pages/index/index',
text: '首页',
iconPath: './assets/tabbar/house.png',
selectedIconPath: './assets/tabbar/house-active.png',
},
{
pagePath: 'pages/settings/index',
text: '设置',
iconPath: './assets/tabbar/settings.png',
selectedIconPath: './assets/tabbar/settings-active.png',
},
{
pagePath: 'pages/user/index',
text: '用户',
iconPath: './assets/tabbar/user.png',
selectedIconPath: './assets/tabbar/user-active.png',
},
],
},
})
### Tailwind CSS 样式开发
IMPORTANT:必须使用 tailwindcss 实现样式,只有在必要情况下才能 fallback 到 css / less
> 项目已集成 Tailwind CSS 4.x + weapp-tailwindcss,支持跨端原子化样式:
```tsx
标题
```
### 性能优化
#### 图片懒加载
```tsx
import { Image } from '@tarojs/components'
```
#### 虚拟列表
```tsx
import { VirtualList } from '@tarojs/components'
(
{data[index].name}
)}
/>
```
#### 分包加载
```typescript
// src/app.config.ts
export default defineAppConfig({
pages: ['pages/index/index'],
subPackages: [
{
root: 'packageA',
pages: ['pages/detail/index'],
},
],
})
```
### 小程序限制
| 限制项 | 说明 |
| -------- | ---------------------------------------- |
| 主包体积 | ≤ 2MB |
| 总包体积 | ≤ 20MB |
| 域名配置 | 生产环境需在小程序后台配置合法域名 |
| 本地开发 | 需在微信开发者工具开启「不校验合法域名」 |
### 权限配置
```typescript
// src/app.config.ts
export default defineAppConfig({
// ...其他配置
permission: {
'scope.userLocation': {
desc: '你的位置信息将用于小程序位置接口的效果展示'
}
},
requiredPrivateInfos: ['getLocation', 'chooseAddress']
})
```
### 位置服务
```typescript
// 需先在 app.config.ts 中配置 permission
async function getLocation(): Promise {
return await Taro.getLocation({ type: 'gcj02' })
}
```
## 后端核心开发规范
本项目后端基于 NestJS + TypeScript 构建,提供高效、可扩展的服务端能力。
### 项目结构
```sh
.
├── server/ # NestJS 后端服务
│ └── src/
│ ├── main.ts # 服务入口
│ ├── app.module.ts # 根模块
│ ├── app.controller.ts # 根控制器
│ └── app.service.ts # 根服务
```
### 开发命令
```sh
pnpm dev:server // 启动开发服务 (热重载, 默认端口 3000)
pnpm build:server // 构建生产版本
```
### 新建模块流程 (CLI)
快速生成样板代码:
```bash
cd server
# 生成完整的 CRUD 资源 (包含 Module, Controller, Service, DTO, Entity)
npx nest g resource modules/product
# 仅生成特定部分
npx nest g module modules/order
npx nest g controller modules/order
npx nest g service modules/order
```
### 环境变量配置
在 server/ 根目录创建 .env 文件:
```sh
## 服务端口
PORT=3000
## 微信小程序配置
WX_APP_ID=你的AppID
WX_APP_SECRET=你的AppSecret
## JWT 密钥
JWT_SECRET=your-super-secret-key
```
在代码中使用 @nestjs/config 读取环境变量:
```typescript
import { ConfigService } from '@nestjs/config';
// 在 Service 中注入
constructor(private configService: ConfigService) {}
getWxConfig() {
return {
appId: this.configService.get('WX_APP_ID'),
secret: this.configService.get('WX_APP_SECRET'),
};
}
```
### 标准响应封装
建议使用拦截器 (Interceptor) 统一 API 响应格式:
```typeScript
// src/common/interceptors/transform.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response {
code: number;
data: T;
message: string;
}
@Injectable()
export class TransformInterceptor implements NestInterceptor> {
intercept(context: ExecutionContext, next: CallHandler): Observable> {
return next.handle().pipe(
map((data) => ({
code: 200,
data,
message: 'success',
})),
);
}
}
```
在 main.ts 中全局注册:
```typescript
app.useGlobalInterceptors(new TransformInterceptor());
```
### 微信登录后端实现
```typescript
// src/modules/auth/auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { lastValueFrom } from 'rxjs';
@Injectable()
export class AuthService {
constructor(
private httpService: HttpService,
private configService: ConfigService,
) {}
async code2Session(code: string) {
const appId = this.configService.get('WX_APP_ID');
const secret = this.configService.get('WX_APP_SECRET');
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;
const { data } = await lastValueFrom(this.httpService.get(url));
if (data.errcode) {
throw new UnauthorizedException(`微信登录失败: ${data.errmsg}`);
}
return data; // 包含 openid, session_key
}
}
```
### 异常处理
使用全局异常过滤器 (Filter) 统一错误响应:
```typescript
// src/common/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
response.status(status).json({
code: status,
message: typeof exceptionResponse === 'string' ? exceptionResponse : (exceptionResponse as any).message,
data: null,
});
}
}
```
在 main.ts 中注册:
```
app.useGlobalFilters(new HttpExceptionFilter());
```
### 数据库 (Drizzle ORM)
推荐使用 [Drizzle ORM](https://orm.drizzle.team/),已预安装。
### 类型校验 (Zod)
项目集成了 [Zod](https://zod.dev/) 用于运行时类型校验。
#### 定义 Schema
```typescript
import { z } from 'zod';
// 基础类型
const userSchema = z.object({
id: z.number(),
name: z.string().min(1).max(50),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
// 从 schema 推导 TypeScript 类型
type User = z.infer;
```
#### 请求校验
```typescript
// src/modules/user/dto/create-user.dto.ts
import { z } from 'zod';
export const createUserSchema = z.object({
nickname: z.string().min(1, '昵称不能为空').max(20, '昵称最多20个字符'),
avatar: z.string().url('头像必须是有效的URL').optional(),
phone: z.string().regex(/^1[3-9]\d{9}$/, '手机号格式不正确').optional(),
});
export type CreateUserDto = z.infer;
// 在 Controller 中使用
@Post()
create(@Body() body: unknown) {
const result = createUserSchema.safeParse(body);
if (!result.success) {
throw new BadRequestException(result.error.errors);
}
return this.userService.create(result.data);
}
```