This commit is contained in:
pincman 2024-05-11 05:52:36 +08:00
parent 894d296159
commit e729426370
12 changed files with 5607 additions and 6723 deletions

View File

@ -1,3 +1,3 @@
module.exports = { module.exports = {
extends: [require.resolve('@3rapp/code-config/react')], extends: [require.resolve('@3rapp/code-config/stylelint')],
}; };

View File

@ -12,17 +12,21 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@3rapp/common": "workspace:*",
"@3rapp/api": "workspace:*", "@3rapp/api": "workspace:*",
"@3rapp/common": "workspace:*",
"@ant-design/cssinjs": "^1.20.0", "@ant-design/cssinjs": "^1.20.0",
"antd": "^5.17.0", "antd": "^5.17.0",
"axios": "^1.6.8", "axios": "^1.6.8",
"clsx": "^2.1.1",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"immer": "^10.1.1",
"lodash": "^4.17.21",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@3rapp/code-config": "workspace:*", "@3rapp/code-config": "workspace:*",
"@types/lodash": "^4.17.1",
"@types/node": "^20.12.10", "@types/node": "^20.12.10",
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",

View File

@ -1,5 +1,5 @@
.app { .app {
@apply tw-bg-yellow-300 tw-flex tw-flex-auto tw-items-center tw-justify-center; @apply tw-flex tw-flex-auto tw-flex-wrap tw-items-center tw-justify-center;
& > .container { & > .container {
@apply tw-shadow-md tw-p-5 tw-bg-black tw-text-center tw-text-white tw-text-lg; @apply tw-shadow-md tw-p-5 tw-bg-black tw-text-center tw-text-white tw-text-lg;

View File

@ -1,69 +1,30 @@
// src/app.tsx
import { PostEntity } from '@3rapp/api/modules/content/entities/post.entity';
import { StyleProvider } from '@ant-design/cssinjs'; import { StyleProvider } from '@ant-design/cssinjs';
import { Button, ConfigProvider, theme, App as AntdApp } from 'antd'; import { ConfigProvider, theme, App as AntdApp } from 'antd';
// import 'dayjs/locale/zh-cn'; // import 'dayjs/locale/zh-cn';
import zhCN from 'antd/locale/zh_CN'; import zhCN from 'antd/locale/zh_CN';
import axios from 'axios'; import { FC } from 'react';
import { FC, useEffect, useState } from 'react';
import $styles from './app.module.css'; import $styles from './app.module.css';
import EffectDemo from './components/demo/effect';
const getPosts = async () => { import RefDemo from './components/demo/ref';
let data: PostEntity[] = []; import StateDemo from './components/demo/state';
try {
const res = await axios.get('/api/posts');
data = res.data;
} catch (err) {
console.log('Error:', err);
}
return data;
};
const App: FC = () => { const App: FC = () => {
const [data, setData] = useState<PostEntity[]>([]);
useEffect(() => {
(async () => {
setData(await getPosts());
})();
}, []);
return ( return (
<ConfigProvider <ConfigProvider
locale={zhCN} locale={zhCN}
theme={{ theme={{
algorithm: theme.defaultAlgorithm, algorithm: theme.defaultAlgorithm,
token: { token: {},
colorPrimary: '#00B96B',
},
components: {
Layout: {
colorBgBody: '',
},
},
}} }}
> >
<StyleProvider hashPriority="high"> <StyleProvider hashPriority="high">
<AntdApp> <AntdApp>
<div className={$styles.app}> <div className={$styles.app}>
<div className={$styles.container}> <StateDemo />
3R教室<span>React课程第一节</span> <EffectDemo />
<Button <RefDemo />
type="primary"
className="!bg-lime-400 !text-emerald-900"
href="https://pincman.com/3r"
target="_blank"
>
</Button>
<h2></h2>
<ul>
{data.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</div>
</div> </div>
</AntdApp> </AntdApp>
</StyleProvider> </StyleProvider>

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 KiB

View File

@ -0,0 +1,43 @@
import { Button } from 'antd';
import { FC, useEffect, useState } from 'react';
import $styles from './style.module.css';
const EffectDemo: FC = () => {
const [red, setRed] = useState<boolean>(false);
const [ghost, setGhost] = useState<boolean>(false);
const [width, setWidth] = useState(window.innerWidth);
const toggleGhostBtn = () => setGhost(!ghost);
const resizeHandle = () => setWidth(window.innerWidth);
useEffect(() => {
console.log('浏览器宽度改变');
window.addEventListener('resize', resizeHandle);
}, [width]);
useEffect(() => {
console.log('切换幽灵按钮');
}, [ghost]);
useEffect(() => {
console.log('只在第一次或重新渲染组件时触发');
}, []);
useEffect(() => {
(async () => {
await new Promise((resolve) => {
setTimeout(() => resolve(true), 1000);
});
setRed(ghost);
})();
}, [ghost]);
return (
<div className={$styles.container}>
<h2 className="tw-text-center">useEffect Demo</h2>
<p className="tw-text-center tw-py-5">{ghost ? 'ghost' : '普通'}</p>
<div className="tw-flex tw-justify-center tw-flex-col">
<Button type="primary" onClick={toggleGhostBtn} ghost={ghost} danger={red}>
</Button>
<p className="tw-pt-5 tw-text-center">: {width}</p>
</div>
</div>
);
};
export default EffectDemo;

View File

@ -0,0 +1,75 @@
import { Button } from 'antd';
import clsx from 'clsx';
import { isNaN, isNil } from 'lodash';
import {
ChangeEventHandler,
FC,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react';
import $styles from './style.module.css';
interface RefFunc {
focus: () => void;
memo: () => number;
}
const MyInput = forwardRef<RefFunc, { value: number; changeValue: (v: number) => void }>(
({ value, changeValue }, ref) => {
const [local, setLocal] = useState<number | string>(value);
const inputRef = useRef<HTMLInputElement | null>(null);
useImperativeHandle(
ref,
() => ({
focus: () => inputRef.current.focus(),
memo: () => value,
}),
[value],
);
useEffect(() => {
changeValue(isNaN(Number(local)) ? 0 : Number(local));
}, [changeValue, local]);
const handleChange: ChangeEventHandler<HTMLInputElement> = useCallback((e) => {
setLocal(e.target.value);
}, []);
return (
<input value={value} ref={inputRef} placeholder="请输入值" onChange={handleChange} />
);
},
);
const RefDemo: FC = () => {
const [count, setCount] = useState(0);
const inited = useRef(count);
const ref = useRef<RefFunc | null>(null);
useEffect(() => {
ref.current.focus();
}, []);
useEffect(() => {
if (inited.current !== count) {
inited.current = count;
console.log('changed');
}
}, [count]);
return (
<div className={clsx($styles.container, 'tw-w-[20rem]')}>
<h2 className="tw-text-center">useRef Demo</h2>
<p className="tw-text-center tw-py-5">{count}</p>
<div className="tw-flex tw-justify-around">
<Button onClick={() => setCount(Math.ceil(Math.random() * 10))} type="dashed">
</Button>
</div>
<div className="flex flex-col">
{!isNil(ref.current) && <p className="tw-my-3">{ref.current.memo()}</p>}
<MyInput ref={ref} value={count} changeValue={setCount} />
</div>
</div>
);
};
export default RefDemo;

View File

@ -0,0 +1,29 @@
import { Button } from 'antd';
import { clsx } from 'clsx';
import { FC, useState } from 'react';
import $styles from './style.module.css';
const StateDemo: FC = () => {
const [count, setCount] = useState(1);
const [isShow, toggleShow] = useState(true);
return (
<div className={clsx($styles.container, 'tw-w-[20rem]')}>
<h2 className="tw-text-center">useState Demo</h2>
{isShow && <p className="tw-text-center tw-py-5">{count}</p>}
<div className="tw-flex tw-justify-around">
<Button onClick={() => setCount(count + 1)} type="dashed">
</Button>
<Button onClick={() => setCount(count - 1)} type="dashed">
</Button>
<Button onClick={() => toggleShow(!isShow)} type="dashed">
{isShow ? '显示' : '隐藏'}
</Button>
</div>
</div>
);
};
export default StateDemo;

View File

@ -0,0 +1,9 @@
.container {
@apply tw-bg-neutral-100/40 tw-shadow-black/20 tw-backdrop-blur-sm tw-shadow-md tw-rounded-md tw-p-5 tw-m-5 tw-min-w-[20rem];
}
body(:global(.dark)) {
.container {
@apply tw-bg-neutral-800/40 tw-shadow-slate-100/30;
}
}

View File

@ -4,3 +4,7 @@ body,
.ant-app { .ant-app {
@apply tw-h-[100vh] tw-w-full tw-flex tw-p-0 tw-m-0; @apply tw-h-[100vh] tw-w-full tw-flex tw-p-0 tw-m-0;
} }
body {
@apply tw-bg-[url(@/assets/images/bg-light.png)];
}

File diff suppressed because it is too large Load Diff