实现一个函数,可以将数组转化为树状数据结构
// 入参格式参考:
const arr = [
{ id: 1, name: "i1" },
{ id: 2, name: "i2", parentId: 1 },
{ id: 4, name: "i4", parentId: 3 },
{ id: 3, name: "i3", parentId: 2 },
{ id: 8, name: "i8", parentId: 7 }
];
// 出参格式可自行设计, 举例:
{
id: 1,
name: 'i1',
children: [
{
id: 2,
name: 'i2',
children: []
}
]
}
function buildTree(arr) {
let result = []
let map = {}
if (!Array.isArray(data)) {//验证data是不是数组类型
return []
}
data.forEach(item => {//建立每个数组元素id和该对象的关系
map[item.id] = item //这里可以理解为浅拷贝,共享引用
})
data.forEach(item => {
let parent = map[item.parentId] //找到data中每一项item的爸爸
if (parent) {//说明元素有爸爸,把元素放在爸爸的children下面
(parent.children || (parent.children = [])).push(item)
} else {//说明元素没有爸爸,是根节点,把节点push到最终结果中
result.push(item) //item是对象的引用
}
})
return result //数组里的对象和data是共享的
}
实现一个 arrange 函数/class,可以进行时间和工作调度
// [ > … ] 表示调用函数后的打印内容
// arrange('William').execute();
// > William is notified
// arrange('William').do('commit').execute();
// > William is notified
// > Start to commit
// arrange('William').wait(5).do('commit').execute();
// > William is notified
// 等待 5 秒
// > Start to commit
// arrange('William').waitFirst(5).do('push').execute();
// 等待 5 秒
// > William is notified
// > Start to push
代码
function arrange(taskId) {
const tasks = [];
tasks.push(() => {
console.log(`${taskId} is notified`);
});
async function execute() {
for (const t of tasks) {
await t();
}
}
function doSomething(something) {
tasks.push(() => {
console.log(`Start to ${something}`);
});
return this;
}
function wait(duration) {
tasks.push(
() =>
new Promise((resolve) => {
setTimeout(resolve, duration * 1000);
})
);
return this;
}
function waitFirst(duration) {
tasks.unshift(
() =>
new Promise((resolve) => {
setTimeout(resolve, duration * 1000);
})
);
return this;
}
return {
execute,
do: doSomething,
wait,
waitFirst,
};
}
//或使用类组件, 如果使用类组件,调用形式可以改为: new arrange('William').execute();
class arrange {
}
自研数字键盘
要求:
- 请实现以下功能,并尽可能考虑异常场景的处理。附加题有余力请尽可能完成。
- 可以拆出多个组件来实现该功能
请使用React编程 实现下面的效果 (函数式或类组件均可)
功能描述:
- 自研数字键盘,能正常输入数字,点击退格键可以删除对应的数字。聚焦提现时唤起,失焦时隐藏。
- 输入数字的时候,左上分别有千、万、十万、百万、千万的提醒。
- 总免费额度为1000,超过部分提示收手续费0.1%。
- 总可用余额为3000,超出部分直接提醒文案标红,提现按钮不可点击
- 点击提现(能点击状态下)的时候出弹窗告诉是多少钱,提供一键清除输入功能。
- 附加题:本地展示最近3次提现记录。只要求功能实现,UI随意。
- 附加题:每次提现过后的免费额度和可用额度累计(即提现3次1000元,第2/3次收手续费,且之后不可再提现)。
import { useState, useRef } from 'react';
import { Input, Row, Col, InputRef, Modal, Button } from 'antd';
import { InfoCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
const returnFloat = (value: any) => {
value = Math.round(parseFloat(value) * 100) / 100;
let xsd = value.toString().split(".");
if (xsd.length == 1) {
value = value.toString() + ".00";
return value;
}
if (xsd.length > 1) {
if (xsd[1].length < 2) {
value = value.toString() + "0";
}
return value;
}
}
let numberMap = {
4: '千',
5: '万',
6: '十万',
7: '百万',
8: '千万',
}
export default () => {
const [value, setValue] = useState<any>('')
const [isFocus, setIsFocus] = useState(false)
const [cashHistory, setCashHistory] = useState([]) //显示的记录
const [realCashHistory, setRealCashHistory] = useState([]) //实际的记录
const [freeCash, setFreeCash] = useState(1000) //免费额度
const [canGetMoney, setCanGetMoney] = useState(true) //是否可提现的标红
const inputRef = useRef<InputRef>(null);
const addMoney = (value: any) => {
let realCashArr: any = [...realCashHistory]
let total: number = 0
realCashArr.map((item: any) => total += Number(item))
console.log('total---', total)
if (total + Number(value) > 3000) {
return setCanGetMoney(false)
} else {
setCanGetMoney(true)
}
}
return (
<div onClick={() => setIsFocus(false)}>
<div style={{ background: '#ffffff', padding: '10px' }} >
<div>提现金额</div>
<div style={{ marginLeft: 20, height: 30 }}>{value.split('.')[0].length > 3 ? `|${numberMap[value.split('.')[0].length]}` : ''}</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ display: 'flex' }}>
<span>
¥
</span>
<div onClick={(e) => {
e.stopPropagation()
setIsFocus(true)
}}>
<Input style={{ width: 100, }}
ref={inputRef}
maxLength={8} value={value} />
{
isFocus ?
<div style={{ display: 'flex', position: 'fixed', bottom: 10, right: 0, width: '100% ' }}>
<div style={{ flex: 1, background: '#ffffff' }} >
<Row>
{['1', '2', '3', '4', '5', '6', '7', '8', '9'].map((item: string) =>
<Col span={8} style={{ textAlign: 'center', height: 30, borderBottom: '1px solid rgb(246 242 242)', borderRight: '1px solid rgb(246 242 242)' }}
onClick={(e: any) => {
//输入的时候
if (value.length < 8) {
if (value.includes('.')) {
if (value.split('.')[1].length < 2) {
setValue(value + e.target.textContent)
addMoney(value + e.target.textContent)
}
} else {
setValue(value + e.target.textContent)
addMoney(value + e.target.textContent)
}
}
inputRef.current!.focus({
cursor: 'end',
});
}}
>
{item}
</Col>
)}
<Col span={16} style={{ textAlign: 'center', height: 30, borderBottom: '1px solid rgb(246 242 242)', borderRight: '1px solid rgb(246 242 242)' }} onClick={(e: any) => {
if (value.length < 8) {
setValue(value + e.target.textContent)
addMoney(value + e.target.textContent)
}
inputRef.current!.focus({
cursor: 'end',
});
}}>
0
</Col>
<Col span={8} style={{ textAlign: 'center', height: 30, borderBottom: '1px solid rgb(246 242 242)', borderRight: '1px solid rgb(246 242 242)' }} onClick={(e: any) => {
if (value.length < 8) {
if (value) {
if (!value.includes('.')) {
setValue(value + e.target.textContent)
addMoney(value + e.target.textContent)
}
} else {
setValue('0' + e.target.textContent)
addMoney('0' + e.target.textContent + e.target.textContent)
}
}
inputRef.current!.focus({
cursor: 'end',
});
}}>
.
</Col>
</Row>
</div>
<div style={{ width: 60 }}>
<div style={{ textAlign: 'center', background: '#ffffff', height: 30 }} >
<CloseCircleOutlined onClick={() => {
setValue(value.slice(0, value.length - 1))
addMoney(value.slice(0, value.length - 1))
inputRef.current!.focus({
cursor: 'end',
});
}} />
</div>
<div style={{ background: value && Number(value) < 3000 && canGetMoney ? 'blue' : 'grey', display: 'flex', alignItems: 'center', justifyContent: 'center', height: 90, }}
onClick={() => {
if (value && Number(value) < 3000 && canGetMoney) {
Modal.confirm({
title: `提现金额一共${value}元`,
content: (
<>
<Button onClick={() => {
setValue('')
Modal.destroyAll()
}}>一键清除</Button>
</>
),
onOk: async () => {
if (cashHistory.length < 3) {
let arr: any = [...cashHistory]
arr.push(value)
setCashHistory(arr)
} else {
let arr: any = [...cashHistory]
arr.shift()
arr.push(value)
setCashHistory(arr)
}
let realCashArr: any = [...realCashHistory]
realCashArr.push(value)
setRealCashHistory(realCashArr)
setCanGetMoney(true)
freeCash - value > 0 ? setFreeCash(freeCash - value) : setFreeCash(0)
setValue('')
}
})
}
}}
>提现</div>
</div>
</div>
:
null
}
</div>
</div>
{
true ? <CloseCircleOutlined onClick={(e) => {
e.stopPropagation()
setValue('')
}} />
:
<div>
<span>全部提现</span>
<span>¥30000</span>
</div>
}
</div>
<div>
{
freeCash > 0 ?
<span> 免费额度还剩{freeCash}元,超出部分收取0.1%服务费 <InfoCircleOutlined /></span>
:
canGetMoney ?
<span> 预计收取服务费¥{returnFloat(Number(value) * 0.001)} <InfoCircleOutlined /></span>
:
<span style={{ color: 'red' }}> 超出可用余额¥3000 </span>
}
</div>
<div>
{cashHistory.length ? <div>
{cashHistory.map((item: any, index: number) => {
return <div>
第{index + 1}次提现:{item}元
</div>
})}
</div> : ''}
</div>
</div>
</div >
);
};