Project introduction
A computer room monitoring project, including data acquisition terminal and management terminal. After the camera is installed in the machine room, various types of alarms (carrying large equipment, fire supervision, important equipment supervision, stacking sundries and smoking operation) will be generated by various models analysis. The management terminal can deal with the temporary approval (the person in charge of the machine room or the office approves whether to enter the machine room), the display of various alarm reports, and the person in charge management, etc.
Template address: github.com/AlienGao/re…
The directory structure
\
Src
api
This directory stores the interface address of the project, and each module corresponds to an interface file. The following are some interfaces of the approval module.
trial.js
import request from "@/utils/request"; import { CancelToken } from 'axios'; import cancels from "./cancels"; Const trial = {trialForm: (data) => request. Post ("/approval/approval", data), // list getTrialRecord: (data) => { cancels.getTrialRecord() return request.get('/approval/approval', { params: data, cancelToken: new CancelToken(cancel => { cancels.getTrialRecord = cancel; }}})}); export default trial;Copy the code
CancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken cancelToken
components
This directory houses common components
Because the scroll area of this project is not body, the menu will have scroll positioning problems when using antD components. Therefore, a simple package of ANTD components is made for this problem.
Antdd/BackTop.js
import { BackTop } from "antd"; function backTop(props) { return <BackTop {... props} />; } backTop.propTypes = BackTop.propTypes; backTop.defaultProps = { ... BackTop.defaultProps, target: () => document.getElementById("BackTopMark"), }; export default backTop;Copy the code
FilterComponent.js\
import { useImperativeHandle, forwardRef } from "react"; import { Form, Row, Col, Button } from "antd"; import { Select } from "@/components/Antdd"; import SeverRoomChoose from "./SeverRoomChoose"; import { useSelector } from "react-redux"; import RangeTime from "@/components/RangeTime"; const FormItem = Form.Item; const FilterComponent = (props, ref) => { const dict = useSelector(({ dict }) => dict); // Reset the form value const locationIfm = useSelector(({location}) => location); const [form] = Form.useForm(); useImperativeHandle(ref, () => ({ formFields: form, })); const initialValues = props.initialValues ? props.initialValues : null; return ( <Form layout="inline" onFinish={props.onFinish} form={form} initialValues={initialValues}> <Row gutter={[20, 20]} style={{ width: "100%" }}>{props.hiddenPlacePick ? null : (<Col span={24}> <SeverRoomChoose hiddenRoom={props.hiddenRoom} form={form} locationIfm={locationIfm}/> </Col>)} {props.hiddenStatus ? null : (<Col span={3}> <FormItem name="approval_status"> <Select options={dict. Approval_status} placeholder=" approval_status"> </Select> </FormItem> </Col>)} {props.extraFilters ? props.extraFilters.map((item) => item) : Null} <RangeTime form={form} /> <Col span={4}> <FormItem> <Button type="primary" htmlType="submit"> </FormItem> </Col> </Row> </Form>); }; const WrappedForm = forwardRef(FilterComponent); export default WrappedForm;Copy the code
The filter items at the top of almost all query pages are similar, thus encapsulating a filter component that allows the expansion of other filters. You simply pass in the extraFilters array in the parent component. The filter component also contains a city, region, office, machine room cascade component (SeverRoomChoose). You can see that the filter component also exposes the instance formFields to the parent component, where it can be retrieved via the bound ref if necessary.
const getFormValue = useRef();
const { formFields } = getFormValue.current;
<FilterComponent ref={getFormValue}/>
Copy the code
layout
Define the layout of the project
LayoutMain.js
<Layout className="layout">
<Sider style={{overflow: "auto", height: "100vh", position: "fixed",left: 0,}}>
<SubMenu />
</Sider>
<Layout className="site-layout" style={{ marginLeft: 200 }}>
<Header />
<Layout.Content className="content-container">
<Content />
</Layout.Content>
</Layout>
</Layout>
Copy the code
It consists of the left sidebar, the head navigation bar and the right content.
Content.js
<Layout.Content style={{ margin: "30px 16px 0", overflow: "auto", background: "#fff", position: "relative", }} id="BackTopMark" > <BackTop visibilityHeight={100} /> <Switch> <Redirect exact from="/" To ={routerConfig[0]. Path}></Redirect> <Redirect path="/guide" to={routerConfig[0]. Path}></Redirect> {/* Add video routes <Redirect path="/videos" to="/404"></Redirect> {renderRouteList(routerConfig)} <Redirect to="/404"></Redirect> </Switch> </Layout.Content>Copy the code
RenderRouteList generates route \ based on the route configuration
const renderRouteItem = (item) => (
<Route
key={item.path}
path={item.path}
component={item.component ? item.component : null}
></Route>
);
const renderRouteList = (list) =>
list.reduce((acc, item) => {
if (item.children === void 0) {
acc.push(renderRouteItem(item));
return acc;
}
item.children.forEach((subItem) => {
acc.push(renderRouteItem(subItem));
});
return acc;
}, []);
Copy the code
pages
Each module is stored here, and routes corresponding to the React-router need to be mapped one by one. Each module has multiple pages, each page is a folder, the file name is the name of the page, each page should contain the following files:
- Index.jsx entry file for the —- page
- Use CSS Modules to avoid style contamination
routes
Routing profile
import TemporaryTrial from "@/pages/Trial/TemporaryTrial/index"; import TrialPage from "@/pages/Trial/TrialPage/index"; Const routes = [{path: "/guide/trial", name: "temporary trial", children: [{path: "/guide/ temporary /trial", name: ", Component: TemporaryTrial, showStationListTitle: True,},]}]; export default routes;Copy the code
You can configure showStationListTitle in a route to display the site information, traverse routes in LayoutMain, and add components to the routes that need to be displayed.
Store
Folder that holds global status
We know that each page has its own actions. Js, actiontypes. js, reducer.js, but this is global, and index.js exposes the store, which is then imported into the index.js directory at the SRC root.
index.js
import { createStore, applyMiddleware, combineReducers } from "redux"; import thunkMiddleware from "redux-thunk"; import { persistStore, persistReducer } from "redux-persist"; import storage from "redux-persist/lib/storage"; /** * This is a reducer pure function of the form (state, action) => state. * describes how an action converts state to the next state. * The form of state is up to you. It can be primitive types, arrays, objects, * The only point is that you need to return the new object when state changes, rather than modifying the parameters passed in. */ import loginReducer from "./login/reducer"; const rootReducer = combineReducers({ login: loginReducer,}); const persistConfig = { key: "root", storage,}; const persistedReducer = persistReducer(persistConfig, rootReducer); export default () => { let store = createStore(persistedReducer, applyMiddleware(thunkMiddleware)); let persistor = persistStore(store); return { store, persistor }; };Copy the code
It is worth mentioning that the Store data is cleared after the PC browser refreshes, so it is necessary to save the Store in localStorage. PersistStore was added to index.js, and the persistReducer fetched the login and address information from the Store even after the browser refresh.
Style
Store some global styles \
utils
This will store some of its own wrapped JS tool files. For example, I wrapped a request.js file based on AXIos in the project to simplify the operation of Axios. \
request.js
import axios from "axios"; import { message } from "antd"; import config from "@/config"; // import qs from "qs"; const TIMEOUT = 5000; const _axios = axios.create({ baseURL: config.url, timeout: TIMEOUT, headers: { "Access-Control-Allow-Origin": "*",}}); _axios.interceptors.request.use( (config) => { // if (config.method === 'get') { // config.paramsSerializer = function(params) { // return qs.stringify(params, { arrayFormat: 'repeat' }) // } // } if (localStorage.getItem("token")) { config.headers.Authorization = localStorage.getItem("token"); _axios.defaults.headers.Authorization = localStorage.getItem("token"); } return config; }, (err) => { return Promise.reject(err); }); _axios.interceptors.response.use( (response) => { if (response.status ! == 200) {message.error(" interface error "); return Promise.reject(response.data); } return response.data; }, (err) => {if (err.message === 'timeout of ${timeout}ms exceeded') {message.error(" Request timeout "); } else if (err.toString() === 'Cancel') {} else {message.error(" interface error "); } return Promise.reject(err); }); export default _axios;Copy the code
config.js
Configuring the Proxy Address
index.js
Webpack entry file, mainly some global JS or SCSS import, and executes the render method under the react-DOM, code as follows: \
import React from "react"; import ReactDOM from "react-dom"; import "@/style/index.css"; import "antd/dist/antd.css"; import LayoutMain from "./layout"; import { history } from "@/routers"; import { Router } from "react-router-dom"; import { Provider } from "react-redux"; import storeConfig from "@/store"; import reportWebVitals from "./reportWebVitals"; import { PersistGate } from "redux-persist/integration/react"; import zhCN from "antd/es/locale/zh_CN"; import moment from "moment"; import "moment/locale/zh-cn"; import { ConfigProvider } from "antd"; const { persistor, store } = storeConfig(); moment.locale("zh-cn"); Reactdom.render (<ConfigProvider // global Settings popup container getPopupContainer={(node) => {// if (node) {// return node //} return document.getElementById('BackTopMark'); }} locale={zhCN} > <Router history={history}> <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <LayoutMain /> </PersistGate> </Provider> </Router> </ConfigProvider>, document.getElementById("root"));Copy the code
config-overrides.js
Modify the WebPack configuration file
const path = require('path') module.exports = { webpack: (config) = > {config. The output. The library = 'server - room - guard' config. The output. The libraryTarget = 'umd' / / add an alias config.resolve.alias = { ... config.resolve.alias, '@': path.resolve(__dirname, 'src'), } return config }, devServer: (configFunction) => { return function (proxy, allowHost) { const config = configFunction(proxy, allowHost) config.headers = { 'Access-Control-Allow-Origin': '*', } return config } },Copy the code
conclusion
The project uses react hooks to develop all of its components. As a beginner of React, I found hooks very user-friendly and quick to use. During the development process, we also found that there are many areas that can be optimized, and there are related component encapsulation (such as filtering, tables, cascade selection, export, etc.), which greatly improves the development efficiency while simplifying the code.