react-问卷星项目(6)

实战

React常用UI组件库

  • Ant Design国内最常用组件库,稳定,强大
  • Material UI国外流行
  • TailWind UI 国外流行,收费

Ant Design

官网地址

这一章基本内容就是使用UI重构页面,也没有什么知识点,直接上代码

下载

npm install antd --save

安装icon组件包

npm install @ant-design/icons --save

router/index.tsx

// 导出常用路由
export const LOGIN_PATHNAME = "/login";
export const REGISTER_PATHNAME = "/register";
export const HOME_PATHNAME = "/home";
export const MANAGER_INDEX_PATHNAME = "/manager/list";

大致更新的顺序为组件 -> 布局 -> 页面

组件当前结构如下

Logo.tsx

import React, { FC } from "react";
import { Space, Typography } from "antd";
import { FormOutlined } from "@ant-design/icons";
import { Link } from "react-router-dom";
import styled from "./Logo.module.scss";
const { Title } = Typography;const Logo: FC = () => {return (<div className={styled.container}><Link to="/"><Space><Title><FormOutlined /></Title><Title>小木问卷</Title></Space></Link></div>);
};
export default Logo;

Logo.module.scss

.container{width: 200px;h1{font-size: 32px;color: #f7f7f7;}
}

QuestionCard.tsx

import React, { FC, useEffect } from "react";
// import "./QuestionCard.css";
import styled from "./QuestionCard.module.scss";
import { Button, Space, Divider, Tag, Popconfirm, Modal, message } from "antd";
import { useNavigate, Link } from "react-router-dom";
// import classnames from "classnames";
const { confirm } = Modal;import {EditOutlined,LineChartOutlined,StarOutlined,CopyOutlined,DeleteOutlined,ExclamationCircleOutlined,
} from "@ant-design/icons";type PropsType = {_id: string;title: string;isPublished: boolean;isStar: boolean;answerCount: number;createAt: string;// 问号是可写可不写,跟flutter语法相似deletQuestion?: (id: string) => void;pubQuestion?: (id: string) => void;
};const QuestionCard: FC<PropsType> = (props: PropsType) => {const { _id, title, createAt, answerCount, isPublished, isStar } = props;const nav = useNavigate();// const {confi} = Modal();function duplicate() {message.success("执行复制");// alert("执行复制");}function del() {confirm({title: "确定删除该问卷?",icon: <ExclamationCircleOutlined />,onOk: () => message.success("删除成功!"),});}return (<div className={styled.container}><div className={styled.title}><div className={styled.left}><Linkto={isPublished ? `/question/static/${_id}` : `/question/edit/${_id}`}><Space>{isStar && <StarOutlined style={{ color: "red" }} />}{title}</Space></Link></div><div className={styled.right}><Space>{isPublished ? (<Tag color="processing">已发布</Tag>) : (<Tag>未发布</Tag>)}<span>答卷:{answerCount}</span><span>{createAt}</span></Space></div></div><Divider style={{ margin: "12px" }} /><div className={styled["button-container"]}><div className={styled.left}><Space><Buttonicon={<EditOutlined />}type="text"size="small"onClick={() => nav(`/question/edit/${_id}`)}>统计问卷</Button><Buttonicon={<LineChartOutlined />}type="text"size="small"onClick={() => nav(`/question/static/${_id}`)}disabled={!isPublished}>问卷统计</Button></Space></div><div className={styled.right}><Space><Button type="text" icon={<StarOutlined />} size="small">{isStar ? "取消标星" : "标星"}</Button><Popconfirmtitle="确定复制该问卷?"okText="确定"cancelText="取消"onConfirm={duplicate}><Button type="text" icon={<CopyOutlined />} size="small">复制</Button></Popconfirm><Buttontype="text"icon={<DeleteOutlined />}size="small"onClick={del}>删除</Button></Space></div></div></div>);
};export default QuestionCard;

QuestionCard.module.scss

.container{margin-bottom: 20px;padding: 12px;border-radius: 3px;background-color: white;&:hover{box-shadow: 0 4px 10px lightgray;}
}.title{display: flex;.left{flex: 1;}.right{flex: 1;text-align: right;}
}.button-container{display: flex;.left{flex: 1;}.right{flex: 1;text-align: right;button{color: #999;}}
}

UserInfo.tsx

import React, { FC } from "react";
import { Link } from "react-router-dom";
import { LOGIN_PATHNAME } from "../router";const UserInfo: FC = () => {return (<><Link to={LOGIN_PATHNAME}>登录</Link></>);
};
export default UserInfo;

布局这一块的样式我和视频里老师的写法不一致,能实现效果就行

MainLayout.tsx

import React, { FC } from "react";
import { Outlet } from "react-router-dom";
import { Layout } from "antd";
import styled from "./MainLayout.module.scss";
import Logo from "../components/Logo";
import UserInfo from "../components/UserInfo";const { Header, Content, Footer } = Layout;const MainLayout: FC = () => {return (<Layout><Header className={styled.header}><div className={styled.left}><Logo /></div><div className={styled.right}><UserInfo /></div></Header><Layout className={styled.main}><Content><Outlet /></Content></Layout>{/* <Content className={styled.main}><Outlet /></Content> */}<Footer className={styled.footer}>小木问卷 &copy; 2023-present. Created by 双</Footer></Layout>);
};export default MainLayout;

MainLayout.module.scss

.header{height: auto;display: flex;justify-content: space-between;align-items: center;font-size: 20px;.left{float: left;}.right{float: right;}
}.main{// 减去的分别是header和footer的高度min-height: calc(100vh - 64px - 71px);
}.footer{text-align: center;background-color: #f7f7f7;border-top: 1px solid #e8e8e8;
}

ManagerLayout.tsx

import React, { FC } from "react";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import styled from "./MangerLayout.module.scss";import { Button, Space, Divider } from "antd";
import {PlusOutlined,BarsOutlined,StarOutlined,DeleteOutlined,
} from "@ant-design/icons";const MangerLayout: FC = () => {const nav = useNavigate();const { pathname } = useLocation();console.log(pathname);return (<div className={styled.container}><div className={styled.left}><Space direction="vertical"><Button type="primary" size="large" icon={<PlusOutlined />}>新建问卷</Button><Divider style={{ border: "none" }} /><Buttontype={pathname.startsWith("/manager/list") ? "default" : "text"}size="large"icon={<BarsOutlined />}onClick={() => nav("/manager/list")}>我的问卷</Button><Buttontype={pathname.startsWith("/manager/star") ? "default" : "text"}size="large"icon={<StarOutlined />}onClick={() => nav("/manager/star")}>星标问卷</Button><Buttontype={pathname.startsWith("/manager/trash") ? "default" : "text"}size="large"icon={<DeleteOutlined />}onClick={() => nav("/manager/trash")}>回收站</Button></Space></div><div className={styled.right}><Outlet /></div></div>);
};
export default MangerLayout;

ManagerLayout.module.scss

.container{display: flex;padding: 24px 0;width: 1200px;margin: 0 auto; // 水平居中.left{width: 120px;}.right{flex: 1;margin-left: 60px;}}

接下来就是零散的各个页面的布局,下面的图片是管理问卷的大概样式设置,可以按照登录时看到的页面顺序进行样式更新,比如Home -> List -> Star -> Trash,大部分从antd导入的都是文档中能看到的组件,直接用了看效果就行,想更进一步了解的可以去翻阅相关的文档

Home.tsx

// 首页
import React, { FC } from "react";
import { useNavigate, Link } from "react-router-dom";
import { Button, Typography } from "antd";
// router中导出的设置好的路径
import { MANAGER_INDEX_PATHNAME } from "../router";
import styled from "./Home.module.scss";// Typography可以理解为文章组件,可以分解出标题段落等,经常使用const { Title, Paragraph } = Typography;const Home: FC = () => {const nav = useNavigate();// function clickHandler() {//   nav({//     pathname: "/login", // 路径//     search: "b=21", // 路径附加参数,类似get//   });// }return (<div className={styled.contain}><div className={styled.info}><Title>问卷调查|在线投票</Title><Paragraph>已累计创建问卷100份,发布问卷90份,收到答卷989份</Paragraph><div><Button type="primary" onClick={() => nav(MANAGER_INDEX_PATHNAME)}>开始使用</Button></div></div></div>);
};
export default Home;

Home.module.scss

其中background-image就是一个渐变的css设置,颜色可以参考渐变颜色,直接复制即可

.contain{background-color: aqua;height: 100vh;width: 100vw;display: flex;flex-direction: column;justify-content: center;align-items: center;background-image: linear-gradient(to top, #5ee7df 0%, #b490ca 100%);// background-image: linear-gradient(to top, #9890e3 0%, #b1f4cf 100%);.info{text-align: center;button{height: 60px;font-size: 24px;}}
}

Manager/list.tsx

注意Home界面的跳转导入的路径就是这个list,可以自己命名

import React, { FC, useState } from "react";
import { useSearchParams } from "react-router-dom";
import QuestionCard from "../../components/QuestionCard";
import styled from "./Common.module.scss";
import { Typography } from "antd";
import { useTitle } from "ahooks";const { Title } = Typography;const rawQuestionList = [{_id: "q1",title: "问卷1",isPublished: true,isStar: false,answerCount: 5,createAt: "3月10日 13:23",},{_id: "q2",title: "问卷2",isPublished: false,isStar: true,answerCount: 15,createAt: "3月22日 13:23",},{_id: "q3",title: "问卷3",isPublished: true,isStar: true,answerCount: 100,createAt: "4月10日 13:23",},{_id: "q4",title: "问卷4",isPublished: false,isStar: false,answerCount: 98,createAt: "3月23日 13:23",},
];const List: FC = () => {// const [searchParams] = useSearchParams();// console.log("keyword", searchParams.get("keyword"));useTitle("小木问卷-我的问卷");const [questionList, setQuestionList] = useState(rawQuestionList);return (<><div className={styled.header}><div className={styled.left}>// level 3表示h3<Title level={3}>我的问卷</Title></div><div className={styled.right}>搜索</div></div><div className={styled.content}>{/* {问卷列表} */}{questionList.length > 0 &&questionList.map((q) => {const { _id } = q;return <QuestionCard key={_id} {...q} />;})}</div><div className={styled.footer}>loadMore 上划加载更多</div></>);
};export default List;

原先的List.module.scss改为Common.module.scss

.header{display: flex;.left{flex: 1;}.right{flex: 1;text-align: right;}
}.content{margin-bottom: 20px;
}.footer{text-align: center;
}body{background-color: #f1f1f1;
}

Star.tsx

// 收藏问卷
import React, { FC, useState } from "react";
import QuestionCard from "../../components/QuestionCard";
import styled from "./Common.module.scss";
import { Typography, Empty } from "antd";
import { useTitle } from "ahooks";
const { Title } = Typography;const rawQuestionList = [{_id: "q1",title: "问卷1",isPublished: true,isStar: true,answerCount: 5,createAt: "3月10日 13:23",},{_id: "q2",title: "问卷2",isPublished: false,isStar: true,answerCount: 15,createAt: "3月22日 13:23",},{_id: "q3",title: "问卷3",isPublished: true,isStar: true,answerCount: 100,createAt: "4月10日 13:23",},
];const Star: FC = () => {useTitle("小木问卷-星标问卷");const [questionList, setQuestionList] = useState(rawQuestionList);return (<><div className={styled.header}><div className={styled.left}><Title level={3}>星标问卷</Title></div><div className={styled.right}>搜索</div></div><div className={styled.content}>{/* {问卷列表} */}{questionList.length === 0 && <Empty description="暂无数据" />}{questionList.length > 0 &&questionList.map((q) => {const { _id } = q;return <QuestionCard key={_id} {...q} />;})}</div><div className={styled.footer}>分页</div></>);
};
export default Star;

Trash.tsx

// 回收站页面
import React, { FC, useState } from "react";
import QuestionCard from "../../components/QuestionCard";
import styled from "./Common.module.scss";
import { Typography, Empty, Table, Tag, Button, Space, Modal } from "antd";
import { useTitle } from "ahooks";
import { ExclamationOutlined } from "@ant-design/icons";const { Title } = Typography;
const { confirm } = Modal;const rawQuestionList = [{_id: "q1",title: "问卷1",isPublished: true,isStar: false,answerCount: 5,createAt: "3月10日 13:23",},{_id: "q2",title: "问卷2",isPublished: false,isStar: true,answerCount: 15,createAt: "3月22日 13:23",},{_id: "q3",title: "问卷3",isPublished: true,isStar: true,answerCount: 100,createAt: "4月10日 13:23",},{_id: "q4",title: "问卷4",isPublished: false,isStar: false,answerCount: 98,createAt: "3月23日 13:23",},
];// 表格列元素
const tableColumn = [{title: "标题",dataIndex: "title",// key:'title' // 循环列的key,会默认取dataIndex的值,dataIndex的值不重复可以不使用key},{title: "是否发布",dataIndex: "isPublished",// 根据当前这一列根据数据源进行筛选,返回自定义的JSX片段render: (isPublished: boolean) => {return isPublished ? (<Tag color="processing">已发布</Tag>) : (<Tag>未发布</Tag>);},},{title: "答卷",dataIndex: "answerCount",},{title: "创建时间",dataIndex: "createAt",},
];const Trash: FC = () => {useTitle("小木问卷-回收站");const [questionList, setQuestionList] = useState(rawQuestionList);// 泛形定义数组类型const [selectedId, setSelectedId] = useState<string[]>([]);function del() {confirm({title: "确认彻底删除该问卷?",icon: <ExclamationOutlined />,content: "删除以后不可以找回,请谨慎操作!",onOk: () => alert(JSON.stringify(selectedId)),});}const TableElement = (<><div style={{ marginBottom: "15px" }}><Space><Button type="primary" disabled={selectedId.length == 0}>恢复</Button><Button danger onClick={del}>彻底删除</Button></Space></div><TabledataSource={questionList}columns={tableColumn}pagination={false}// 告诉表格用什么属性作为keyrowKey={(q) => q._id}// 多选框的设置,打印出来的是选择的条数id,将选择条数id赋值给当前选择变量rowSelection={{type: "checkbox",onChange: (selectedRowKey) => {// selectedRowKey打印出的选中的key// as强制认为是数组类型setSelectedId(selectedRowKey as string[]);},}}></Table></>);return (<><div className={styled.header}><div className={styled.left}><Title level={3}>回收站</Title></div><div className={styled.right}>搜索{selectedId}</div></div><div className={styled.content}>{/* {问卷列表} */}{questionList.length === 0 && <Empty description="暂无数据" />}{questionList.length > 0 && TableElement}</div></>);
};
export default Trash;

NotFound.tsx

// 未找到页面
import React, { FC } from "react";
import { Result, Button } from "antd";
import { useNavigate } from "react-router-dom";
import { MANAGER_INDEX_PATHNAME } from "../router";const NotFound: FC = () => {const nav = useNavigate();return (<Resultstatus="404"title="404"subTitle="抱歉,您访问的界面不存在"extra={<Button type="primary" onClick={() => nav(MANAGER_INDEX_PATHNAME)}>返回标签页</Button>}></Result>);
};
export default NotFound;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1554999.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

[Linux]:线程(三)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. POSIX 信号量 1.1 信号量的概念 为了解决多执行流访问临界区&#xff0c…

Nuxt.js 应用中的 app:mounted 钩子详解

title: Nuxt.js 应用中的 app:mounted 钩子详解 date: 2024/10/5 updated: 2024/10/5 author: cmdragon excerpt: app:mounted 钩子在 Vue 应用的生命周期中扮演着重要角色,提供了在组件被挂载后的执行时机。通过合理利用这个钩子,我们能够提高组件的交互性、用户体验以及…

华为OD机试 - 核酸最快检测效率 - 动态规划、背包问题(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

基于单片机的智能浇花系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采样DHT11温湿度传感器检测温湿度&#xff0c;通过LCD1602显示 4*4按键矩阵可以设置温度湿度阈值&#xff0c;温度大于阈值则开启水泵&#xff0c;湿度大于阈值则开启风扇…

基于STM32的智能窗帘控制系统设计

引言 本项目将基于STM32微控制器设计一个智能窗帘控制系统&#xff0c;用户可以通过按钮或遥控器控制窗帘的开关&#xff0c;并且系统能够根据光照强度自动调节窗帘的开合状态。该项目展示了STM32微控制器在家居自动化中的应用&#xff0c;以及与光照传感器、直流电机和红外接…

Linux网络编程 -- 网络基础

本文主要介绍网络的一些基础概念&#xff0c;不涉及具体的操作原理&#xff0c;旨在构建对网络的基础认识。 1、网络的早期发展历程 20世纪50年代 在这一时期&#xff0c;计算机主机非常昂贵&#xff0c;而通信线路和设备相对便宜。为了共享计算机主机资源和进行信息的综合处…

模拟器GSN3之DHCP动态分配IP地址配置案例

前文《详解DHCP服务工作原理及配置案例》介绍了DHCP服务工作原理&#xff0c;要想彻底理解、应用DHCP服务&#xff0c;须通过实证案例学习&#xff0c;该文在GSN3虚拟环境下&#xff0c;构建DHCP服务的环境。 一、配置环境&#xff1a; 1、GSN3 2、路由器&#xff1a;R1、R2…

冥想第一千三百零一天(1301)

1.今天上午溪溪和小侄子写作业&#xff0c;我带着桐桐去了惠济区的裕华广场永辉&#xff0c;给家人买了好吃的&#xff0c;下午4点半左右去了妈妈朋友家里摘石榴。 2.感谢父母&#xff0c;感谢朋友&#xff0c;感谢家人&#xff0c;感谢不断进步的自己。

[C++]使用纯opencv部署yolov11旋转框目标检测

【官方框架地址】 GitHub - ultralytics/ultralytics: Ultralytics YOLO11 &#x1f680; 【算法介绍】 YOLOv11是一种先进的对象检测算法&#xff0c;它通过单个神经网络实现了快速的物体检测。其中&#xff0c;旋转框检测是YOLOv11的一项重要特性&#xff0c;它可以有效地检…

利用 Python 爬虫采集 1688商品详情

1688是中国的一个大型B2B电子商务平台&#xff0c;主要用于批发和采购各种商品。对于需要从1688上获取商品详情数据、工程数据或店铺数据的用户来说&#xff0c;可以采用以下几种常见的方法&#xff1a; 官方API接口&#xff1a;如果1688提供了官方的API接口&#xff0c;那么可…

FinOps现状分析:行业趋势与未来展望

一、FinOps 的国内现状 《FinOps 现状》是 FinOps 基金会自 2020 年以来开展的一项年度调查&#xff0c;旨在收集对关键优先、行业趋势和 FinOps 实践方向 的见解。该调查有助于为 FinOps 基金会的活动提供信息&#xff0c;并为更广泛的市场提供有关 FinOps 在各种组织中如何实…

redhat7.7 linux 网络配置文件

一、为什么虚拟网卡配置文件是ens33 变更目录至网络脚本&#xff08;network-scripts&#xff09;文件夹&#xff0c;发现网络配置文件名称为“ifcfg-ens33” cd /etc/sysconfig/network-scripts ls扩展&#xff1a;“ifcfg-ens33”文件下面还有一个“ifcfg”前缀的文件&…

线程互斥函数的例子

代码 #include<stdio.h> #include<pthread.h> #include<sched.h> void *producter_f(void *arg); void *consumer_f(void *arg); int buffer_has_item0; pthread_mutex_t mutex; int running1; int main(void) {pthread_t consumer_t;pthread_t producter_t…

【ubuntu】APT、apt、apt-get介绍

目录 1.apt简介 2.常用apt指令 2.1安装 2.2更新列表 2.3更新已经安装的软件包 2.4搜索软件包 2.5显示软件包信息 2.6移除软件包 2.7清理无用的安装包 2.8清理无用的依赖项 3.apt和apt-get 3.1区别 3.2 总结 1.apt简介 apt的全称是advanced package …

大学生就业桥梁:基于Spring Boot的招聘系统

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

No.1 | 从小白到入门:我的渗透测试笔记

嘿&#xff0c;小伙伴们&#xff01;好久不见啊&#xff0c;是不是都以为我失踪了&#xff1f;&#x1f602; 其实呢&#xff0c;最近一直在埋头苦学&#xff0c;感觉自己就像是在技术的海洋里游泳&#xff0c;每天都在吸收新知识。现在终于有时间冒个泡&#xff0c;跟大家分享…

脱口秀演员调侃王楚钦引争议

听说脱口秀演员调侃王楚钦输球&#xff0c;野生喜剧回应暂停演出合作&#xff0c;这不仅引发了关于脱口秀表演冒犯边界的讨论&#xff0c;也让我们反思言论自由与尊重他人之间的界限。 脱口秀作为一种艺术形式&#xff0c;其核心在于通过幽默、讽刺的方式&#xff0c;对社会现象…

Meta MovieGen AI:颠覆性的文本生成视频技术详解

近年来&#xff0c;生成式AI技术的发展迅猛&#xff0c;尤其是在文本生成图像、文本生成视频等领域。Meta公司近期推出的MovieGen AI&#xff0c;以其强大的文本生成视频能力震撼了整个AI行业。本文将详细解读Meta MovieGen AI的核心技术、功能特性及其在实际应用中的潜力。 一…

Mac 安装OpenAI的开源语音神器Whisper

一.Whisper 项目地址 1.GitHub项目地址 https://github.com/openai/whisper二.Whisper项目简介 Whisper 是 OpenAI 开源的语音神器&#xff0c;可以实现识别音频、视频中的人声&#xff0c;并将人声转换为字幕内容&#xff0c;保存到文件&#xff1b; 三.Whisper 安装教程 …

一“填”到底:深入理解Flood Fill算法

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 floodfill算法是什么&#xff1f; 二 相关OJ题练习 2.1 图像渲染 2.2 岛屿数量 2.3 岛屿的最大面积 2.4 被围绕的区域 2.5 太平洋大西洋水流问题 2.6 扫雷游戏 2.7 衣橱整…