React后台管理前端系统(基于开源框架开发)起步式

举报
拿我格子衫来 发表于 2022/03/17 22:49:52 2022/03/17
【摘要】 写博客不容易,如果对你有帮助请点个赞吧。有时间的可以评论一下。        这个系统的搭建背景是这样的,有一个朋友想看到现有系统中的一些,用户数据,新闻数据,只需要看到,短期不需要增删改功能,让我搭建一个简单的后台系统给他看.接到任务作为一个有四年开发经验的人来说这也太简单了吧,开始干吧,最简...

写博客不容易,如果对你有帮助请点个赞吧。有时间的可以评论一下。

       这个系统的搭建背景是这样的,有一个朋友想看到现有系统中的一些,用户数据,新闻数据,只需要看到,短期不需要增删改功能,让我搭建一个简单的后台系统给他看.接到任务作为一个有四年开发经验的人来说这也太简单了吧,开始干吧,最简单的办法是直接使用开源项目https://github.com/PanJiaChen/vue-element-admin 下载下来,把不要的路由去掉,新建几个表格路由,页面直接复制过来就用了.

      这样算下来,整个系统搭建时间不会超过半天,而且这个开源框架我已经玩的很溜了,后期开发完全不费事.但是,这个方案被我拒绝了。为什么那?因为我想跳出舒适区.接受更大的挑战,目前和vue并驾齐驱的React项目也很流程,很多公司也在使用.于是乎,技术栈就是用React了。但是要从零搭建,时间上划不来,因为需要集成很多东西,基础建设就要耗费很长时间.于是乎在GitHub上一顿搜,Ok 找到了二个比较可靠的项目使用,一个是 ant-design-pro 另一个是antd-admin 二个项目大同小异, 使用的技术栈reactant-designdvaMock  基于 Antd UI 设计语言  虽然我对dva和ant-design 了解不深,但我还是准备挑战一下.不入虎穴焉得虎子.

  一开始我选择的是antd-admin 因为代码比较规范,UI也比较符合我的审美.开始做吧

 首先找到一个列表页,仔细阅读代码,弄清楚依赖关系,然后新建一个目录,把列表页的相关文件都拷贝进去, 配路由,设置权限.

但是在调取接口的时候,接口虽然调取了,但是页面没有刷新出来,怎么调试都不出来,急死了.最后只好需求同事的帮助,但是他们也很少有人用React.就这样过了一天,我决定先把问题放一下, 问题的答案肯定就在代码里,先休息一下,别被这个问题让自己的眼光太局限,导致看不到问题的本质.在这个问题还没有解决的情况下,我又开始了另一个项目 ant-design-pro的尝试.因为我知道,那个页面没有显示数据的问题,我肯定会解决的,只是时间问题,当我解决了,肯定会觉得自己当时太笨了,后来的进展证明确实是这样.

  在数据不显示的问题上短暂停留下,我开始探索ant-design-pro

  这里我来说一下,当一个菜鸟接手一个新项目时遇到的迷茫和问题。

 首先项目开启我们会看到页面的URL,URL与我们的页面是一一对应的,意思就是我们根据URL可以再项目中准确地找到对应的页面,或页面入口.当然页面可能会一个主页面,多个子页面.这里忘记说一点,URL对应页面 这种对应关系的配置,叫做路由.

看到URL,就能找到路由,就能找到页面, 找到页面就完成了项目熟悉的第一步, 到了这一步,你就能自己写个静态页面,自己配个路由就能在浏览器看到了. 是不是很简单.这是大致的思路.不过有的框架有权限拦截,新的页面路由可能需要给登录的人赋权限才能出现. 等下我会以具体页面案例来做个演示.

第一步已经学会了,接下来就是进入到页面内,看看页面的数据是怎么流转的,事件,参数是怎么配置的.这一块是比较难的,也是熟悉前端项目的核心知识点.敲重点,重点,重点 一般一个页面,都会有初始化函数, 比如一个vue组件会在mounted状态下调取获取数据的接口,来渲染页面.React会在componentDidMount生命周期调取获取数据的接口. 每个页面或组件都有可能是两个或更多个组件,组合而来的,而组件的参数也是错综复杂,组件的表现,事件都是有这些参数控制的.除了简单理解这些参数,还需要理解这些组件是怎么组合起来的.

说也说了够多的 下面我就用一个列表页来给你看一下 我是怎么了解一个前端项目和开发的

我以ant-design-pro的查询表格页面为例子

我们拿着/list/table-list这个路由去项目中搜索 

打开这个文件 

很明显这个文件是路由管理  这里清楚的写着 /list/table-list路由指向文件../routes/List/TableList

其中代码中的

dynamicWrapper(app, ['rule'], ()
 

我们暂时不需要去理解他的意思,因为我们的第一步是根据路由找到对应的页面. 而不是去理解每一行代码.

打开../routes/List/TableList我们看到

果然是我们要寻找的页面, 改一二个文字 刷新一下页面,果然改变了.好开心.第一步就这样完了.

必须要提的全局搜索是一个非常很好的技能,大家一定要学会.另外 搜素路由的时候,有的路由是分开写的,比如路由/list/table-list 是有/list 和/table-list 直接搜/list/table-list搜不到,这个时候你就要去尝试单个搜索了,交叉比对,或者,找像路由配合的目录 这是一个简单实用的方法.走到这一步,你就可以自己去创建一个页面,配一个路由看看了.

接下来说一下进入页面内部改如何快速理解页面大致结构


  
  1. import React, { PureComponent, Fragment } from 'react';
  2. import { connect } from 'dva';
  3. import moment from 'moment';
  4. import {
  5. Row,
  6. Col,
  7. Card,
  8. Form,
  9. Input,
  10. Select,
  11. Icon,
  12. Button,
  13. Dropdown,
  14. Menu,
  15. InputNumber,
  16. DatePicker,
  17. Modal,
  18. message,
  19. Badge,
  20. Divider,
  21. } from 'antd';
  22. import StandardTable from 'components/StandardTable';
  23. import PageHeaderLayout from '../../layouts/PageHeaderLayout';
  24. import styles from './TableList.less';
  25. const FormItem = Form.Item;
  26. const { Option } = Select;
  27. const getValue = obj =>
  28. Object.keys(obj)
  29. .map(key => obj[key])
  30. .join(',');
  31. const statusMap = ['default', 'processing', 'success', 'error'];
  32. const status = ['关闭', '运行中', '已上线', '异常'];
  33. const CreateForm = Form.create()(props => {
  34. const { modalVisible, form, handleAdd, handleModalVisible } = props;
  35. const okHandle = () => {
  36. form.validateFields((err, fieldsValue) => {
  37. if (err) return;
  38. form.resetFields();
  39. handleAdd(fieldsValue);
  40. });
  41. };
  42. return (
  43. <Modal
  44. title="新建规则"
  45. visible={modalVisible}
  46. onOk={okHandle}
  47. onCancel={() => handleModalVisible()}
  48. >
  49. <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="描述">
  50. {form.getFieldDecorator('desc', {
  51. rules: [{ required: true, message: 'Please input some description...' }],
  52. })(<Input placeholder="请输入" />)}
  53. </FormItem>
  54. </Modal>
  55. );
  56. });
  57. @connect(({ rule, loading }) => ({
  58. rule,
  59. loading: loading.models.rule,
  60. }))
  61. @Form.create()
  62. export default class TableList extends PureComponent {
  63. state = {
  64. modalVisible: false,
  65. expandForm: false,
  66. selectedRows: [],
  67. formValues: {},
  68. };
  69. componentDidMount() {
  70. const { dispatch } = this.props;
  71. dispatch({
  72. type: 'rule/fetch',
  73. });
  74. }
  75. handleStandardTableChange = (pagination, filtersArg, sorter) => {
  76. const { dispatch } = this.props;
  77. const { formValues } = this.state;
  78. const filters = Object.keys(filtersArg).reduce((obj, key) => {
  79. const newObj = { ...obj };
  80. newObj[key] = getValue(filtersArg[key]);
  81. return newObj;
  82. }, {});
  83. const params = {
  84. currentPage: pagination.current,
  85. pageSize: pagination.pageSize,
  86. ...formValues,
  87. ...filters,
  88. };
  89. if (sorter.field) {
  90. params.sorter = `${sorter.field}_${sorter.order}`;
  91. }
  92. dispatch({
  93. type: 'rule/fetch',
  94. payload: params,
  95. });
  96. };
  97. handleFormReset = () => {
  98. const { form, dispatch } = this.props;
  99. form.resetFields();
  100. this.setState({
  101. formValues: {},
  102. });
  103. dispatch({
  104. type: 'rule/fetch',
  105. payload: {},
  106. });
  107. };
  108. toggleForm = () => {
  109. const { expandForm } = this.state;
  110. this.setState({
  111. expandForm: !expandForm,
  112. });
  113. };
  114. handleMenuClick = e => {
  115. const { dispatch } = this.props;
  116. const { selectedRows } = this.state;
  117. if (!selectedRows) return;
  118. switch (e.key) {
  119. case 'remove':
  120. dispatch({
  121. type: 'rule/remove',
  122. payload: {
  123. no: selectedRows.map(row => row.no).join(','),
  124. },
  125. callback: () => {
  126. this.setState({
  127. selectedRows: [],
  128. });
  129. },
  130. });
  131. break;
  132. default:
  133. break;
  134. }
  135. };
  136. handleSelectRows = rows => {
  137. this.setState({
  138. selectedRows: rows,
  139. });
  140. };
  141. handleSearch = e => {
  142. e.preventDefault();
  143. const { dispatch, form } = this.props;
  144. form.validateFields((err, fieldsValue) => {
  145. if (err) return;
  146. const values = {
  147. ...fieldsValue,
  148. updatedAt: fieldsValue.updatedAt && fieldsValue.updatedAt.valueOf(),
  149. };
  150. this.setState({
  151. formValues: values,
  152. });
  153. dispatch({
  154. type: 'rule/fetch',
  155. payload: values,
  156. });
  157. });
  158. };
  159. handleModalVisible = flag => {
  160. this.setState({
  161. modalVisible: !!flag,
  162. });
  163. };
  164. handleAdd = fields => {
  165. const { dispatch } = this.props;
  166. dispatch({
  167. type: 'rule/add',
  168. payload: {
  169. description: fields.desc,
  170. },
  171. });
  172. message.success('添加成功');
  173. this.setState({
  174. modalVisible: false,
  175. });
  176. };
  177. renderSimpleForm() {
  178. const { form } = this.props;
  179. const { getFieldDecorator } = form;
  180. return (
  181. <Form onSubmit={this.handleSearch} layout="inline">
  182. <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
  183. <Col md={8} sm={24}>
  184. <FormItem label="规则编号">
  185. {getFieldDecorator('no')(<Input placeholder="请输入" />)}
  186. </FormItem>
  187. </Col>
  188. <Col md={8} sm={24}>
  189. <FormItem label="使用状态">
  190. {getFieldDecorator('status')(
  191. <Select placeholder="请选择" style={{ width: '100%' }}>
  192. <Option value="0">关闭</Option>
  193. <Option value="1">运行中</Option>
  194. </Select>
  195. )}
  196. </FormItem>
  197. </Col>
  198. <Col md={8} sm={24}>
  199. <span className={styles.submitButtons}>
  200. <Button type="primary" htmlType="submit">
  201. 查询
  202. </Button>
  203. <Button style={{ marginLeft: 8 }} onClick={this.handleFormReset}>
  204. 重置
  205. </Button>
  206. <a style={{ marginLeft: 8 }} onClick={this.toggleForm}>
  207. 展开 <Icon type="down" />
  208. </a>
  209. </span>
  210. </Col>
  211. </Row>
  212. </Form>
  213. );
  214. }
  215. renderAdvancedForm() {
  216. const { form } = this.props;
  217. const { getFieldDecorator } = form;
  218. return (
  219. <Form onSubmit={this.handleSearch} layout="inline">
  220. <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
  221. <Col md={8} sm={24}>
  222. <FormItem label="规则编号">
  223. {getFieldDecorator('no')(<Input placeholder="请输入" />)}
  224. </FormItem>
  225. </Col>
  226. <Col md={8} sm={24}>
  227. <FormItem label="使用状态">
  228. {getFieldDecorator('status')(
  229. <Select placeholder="请选择" style={{ width: '100%' }}>
  230. <Option value="0">关闭</Option>
  231. <Option value="1">运行中</Option>
  232. </Select>
  233. )}
  234. </FormItem>
  235. </Col>
  236. <Col md={8} sm={24}>
  237. <FormItem label="调用次数">
  238. {getFieldDecorator('number')(<InputNumber style={{ width: '100%' }} />)}
  239. </FormItem>
  240. </Col>
  241. </Row>
  242. <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
  243. <Col md={8} sm={24}>
  244. <FormItem label="更新日期">
  245. {getFieldDecorator('date')(
  246. <DatePicker style={{ width: '100%' }} placeholder="请输入更新日期" />
  247. )}
  248. </FormItem>
  249. </Col>
  250. <Col md={8} sm={24}>
  251. <FormItem label="使用状态">
  252. {getFieldDecorator('status3')(
  253. <Select placeholder="请选择" style={{ width: '100%' }}>
  254. <Option value="0">关闭</Option>
  255. <Option value="1">运行中</Option>
  256. </Select>
  257. )}
  258. </FormItem>
  259. </Col>
  260. <Col md={8} sm={24}>
  261. <FormItem label="使用状态">
  262. {getFieldDecorator('status4')(
  263. <Select placeholder="请选择" style={{ width: '100%' }}>
  264. <Option value="0">关闭</Option>
  265. <Option value="1">运行中</Option>
  266. </Select>
  267. )}
  268. </FormItem>
  269. </Col>
  270. </Row>
  271. <div style={{ overflow: 'hidden' }}>
  272. <div style={{ float: 'right', marginBottom: 24 }}>
  273. <Button type="primary" htmlType="submit">
  274. 查询
  275. </Button>
  276. <Button style={{ marginLeft: 8 }} onClick={this.handleFormReset}>
  277. 重置
  278. </Button>
  279. <a style={{ marginLeft: 8 }} onClick={this.toggleForm}>
  280. 收起 <Icon type="up" />
  281. </a>
  282. </div>
  283. </div>
  284. </Form>
  285. );
  286. }
  287. renderForm() {
  288. const { expandForm } = this.state;
  289. return expandForm ? this.renderAdvancedForm() : this.renderSimpleForm();
  290. }
  291. render() {
  292. const {
  293. rule: { data },
  294. loading,
  295. } = this.props;
  296. const { selectedRows, modalVisible } = this.state;
  297. const columns = [
  298. {
  299. title: '规则编号',
  300. dataIndex: 'no',
  301. },
  302. {
  303. title: '描述',
  304. dataIndex: 'description',
  305. },
  306. {
  307. title: '服务调用次数',
  308. dataIndex: 'callNo',
  309. sorter: true,
  310. align: 'right',
  311. render: val => `${val} 万`,
  312. // mark to display a total number
  313. needTotal: true,
  314. },
  315. {
  316. title: '状态',
  317. dataIndex: 'status',
  318. filters: [
  319. {
  320. text: status[0],
  321. value: 0,
  322. },
  323. {
  324. text: status[1],
  325. value: 1,
  326. },
  327. {
  328. text: status[2],
  329. value: 2,
  330. },
  331. {
  332. text: status[3],
  333. value: 3,
  334. },
  335. ],
  336. onFilter: (value, record) => record.status.toString() === value,
  337. render(val) {
  338. return <Badge status={statusMap[val]} text={status[val]} />;
  339. },
  340. },
  341. {
  342. title: '更新时间',
  343. dataIndex: 'updatedAt',
  344. sorter: true,
  345. render: val => <span>{moment(val).format('YYYY-MM-DD HH:mm:ss')}</span>,
  346. },
  347. {
  348. title: '操作',
  349. render: () => (
  350. <Fragment>
  351. <a href="">配置</a>
  352. <Divider type="vertical" />
  353. <a href="">订阅警报</a>
  354. </Fragment>
  355. ),
  356. },
  357. ];
  358. const menu = (
  359. <Menu onClick={this.handleMenuClick} selectedKeys={[]}>
  360. <Menu.Item key="remove">删除</Menu.Item>
  361. <Menu.Item key="approval">批量审批</Menu.Item>
  362. </Menu>
  363. );
  364. const parentMethods = {
  365. handleAdd: this.handleAdd,
  366. handleModalVisible: this.handleModalVisible,
  367. };
  368. return (
  369. <PageHeaderLayout title="查询表格">
  370. <Card bordered={false}>
  371. <div className={styles.tableList}>
  372. <div className={styles.tableListForm}>{this.renderForm()}</div>
  373. <div className={styles.tableListOperator}>
  374. <Button icon="plus" type="primary" onClick={() => this.handleModalVisible(true)}>
  375. 新建
  376. </Button>
  377. {selectedRows.length > 0 && (
  378. <span>
  379. <Button>批量操作</Button>
  380. <Dropdown overlay={menu}>
  381. <Button>
  382. 更多操作 <Icon type="down" />
  383. </Button>
  384. </Dropdown>
  385. </span>
  386. )}
  387. </div>
  388. <StandardTable
  389. selectedRows={selectedRows}
  390. loading={loading}
  391. data={data}
  392. columns={columns}
  393. onSelectRow={this.handleSelectRows}
  394. onChange={this.handleStandardTableChange}
  395. />
  396. </div>
  397. </Card>
  398. <CreateForm {...parentMethods} modalVisible={modalVisible} />
  399. </PageHeaderLayout>
  400. );
  401. }
  402. }

 

以这个页面为例

第一次阅读代码我们一定要按照从上到下的顺序阅读,记住 是第一次.第一次第一次

首先这个文件引入了几个组件 React dva moment antd  .....

我们要对这个库或组件有个大致印象,比如我们要在页面修改antd 的Dropdown组件,就要去antd官文看看有那些参数可以调整.

其他组件和工具库都是这样.假如你把页面头部的import 的东西有了大致了解,就继续往下看.

页面有const 定义了一个方法,或一系列对象,这个对象有的是作为组件的参数 有的本身就是小子组件

 但我们看到这段代码时就要特别注意了


  
  1. componentDidMount() {
  2. const { dispatch } = this.props;
  3. dispatch({
  4. type: 'rule/fetch',
  5. });
  6. }

在React组件的componentDidMount生命周期时 执行了


  
  1. dispatch({
  2. type: 'rule/fetch',
  3. });

获取你不知道dva 或许你根本看不懂这段代码,但是你可以想象他到底是什么意思.有什么作用. 或者你直接删掉这段代码,看看页面有什么变化. 了解代码效果最简单最明了的方法是 直接删掉,看看报什么错,或有什么变化...O(∩_∩)O哈哈~虽然不是什么好方法,但是简单易用.到这里 你大概知道 这段代码大概就是去调取接口获取数据渲染到页面的.这种写法可能与我们平时写的不一样

我们平时调取接口大概是这样的


  
  1. copyrightTypeList().then(res => {
  2. if (res.data.code === 0) {
  3. this.caseTypeList = res.data.data
  4. }
  5. })

少年不要迷茫了,要接受改变.学习新的知识吧,跳出你的舒适区.

dispatch({ type: 'rule/fetch', }); 这段代码的关键字是rule/fetch 要先在页面仔细搜索关于rule和fetch相关的代码.

在搜素了很久我终于好到 rule是在那里

......文章到这里就吿一段落了 剩余的就好理解了

属性一个项目还有一个方法就是打开项目的package.json 将里面的包 挨个查一遍,了解每个包的作用,用法.这也不失为一种好的快速上手的方法

谢谢阅读.如果觉得对你有帮助请记得点赞或收藏.欢迎留言讨论.谢谢.

 

文章来源: fizzz.blog.csdn.net,作者:拿我格子衫来,版权归原作者所有,如需转载,请联系作者。

原文链接:fizzz.blog.csdn.net/article/details/81975420

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。