一个移动端友好的网页应用,供内部员工对政府股室的"德、能、勤、绩"四个维度进行评价。
- 18个股室评价:完整覆盖所有政府股室
- 四维度评价:德、能、勤、绩四个评价维度
- 四级评分:优秀、较好、一般、差
- 智能防重复:
- 基于 localStorage 的设备 ID 标识
- 同一设备每天只能评价同一股室一次
- 支持多设备分别评价(PC、手机)
- 评价状态追踪:评价完的股室当天不再显示,次日自动恢复
- 统计可视化:图表展示各维度的评价分布
| 层级 | 技术 | 说明 |
|---|---|---|
| 前端框架 | Next.js 14 | App Router,服务端渲染 |
| UI 样式 | Tailwind CSS | 移动端响应式设计 |
| 数据库 | PostgreSQL | 关系型数据库 |
| ORM | Prisma | 类型安全的数据库访问 |
| 图表库 | Recharts | React 友好的图表库 |
evaluate/
├── app/ # Next.js App Router
│ ├── page.tsx # 首页:股室列表
│ ├── evaluate/[id]/page.tsx # 评价页面
│ ├── results/page.tsx # 统计结果页面
│ └── api/ # API 路由
│ ├── offices/ # 股室相关 API
│ ├── evaluations/ # 评价提交 API
│ └── stats/ # 统计数据 API
├── components/ # React 组件
│ ├── office-card.tsx # 股室卡片
│ ├── stats-chart.tsx # 统计图表
│ └── ui/ # UI 基础组件
├── lib/ # 工具函数
│ ├── db.ts # 数据库客户端
│ ├── device-id.ts # 设备 ID 生成
│ ├── ip.ts # IP 获取工具
│ ├── constants.ts # 常量定义
│ └── utils.ts # 通用工具
├── prisma/ # Prisma 配置
│ ├── schema.prisma # 数据模型
│ └── seed.ts # 初始数据
└── styles/ # 样式文件
- Node.js 18+
- PostgreSQL 数据库
- npm 或 yarn
npm install复制 .env.example 为 .env:
cp .env.example .env编辑 .env 文件,配置数据库连接:
# 数据库连接字符串
DATABASE_URL="postgresql://用户名:密码@localhost:5432/数据库名"
# 应用 URL
NEXT_PUBLIC_APP_URL="http://localhost:8080"# 创建数据库表结构
npx prisma migrate dev --name init
# 初始化 18 个股室数据
npx prisma db seednpm run dev应用将在 http://localhost:8080 启动(默认端口 8080,避免 Windows 保留端口冲突)。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Int | 主键 |
| name | String(100) | 股室名称 |
| display_order | Int | 显示顺序 |
| created_at | DateTime | 创建时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Int | 主键 |
| office_id | Int | 股室 ID |
| ip_address | String(45) | IP 地址(用于记录) |
| device_id | String(100) | 设备 ID(用于去重) |
| virtue | String(10) | 德:优秀/较好/一般/差 |
| ability | String(10) | 能:优秀/较好/一般/差 |
| diligence | String(10) | 勤:优秀/较好/一般/差 |
| performance | String(10) | 绩:优秀/较好/一般/差 |
| evaluated_at | Date | 评价日期 |
| created_at | DateTime | 创建时间 |
唯一约束:office_id + device_id + evaluated_at 确保同一设备每天只能评价同一股室一次。
获取今日可评价的股室列表(排除已评价的股室)。
请求头:
x-device-id: 设备ID
响应:
{
"offices": [
{ "id": 1, "name": "安监办主任", "display_order": 1 },
{ "id": 2, "name": "审计室主任", "display_order": 2 }
]
}提交评价记录。
请求头:
Content-Type: application/json
x-device-id: 设备ID
请求体:
{
"office_id": 1,
"virtue": "优秀",
"ability": "较好",
"diligence": "一般",
"performance": "优秀"
}响应:
{
"success": true,
"message": "评价提交成功"
}获取所有股室概览统计。
响应:
{
"stats": [
{ "id": 1, "name": "安监办主任", "evaluationCount": 15 }
]
}获取指定股室的详细统计。
响应:
{
"officeName": "安监办主任",
"totalPeople": 15,
"totalCount": 18,
"virtue": { "优秀": 10, "较好": 5, "一般": 2, "差": 1 },
"ability": { "优秀": 8, "较好": 6, "一般": 3, "差": 1 },
"diligence": { "优秀": 12, "较好": 4, "一般": 1, "差": 1 },
"performance": { "优秀": 9, "较好": 5, "一般": 3, "差": 1 }
}- 安监办主任
- 审计室主任
- 计财股股长
- 信访接待室主任
- 督导室主任
- 后勤办主任
- 办公室主任
- 招生考试信息中心主任
- 行政审批办公室主任
- 资助中心主任
- 体卫艺幼股副股长
- 人事股股长
- 监察室主任
- 现代教育技术管理中心主任
- 基础教育股股长
- 项目办主任
- 党办主任
- 职成教股股长
编辑 prisma/seed.ts,在 offices 数组中添加新的股室名称:
const offices = [
"安监办主任",
"审计室主任",
"新股室名称", // 添加新股室
// ...
]然后运行:
npx prisma db seed编辑 lib/constants.ts:
export const RATING_OPTIONS = ["优秀", "较好", "一般", "差"] as const
export const DIMENSION_DESCRIPTIONS = {
virtue: "德:思想品德、职业道德",
ability: "能:业务能力、工作水平",
diligence: "勤:工作态度、出勤情况",
performance: "绩:工作实绩、完成任务",
} as const如需清空所有数据重新开始:
npx prisma migrate reset
npx prisma db seed- 连接 GitHub 仓库到 Vercel
- 配置环境变量
DATABASE_URL - 使用外部 PostgreSQL(Supabase、Neon、Railway)
- 自动部署
首次部署后运行:
npx prisma migrate deploy
npx prisma db seed使用 Docker Compose 部署应用,连接线上 PostgreSQL 数据库。
确保已创建 .env 文件并配置线上数据库连接:
DATABASE_URL="postgresql://用户名:密码@线上主机:端口/数据库名"# 构建并启动应用
docker-compose up -d
# 查看日志
docker-compose logs -f
# 停止应用
docker-compose down
# 重新构建并启动
docker-compose up -d --build启动后访问:http://localhost:8080
仅部署应用(需要外部数据库):
# 构建镜像
docker build -t evaluate .
# 运行容器
docker run -d \
--name evaluate \
-p 8080:3000 \
-e DATABASE_URL="postgresql://用户名:密码@主机:5432/数据库名" \
evaluate默认使用端口 8080,因为 Windows 系统保留了 3000-3077 端口段。
修改端口:
// package.json
"dev": "cross-env HOST=127.0.0.1 PORT=你的端口 next dev"- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- 移动端浏览器(iOS Safari、Chrome Mobile)
- 微信内置浏览器
MIT