update
This commit is contained in:
parent
894d296159
commit
e729426370
@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [require.resolve('@3rapp/code-config/react')],
|
extends: [require.resolve('@3rapp/code-config/stylelint')],
|
||||||
};
|
};
|
@ -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",
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
BIN
apps/admin/src/assets/images/bg-dark.png
Normal file
BIN
apps/admin/src/assets/images/bg-dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 266 KiB |
BIN
apps/admin/src/assets/images/bg-light.png
Normal file
BIN
apps/admin/src/assets/images/bg-light.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 923 KiB |
43
apps/admin/src/components/demo/effect.tsx
Normal file
43
apps/admin/src/components/demo/effect.tsx
Normal 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;
|
75
apps/admin/src/components/demo/ref.tsx
Normal file
75
apps/admin/src/components/demo/ref.tsx
Normal 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;
|
29
apps/admin/src/components/demo/state.tsx
Normal file
29
apps/admin/src/components/demo/state.tsx
Normal 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;
|
9
apps/admin/src/components/demo/style.module.css
Normal file
9
apps/admin/src/components/demo/style.module.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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)];
|
||||||
|
}
|
||||||
|
12103
pnpm-lock.yaml
12103
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user