专门针对 aheadofcow_web 前端项目的代码生成和修改技能。
本技能仅操作前端项目,严禁修改后端代码!
| 项目 | 允许操作 | 禁止操作 |
|---|---|---|
| ------ | --------- | --------- |
| 前端项目 | ✅ aheadofcow_web/ 目录 | - |
| 后端项目 | - | ❌ backend_transaction/ 目录 |
| API 接口定义 | ❌ 禁止生成 | 不生成任何 API 接口文件 |
| 路由配置 | ✅ 前端路由(src/router/) | ❌ 后端路由配置 |
本技能专门用于 aheadofcow_web 前端项目的页面生成、修改和维护。
工作目录:/home/admin/codespace/scp/aheadofcow_web
适用场景:
src/router/index.js)/home/admin/codespace/scp/aheadofcow_web/
├── src/
│ ├── views/ # 页面组件
│ │ └── requirements-planning/ # 需求计划模块
│ │ ├── new-retail-reporting/
│ │ │ └── index.jsx
│ │ └── new-retail-plan/
│ │ ├── index.jsx
│ │ └── components/
│ └── router/ # 路由配置
│ └── index.js
├── package.json
└── ...
用户提供截图后,AI 自动:
aheadofcow_web/ 目录)当 image 工具调用失败(如 API key 错误)时:
帮我根据这张截图生成前端页面
参照这个页面的样式,生成 XXX 功能页面
修改 XXX 页面,改成和图中一样的列结构
import React, { memo, useState, useEffect, useRef } from 'react'
import { Button, Space, App, Table, Tag, InputNumber, Select } from 'antd'
import { AuthButton } from '@/components/AuthBtn'
import PageTitle from '@/components/PageTitle/Index'
const View = memo(() => {
const { message } = App.useApp()
// 状态定义
const [loading, setLoading] = useState(false)
const [dataList, setDataList] = useState([])
// Mock 数据生成(仅列表页面)
const generateMockData = () => { ... }
// 数据查询
const queryData = async () => { ... }
// 初始化
useEffect(() => { queryData() }, [])
// 操作函数
const handleSave = async () => { ... }
return (
<>
<PageTitle>页面标题</PageTitle>
<div className='pageContent'>
{/* 页面内容 */}
</div>
</>
)
})
export default View
> ⚠️ 注意:严禁导入或生成 @/api/ 相关的 API 接口代码。
| 图片元素 | Ant Design 组件 | 代码示例 |
|---|---|---|
| --------- | ---------------- | ---------- |
| 纯文本 | 直接 render | render: (_, record) => record.xxx |
| 状态标签 | | render: (val) => val === 1 ? |
| 数字输入 | | render: (val) => |
| 日期 | 直接 render | render: (val) => dayjs(val).format('YYYY-MM-DD') |
| 操作按钮 | | render: () => |
| 位置 | 类型 | 样式 |
|---|---|---|
| ------ | ------ | ------ |
| 顶部操作区 | 默认 | |
| 底部主要操作 | primary | |
| 确认提交 | primary + 绿色 | style={{ background: '#52c41a' }} |
| 表格内操作 | link/small | 或 |
// 页面布局
<>
<PageTitle>标题</PageTitle>
<div className='pageContent'>
{/* 内容 */}
</div>
</>
// 顶部/底部操作区样式
{
marginBottom: 16,
padding: '16px 24px',
background: '#fafafa',
borderRadius: 4,
display: 'flex',
alignItems: 'center',
gap: 12
}
src/views/模块名/页面名/
├── index.jsx # 主页面
└── components/ # 子组件(可选)
├── submit.jsx # 提交对话框
└── mark.jsx # 标记对话框
> ⚠️ 注意:不创建 src/api/ 目录下的任何文件。
// src/router/index.js
{
element: <Layout />,
meta: { title: '需求计划', requiresAuth: true },
children: [
{
path: '/new-retail-reporting',
Component: lazy(() => import('@/views/requirements-planning/new-retail-reporting')),
meta: { title: '新零售需求提报', requiresAuth: true }
}
]
}
src/views/xxx/index.jsx)src/views/xxx/components/*.jsx)src/router/index.js)> ⚠️ 严禁:不生成任何 src/api/ 目录下的 API 接口文件。
必须输出以下格式的文件清单:
## 📁 文件清单
### 新增文件(X 个)
| 文件路径 | 说明 |
|----------|------|
| src/views/xxx/index.jsx | 主页面(含 Mock 数据) |
| src/views/xxx/components/submit.jsx | 提交组件 |
### 修改文件(X 个)
| 文件路径 | 修改内容 |
|----------|----------|
| src/router/index.js | 添加路由配置 |
> ⚠️ 注意:文件清单中不应包含 src/api/ 目录的文件。
<AuthButton
authCode="模块_功能_操作"
onClick={handleXXX}
disabled={条件}
>
按钮文字
</AuthButton>
格式:模块名_功能名_操作名
示例:
new_retail_reporting_savenew_retail_reporting_submitnew_retail_plan_export适用场景:
基本结构:
// 在组件外部定义(与 View 同级),方便复用和测试
const generateMockData = () => {
// 1. 定义字典数据(枚举/字典字段)
const warehouses = ['北京仓', '上海仓', '广州仓']
const skus = [
{ code: 'SKU001', name: '纯牛奶' },
{ code: 'SKU002', name: '酸奶' },
{ code: 'SKU003', name: '草莓味酸奶' },
{ code: 'SKU004', name: '高钙奶' },
{ code: 'SKU005', name: '脱脂牛奶' }
]
const types = ['生产', '调拨', '退货']
// 2. 循环生成数据(15 条)
const mockData = []
for (let i = 1; i <= 15; i++) {
const planDay = String(10 + ((i * 3) % 10)).padStart(2, '0')
const productionDate = `2026-04-${planDay}`
const warrantyDays = 25 + ((i * 5) % 6)
const warrantyDate = utils.dayjsIns(productionDate).add(warrantyDays, 'day').format('YYYY-MM-DD')
mockData.push({
// 唯一 id(使用业务前缀,非 MOCK)
id: `业务前缀${String(i).padStart(6, '0')}`,
// 字典字段:循环取值
warehouseName: warehouses[(i - 1) % warehouses.length],
skuCode: skus[(i - 1) % skus.length].code,
skuName: skus[(i - 1) % skus.length].name,
// 日期字段:用 dayjs 计算派生日期
planMonth: '202604',
planProductionDate: productionDate,
warrantyDays: warrantyDays,
warrantyDate: warrantyDate,
// 枚举字段
type: types[i % types.length],
// 数值字段
planInboundQty: 500 + ((i * 200) % 1500)
})
}
return mockData
}
Mock 数据使用方式(handleSearch 完整示例):
const handleSearch = () => {
setLoading(true)
const values = form.getFieldsValue()
const allMockData = generateMockData()
let filtered = allMockData
// 根据表单筛选
if (values.skuCode) {
filtered = filtered.filter(item => item.skuCode.includes(values.skuCode))
}
if (values.planProductionDate) {
const dateStr = utils.dayjsIns(values.planProductionDate).format('YYYY-MM-DD')
filtered = filtered.filter(item => item.planProductionDate === dateStr)
}
if (values.planMonth) {
filtered = filtered.filter(item => item.planMonth.includes(values.planMonth))
}
// 分页
const total = filtered.length
const start = (query.pageNum - 1) * query.pageSize
const end = start + query.pageSize
const paginatedData = filtered.slice(start, end)
// 模拟网络延迟(300ms)
setTimeout(() => {
setTableData({ columns, data: paginatedData })
setQuery(prev => ({ ...prev, total }))
setLoading(false)
}, 300)
}
// 初始化加载
useEffect(() => {
handleSearch()
}, [])
注意事项:
id 字段(用于 rowKey),使用业务前缀而非 MOCKgenerateMockData 定义在组件外部,与 View 同级(i * N) % M 模式生成变化值i % array.length 循环取值utils.dayjsIns(date).add(days, 'day').format('YYYY-MM-DD') 计算派生日期handleSearch 为同步函数(非 async),不区分开发/生产环境,直接写死使用 mock参考商品主数据页面 (src/views/main-data/product-data/index.jsx):
状态定义:
const [query, setQuery] = useState({
pageNum: 1,
pageSize: 10,
total: 0
})
分页函数:
const pageChange = (pageNum, pageSize) => {
setQuery(prev => ({ ...prev, pageNum, pageSize }))
handleSearch()
}
Table 组件配置:
<Table
columns={tableData.columns}
data={tableData.data}
rowKey={'id'}
pageChange={pageChange}
pagination={{
pageNum: query.pageNum,
pageSize: query.pageSize,
total: query.total
}}
/>
Mock 数据分页处理(已合并到 handleSearch 中):
const start = (query.pageNum - 1) * query.pageSize
const end = start + query.pageSize
const paginatedData = filtered.slice(start, end)
使用 UploadRender 组件(推荐):
import UploadRender from '@/cn-components/upload-render'
<UploadRender
dataCode='模块导入模板编码'
btnProps={{ type: 'normal', childrenText: '导入' }}
otherTemplateParams={{ ...otherFilters }}
onOK={() => {
queryData() // 导入成功后刷新列表
}}
/>
筛选条件处理:
const otherFilters = useMemo(() => {
let obj = { 关键参数:值 }
return obj
}, [依赖项])
替代方案:手动创建 input(旧方式,不推荐):
const handleImport = () => {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.xlsx,.xls'
input.onchange = async (e) => { ... }
input.click()
}
组件引入:
import Download from '@/cn-components/download'
场景 1:统一导出按钮(表格上方)
适用于导出当前筛选条件下的所有数据:
// 筛选条件处理
const otherFilters = useMemo(() => {
let obj = query.condition || {}
delete obj.condition
return { ...obj }
}, [query])
// 顶部操作区
<div style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<div style={{ fontSize: 16, fontWeight: 500 }}>数据列表</div>
<Download
btnText={'导出'}
params={{
dataCode: '模块导出编码',
...otherFilters // 传递筛选条件
}}
/>
</div>
场景 2:行级导出按钮(操作列)
适用于导出单行数据或某版本详细信息:
// 表格列定义
{
title: '操作',
key: 'action',
width: 280,
render: (_, record) => (
<Space size="small">
<Button size="small" onClick={() => handleView(record)}>
查看
</Button>
<Download
btnText="导出"
params={{
dataCode: '模块导出编码',
rollingVersion: record.rollingVersion // 传递行数据
}}
/>
</Space>
)
}
参数说明:
| 参数 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| ------ | ------ | ------ | ------ | ------ |
btnText | String | 否 | 按钮文字 | "导出" / "下载模板" |
params.dataCode | String | 是 | 导出模板编码 | "newRetailPlanExport" |
params.xxx | Any | 否 | 其他筛选参数 | rollingVersion: "202604W14" |
组件特性:
替代方案:手动 API 调用(旧方式,不推荐):
const handleExport = async (record) => {
try {
const blob = await exportXXX({ rollingVersion: record.rollingVersion })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `文件名_${record.rollingVersion}.xlsx`
link.click()
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
message.error('导出失败')
}
}
使用 Download 组件(推荐),不手动编写下载逻辑。
使用 UploadRender 组件(推荐),不手动编写上传逻辑。
子组件:
import { useImperativeHandle } from 'react'
const Dialog = memo(({ onRef, onSubmit }) => {
const [visible, setVisible] = useState(false)
const open = (record) => {
setCurrent(record)
setVisible(true)
}
const close = () => {
setVisible(false)
}
useImperativeHandle(onRef, () => ({ open, close }))
return <Modal open={visible} ... />
})
父组件调用:
const submitDialog = useRef()
<SubmitDialog onRef={submitDialog} onSubmit={handleSubmit} />
// 调用
submitDialog.current.open(record)
生成完成后必须检查:
组件className='pageContent'memo 包裹src/api/)✅pageChange + pagination)UploadRender 组件(非手动 input)Download 组件(非手动 API 调用)| 操作 | 是否允许 | 说明 |
|---|---|---|
| ------ | --------- | ------ |
修改 backend_transaction/ 目录 | ❌ 严禁 | 后端代码不在本技能范围内 |
| 修改后端 Controller | ❌ 严禁 | 需要后端开发人员实现 |
| 修改后端 Service | ❌ 严禁 | 需要后端开发人员实现 |
| 修改后端 DAO/Mapper | ❌ 严禁 | 需要后端开发人员实现 |
| 修改后端数据库配置 | ❌ 严禁 | 需要后端开发人员实现 |
| 操作 | 是否允许 | 说明 |
|---|---|---|
| ------ | --------- | ------ |
修改 aheadofcow_web/ 目录 | ✅ 允许 | 前端项目目录 |
| 创建前端页面 | ✅ 允许 | src/views/ 下创建 |
| 修改前端路由 | ✅ 允许 | src/router/index.js |
| 启动前端服务 | ✅ 允许 | npm run dev |
| 检查前端编译 | ✅ 允许 | webpack 编译检查 |
| 生成 Mock 数据 | ✅ 允许 | 仅列表页面,15 条 |
| 操作 | 是否允许 | 说明 |
|---|---|---|
| ------ | --------- | ------ |
生成 src/api/ 目录文件 | ❌ 禁止 | 不生成任何 API 接口定义 |
| 导入 API 接口 | ❌ 禁止 | 不使用 @/api/xxx 导入语句 |
完整示例参考 examples/ 目录。
/home/admin/codespace/scp/aheadofcow_websrc/api/ 目录下的 API 接口文件UploadRender 组件,而非手动创建 input 元素Download 组件,支持统一导出和行级导出两种场景image 工具不可用,根据截图手动分析,继续执行流程handleSearch 直接使用 mock,联调时再替换为真实 APIgenerateMockData 定义在组件外部,与 View 同级共 2 个版本