AI实施提示词_辅侦工具箱.md

text
2026年7月17日9 min read1,727 words

Updated 2026年7月17日

辅侦工具箱 · 全量页面实施提示词

以下为所有页面的统一实施指南。请逐页阅读 docs 目录下的需求文档,按本提示词执行前后端实现。


一、项目技术栈

层级 技术选型
前端框架 Next.js 14 (App Router),TypeScript,Tailwind CSS,shadcn/ui
前端 UI 库 lucide-react(图标),@/components/ui/*(Button, Input, Select, Dialog, Table 等)
后端框架 FastAPI + SQLAlchemy + Pydantic
数据库 PostgreSQL(通过 psycopg2 / asyncpg)
接口规范 RESTful,Bearer Token 认证,统一 BaseResponse { code, message, data }
代码风格 中文注释,模块化组件,hooks 抽离业务逻辑

二、公共文件规范(所有页面必须遵循)

2.1 数据库表 → setup.sql

文件路径:backend/scripts/setup.sql

每张表统一格式:

-- 表名:xx
CREATE TABLE IF NOT EXISTS tb_xx (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL COMMENT '姓名',
    id_card VARCHAR(18) UNIQUE COMMENT '身份证号',
    status VARCHAR(20) DEFAULT '待查询' COMMENT '状态:待查询/查询成功/查询失败/查无结果',
    creator_id INTEGER REFERENCES tb_user(id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    is_deleted BOOLEAN DEFAULT FALSE
);

-- 创建索引
CREATE INDEX IF NOT EXISTS idx_xx_id_card ON tb_xx(id_card);
CREATE INDEX IF NOT EXISTS idx_xx_status ON tb_xx(status);
CREATE INDEX IF NOT EXISTS idx_xx_creator ON tb_xx(creator_id);

-- 初始数据(可选)
-- INSERT INTO tb_xx (...) VALUES (...);

通用字段规则(除非文档明确说没有):

  • id SERIAL PRIMARY KEY — 必有
  • created_at / updated_at — 必有
  • creator_id INTEGER REFERENCES tb_user(id) — 必有(记录创建人)
  • is_deleted BOOLEAN DEFAULT FALSE — 逻辑删除标志

2.2 后端 Schema → backend/app/schemas/xx.py

from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime

class XXBase(BaseModel):
    """共享字段"""
    name: Optional[str] = None

class XXCreate(XXBase):
    """创建入参"""
    name: str

class XXUpdate(XXBase):
    """更新入参"""
    pass

class XXItem(XXBase):
    """列表/详情返回"""
    id: int
    status: str
    creator_id: Optional[int]
    created_at: Optional[datetime]
    updated_at: Optional[datetime]

    class Config:
        from_attributes = True

2.3 后端 CRUD → backend/app/crud/xx.py

from sqlalchemy.orm import Session
from app.models.xx import XX as ModelXX
from app.schemas.xx import XXCreate, XXUpdate
from typing import List, Optional, Tuple

def create_xx(db: Session, obj: XXCreate, creator_id: int) -> ModelXX:
    db_obj = ModelXX(**obj.model_dump(), creator_id=creator_id)
    db.add(db_obj)
    db.commit()
    db.refresh(db_obj)
    return db_obj

def get_xx_list(db: Session, skip: int = 0, limit: int = 20, **filters) -> Tuple[List[ModelXX], int]:
    query = db.query(ModelXX).filter(ModelXX.is_deleted == False)
    for k, v in filters.items():
        if v is not None:
            query = query.filter(getattr(ModelXX, k) == v)
    total = query.count()
    items = query.order_by(ModelXX.id.desc()).offset(skip).limit(limit).all()
    return items, total

def get_xx_by_id(db: Session, id: int) -> Optional[ModelXX]:
    return db.query(ModelXX).filter(ModelXX.id == id, ModelXX.is_deleted == False).first()

def update_xx(db: Session, id: int, obj: XXUpdate) -> Optional[ModelXX]:
    db_obj = get_xx_by_id(db, id)
    if not db_obj:
        return None
    for k, v in obj.model_dump(exclude_unset=True).items():
        setattr(db_obj, k, v)
    db.commit()
    db.refresh(db_obj)
    return db_obj

def delete_xx(db: Session, id: int) -> bool:
    db_obj = get_xx_by_id(db, id)
    if not db_obj:
        return False
    db_obj.is_deleted = True
    db.commit()
    return True

2.4 后端路由 → backend/app/api/xx.py

from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.security import get_current_user
from app.schemas.xx import XXCreate, XXUpdate, XXItem
from app.crud import xx as crud_xx
from typing import List, Optional

router = APIRouter(prefix="/xx", tags=["XX模块"])

@router.get("", response_model=dict)
def list_xx(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    status: Optional[str] = None,
    keyword: Optional[str] = None,
    db: Session = Depends(get_db),
    user = Depends(get_current_user),
):
    skip = (page - 1) * page_size
    items, total = crud_xx.get_xx_list(db, skip=skip, limit=page_size, status=status, keyword=keyword)
    return success({
        "items": [XXItem.model_validate(i) for i in items],
        "total": total, "page": page, "page_size": page_size
    })

@router.get("/{id}", response_model=dict)
def get_xx(id: int, db: Session = Depends(get_db), user = Depends(get_current_user)):
    obj = crud_xx.get_xx_by_id(db, id)
    if not obj:
        return error("记录不存在")
    return success(XXItem.model_validate(obj))

@router.post("", response_model=dict)
def create_xx(body: XXCreate, db: Session = Depends(get_db), user = Depends(get_current_user)):
    obj = crud_xx.create_xx(db, body, creator_id=user["id"])
    return success(XXItem.model_validate(obj))

@router.put("/{id}", response_model=dict)
def update_xx(id: int, body: XXUpdate, db: Session = Depends(get_db), user = Depends(get_current_user)):
    obj = crud_xx.update_xx(db, id, body)
    if not obj:
        return error("记录不存在")
    return success(XXItem.model_validate(obj))

@router.delete("/{id}", response_model=dict)
def delete_xx(id: int, db: Session = Depends(get_db), user = Depends(get_current_user)):
    ok = crud_xx.delete_xx(db, id)
    if not ok:
        return error("删除失败")
    return success({"deleted": True})

注册路由到 main.py:

from app.api import xx
router.include_router(xx.router)

2.5 前端类型 → features/xx/types.ts

export interface XXItem {
  id: number;
  name: string;
  status: string;
  creator_id?: number;
  created_at?: string;
  updated_at?: string;
}

export interface XXFilters {
  keyword?: string;
  status?: string;
  id_card?: string;
  page?: number;
  page_size?: number;
}

export interface XXListResponse {
  items: XXItem[];
  total: number;
  page: number;
  page_size: number;
}

2.6 前端 API 层 → features/xx/api.ts

import { api } from '@/utils/api';

export const xxApi = {
  list: (params: Record<string, any>) =>
    api.get<XXListResponse>('/xx', { headers: { params } }),
  get: (id: number) =>
    api.get<XXItem>(`/xx/${id}`),
  create: (body: Partial<XXItem>) =>
    api.post<XXItem>('/xx', body),
  update: (id: number, body: Partial<XXItem>) =>
    api.put<XXItem>(`/xx/${id}`, body),
  delete: (id: number) =>
    api.delete<void>(`/xx/${id}`),
};

2.7 前端 Feature 入口 → features/xx/index.tsx

"use client";
import { XXFeature } from './components/XXFeature';
export { XXFeature };

三、三种页面类型 — 实施模式


类型 A:主界面(主列表页)

识别特征:文档中有「数据展示区」「搜索筛选」「数据表格」「分页」「操作按钮组」

对应路由app/page.tsx 中的 renderContent() 里添加:

if (activeItem === "页面标题") return <XXFeature />;

Feature 组件结构features/xx/components/XXFeature.tsx):

"use client";

import { useState } from 'react';
import { XXSearchFilter } from './XXSearchFilter';
import { XXTable } from './XXTable';
import { XXDialog } from './XXDialog';
import { XXFormDialog } from './XXFormDialog';
import { api } from '@/utils/api';
import { XXItem, XXFilters } from '../types';

export function XXFeature() {
  const [filters, setFilters] = useState<XXFilters>({});
  const [selectedIds, setSelectedIds] = useState<number[]>([]);
  const [dialogOpen, setDialogOpen] = useState(false);
  const [editId, setEditId] = useState<number | null>(null);
  const [formOpen, setFormOpen] = useState(false);

  const handleSearch = (f: XXFilters) => setFilters(f);
  const handleReset = () => setFilters({});
  const handleEdit = (id: number) => { setEditId(id); setFormOpen(true); };
  const handleDelete = async (id: number) => { /* ... */ };

  return (
    <div className="flex flex-col h-full">
      <XXSearchFilter onSearch={handleSearch} onReset={handleReset} />
      <XXTable
        filters={filters}
        selectedIds={selectedIds}
        onSelectionChange={setSelectedIds}
        onEdit={handleEdit}
        onDelete={handleDelete}
        onRefresh={() => {}}  // passed as prop, triggers table reload
      />
    </div>
  );
}

搜索筛选组件XXSearchFilter.tsx):

"use client";
import { useState } from 'react';
import { Search, RotateCcw } from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Select } from '@/components/ui/Select';
import { XXFilters } from '../types';

const STATUS_OPTIONS = [
  { label: '待查询', value: '待查询' },
  { label: '查询成功', value: '查询成功' },
  // ... 按文档中的审核/状态字段填充
];

interface Props {
  onSearch: (f: XXFilters) => void;
  onReset: () => void;
}

export function XXSearchFilter({ onSearch, onReset }: Props) {
  const [f, setF] = useState<XXFilters>({});

  return (
    <div className="bg-white px-4 py-3 border-b border-border-custom space-y-2">
      <div className="flex items-center flex-wrap gap-2">
        {/* 身份证筛选 — 仅当文档有「身份证」列时添加 */}
        <div className="flex items-center space-x-1">
          <span className="text-[12px] text-text-secondary w-14">身份证</span>
          <Input className="h-8 w-44 text-[12px]" placeholder="请输入身份证"
            value={f.id_card || ''} onChange={e => setF({...f, id_card: e.target.value})} />
        </div>
        {/* 关键词筛选 */}
        <div className="flex items-center space-x-1">
          <span className="text-[12px] text-text-secondary w-14">关键词</span>
          <Input className="h-8 w-36 text-[12px]" placeholder="请输入"
            value={f.keyword || ''} onChange={e => setF({...f, keyword: e.target.value})} />
        </div>
        {/* 状态筛选 — 仅当文档有「审核状态」「查询状态」列时添加 */}
        <div className="flex items-center space-x-1">
          <span className="text-[12px] text-text-secondary w-14">审核状态</span>
          <Select className="h-8 w-32 text-[12px]" options={STATUS_OPTIONS}
            value={f.status || ''} onChange={v => setF({...f, status: v})} placeholder="请选择" />
        </div>
        {/* 查询/重置按钮 */}
        <div className="flex items-center space-x-2 ml-auto">
          <Button variant="primary" size="sm" className="h-8" onClick={() => onSearch(f)}>
            <Search className="w-3.5 h-3.5 mr-1" />查询
          </Button>
          <Button variant="outline" size="sm" className="h-8" onClick={() => { setF({}); onReset(); }}>
            <RotateCcw className="w-3.5 h-3.5 mr-1" />重置
          </Button>
        </div>
      </div>
    </div>
  );
}

表格组件XXTable.tsx):

"use client";
import { useState, useEffect } from 'react';
import { ChevronLeft, ChevronRight, RotateCw, Trash2, Eye } from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { Checkbox } from '@/components/ui/Checkbox';
import { XXItem, XXFilters, XXListResponse } from '../types';
import { xxApi } from '../api';

const STATUS_COLORS: Record<string, string> = {
  '查询成功': 'bg-green-100 text-green-700',
  '查询失败': 'bg-red-100 text-red-700',
  '待查询': 'bg-yellow-100 text-yellow-700',
  // ... 按实际状态值填充
};

interface Props {
  filters: XXFilters;
  selectedIds: number[];
  onSelectionChange: (ids: number[]) => void;
  onEdit: (id: number) => void;
  onDelete: (id: number) => void;
  onRefresh: () => void;
}

export function XXTable({ filters, selectedIds, onSelectionChange, onEdit, onDelete, onRefresh }: Props) {
  const [data, setData] = useState<XXListResponse>({ items: [], total: 0, page: 1, page_size: 10 });
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const PAGE_SIZE = 10;

  const load = async () => {
    setLoading(true);
    try {
      const res = await xxApi.list({ ...filters, page, page_size: PAGE_SIZE });
      setData(res);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { load(); }, [filters, page]);

  const toggleAll = (checked: boolean) => {
    if (checked) onSelectionChange(data.items.map(i => i.id));
    else onSelectionChange([]);
  };

  const toggleOne = (id: number) => {
    if (selectedIds.includes(id)) onSelectionChange(selectedIds.filter(x => x !== id));
    else onSelectionChange([...selectedIds, id]);
  };

  const handleBatchDelete = async () => {
    if (!selectedIds.length) return;
    if (!confirm(`确认删除 ${selectedIds.length} 条记录?`)) return;
    await Promise.all(selectedIds.map(id => xxApi.delete(id)));
    onSelectionChange([]);
    load();
  };

  const columns = [
    { label: '序号', width: 60 },
    { label: '身份证', dataIndex: 'id_card', width: 180 },
    { label: '姓名', dataIndex: 'name', width: 120 },
    { label: '状态', dataIndex: 'status', width: 120, render: (v: string) =>
      <span className={`px-2 py-0.5 rounded text-[11px] font-medium ${STATUS_COLORS[v] || 'bg-gray-100 text-gray-600'}`}>{v}</span> },
    { label: '创建时间', dataIndex: 'created_at', width: 180 },
    { label: '操作', width: 120, render: (_: any, row: XXItem) => (
      <div className="flex items-center space-x-2">
        <button onClick={() => onEdit(row.id)} className="text-primary-active hover:underline text-[12px]">编辑</button>
        <button onClick={() => onDelete(row.id)} className="text-red-500 hover:underline text-[12px]">删除</button>
      </div>
    )},
  ];

  return (
    <div className="flex-1 overflow-auto p-4 space-y-3">
      {/* 批量操作栏 */}
      {selectedIds.length > 0 && (
        <div className="bg-blue-50 border border-blue-200 rounded px-4 py-2 flex items-center justify-between">
          <span className="text-[12px] text-blue-700">已选中 {selectedIds.length} 项</span>
          <div className="flex space-x-2">
            <Button variant="outline" size="sm" className="h-7 text-[12px]"
              onClick={handleBatchDelete}>
              <Trash2 className="w-3.5 h-3.5 mr-1" />批量删除
            </Button>
          </div>
        </div>
      )}

      {/* 数据表格 */}
      <div className="bg-white rounded border border-border-custom overflow-hidden">
        <table className="w-full text-[13px]">
          <thead className="bg-[#F7F8FA]">
            <tr>
              <th className="px-3 py-2.5 text-left w-10">
                <Checkbox checked={selectedIds.length === data.items.length && data.items.length > 0}
                  onChange={toggleAll} />
              </th>
              {columns.map(col => (
                <th key={col.label} className="px-3 py-2.5 text-left text-[#999] font-normal"
                  style={{ width: col.width }}>
                  {col.label}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {loading ? (
              <tr><td colSpan={columns.length + 1} className="text-center py-10 text-[#999]">加载中...</td></tr>
            ) : data.items.length === 0 ? (
              <tr><td colSpan={columns.length + 1} className="text-center py-10 text-[#999]">暂无数据</td></tr>
            ) : data.items.map((row, idx) => (
              <tr key={row.id} className="border-t border-border-custom hover:bg-slate-50">
                <td className="px-3 py-2.5"><Checkbox checked={selectedIds.includes(row.id)} onChange={() => toggleOne(row.id)} /></td>
                {columns.map(col => (
                  <td key={col.label} className="px-3 py-2.5 text-text-primary">
                    {col.render ? col.render((row as any)[col.dataIndex!], row) : (row as any)[col.dataIndex!]}
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* 分页 */}
      <div className="flex items-center justify-between px-1">
        <span className="text-[12px] text-text-secondary">共 {data.total} 条</span>
        <div className="flex items-center space-x-1">
          <Button variant="outline" size="sm" className="h-7 w-7 p-0"
            disabled={page <= 1} onClick={() => setPage(p => p - 1)}>
            <ChevronLeft className="w-4 h-4" />
          </Button>
          <span className="text-[12px] text-text-primary px-2">{page}</span>
          <Button variant="outline" size="sm" className="h-7 w-7 p-0"
            disabled={page * PAGE_SIZE >= data.total} onClick={() => setPage(p => p + 1)}>
            <ChevronRight className="w-4 h-4" />
        …76272 tokens truncated…面全局顶部导航、侧边菜单栏、数据表格、筛选表单、分页控件、页面底部信息区。
分组规则:配置项垂直单行均匀排布,底部按钮横向并排。

# 二、分区域元素拆解
## (一)弹窗顶部标题栏
1. 静态标题文本:列设置
2. 右上角图标按钮组
    - 全屏/还原图标按钮
    - 关闭弹窗图标按钮(×)

## (二)列配置多选列表区(垂直排布单行配置项)
每行统一结构:拖拽排序图标 + 勾选复选框 + 字段名称静态文本
1. 配置项1:复选框勾选,字段名「图片标题」
2. 配置项2:复选框勾选,字段名「人像图片」
3. 配置项3:复选框勾选,字段名「任务名称」
4. 配置项4:复选框勾选,字段名「审核状态」
5. 配置项5:复选框勾选,字段名「查询状态」
6. 配置项6:复选框勾选,字段名「案件编号」
7. 配置项7:复选框勾选,字段名「查询事由」
8. 配置项8:复选框勾选,字段名「提交人/提交IP」
9. 配置项9:复选框勾选,字段名「提交单位/时间」
10. 配置项10:复选框勾选,字段名「任务证书」

## (三)弹窗底部操作按钮组(横向排布)
1. 次按钮:重置
2. 主按钮:确定

## (四)无交互元素
页面半透明重复水印文本

# 三、元素类型分类汇总清单
## 1. 表单类输入控件
### 选择类控件
复选框(共10个,全部勾选,对应表格列显隐配置)
### 缺失表单元素
无单行输入框、多行文本域、下拉、单选、日期、上传控件

## 2. 按钮类元素
### 弹窗控制图标按钮
弹窗全屏按钮、弹窗关闭×图标按钮
### 配置操作按钮
重置(次按钮)、确定(主按钮)
### 辅助交互图标
每行左侧拖拽排序图标(用于调整列展示顺序)

## 3. 表格/列表类组件
无业务数据表格、数据行、分页、筛选、行操作按钮;仅表格列配置清单

## 4. 静态文本展示元素
弹窗标题「列设置」、各表格字段名称文本、水印背景文本

## 5. 缺失组件说明
无顶部全局导航、左侧树形侧边菜单、页面标签、业务查询表单、数据明细表格、批量操作按钮、图片上传区域、页面底部状态栏、新增/编辑/删除/查询功能按钮