# 第一题
# 目标
使用 React, 参考下述需求优先级和设计稿 ,实现倒计时抢券组件。
# 关键需求点
- 功能 1: 按钮自动倒计时 (此功能重要占比 60%)
- 进入页面时,卡片中的按钮开始自动 10s 倒计时
- 倒计时过程中,按钮显示剩余时间 (文案为: 10s、9s、8s、...、1s)
- 倒计时结束后,按钮文案变为「 抢购 」
- 功能 2: 抢购功能模拟 (此功能重要占比 20%)
- 点击抢购按钮时,调用异步模拟请求方法,请求完成后按钮文字变为「 已抢购 」
- 异步请求模拟方法需自行实现,延迟 1s 后返回成功即可
- 功能 3: 还原券 css 样式 (此功能重要占比 20%)
- 副标题最多支持 2 行,多余 2 行省略号表示
# 设计稿
/** | |
* 第一题 | |
*/ | |
import React, { FC, useEffect, useState } from 'react'; | |
import './style.less'; | |
/** | |
* 渲染测试数据 | |
*/ | |
export const cardDataList: IDirectVoucher[] = [ | |
{ | |
title: '杭州市通用5元券', | |
subTitle: | |
'杭味面馆非常好吃,太好吃了,相当不错,味道鲜美,特别划算,快快抢购,聚划算', | |
}, | |
{ | |
title: '杭州市10元券', | |
subTitle: '兰州拉面非常好吃', | |
}, | |
]; | |
/** | |
* 券卡片渲染数据类型 | |
*/ | |
export interface IDirectVoucher { | |
/** 标题 */ | |
title?: string; | |
/** 副标题 */ | |
subTitle?: string; | |
} | |
export interface ICardProps { | |
data: IDirectVoucher; | |
} | |
/** | |
* 卡片组件 | |
*/ | |
export const Card: FC<ICardProps> = (props) => { | |
// 在这里完成代码 | |
const { data } = props; | |
const max = 10; | |
const [str, setStr] = useState('抢购'); | |
const [count, setCount] = useState(max); | |
// 倒计时逻辑 | |
// useEffect(() => { | |
// if (count >= 0) { | |
// setTimeout(() => { | |
// setCount(count - 1); | |
// if (count == 0) { | |
// setStr (' 抢购 '); | |
// } else { | |
// setStr(count + 's'); | |
// } | |
// }, 1000); | |
// } | |
// },[count]); | |
useEffect(() => { | |
if (count >= 0) { | |
const interval = setInterval(() => { | |
setCount(count - 1); | |
if (count === 0) { | |
setStr('抢购'); | |
} else { | |
setStr(count + 's'); | |
} | |
}, 1000); | |
return () => clearInterval(interval); | |
} | |
}); | |
const timeClick = async () => { | |
await setTimeout(function () { | |
setStr('已抢购'); | |
}, 1000); | |
}; | |
return ( | |
<div className="card"> | |
<div className="left"> | |
<div className="title">{data.title}</div> | |
<div className="subTitle">{data.subTitle}</div> | |
</div> | |
<div className="right"> | |
<button onClick={() => timeClick()}>{str}</button> | |
</div> | |
</div> | |
); | |
}; |
# 第二题
# 目标
实现一个 query
方法,实现对数据的链式查询和处理。
# 要求
query
传入参数为原始数据(数组格式,每个元素都是对象)- 通过进行链式调用对数据执行操作,支持的方法有
where(predicate)
: 根据参数的条件进行筛选,参数与[].filter
的参数类似orderBy(key, desc)
: 根据key
的值进行排列,默认升序排列,当第二个参数为true
时降序排列groupBy(key)
: 根据key
的值对数据元素进行分组,合并为二维数组execute()
: 执行所有处理并返回最终结果
- 请结合下面示例理解需求
/** | |
* 第二题 | |
*/ | |
import { isEqual } from 'lodash-es'; | |
function query(data: Object[]) { | |
let list = data; | |
function myArray() {} | |
// 判断数据类型 | |
if (Object.prototype.toString.call(data).slice(8, -1) !== 'Array') { | |
return console.error('must be an array'); | |
} | |
myArray.prototype.where = function (key) { | |
list = list.filter(key); | |
return { ...myArray.prototype }; | |
}; | |
myArray.prototype.orderBy = function (key) { | |
list = list.sort((a, b) => a[key] - b[key]); | |
return { ...myArray.prototype }; | |
}; | |
myArray.prototype.groupBy = function (key) { | |
let group = list.reduce((pre, item) => { | |
pre[item[key]] = [...(pre[item[key]] || []), item]; | |
return pre; | |
}, {}); | |
let ordList = []; | |
for (let key in group) { | |
ordList.push(group[key]); | |
} | |
list = ordList; | |
return { ...myArray.prototype }; | |
}; | |
myArray.prototype.execute = function (key) { | |
return list; | |
}; | |
return { ...myArray.prototype }; | |
} | |
/** | |
* 以下为测试用例,无需修改 | |
*/ | |
export default () => { | |
try { | |
const data = [ | |
{ name: 'foo', age: 16, city: 'shanghai' }, | |
{ name: 'bar', age: 24, city: 'hangzhou' }, | |
{ name: 'fiz', age: 22, city: 'shanghai' }, | |
{ name: 'baz', age: 19, city: 'hangzhou' }, | |
]; | |
const result = query(data) | |
// @ts-ignore | |
.where((item) => item.age > 18) | |
.orderBy('age') | |
.groupBy('city') | |
.execute(); | |
console.log('result:', result); | |
if ( | |
!isEqual(result, [ | |
[ | |
{ name: 'baz', age: 19, city: 'hangzhou' }, | |
{ name: 'bar', age: 24, city: 'hangzhou' }, | |
], | |
[{ name: 'fiz', age: 22, city: 'shanghai' }], | |
]) | |
) { | |
throw new Error('Wrong answer'); | |
} | |
return true; | |
} catch (err) { | |
console.warn('测试运行失败'); | |
console.error(err); | |
return false; | |
} | |
}; |