From 59c14497b19447dd0812177aa0e473f645ca522d Mon Sep 17 00:00:00 2001 From: afc163 Date: Wed, 4 Mar 2026 12:00:05 +0800 Subject: [PATCH] fix: add missing basic-list files and update snapshots - Add missing basic-list components and service files - Fix jest.config.ts import path for @umijs/max/test.js - Update login test snapshots (fix 'Ant Desgin' typo) Co-Authored-By: Claude Opus 4.6 --- .claude/settings.local.json | 17 ++ jest.config.ts | 2 +- src/pages/list/basic-list/_mock.ts | 165 ++++++++++++++++++ .../basic-list/components/OperationModal.tsx | 129 ++++++++++++++ src/pages/list/basic-list/data.d.ts | 29 +++ src/pages/list/basic-list/service.ts | 50 ++++++ src/pages/list/basic-list/style.style.ts | 141 +++++++++++++++ .../list/basic-list/utils/utils.style.ts | 6 + .../login/__snapshots__/login.test.tsx.snap | 71 +++----- 9 files changed, 559 insertions(+), 51 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 src/pages/list/basic-list/_mock.ts create mode 100644 src/pages/list/basic-list/components/OperationModal.tsx create mode 100644 src/pages/list/basic-list/data.d.ts create mode 100644 src/pages/list/basic-list/service.ts create mode 100644 src/pages/list/basic-list/style.style.ts create mode 100644 src/pages/list/basic-list/utils/utils.style.ts diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..f6e2c55f --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,17 @@ +{ + "permissions": { + "allow": [ + "Bash(git ls-tree:*)", + "Bash(npm show:*)", + "Bash(git merge:*)", + "Bash(git add:*)", + "Bash(git checkout:*)", + "Bash(ls:*)", + "Bash(git commit:*)", + "Bash(npm test:*)", + "Bash(npm install:*)", + "Bash(npm publish:*)", + "Bash(tnpm publish:*)" + ] + } +} diff --git a/jest.config.ts b/jest.config.ts index 99051c41..230bef7d 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,4 +1,4 @@ -import { configUmiAlias, createConfig } from '@umijs/max/test'; +import { configUmiAlias, createConfig } from '@umijs/max/test.js'; export default async (): Promise => { const config = await configUmiAlias({ diff --git a/src/pages/list/basic-list/_mock.ts b/src/pages/list/basic-list/_mock.ts new file mode 100644 index 00000000..680f6883 --- /dev/null +++ b/src/pages/list/basic-list/_mock.ts @@ -0,0 +1,165 @@ +import type { Request, Response } from 'express'; +import type { BasicListItemDataType } from './data.d'; + +const titles = [ + 'Alipay', + 'Angular', + 'Ant Design', + 'Ant Design Pro', + 'Bootstrap', + 'React', + 'Vue', + 'Webpack', +]; +const avatars = [ + 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', // Alipay + 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png', // Angular + 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png', // Ant Design + 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png', // Ant Design Pro + 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png', // Bootstrap + 'https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png', // React + 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png', // Vue + 'https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png', // Webpack +]; + +const covers = [ + 'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png', + 'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png', + 'https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png', + 'https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png', +]; +const desc = [ + '那是一种内在的东西, 他们到达不了,也无法触及的', + '希望是一个好东西,也许是最好的,好东西是不会消亡的', + '生命就像一盒巧克力,结果往往出人意料', + '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆', + '那时候我只会想自己想要什么,从不想自己拥有什么', +]; + +const user = [ + '付小小', + '曲丽丽', + '林东东', + '周星星', + '吴加好', + '朱偏右', + '鱼酱', + '乐哥', + '谭小仪', + '仲尼', +]; + +function fakeList(count: number): BasicListItemDataType[] { + const list = []; + for (let i = 0; i < count; i += 1) { + list.push({ + id: `fake-list-${i}`, + owner: user[i % 10], + title: titles[i % 8], + avatar: avatars[i % 8], + cover: + parseInt(`${i / 4}`, 10) % 2 === 0 + ? covers[i % 4] + : covers[3 - (i % 4)], + status: ['active', 'exception', 'normal'][i % 3] as + | 'normal' + | 'exception' + | 'active' + | 'success', + percent: Math.ceil(Math.random() * 50) + 50, + logo: avatars[i % 8], + href: 'https://ant.design', + updatedAt: new Date(Date.now() - 1000 * 60 * 60 * 2 * i).getTime(), + createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2 * i).getTime(), + subDescription: desc[i % 5], + description: + '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。', + activeUser: Math.ceil(Math.random() * 100000) + 100000, + newUser: Math.ceil(Math.random() * 1000) + 1000, + star: Math.ceil(Math.random() * 100) + 100, + like: Math.ceil(Math.random() * 100) + 100, + message: Math.ceil(Math.random() * 10) + 10, + content: + '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。', + members: [ + { + avatar: + 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', + name: '曲丽丽', + id: 'member1', + }, + { + avatar: + 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png', + name: '王昭君', + id: 'member2', + }, + { + avatar: + 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png', + name: '董娜娜', + id: 'member3', + }, + ], + }); + } + + return list; +} + +let sourceData: BasicListItemDataType[] = []; + +function getFakeList(req: Request, res: Response) { + const params = req.query as any; + + const count = Number(params.count) * 1 || 20; + + const result = fakeList(count); + sourceData = result; + return res.json({ + data: { + list: result, + }, + }); +} + +function postFakeList(req: Request, res: Response) { + const { /* url = '', */ body } = req; + // const params = getUrlParams(url); + const { method, id } = body; + // const count = (params.count * 1) || 20; + let result = sourceData || []; + + switch (method) { + case 'delete': + result = result.filter((item) => item.id !== id); + break; + case 'update': + result.forEach((item, i) => { + if (item.id === id) { + result[i] = { ...item, ...body }; + } + }); + break; + case 'post': + result.unshift({ + ...body, + id: `fake-list-${result.length}`, + createdAt: Date.now(), + }); + break; + default: + break; + } + + return res.json({ + data: { + list: result, + }, + }); +} + +export default { + 'GET /api/get_list': getFakeList, + 'POST /api/post_fake_list': postFakeList, +}; diff --git a/src/pages/list/basic-list/components/OperationModal.tsx b/src/pages/list/basic-list/components/OperationModal.tsx new file mode 100644 index 00000000..ebb2e082 --- /dev/null +++ b/src/pages/list/basic-list/components/OperationModal.tsx @@ -0,0 +1,129 @@ +import { + ModalForm, + ProFormDateTimePicker, + ProFormSelect, + ProFormText, + ProFormTextArea, +} from '@ant-design/pro-components'; +import { Button, Result } from 'antd'; +import type { FC } from 'react'; +import type { BasicListItemDataType } from '../data.d'; +import useStyles from '../style.style'; + +type OperationModalProps = { + done: boolean; + open: boolean; + current: Partial | undefined; + onDone: () => void; + onSubmit: (values: BasicListItemDataType) => void; + children?: React.ReactNode; +}; +const OperationModal: FC = (props) => { + const { styles } = useStyles(); + const { done, open, current, onDone, onSubmit, children } = props; + if (!open) { + return null; + } + return ( + + open={open} + title={done ? null : `任务${current ? '编辑' : '添加'}`} + className={styles.standardListForm} + width={640} + onFinish={async (values) => { + onSubmit(values); + }} + initialValues={current} + submitter={{ + render: (_, dom) => (done ? null : dom), + }} + trigger={children} + modalProps={{ + onCancel: () => onDone(), + destroyOnHidden: true, + bodyStyle: done + ? { + padding: '72px 0', + } + : {}, + }} + > + {!done ? ( + <> + + + + + + ) : ( + + 知道了 + + } + className={styles.formResult} + /> + )} + + ); +}; +export default OperationModal; diff --git a/src/pages/list/basic-list/data.d.ts b/src/pages/list/basic-list/data.d.ts new file mode 100644 index 00000000..f1cfa668 --- /dev/null +++ b/src/pages/list/basic-list/data.d.ts @@ -0,0 +1,29 @@ +export type Member = { + avatar: string; + name: string; + id: string; +}; + +export type BasicListItemDataType = { + id: string; + owner: string; + title: string; + avatar: string; + cover: string; + status: 'normal' | 'exception' | 'active' | 'success'; + percent: number; + logo: string; + href: string; + body?: any; + updatedAt: number; + createdAt: number; + subDescription: string; + description: string; + activeUser: number; + newUser: number; + star: number; + like: number; + message: number; + content: string; + members: Member[]; +}; diff --git a/src/pages/list/basic-list/service.ts b/src/pages/list/basic-list/service.ts new file mode 100644 index 00000000..99eb3541 --- /dev/null +++ b/src/pages/list/basic-list/service.ts @@ -0,0 +1,50 @@ +import { request } from '@umijs/max'; +import type { BasicListItemDataType } from './data.d'; + +type ParamsType = { + count?: number; +} & Partial; + +export async function queryFakeList( + params: ParamsType, +): Promise<{ data: { list: BasicListItemDataType[] } }> { + return request('/api/get_list', { + params, + }); +} + +export async function removeFakeList( + params: ParamsType, +): Promise<{ data: { list: BasicListItemDataType[] } }> { + return request('/api/post_fake_list', { + method: 'POST', + data: { + ...params, + method: 'delete', + }, + }); +} + +export async function addFakeList( + params: ParamsType, +): Promise<{ data: { list: BasicListItemDataType[] } }> { + return request('/api/post_fake_list', { + method: 'POST', + data: { + ...params, + method: 'post', + }, + }); +} + +export async function updateFakeList( + params: ParamsType, +): Promise<{ data: { list: BasicListItemDataType[] } }> { + return request('/api/post_fake_list', { + method: 'POST', + data: { + ...params, + method: 'update', + }, + }); +} diff --git a/src/pages/list/basic-list/style.style.ts b/src/pages/list/basic-list/style.style.ts new file mode 100644 index 00000000..7e120666 --- /dev/null +++ b/src/pages/list/basic-list/style.style.ts @@ -0,0 +1,141 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ token }) => { + return { + standardList: { + '.ant-card-head': { borderBottom: 'none' }, + '.ant-card-head-title': { padding: '24px 0', lineHeight: '32px' }, + '.ant-card-extra': { padding: '24px 0' }, + '.ant-list-pagination': { marginTop: '24px', textAlign: 'right' }, + '.ant-avatar-lg': { width: '48px', height: '48px', lineHeight: '48px' }, + [`@media screen and (max-width: ${token.screenXS}px)`]: { + '.ant-list-item-content': { + display: 'block', + flex: 'none', + width: '100%', + }, + '.ant-list-item-action': { + marginLeft: '0', + }, + }, + }, + headerInfo: { + position: 'relative', + textAlign: 'center', + '& > span': { + display: 'inline-block', + marginBottom: '4px', + color: token.colorTextSecondary, + fontSize: token.fontSize, + lineHeight: '22px', + }, + '& > p': { + margin: '0', + color: token.colorTextHeading, + fontSize: '24px', + lineHeight: '32px', + }, + '& > em': { + position: 'absolute', + top: '0', + right: '0', + width: '1px', + height: '56px', + backgroundColor: token.colorSplit, + }, + [`@media screen and (max-width: ${token.screenSM}px)`]: { + marginBottom: '16px', + '& > em': { + display: 'none', + }, + }, + }, + listContent: { + fontSize: '0', + [`@media screen and (max-width: ${token.screenXS}px)`]: { + marginLeft: '0', + '& > div': { + marginLeft: '0', + }, + }, + [`@media screen and (max-width: ${token.screenMD}px)`]: { + '& > div': { + display: 'block', + }, + '& > div:last-child': { + top: '0', + width: '100%', + }, + }, + [`@media screen and (max-width: ${token.screenLG}px) and (min-width: @screen-md)`]: + { + '& > div': { + display: 'block', + }, + '& > div:last-child': { + top: '0', + width: '100%', + }, + }, + [`@media screen and (max-width: ${token.screenXL}px)`]: { + '& > div': { + marginLeft: '24px', + }, + '& > div:last-child': { + top: '0', + }, + }, + '@media screen and (max-width: 1400px)': { + textAlign: 'right', + '& > div:last-child': { + top: '0', + }, + }, + }, + listContentItem: { + display: 'inline-block', + marginLeft: '40px', + color: token.colorTextSecondary, + fontSize: token.fontSize, + verticalAlign: 'middle', + '> span': { lineHeight: '20px' }, + '> p': { marginTop: '4px', marginBottom: '0', lineHeight: '22px' }, + }, + extraContentSearch: { + width: '272px', + marginLeft: '16px', + [`@media screen and (max-width: ${token.screenSM}px)`]: { + width: '100%', + marginLeft: '0', + }, + }, + listCard: { + [`@media screen and (max-width: ${token.screenXS}px)`]: { + '.ant-card-head-title': { + overflow: 'open', + }, + }, + [`@media screen and (max-width: ${token.screenMD}px)`]: { + '.ant-radio-group': { + display: 'block', + marginBottom: '8px', + }, + }, + }, + standardListForm: { + '.ant-form-item': { + marginBottom: '12px', + '&:last-child': { + marginBottom: '32px', + paddingTop: '4px', + }, + }, + }, + formResult: { + width: '100%', + "[class^='title']": { marginBottom: '8px' }, + }, + }; +}); + +export default useStyles; diff --git a/src/pages/list/basic-list/utils/utils.style.ts b/src/pages/list/basic-list/utils/utils.style.ts new file mode 100644 index 00000000..0ad5e64a --- /dev/null +++ b/src/pages/list/basic-list/utils/utils.style.ts @@ -0,0 +1,6 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(() => { + return {}; +}); +export default useStyles; diff --git a/src/pages/user/login/__snapshots__/login.test.tsx.snap b/src/pages/user/login/__snapshots__/login.test.tsx.snap index dd02b8ad..8cc10ba8 100644 --- a/src/pages/user/login/__snapshots__/login.test.tsx.snap +++ b/src/pages/user/login/__snapshots__/login.test.tsx.snap @@ -3,7 +3,7 @@ exports[`Login Page should login success 1`] = `
- Powered by Ant Desgin + Powered by Ant Design
@@ -607,7 +581,7 @@ exports[`Login Page should show login form 1`] = `
@@ -931,9 +905,6 @@ exports[`Login Page should show login form 1`] = ` id="autoLogin" type="checkbox" /> -