電子發(fā)燒友App

硬聲App

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示
創(chuàng)作
電子發(fā)燒友網(wǎng)>電子資料下載>電子資料>opennft-front開源NFT前端平臺(tái)

opennft-front開源NFT前端平臺(tái)

2022-06-06 | zip | 6.32 MB | 次下載 | 2積分

資料介紹

授權(quán)協(xié)議 Apache
開發(fā)語言 JavaScript HTML/CSS
操作系統(tǒng) 跨平臺(tái)
軟件類型 開源軟件

軟件簡介

?

項(xiàng)目開始 BEGIN

項(xiàng)目說明

基于 React16.x、Ant Design4.x,react-admin

目錄結(jié)構(gòu)

├── config              // 項(xiàng)目構(gòu)建配置
├── public              // 不參與構(gòu)建的靜態(tài)文件
├── scripts             // 構(gòu)建腳本
├── src
│   ├── assets          // 全項(xiàng)目通用圖片文件等
│   ├── commons         // 全項(xiàng)目通用js,業(yè)務(wù)相關(guān)
│   ├── components      // 全項(xiàng)目通用組件,業(yè)務(wù)相關(guān)
│   ├── config          // 項(xiàng)目構(gòu)建補(bǔ)充配置
│   ├── layouts         // 頁面框架布局組件+
│   ├── mock            // 模擬數(shù)據(jù)
│   ├── models          // 模塊封裝,基于redux,提供各組件共享數(shù)據(jù)、共享邏輯
│   ├── pages           // 主項(xiàng)目頁面目錄
│   ├── ├── Project                 // 子項(xiàng)目頂級目錄
│   ├── ├── ├── nft                 // NFT--PC版本項(xiàng)目目錄
│   ├── ├── ├── ├── assets               // NFT--PC版本公共圖片文件
│   ├── ├── ├── ├── components           // NFT--PC版本公共組件庫
│   ├── ├── ├── ├── pages                // NFT--PC版本頁面目錄
│   ├── ├── ├── mobile              // 移動(dòng)端項(xiàng)目目錄
│   ├── ├── ├── ├── nft-mobile           // NFT--微信版本項(xiàng)目目錄
│   ├── ├── ├── ├── ├── assets              //  NFT--微信版本公共圖片文件
│   ├── ├── ├── ├── ├── components          //  NFT--微信版本公共組件庫
│   ├── ├── ├── ├── ├── pages               //  NFT--微信版本頁面目錄
│   ├── router              // 路由
│   ├── ant.less            // 主體配置
│   ├── App.js              // 根組件
│   ├── index.css           // 全局樣式 慎用
│   ├── index.dark.css       // 全局樣式 慎用
│   ├── index.js            // 項(xiàng)目入口
│   ├── menus.js            // 菜單配置
│   ├── setupProxy.js       // 后端聯(lián)調(diào)代理配置
│   └── theme.less          // 主題變量
├── package.json
├── README.md
└── yarn.lock

安裝依賴

$ yarn

啟動(dòng)注意事項(xiàng)

依賴安裝完成后,src/setupProxy.js 為項(xiàng)目本地代理文件,請及時(shí)更改您需要代理的接口地址!

?。。∥⑿虐姹局豢梢栽谖⑿糯蜷_運(yùn)行,并直接在 router/AuthRoute.jsx 中填入自己申請的 appid,目前為''

此為兩個(gè)項(xiàng)目依托同一底層框架,PC 及微信端口分離,如需切換請?jiān)?router/app.router.js 中更換路由,router/AppRoute.jsx 中切換引入的 AuthRoute(分別為微信版本:AuthRoute.jsx,PC 版本:AuthRoutePC.jsx) 并在 src/commons/PRE_ROUTER.js 中進(jìn)行相關(guān)配置,以及 package.json 中更改 homepage 為相應(yīng)的項(xiàng)目名稱。

兩套 PRE_ROUTER.js 中配置分別如下:

微信版本: 路由前綴----/front_nft_mobile 請求地址前綴----/api/nft 登錄頁面----/nft_mobile_home

PC 版本: 路由前綴----/front_nft_pc 請求地址前綴----/api/nft 登錄頁面----/nft_home

開發(fā)啟動(dòng)

$ yarn start

#指定端口
$ PORT=8080 yarn start

# HTTPS方式啟動(dòng)
$ HTTPS=true yarn start

生產(chǎn)構(gòu)建

$ yarn build

// 構(gòu)建輸入到指定目錄
$ BUILD_PATH=../dist yarn build

域名子目錄發(fā)布項(xiàng)目

// 開發(fā)啟動(dòng)
$ BASE_NAME=/synext-admin yarn start

// 開發(fā)訪問
'http://localhost:XXXX/synext-admin/'

//生產(chǎn)環(huán)境 同上
$ BASE_NAME=/synext-admin yarn start
// 訪問
'http://xxx.com/synext-admin'

菜單配置

//在/src/menus.js文件中配置菜單數(shù)據(jù),前端硬編碼或異步加載菜單數(shù)據(jù)。
// 菜單支持頭部、左側(cè)、頭部+左側(cè)三種布局方式,默認(rèn)左側(cè)菜單。如需放開設(shè)置,請到'src/layouts/index.jsx'放開注釋
//菜單字段說明。
字段	必須	說明
key	    	//需要唯一
parentKey		//用于關(guān)聯(lián)父級
path		//菜單對應(yīng)的路由地址
text		//菜單標(biāo)題
icon		//菜單圖標(biāo)配置
url	    	//菜單對應(yīng)會(huì)打開url對應(yīng)的iframe頁面,如果配置了url,path將無效
target		//配合url使用,菜單將為a標(biāo)簽 {text}
order		//菜單排序,數(shù)值越大越靠前顯示
type		//如果菜單數(shù)據(jù)中攜帶功能權(quán)限配置,type= 為菜單,type=為功能
code		//功能碼,如果是type=,會(huì)用到此字段

頁面開發(fā)

//配置組件
import React, {Component} from 'react';
import config from 'src/commons/config-hoc';

@config({
    title: '頁面title',
    ajax: true,
    ...
})
export default class SomePage extend Component {
    componentDidMount() {
        this.props.ajax
            .get(...)
            .then(...)
    }
    ...
}
參數(shù) 類型 默認(rèn)值 說明
noFrame boolean false 標(biāo)記當(dāng)前頁面為不需要導(dǎo)航框架的頁面,比如登錄頁,通過腳本抓取實(shí)現(xiàn)
noAuth boolean false 標(biāo)記當(dāng)前頁面為不需要登錄即可訪問的頁面,通過腳本抓取實(shí)現(xiàn)
keepAlive boolean - 標(biāo)記當(dāng)前頁面內(nèi)容在頁面切換之后是否保持
title boolean 或 string 或 ReactNode 或 object 或 function(props) true true:當(dāng)前頁面顯示通過菜單結(jié)構(gòu)自動(dòng)生成的 title;false:當(dāng)前頁面不顯示 title;string:自定義 title;object:{text,icon} text 為顯示的名稱,icon 為圖標(biāo);function(props): 返回值作為 title
breadcrumbs boolean 或 array 或 function(props) true true:當(dāng)前頁面顯示通過菜單結(jié)構(gòu)自動(dòng)生成的面包屑;false:當(dāng)前頁面不顯示面包屑;object:[{icon, text, ...}];function(props): 返回值作為面包屑
appendBreadcrumbs array 或 function(props) [] 在當(dāng)前面包屑基礎(chǔ)上添加;function(props): 返回值作為新添加的面包屑
pageHead boolean - 頁面頭部是否顯示
side boolean - 頁面左側(cè)是否顯示
sideCollapsed boolean - 左側(cè)是否收起
ajax boolean true 是否添加 ajax 高階組件,內(nèi)部可以通過 this.props.ajax 使用 ajax API,組件卸載時(shí),會(huì)自動(dòng)打斷未完成的請求
router boolean false 是否添加 withRouter 裝飾器,組件內(nèi)部可以使用 this.props.history 等 API
query boolean false 是否添加地址查詢字符串轉(zhuǎn)換高階組件,內(nèi)部可以通過 this.props.query 訪問查詢字符串
connect boolean 或 function(state) false 是否與 redux 進(jìn)行連接,true:只注入了 this.props.action 相關(guān)方法;false:不與 redux 進(jìn)行連接;(state) => ({title: state.page.title}):將函數(shù)返回的數(shù)據(jù)注入 this.props
event boolean false 是否添加 event 高階組件,可以使用 this.props.addEventListener 添加 dom 事件,并在組件卸載時(shí)會(huì)自動(dòng)清理;通過 this.props.removeEventListener 移出 dom 事件
pubSub boolean false 是否添加發(fā)布訂閱高階組件,可以使用 this.props.subscribe(topic, (msg, data) => {...})訂閱事件,并在組件卸載時(shí),會(huì)自動(dòng)取消訂閱; 通過 this.props.publish(topic, data)發(fā)布事件
modal string 或 object false 當(dāng)前組件是否是 modal。string: 彈框標(biāo)題;object:彈框配置

注:

  • noFrame、noAuth、keepAlive?只有配置了path才有效!
  • config 裝飾器可以用于任何組件,但是title、breadcrumbs、appendBreadcrumbs、pageHead、side、sideCollapsed最好在路由對應(yīng)的頁面組件中使用
//頁面保持
//頁面渲染一次之后會(huì)保持狀態(tài),再次跳轉(zhuǎn)到此頁面不會(huì)重新創(chuàng)建或重新渲染
開啟方式
1.  /src/models/system.js initState.keepAlive 屬性修改默認(rèn)值
2.  config裝飾器 keepAlive屬性

頁面顯示/隱藏事件

config?裝飾器為組件注入了兩個(gè)事件?onComponentWillShow、onComponentWillHide?,如果頁面使用了 Keep Alive 功能,切換顯示/隱藏時(shí)會(huì)觸發(fā)

@config({
    ...
})
export default class SomePage extends React.Component {
    constructor(...props) {
        super(...props);

        this.props.onComponentWillShow(() => {
            // do some thing
        });

        this.props.onComponentWillHide(() => {
            // do some thing
        });
    }
    ...
}

頁面容器 PageContent

系統(tǒng)提供了頁面的跟節(jié)點(diǎn) PageContent,有如下特性:

  • 添加了 margin padding 樣式;
  • 添加了 footer;
  • 支持頁面 loading;
  • 自動(dòng)判定是否有底部工具條 FixBottom 組件,為底部工具條騰出空間;

是否顯示 footer,默認(rèn) true

<PageContent footer={false}>...</PageContent>

顯示 loading,有兩種方式。

  1. model 方式

    this.props.action.page.showLoading();
    this.props.action.page.hideLoading();
    ?
  2. props 方式

    const { loading } = this.state;
    
    <PageContent loading={loading}>...</PageContent>;
    

彈框頁面開發(fā)

添加、修改等場景,往往會(huì)用到彈框,antd Modal 組件使用不當(dāng)會(huì)產(chǎn)生臟數(shù)據(jù)問題(兩次彈框渲染數(shù)據(jù)互相干擾)

系統(tǒng)提供了基于 modal 封裝的高階組件,每次彈框關(guān)閉,都會(huì)銷毀彈框內(nèi)容,避免互相干擾

modal 高階組件

modal 高階組件集成到了 config 中,也可以單獨(dú)引用:import { ModalContent } from 'src/commons/ra-lib';

import React from "react";
import config from "src/commons/config-hoc";
import { ModalContent } from "src/commons/ra-lib";

export default config({
    modal: {
        title: "彈框標(biāo)題",
    },
})((props) => {
    const { onOk, onCancel } = props;

    return (
        <ModalContent onOk={onOk} onCancel={onCancel}>
            彈框內(nèi)容
        ModalContent>
    );
});

modal 所有參數(shù)說明如下:

  1. 如果是 string,作為 modal 的 title
  2. 如果是函數(shù),函數(shù)返回值作為 Modal 參數(shù)
  3. 如果是對象,為 Modal 相關(guān)配置,具體參考?antd Modal 組件
  4. options.fullScreen boolean 默認(rèn) false,是否全屏顯示彈框

ModalContent 組件

彈框內(nèi)容通過 ModalContent 包裹,具體參數(shù)如下:

參數(shù) 類型 默認(rèn)值 說明
surplusSpace boolean false 是否使用屏幕垂直方向剩余空間
otherHeight number - 除了主體內(nèi)容之外的其他高度,用于計(jì)算主體高度;
loading boolean false 加載中
loadingTip - - 加載提示文案
footer - - 底部
okText string - 確定按鈕文案
onOk function - 確定按鈕事件
cancelText string - 取消按鈕文案
onCancel function - 取消按鈕事件
resetText string - 重置按鈕文案
onReset function - 重置按鈕事件
style object - 最外層容器樣式
bodyStyle object - 內(nèi)容容器樣式

系統(tǒng)路由

系統(tǒng)路由使用 react-router,通過 route-loader 將路由內(nèi)容填充到/src/pages/page-routes.js 文件,支持兩種寫法:

  1. 常量方式
    export const PAGE_ROUTE = "/path";
    ?
  2. 頁面 config 裝飾器(推薦)
    @config({
        path: '/path',
    })
    export default class SomePage extends React.Component {
        ...
    }
    

二級頁面

二級頁面如果要保持父級菜單的選中狀態(tài),以父級 path 開始并以/_/作為分隔符即可:parent/path/_/child/path

// parent page
@config({
    path: '/parent/path'
})
export default class Parent extends React.Component {
    ...
}

// child page
@config({
    path: '/parent/path/_/child/path'
})
export default class Parent extends React.Component {
    ...
}

AJAX 請求

系統(tǒng)的 ajax 請求基于 axios 封裝。 基于 restful 規(guī)范,提供了 5 個(gè)方法:

  • get 獲取服務(wù)端數(shù)據(jù),參數(shù)拼接在 url 上,以 query string 方式發(fā)送給后端
  • post 新增數(shù)據(jù),參數(shù)以 body 形式發(fā)送給后端
  • put 修改數(shù)據(jù),參數(shù)以 body 形式發(fā)送給后端
  • del 刪除數(shù)據(jù),參數(shù)拼接在 url 上,以 query string 方式發(fā)送給后端
  • patch 修改部分?jǐn)?shù)據(jù),參數(shù)以 body 形式發(fā)送給后端

調(diào)用方式 三種

//第一種  config裝飾器ajax屬性(推薦)
 import React, {Component} from 'react';
    import config from 'src/commons/config-hoc';

    @config({
        ajax: true,
        ...
    })
    export default class SomePage extend Component {
        componentDidMount() {
            this.props.ajax
                .get(...)
                .then(...)
        }
        ...
    }
//第二種 ajax裝飾器
 import React, {Component} from 'react';
    import {ajaxHoc} from 'src/commpons/ajax';

    @ajaxHoc()
    export default class SomePage extend Component {
        componentDidMount() {
            this.props.ajax
                .get(...)
                .then(...)
        }
        ...
    }
//第三種 直接引入ajax對象
import React, {Component} from 'react';
import {sxAjax} from 'src/commpons/ajax';

    export default class SomePage extend Component {
        componentDidMount() {
            sxAjax.post(...).then(...);

            // 組件卸載或者其他什么情況,需要打算ajax請求,可以用如下方式
            const ajax = sxAjax.get(...);
            ajax.then(...).finally(...);
            ajax.cancel();
        }
        ...
    }

注:config、ajaxHoc 方式做了封裝,頁面被卸載之后會(huì)自動(dòng)打斷未完成的請求

接收參數(shù)

所有的 ajax 方法參數(shù)統(tǒng)一,都能夠接受三個(gè)參數(shù): 參數(shù)|說明 ---|--- url|請求地址 params|請求傳遞給后端的參數(shù) options|請求配置,即 axios 的配置。

options 配置

參數(shù) 說明
axios 配置 可以接受 axios 參數(shù)
successTip 擴(kuò)展的參數(shù),成功提示
errorTip 擴(kuò)展的參數(shù),失敗提示
noEmpty 擴(kuò)展的參數(shù),過濾掉 ''、null、undefined 的參數(shù),不提交給后端
originResponse 擴(kuò)展參數(shù),.then 中可以拿到完整的 response,而不只是 response.data

注:全局默認(rèn)參數(shù)可以在 src/commons/ajax.js 中進(jìn)行配置,默認(rèn) 、timeout=1000 * 60。

請求結(jié)果提示

  1. 系統(tǒng)對 ajax 失敗做了自動(dòng)提示,開發(fā)人員可通過 src/commons/handle-error.js 進(jìn)行配置;
  2. 成功提示默認(rèn)不顯示,如果需要成功提示,可以配置 successTip 參數(shù),或者.then()中自行處理;
  3. 成功提示在 src/commons/handle-success.js 中配置;
this.props.ajax.del("/user/1", null, {
    successTip: "刪除成功!",
    errorTip: "刪除失?。?/span>",
    noEmpty: true,
});

loading 處理

系統(tǒng)擴(kuò)展了 promise,提供了 finally 方法,用于無論成功還是失敗,都要進(jìn)行的處理。一般用于關(guān)閉 loading

this.setState({loading: true});
this.props.ajax
    .get('/url')
    .then(...)
    .finally(() => this.setState({loading: false}));

Mock 模擬數(shù)據(jù)

前后端并行開發(fā),為了方便后端快速開發(fā),不需要等待后端接口,系統(tǒng)提供了 mock 功能。基于mockjs

編寫模擬數(shù)據(jù)

在/src/mock 目錄下進(jìn)行 mock 數(shù)據(jù)編寫,比如:

import { getUsersByPageSize } from "./mockdata/user";

export default {
    "post /mock/login": (config) => {
        const { userName, password } = JSON.parse(config.data);
        return new Promise((resolve, reject) => {
            if (userName !== "test" || password !== "111") {
                setTimeout(() => {
                    reject({
                        code: 1001,
                        message: "用戶名或密碼錯(cuò)誤",
                    });
                }, 1000);
            } else {
                setTimeout(() => {
                    resolve([
                        200,
                        {
                            id: "1234567890abcde",
                            name: "MOCK 用戶",
                            loginName: "MOCK 登錄名",
                        },
                    ]);
                }, 1000);
            }
        });
    },
    "post /mock/logout": {},

    "get /mock/user-center": (config) => {
        const { pageSize, pageNum } = config.params;

        return new Promise((resolve) => {
            setTimeout(() => {
                resolve([
                    200,
                    {
                        pageNum,
                        pageSize,
                        total: 888,
                        list: getUsersByPageSize(pageSize),
                    },
                ]);
            }, 1000);
        });
    },
    "get re:/mock/user-center/.+": {
        id: 1,
        name: "熊大",
        age: 22,
        job: "前端",
    },
    "post /mock/user-center": true,
    "put /mock/user-center": true,
    "delete re:/mock/user-center/.+": "id",
};

簡化

為了方便 mock 接口編寫,系統(tǒng)提供了簡化腳本(/src/mock/simplify.js),上面的例子就是簡化寫法

對象的 key 由 method url delay,各部分組成,以空格隔開

字段 說明
method 請求方法 get post 等
url 請求的 url
delay 模擬延遲,毫秒 默認(rèn) 1000

調(diào)用

系統(tǒng)封裝的 ajax 可以通過以下兩種方式,自動(dòng)區(qū)分是 mock 數(shù)據(jù),還是真實(shí)后端數(shù)據(jù),無需其他配置

mock 請求:

  • url 以/mock/開頭的請求
  • /src/mock/url-config.js 中配置的請求
this.props.ajax.get('/mock/users').then(...);

如果后端真實(shí)接口準(zhǔn)備好之后,去掉 url 中的/mock 即可

注:mock 功能只有開發(fā)模式下開啟了,生產(chǎn)模式不會(huì)開啟 mock 功能,如果其他環(huán)境要開啟 mock 使用 MOCK=true 參數(shù),比如?MOCK=true yarn build

樣式

系統(tǒng)使用less進(jìn)行樣式的編寫。 為了避免多人合作樣式?jīng)_突,系統(tǒng)對 src 下的 less 文件啟用了 Css Module,css 文件沒有使用 Css Module。

style.less

.root {
    width: 100%;
    height: 100%;
}

Some.jsx

import "/path/to/style.less";

export default class Some extends React.Component {
    render() {
        return <div styleName="root">div>;
    }
}
?

注:基礎(chǔ)組件不使用 Css Module,不利于樣式覆蓋;

主題

使用 less,通過樣式覆蓋來實(shí)現(xiàn)。

編寫主題

  • less 文件中使用主題相關(guān)變量;
  • 編寫/src/theme.less通過less-loadermodifyVars覆蓋 less 中的變量;

注:目前每次修改了 theme.less 需要重新 yarn start 才能生效

參考

導(dǎo)航布局

為了滿足不同系統(tǒng)的需求,提供了四種導(dǎo)航布局:

  • 頭部菜單
  • 左側(cè)菜單
  • 頭部+左側(cè)菜單
  • tab 頁方式

更改方式

  • 用戶可以通過 頁面有上角用戶頭像 -> 設(shè)置 頁面進(jìn)行選擇(如果您為用戶提供了此頁面);
  • 開發(fā)人員可以通過修改src/models/index.js指定布局方式;

不需要導(dǎo)航

有些頁面可能不需要顯示導(dǎo)航,可以通過如下任意一種方式進(jìn)行設(shè)置:

  • 頁面配置高級組件
    @config({
        noFrame: true,
    })
    ?
  • 瀏覽器 url 中 noFrame=true 參數(shù)
    /path/to?noFrame=true
    

Tab 標(biāo)簽頁

頁面頭部標(biāo)簽,有如下特性:

  1. 在當(dāng)前 tab 標(biāo)簽之后打開新的 tab 標(biāo)簽;
  2. 記錄并恢復(fù)滾動(dòng)條位置;
  3. 保持頁面狀態(tài)(需要開啟Keep Page Alive);
  4. tab 標(biāo)簽右鍵操作;
  5. tab 頁操作 API;
  6. tab 標(biāo)簽拖拽排序;
  7. 關(guān)閉一個(gè)二級頁面 tab,嘗試打開它的父級;

Tab 操作 API

system model(redux)中提供了如下操作 tab 頁的方法:

API 說明
setCurrentTabTitle(title) 設(shè)置當(dāng)前激活的 tab 標(biāo)題 title: stirng 或 {text, icon}
refreshTab(targetPath) 刷新 targetPath 指定的 tab 頁內(nèi)容(重新渲染)
refreshAllTab() 刷新所有 tab 頁內(nèi)容(重新渲染)
closeCurrentTab() 關(guān)閉當(dāng)前 tab 頁
closeTab(targetPath) 關(guān)閉 targetPath 對應(yīng)的 tab 頁
closeOtherTabs(targetPath) 關(guān)閉除了 targetPath 對應(yīng)的 tab 頁之外的所有 tab 頁
closeAllTabs() 關(guān)閉所有 tab 頁,系統(tǒng)將跳轉(zhuǎn)首頁
closeLeftTabs(targetPath) 關(guān)閉 targetPath 對應(yīng)的 tab 頁左側(cè)所有 tab 頁
closeRightTabs(targetPath) 關(guān)閉 targetPath 對應(yīng)的 tab 頁右側(cè)所有的 tab 頁

使用方式:

import config from 'src/commons/config-hoc';

@config({
    connect: true,
})
export default class SomeComponent extends React.Component {
    componentDidMount() {
        this.props.action.system.closeTab('/some/path');
    }
    ...
}

注:

  1. tab 基于頁面地址,每當(dāng)使用this.props.history.push('/some/path'),就會(huì)選中或者新打開一個(gè) tab 頁(/path??/path?name=Tom屬于不同 url 地址,會(huì)對應(yīng)兩個(gè) tab 頁);
  2. 沒有菜單對應(yīng)的頁面,需要單獨(dú)設(shè)置 title,否則 tab 標(biāo)簽將沒有 title;

models(redux) 封裝

基于redux進(jìn)行封裝,不改變 redux 源碼,可以結(jié)合使用 redux 社區(qū)中其他解決方案。

注:一般情況下,用不到 redux~

models 用于管理數(shù)據(jù),解決的問題:

  1. 命名空間(防止數(shù)據(jù)、方法命名沖突):數(shù)據(jù)與方法,都?xì)w屬于具體 model,比如:state.userCenter.xxx,this.props.action.userCenter.xxx();
  2. 如何方便的獲取數(shù)據(jù):connect 與組件連接;@connect(state => ({name: state.user.name}));
  3. 如何方便的修改數(shù)據(jù):this.props.action 中方法;
  4. 客戶端數(shù)據(jù)持久化(保存到 LocalStorage 中):syncStorage 配置;
  5. 異步數(shù)據(jù)處理:基于 promise 異步封裝;
  6. 請求錯(cuò)誤提示:error 處理封裝,errorTip 配置,自動(dòng)提示;
  7. 請求成功提示:successTip 配置,自動(dòng)提示;
  8. 簡化寫法:types actions reducers 可以在一個(gè)文件中編寫,較少?zèng)_突,方便多人協(xié)作,參見models/page.js中的寫法;
  9. 業(yè)務(wù)代碼可集中歸類:在 models 目錄中統(tǒng)一編寫,或者在具體業(yè)務(wù)目錄中,模塊化方式。

src/models

所有的 model 直接在 models 或 pages 下定義:

model 模塊名規(guī)則:

/path/to/models/user-center.js --> userCenter;
/path/to/models/user.js --> user;

/path/to/pages/users/model.js --> users;
/path/to/pages/users/job.model.js --> job;
/path/to/pages/users/user-center.model.js --> userCenter;
/path/to/pages/users/user.center.model.js --> userCenter;

組件與 redux 進(jìn)行連接

提供了多種種方式,裝飾器方式、函數(shù)調(diào)用、hooks、js 文件直接使用;

裝飾器

推薦使用裝飾器方式

import {connect} from 'path/to/models';

@connect(state => {
    return {
        ...
    }
})
class Demo extends Component{
    ...
}

函數(shù)

import {connectComponent} from 'path/to/models';

class Demo extends Component {
   ...
}
function mapStateToProps(state) {
    return {
        ...
    };
}

export default connectComponent({LayoutComponent: Demo, mapStateToProps});
?

hooks

import { useSelector } from "react-redux";
import { useAction } from "src/models";

export default () => {
    const action = useAction();
    const show = useSelector((state) => state.side.show);

    console.log(show);

    useEffect(() => {
        action.side.hide();
    }, []);

    return <div />;
};

對 useSelector 的說明

useSelector(select) 默認(rèn)對 select 函數(shù)的返回值進(jìn)行引用比較 ===,并且僅在返回值改變時(shí)觸發(fā)重渲染。

即:如果 select 函數(shù)返回一個(gè)臨時(shí)對象,會(huì)多次 re-render

最好不要這樣使用:
const someData = useSelector(state => {

    // 每次都返回一個(gè)新對象,導(dǎo)致re-render
    return {name: state.name, age: state.age};
})

最好多次調(diào)用useSelector,單獨(dú)返回?cái)?shù)據(jù),或者返回非引用類型數(shù)據(jù)
const name = useSelector(state => state.firstName + state.lastName);
const age = useSelector(state => state.age);

js 文件中使用

沒有特殊需求,一般不會(huì)在普通 js 文件中使用

import { action, store } from "src/models";

// 獲取數(shù)據(jù)
const state = store.getState();

// 修改數(shù)據(jù)
action.side.hide();

簡化寫法

action reducer 二合一,省去了 actionType,簡化寫法;

注意:

  • 所有的 reducer 方法,無論是什么寫法中的,都可以直接返回新數(shù)據(jù),不必關(guān)心與原數(shù)據(jù)合并(...state),封裝內(nèi)部做了合并;
  • 一個(gè) model 中,除了 initialState syncStorage actions reducers 等關(guān)鍵字之外的屬性,都視為 action reducer 合并寫法;

一個(gè)函數(shù)

一個(gè)函數(shù),即可作為 action 方法,也作為 reduce 使用

  • 調(diào)用 action 方法傳遞的數(shù)據(jù)將不會(huì)做任何處理,會(huì)直接傳遞給 reducer
  • 只能用第一個(gè)參數(shù)接收傳遞過來的數(shù)據(jù),如果多個(gè)數(shù)據(jù),需要通過對象方式傳遞,如果不需要傳遞數(shù)據(jù),但是要使用 state,也需要定義一個(gè)參數(shù)占位
  • 第二個(gè)參數(shù)固定為 state,第三個(gè)參數(shù)固定為 action,不需要可以缺?。ㄒ话愣际侨笔〉模?/li>
  • 函數(shù)的返回值為一個(gè)對象或者 undefined,將于原 state 合并,作為 store 新的 state
// page.model.js
export default {
    initialState: {
        title: void 0,
        name: void 0,
        user: {},
        toggle: true,
    },

    setTitle: (title) => ({ title }),
    setName: (name, state, action) => {
        const { name: prevName } = state;
        if (name !== prevName) return { name: "Different Name" };
    },
    setUser: ({ name, age } = {}) => ({ user: { name, age } }),
    setToggle: (arg, state) => ({ toggle: !state.toggle }),
};

// 使用
this.props.action.page.setTitle("my title");

數(shù)據(jù)同步

通過配置的方式,可以讓 redux 中的數(shù)據(jù)自動(dòng)與 localStorage 同步

export default {
    initialState: {
        title: '',
        show: true,
        user: {},
        users: [],
        job: {},
        total: 0,
        loading: false,
        ...
    },

    // initialState會(huì)全部同步到localStorage中
    // syncStorage: true,

    // 配置部分存數(shù)據(jù)儲(chǔ)到localStorage中
    syncStorage: {
        titel: true,
        user: { // 支持對象指定字段,任意層次
            name: true,
            address: {
                city: true,
            },
        },
        job: true,
        users: [{name: true, age: true}], // 支持?jǐn)?shù)組
    },
}

action reducer 合并寫法

如果 action 有額外的數(shù)據(jù)處理,并且一個(gè) action 只對應(yīng)一個(gè) reducer,這種寫法不需要指定 actionType,可以有效簡化代碼;

export default {
    initialState: {
        title: '',
        ...
    },

    arDemo: {
        // 如果是函數(shù)返回值將作為action.payload 傳遞給reducer,如果非函數(shù),直接將payload的值,作為action.payload;
        payload(options) {...},

        // 如果是函數(shù)返回值將作為action.meta 傳遞給reducer,如果非函數(shù),直接將meta的值,作為action.meta;
        meta(options) {...},
        reducer(state, action) {
            returtn {...newState}; // 可以直接返回要修改的數(shù)據(jù),內(nèi)部封裝會(huì)與原state合并`{...state, ...newState}`;
        },
    },
};

異步 action 寫法

export default {
    initialState: {
        title: '',
        ...
    },
    fetchUser: {
        // 異步action payload 返回promise
        payload: ({params, options}) => axios.get('/mock/users', params, options),

        // 異步action 默認(rèn)使用通用異步meta配置 commonAsyncMeta,對successTip errorTip onResolve onReject onComplete 進(jìn)行了合理的默認(rèn)值處理,需要action以對象形式傳參調(diào)用
        // meta: commonAsyncMeta,
        // meta: {
        //     successTip: '查詢成功!歐耶~',
        //     errorTip: '自定義errorTip!馬丹~',
        // },
        // meta: () => {
        //    return {...};
        // },

        // 基于promise 異步reducer寫法;
        reducer: {
            pending: (state, action) => ({loading: true}),
            resolve(state, {payload = {}}) {
                const {total = 0, list = []} = payload;
                return {
                    users: list,
                    total,
                }
            },
            complete: (state, action) => ({loading: false}),
        }
    },
};

調(diào)用方式:

this.props.action.user.fetchUser({
    params,
    options,
    successTip,
    errorTip,
    onResolve,
    onReject,
    onComplete,
});

參數(shù)約定為一個(gè)對象,各個(gè)屬性說明如下:

參數(shù) 說明
params 請求參數(shù)
options 請求配置
successTip 成功提示信息
errorTip 錯(cuò)誤提示信息
onResolve 成功回調(diào)
onReject 失敗回調(diào)
onComplete 完成回調(diào),無論成功、失敗都會(huì)調(diào)用

單獨(dú)定義 action 和 reducer

支持這種比較傳統(tǒng)的寫法,一般也不會(huì)太用到

import {createAction} from 'redux-actions';

export const types = {
    GET_MENU_STATUS: 'MENU:GET_MENU_STATUS', // 防止各個(gè)模塊沖突,最好模塊名開頭
};

export default {
    initialState: {
        title: '',
        ...
    },

    // 單獨(dú)action定義,需要使用actionType與reducer進(jìn)行關(guān)聯(lián)
    actions: {
        getMenuStatus: createAction(types.GET_MENU_STATUS),
    },

    // 單獨(dú)reducer定義,使用了actionType,不僅可以處理當(dāng)前model中的action
    // 也可以處理其他任意action(只要actionType能對應(yīng))
    reducers: {
        [types.GET_MENU_STATUS](state) {
            ...
            return {
                ...
            };
        }
    },
}

權(quán)限控制

系統(tǒng)菜單、具體功能點(diǎn)都可以進(jìn)行權(quán)限控制。

菜單權(quán)限

菜單由后端提供(一般系統(tǒng)都是后端提供),后臺(tái)通過登錄用戶返回用戶的菜單權(quán)限;頁面只顯示獲取到的菜單;

系統(tǒng)提供了一個(gè)基礎(chǔ)的菜單、權(quán)限管理頁面,需要后端配合存儲(chǔ)數(shù)據(jù)。

功能權(quán)限

可以通過src/components/permission組件對功能的權(quán)限進(jìn)行控制

import React, { Component } from "react";
import Permission from "src/components/permission";

export default class SomePage extends Component {
    render() {
        return (
            <div>
                <Permission code="USER_ADD">
                    <Button>添加用戶</Button>
                </Permission>
            </div>
        );
    }
}
?

注:權(quán)限的 code 前端使用時(shí)會(huì)硬編碼,注意語義化、唯一性。

角色

一般系統(tǒng)都會(huì)提供角色管理功能,系統(tǒng)中提供了一個(gè)基礎(chǔ)的角色管理功能,稍作修改即可使用。

開發(fā)代理

開發(fā)時(shí),要與后端進(jìn)行接口對接,可以通過代理與后端進(jìn)行連接,開發(fā)代理配置在src/setupProxy.js中編寫

const proxy = require("http-proxy-middleware");

const prefix = process.env.AJAX_PREFIX || "/api";

module.exports = function (app) {
    app.use(
        proxy(prefix, {
            target: "http://localhost:3000/",
            pathRewrite: {
                ["^" + prefix]: "", // 如果后端接口無前綴,可以通過這種方式去掉
            },
            changeOrigin: true,
            secure: false, // 是否驗(yàn)證證書
            ws: true, // 啟用websocket
        })
    );
};

注:更多代理配置請參考http-proxy-middleware

前端默認(rèn) ajax 前綴 /api 可以通過 AJAX_PREFIX 參數(shù)進(jìn)行修改。

nginx 配置參考

這里只是參考文件,根據(jù)自己的項(xiàng)目需求自行配置

一個(gè)域名對應(yīng)單個(gè)項(xiàng)目

目錄結(jié)構(gòu)

.
├── /usr/local/nginx/html
│   ├── static
│   ├── index.html
│?? └── favicon.ico
?

nginx 配置

# 后端服務(wù)地址
upstream api_service {
  server xxx.xxx.xxx.xxx:xxxx;
  keepalive 2000;
}

server {
    listen      80;
    server_name www.xxxx.com xxxx.com; # 域名地址
    root        /usr/local/nginx/html; # 前端靜態(tài)文件目錄
    location / {
      index index.html;
      try_files $uri $uri/ /index.html; #react-router 防止頁面刷新出現(xiàn)404
    }

    # 靜態(tài)文件緩存,啟用Cache-Control: max-age、Expires
    location ~ ^/static/(css|js|media)/ {
      expires 10y;
      access_log off;
      add_header Cache-Control "public";
    }

     # 代理ajax請求 前端ajax請求以 /api 開頭
    location ^~/api {
       rewrite ^/api/(.*)$ /$1 break; # 如果后端接口不是統(tǒng)一以api開頭,去掉api前綴
       proxy_pass http://api_service/;
       proxy_set_header Host  $http_host;
       proxy_set_header Connection close;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-Server $host;
    }
}

一個(gè)域名對應(yīng)多個(gè)項(xiàng)目

多個(gè)項(xiàng)目掛載到同一個(gè)域名下,可以通過子目錄方式區(qū)分

比如,如下地址各對應(yīng)一個(gè)項(xiàng)目

前端項(xiàng)目構(gòu)建時(shí),添加 BASE_NAME PUBLIC_URL 參數(shù)

BASE_NAME=/project1 PUBLIC_URL=/project1 yarn build

nginx 靜態(tài)文件目錄結(jié)構(gòu)

.
├── /home/ubuntu/synext-admin
│?? ├── build   // 主項(xiàng)目 靜態(tài)文件目錄
│   │   ├── static
│   │   ├── index.html
│   │?? └── favicon.ico
│?? ├── project1   // 子項(xiàng)目靜態(tài)目錄 名稱與 location /project1 location ~ ^/project1/static/.*  配置對應(yīng)
│   │   ├── static
│   │   ├── index.html
│   │?? └── favicon.ico

nginx 配置

upstream api_service {
  server xxx.xxx.xxx.xxx:xxxx;
  keepalive 2000;
}

upstream api_service_project1 {
  server xxx.xxx.xxx.xxx:xxxx;
  keepalive 2000;
}
server {
    listen 80;
    server_name www.xxxx.com xxxx.com; # 域名地址
    # Allow file uploads
    client_max_body_size 100M;

    # 主項(xiàng)目配置,訪問地址 http://www.xxxx.com
    location / {
        root /home/ubuntu/synext-admin/build;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
    # 靜態(tài)文件緩存,啟用Cache-Control: max-age、Expires
    location ~ ^/static/.* {
        root /home/ubuntu/synext-admin/build;
        expires 20y;
        access_log off;
        add_header Cache-Control "public";
    }
    # 代理ajax請求 前端ajax請求以/api開頭
    location ^~/api {
       rewrite ^/api/(.*)$ /$1 break; # 如果后端接口不是統(tǒng)一以api開頭,去掉api前綴
       proxy_pass http://api_service/;
       proxy_set_header Host  $http_host;
       proxy_set_header Connection close;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-Server $host;
    }

    # 子項(xiàng)目配置 訪問地址 http://www.xxxx.com/project1
    location /project1 {
        root /home/ubuntu/synext-admin;
        index index.html;
        try_files $uri $uri/ /project1/index.html;
    }
    # 靜態(tài)文件緩存,啟用Cache-Control: max-age、Expires
    location ~ ^/project1/static/.* {
        root /home/ubuntu/synext-admin;
        expires 10y;
        access_log off;
        add_header Cache-Control "public";
    }
    # 代理ajax請求 前端ajax請求以 /project1_api 開頭
    location ^~/project1_api {
       rewrite ^/api/(.*)$ /$1 break; # 如果后端接口不是統(tǒng)一以api開頭,去掉api前綴
       proxy_pass http://api_service_project1/;
       proxy_set_header Host  $http_host;
       proxy_set_header Connection close;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-Server $host;
    }
}

其他

頁面打印

通過給元素添加相應(yīng)的 class,控制打印內(nèi)容:

  • .just-print?只在打印時(shí)顯示
  • .no-print?在打印時(shí)不顯示

ESLint 說明

如果前端項(xiàng)目,不是 git 根目錄,在提交的時(shí)候,會(huì)報(bào)錯(cuò)?Not a git repository

修改 package.json,lint-staged 如下即可

"lint-staged": {
    "gitDir": "../",
    "linters": {
        "**/*.{js,jsx}": "lint-staged:js",
        "**/*.less": "stylelint --syntax less"
    }
},

Webpack

使用了 alias {'@': '/path/to/src', src:'/path/to/src'}

  • config/webpack.config.js
  • 方便路徑書寫,不必關(guān)心相對路徑結(jié)構(gòu)
  • 復(fù)制粘貼到其他文件,不必修改路徑

支持判斷運(yùn)算符

const name = res?.data?.user?.name || "匿名";

form 表單

  1. FormElement:類型有:
'input', 'hidden', 'number', 'textarea', 'password', 'mobile', 'email', 'select', 'select-tree', 'checkbox', 'checkbox-group', 'radio', 'radio-button', 'radio-group', 'switch', 'date', 'time', 'date-time', 'date-range', 'cascader', 'transfer', 'icon-picker'
?

下載該資料的人也在下載 下載該資料的人還在閱讀
更多 >

評論

查看更多

下載排行

本周

  1. 1山景DSP芯片AP8248A2數(shù)據(jù)手冊
  2. 1.06 MB  |  532次下載  |  免費(fèi)
  3. 2RK3399完整板原理圖(支持平板,盒子VR)
  4. 3.28 MB  |  339次下載  |  免費(fèi)
  5. 3TC358743XBG評估板參考手冊
  6. 1.36 MB  |  330次下載  |  免費(fèi)
  7. 4DFM軟件使用教程
  8. 0.84 MB  |  295次下載  |  免費(fèi)
  9. 5元宇宙深度解析—未來的未來-風(fēng)口還是泡沫
  10. 6.40 MB  |  227次下載  |  免費(fèi)
  11. 6迪文DGUS開發(fā)指南
  12. 31.67 MB  |  194次下載  |  免費(fèi)
  13. 7元宇宙底層硬件系列報(bào)告
  14. 13.42 MB  |  182次下載  |  免費(fèi)
  15. 8FP5207XR-G1中文應(yīng)用手冊
  16. 1.09 MB  |  178次下載  |  免費(fèi)

本月

  1. 1OrCAD10.5下載OrCAD10.5中文版軟件
  2. 0.00 MB  |  234315次下載  |  免費(fèi)
  3. 2555集成電路應(yīng)用800例(新編版)
  4. 0.00 MB  |  33566次下載  |  免費(fèi)
  5. 3接口電路圖大全
  6. 未知  |  30323次下載  |  免費(fèi)
  7. 4開關(guān)電源設(shè)計(jì)實(shí)例指南
  8. 未知  |  21549次下載  |  免費(fèi)
  9. 5電氣工程師手冊免費(fèi)下載(新編第二版pdf電子書)
  10. 0.00 MB  |  15349次下載  |  免費(fèi)
  11. 6數(shù)字電路基礎(chǔ)pdf(下載)
  12. 未知  |  13750次下載  |  免費(fèi)
  13. 7電子制作實(shí)例集錦 下載
  14. 未知  |  8113次下載  |  免費(fèi)
  15. 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德爾著
  16. 0.00 MB  |  6656次下載  |  免費(fèi)

總榜

  1. 1matlab軟件下載入口
  2. 未知  |  935054次下載  |  免費(fèi)
  3. 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
  4. 78.1 MB  |  537798次下載  |  免費(fèi)
  5. 3MATLAB 7.1 下載 (含軟件介紹)
  6. 未知  |  420027次下載  |  免費(fèi)
  7. 4OrCAD10.5下載OrCAD10.5中文版軟件
  8. 0.00 MB  |  234315次下載  |  免費(fèi)
  9. 5Altium DXP2002下載入口
  10. 未知  |  233046次下載  |  免費(fèi)
  11. 6電路仿真軟件multisim 10.0免費(fèi)下載
  12. 340992  |  191187次下載  |  免費(fèi)
  13. 7十天學(xué)會(huì)AVR單片機(jī)與C語言視頻教程 下載
  14. 158M  |  183279次下載  |  免費(fèi)
  15. 8proe5.0野火版下載(中文版免費(fèi)下載)
  16. 未知  |  138040次下載  |  免費(fèi)