背景
最近经常有开发前端的需求,就记录下自己是如何一步一步开发一个增删改查的
主要思路
- 创建项目
- 运行项目
- 准备后端API
- 调用API 【分页列表】
- 调用API 【新增】
- 调用API 【修改】
- 调用API 【删除】
- 环境配置
- 容器发布
一. 创建项目
TIPS:建议直接通过命令行创建,最近使用vscode插件创建的项目无法启动
```latex yarn create v1.22.10 warning package.json: No license field [1/4] 🔍 Resolving packages… [2/4] 🚚 Fetching packages… [3/4] 🔗 Linking dependencies… [4/4] 🔨 Building fresh packages…yarn create ice ice-demo
success Installed “create-ice@1.7.5” with binaries:
- create-ice
[#####################################################################] 139/139create-ice version: 1.7.5 create-ice args ice-demo undefined ? Please select a template (Use arrow keys) ❯ TypeScript + No UI Components TypeScript + Ant Design TypeScript + Fusion Design TypeScript + Fusion Design Pro JavaScript + Fusion Design ice.js plugin development template.
控制键盘上下, 选择 TypeScript + Fusion Design Pro```latex? Please select a template TypeScript + Fusion Design Prodownload tarballURL https://registry.npmmirror.com/@alifd/fusion-design-pro/-/fusion-design-pro-0.5.1.tgz✔ download npm tarball successfully.clean package.json...Initialize project successfully.Starts the development server.cd ice-demonpm installnpm startWe have prepared develop toolkit for you.See: https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks✨ Done in 134.63s.
安装依赖
$ cd ice-demo$ yarn
二. 运行项目
$ yarn start
yarn run v1.22.10 warning ../../../../package.json: No license field $ icejs start ice.js 2.6.4 > Local: http://localhost:3333/ > Network: http://172.100.80.35:3333/ [vite:css] @charset must precede all other statements 4 | body { 5 | -webkit-font-smoothing: antialiased; 6 | } | ^ 7 |
三. 准备后端API
使用ApiPost 提供的Mock
https://console-docs.apipost.cn/preview/eb2002184dcd9d95/bd10a0fd2f1333a2?target_id=da14b01d-4617-4448-f662-781a7adecea0#da14b01d-4617-4448-f662-781a7adecea0
四. 调用API 【分页列表】
1. 创建Page
import * as React from 'react';import { ResponsiveGrid } from '@alifd/next';import PageHeader from '@/components/PageHeader';const { Cell } = ResponsiveGrid;const NewsListPage = () => {return (<ResponsiveGrid gap={20}><Cell colSpan={12}><PageHeadertitle="新闻列表"breadcrumbs={[{ name: '新闻管理' },{ name: '新闻列表' },]}description="展示近期得新闻列表"/></Cell><Cell colSpan={12}>666</Cell></ResponsiveGrid>);};export default NewsListPage;
2. 配置路由页面
const NewsListPage = lazy(() => import('@/pages/NewsListPage'));// 找到routerConfig 添加const routerConfig: IRouterConfig[] = [{path: '/',component: BasicLayout,children: [{path: '/news/list',component: NewsListPage,},]}]
3. 配置菜单和路由
const asideMenuConfig = [{name: '新闻管理',path: '/',icon: 'chart-news',children: [{name: '新闻列表',path: '/news/list',},],},]
4. 查看效果
5. 核心组件代码
import React, { useCallback } from 'react';import { Button, Field, Table, Card, Pagination, Message, Dialog } from '@alifd/next';import { useFusionTable } from 'ahooks';import { request } from 'ice';/** 获取表格数据* @param current 第几页* @param pageSize 每页条数* @param formData 表格数据*/const getTableData = ({ current, pageSize }: { current: number; pageSize: number },formData: { status: 'normal' | 'empty' | 'exception' },): Promise<any> => {if (!formData.status || formData.status === 'normal') {let query = `page=${current}&pageSize=${pageSize}`;Object.entries(formData).forEach(([key, value]) => {if (value) {query += `&${key}=${value}`;}});return request(`/api/news?${query}`).then(res => ({total: res.data.total,list: res.data.list.slice(0, 10),}));}if (formData.status === 'empty') {return Promise.resolve([]);}if (formData.status === 'exception') {return new Promise((resolve, reject) => {setTimeout(() => {reject(new Error('data exception'));}, 1000);});}return Promise.resolve([]);};const NewsDialogTable: React.FC = () => {const field = Field.useField([]);const { paginationProps, tableProps, search, error, refresh } = useFusionTable(getTableData, {field,});const { reset } = search;const handleDelete = useCallback((data: any) => {if (!data) {return;}Dialog.confirm({title: '删除提醒',content: `确定删除 ${data.title} 吗`,onOk() {request({url: `/api/news/${data.id}`,method: 'DELETE',}).then(res => {Message.success(`${data.title} 删除成功!`);reset();});},});}, [reset]);const cellOperation = (...args: any[]): React.ReactNode => {const record = args[2];return (<div><Buttontexttype="primary"onClick={() => handleDelete(record)}>删除</Button></div>);};return (<div><Card free><Card.Content><Table{...tableProps}// onResizeChange={onResizeChange}// emptyContent={error ? <ExceptionBlock onRefresh={refresh} /> : <EmptyBlock />}primaryKey="email"><Table.Column title="标题" dataIndex="title" resizable /><Table.Column title="作者" dataIndex="author" resizable /><Table.Column title="预览地址" dataIndex="url" resizable /><Table.Column title="归属地" dataIndex="ip" resizable /><Table.Column title="内容" dataIndex="content" resizable /><Table.Columntitle="操作"resizablecell={cellOperation}/></Table><Paginationstyle={{ marginTop: 16, textAlign: 'right' }}totalRender={(total) => (<>共{' '}<Button text type="primary">{total}</Button>{' '}个记录</>)}{...paginationProps}/></Card.Content></Card></div>);};export default NewsDialogTable;
<Cell colSpan={12}><NewsDialogTable /></Cell>
配置全局请求路径
const appConfig: IAppConfig = {request: {baseURL: 'https://console-mock.apipost.cn/app/mock/project/035500cd-6c40-4d49-be88-c3f3fbcd28d3',},};runApp(appConfig);

