1 标星
操作:标星和取消标星,有2种状态的布尔值。通过更新问卷功能实现。
后端quetion.js添加接口
{
// 更新问卷信息
url: '/api/question/:id',
method: 'patch',
response() {
return {
errno: 0
}
}
},
前端question.ts添加更新问卷接口
/**
* 更新问卷信息
* @param id 问卷id
* @returns 问卷信息
*/
export async function updateQuestionApi(
id: string,
opt: { [key: string]: any }
): Promise<ResDataType> {
const url = `/api/question/${id}`;
const data = (await request.patch(url, opt)) as ResDataType;
return data;
}
问卷卡片页面QuestionCard.tsx代码如下:
import { FC, useState } from "react";
//...
import { useRequest } from "ahooks";
import { updateQuestionApi } from "@/api/question";
type PropsType = {
_id: string;
title: string;
isPublished: boolean;
isStar: boolean;
answerCount: number;
createdAt: string;
};
const { confirm } = Modal;
const QuestionCard: FC<PropsType> = (props: PropsType) => {
//...
const [isStarState, setIsStarState] = useState(isStar);
const { loading: changeStarLoading, run: changeStar } = useRequest(
async () => {
await updateQuestionApi(_id, { isStar: !isStarState });
},
{
manual: true,
onSuccess() {
setIsStarState(!isStarState);
message.success("更新成功")
},
}
);
//...
return (
<div className={styles.container}>
<div className={styles.title}>
<div className={styles.left}>
<Link
to={isPublished ? `/question/stat/${_id}` : `/question/edit/${_id}`}
>
<Space>
{isStarState && <StarOutlined style={{ color: "red" }} />}
{title}
</Space>
</Link>
</div>
// ...
<div className={styles.right}>
<Space>
<Button
type="text"
size="small"
icon={<StarOutlined />}
onClick={changeStar}
disabled={changeStarLoading}
>
{isStarState ? "取消标星" : "标星"}
</Button>
//...
</Space>
</div>
</div>
</div>
);
};
export default QuestionCard;
如下图所示:
2 复制
操作:点击复制按钮,新增一条问卷,id为新的其他内容和被复制的问卷一致。
后端接口question.js新增代码如下:
{
// 复制问卷信息
url: '/api/question/duplicate/:id',
method: 'post',
response() {
return {
errno: 0,
data: {
id: Random.id(),
}
}
}
}
前端qutions.ts新增复制接口代码如下:
/**
* 复制问卷信息
* @param id 问卷id
* @returns 问卷信息
*/
export async function duplicateQuestionApi(id: string): Promise<ResDataType> {
const url = `/api/question/${id}`;
const data = (await request.post(url)) as ResDataType;
return data;
}
卡片QuestionCard.tsx页面代码如下:
import { FC, useState } from "react";
//...
import { useRequest } from "ahooks";
import { duplicateQuestionApi, updateQuestionApi } from "@/api/question";
//...
const QuestionCard: FC<PropsType> = (props: PropsType) => {
const nav = useNavigate();
//...
const { loading: duplicateLoading, run: duplicate } = useRequest(
async () => await duplicateQuestionApi(_id),
{
manual: true,
onSuccess(res) {
message.success("复制成功!");
nav(`/question/edit/${res.id}`);
},
}
);
//...
<Popconfirm
title="确定执行复制吗?"
okText="确定"
cancelText="取消"
onConfirm={duplicate}
>
<Button
type="text"
size="small"
icon={<CopyOutlined />}
disabled={duplicateLoading}
>
复制
</Button>
</Popconfirm>
// ...
);
};
export default QuestionCard;
如下图所示:
3 假删除
操作:改变属性值,布尔值,通过更新问卷实现
QuestionCard.tsx代码如下:
import { FC, useState } from "react";
// ...
import { useRequest } from "ahooks";
import { duplicateQuestionApi, updateQuestionApi } from "@/api/question";
type PropsType = {
_id: string;
title: string;
isPublished: boolean;
isStar: boolean;
answerCount: number;
createdAt: string;
};
const { confirm } = Modal;
const QuestionCard: FC<PropsType> = (props: PropsType) => {
// ...
const { loading: delLoading, run: fakeDel } = useRequest(
async () => await updateQuestionApi(_id, { isDeleted: true }),
{
manual: true,
onSuccess() {
message.success("删除成功!");
// todo重新执行列表请求
},
}
);
function del() {
confirm({
title: "确定要执行删除操作吗?",
icon: <ExclamationCircleOutlined />,
onOk: fakeDel,
});
}
// ...
<Button
type="text"
size="small"
icon={<DeleteOutlined />}
onClick={del}
disabled={delLoading}
>
删除
</Button>
</Space>
</div>
</div>
</div>
);
};
export default QuestionCard;
如下图所示:
4 恢复
操作:回收站批量恢复“假删除”的问卷
后端接口question.js新增接口:
{
// 批量恢复问卷
url: '/api/question/recover',
method: 'patch',
response() {
return {
errno: 0,
}
}
}
前端question.ts新增接口:
/**
* 恢复问卷信息
* @param ids 问卷ids
* @returns 执行结果
*/
export async function recoverQuestionsApi(ids: string[]): Promise<ResDataType> {
const url = "/api/question/recover";
const data = (await request.patch(url, ids)) as ResDataType;
return data;
}
回收站页面Trash.tsx代码如下:
import { FC, useState } from "react";
import { useRequest, useTitle } from "ahooks";
//...
import { deleteQuestionsApi, recoverQuestionsApi } from "@/api/question";
//...
// 选择ids集合
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const { loading: recoverLoading, run: handleRecover } = useRequest(
async () => await recoverQuestionsApi(selectedIds),
{
manual: true,
onSuccess() {
message.success("恢复成功!");
// todo 查询问卷列表
},
}
);
// ...
const TableEle = (
<>
<div style={{ marginBottom: "10px" }}>
<Space>
<Button
type="primary"
disabled={selectedIds.length === 0 || recoverLoading}
onClick={handleRecover}
>
恢复
</Button>
//...
</Space>
</div>
// ...
</>
);
return (
<>
// ...
<div className={styles.content}>
{loading && (
<div style={{ textAlign: "center" }}>
<Spin />
</div>
)}
{!loading && list.length === 0 && <Empty description="暂无数据" />}
{TableEle}
</div>
//...
</>
);
};
export default List;
如下图所示:
5 彻底删除
操作:把数据从数据库彻底删除,危险操作,慎重
后端question.js新增接口:
{
// 批量恢复问卷
url: '/api/question/deleteQuestions',
method: 'delete',
response() {
return {
errno: 0,
}
}
},
前端question.js新增接口:
/**
* 彻底删除问卷
* @param ids 问卷ids
* @returns 执行结果
*/
export async function deleteQuestionsApi(ids: string[]): Promise<ResDataType> {
const url = "/api/question/deleteQuestions";
const data = (await request.delete(url, { data: ids })) as ResDataType;
return data;
}
回收站Trash.tsx代码如下:
import { FC, useState } from "react";
import { useRequest, useTitle } from "ahooks";
//...
import { deleteQuestionsApi, recoverQuestionsApi } from "@/api/question";
//...
const List: FC = () => {
useTitle("调查问卷-回收站");
//...
const { loading: delLoading, run: handleDel } = useRequest(
async () => await deleteQuestionsApi(selectedIds),
{
manual: true,
onSuccess() {
message.success("删除成功!");
// todo 请求问卷列表
},
}
);
function del() {
confirm({
title: "您确定要删除吗?",
okText: "确定",
cancelText: "取消",
content: "删除以后不可找回!",
icon: <ExclamationCircleOutlined />,
onOk: handleDel,
});
}
const TableEle = (
<>
<div style={{ marginBottom: "10px" }}>
<Space>
//...
<Button
danger
disabled={selectedIds.length === 0 || delLoading}
onClick={del}
>
彻底删除
</Button>
</Space>
</div>
//...
</>
);
return (
<>
//...
<div className={styles.content}>
{loading && (
<div style={{ textAlign: "center" }}>
<Spin />
</div>
)}
{!loading && list.length === 0 && <Empty description="暂无数据" />}
{TableEle}
</div>
//...
</>
);
};
export default List;
如下图所示:
说明:
- 上面所有操作成功后,前面会重新执行请求列表接口(目前只是部分前端模拟)
- 后端接口只是mock接口
结语
❓QQ:806797785
⭐️仓库地址:https://gitee.com/gaogzhen
⭐️仓库地址:https://github.com/gaogzhen
[1]ahook官网[CP/OL].
[2]mock文档[CP/OL].
[3]Ant Design官网[CP/OL].