色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

電子發(fā)燒友App

硬聲App

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

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

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

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

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

資料介紹

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

軟件簡(jiǎn)介

?

項(xiàng)目開(kāi)始 BEGIN

項(xiàng)目說(shuō)明

基于 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         // 頁(yè)面框架布局組件+
│   ├── mock            // 模擬數(shù)據(jù)
│   ├── models          // 模塊封裝,基于redux,提供各組件共享數(shù)據(jù)、共享邏輯
│   ├── pages           // 主項(xiàng)目頁(yè)面目錄
│   ├── ├── Project                 // 子項(xiàng)目頂級(jí)目錄
│   ├── ├── ├── nft                 // NFT--PC版本項(xiàng)目目錄
│   ├── ├── ├── ├── assets               // NFT--PC版本公共圖片文件
│   ├── ├── ├── ├── components           // NFT--PC版本公共組件庫(kù)
│   ├── ├── ├── ├── pages                // NFT--PC版本頁(yè)面目錄
│   ├── ├── ├── mobile              // 移動(dòng)端項(xiàng)目目錄
│   ├── ├── ├── ├── nft-mobile           // NFT--微信版本項(xiàng)目目錄
│   ├── ├── ├── ├── ├── assets              //  NFT--微信版本公共圖片文件
│   ├── ├── ├── ├── ├── components          //  NFT--微信版本公共組件庫(kù)
│   ├── ├── ├── ├── ├── pages               //  NFT--微信版本頁(yè)面目錄
│   ├── 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

安裝依賴(lài)

$ yarn

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

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

!!!微信版本只可以在微信打開(kāi)運(yùn)行,并直接在 router/AuthRoute.jsx 中填入自己申請(qǐng)的 appid,目前為''

此為兩個(gè)項(xiàng)目依托同一底層框架,PC 及微信端口分離,如需切換請(qǐng)?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)目名稱(chēng)。

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

微信版本: 路由前綴----/front_nft_mobile 請(qǐng)求地址前綴----/api/nft 登錄頁(yè)面----/nft_mobile_home

PC 版本: 路由前綴----/front_nft_pc 請(qǐng)求地址前綴----/api/nft 登錄頁(yè)面----/nft_home

開(kāi)發(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)目

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

// 開(kāi)發(fā)訪(fǎng)問(wèn)
'http://localhost:XXXX/synext-admin/'

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

菜單配置

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

頁(yè)面開(kāi)發(fā)

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

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

注:

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

頁(yè)面顯示/隱藏事件

config?裝飾器為組件注入了兩個(gè)事件?onComponentWillShowonComponentWillHide?,如果頁(yè)面使用了 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
        });
    }
    ...
}

頁(yè)面容器 PageContent

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

  • 添加了 margin padding 樣式;
  • 添加了 footer;
  • 支持頁(yè)面 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>;
    

彈框頁(yè)面開(kāi)發(fā)

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

系統(tǒng)提供了基于 modal 封裝的高階組件,每次彈框關(guān)閉,都會(huì)銷(xiāo)毀彈框內(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ù)說(shuō)明如下:

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

ModalContent 組件

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

參數(shù) 類(lèi)型 默認(rèn)值 說(shuō)明
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,通過(guò) route-loader 將路由內(nèi)容填充到/src/pages/page-routes.js 文件,支持兩種寫(xiě)法:

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

二級(jí)頁(yè)面

二級(jí)頁(yè)面如果要保持父級(jí)菜單的選中狀態(tài),以父級(jí) path 開(kāi)始并以/_/作為分隔符即可: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 請(qǐng)求

系統(tǒng)的 ajax 請(qǐng)求基于 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對(duì)象
import React, {Component} from 'react';
import {sxAjax} from 'src/commpons/ajax';

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

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

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

接收參數(shù)

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

options 配置

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

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

請(qǐng)求結(jié)果提示

  1. 系統(tǒng)對(duì) ajax 失敗做了自動(dòng)提示,開(kāi)發(fā)人員可通過(guò) 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: "刪除失敗!",
    noEmpty: true,
});

loading 處理

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

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

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

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

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

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

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: "用戶(hù)名或密碼錯(cuò)誤",
                    });
                }, 1000);
            } else {
                setTimeout(() => {
                    resolve([
                        200,
                        {
                            id: "1234567890abcde",
                            name: "MOCK 用戶(hù)",
                            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",
};

簡(jiǎn)化

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

對(duì)象的 key 由 method url delay,各部分組成,以空格隔開(kāi)

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

調(diào)用

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

mock 請(qǐng)求:

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

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

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

樣式

系統(tǒng)使用less進(jìn)行樣式的編寫(xiě)。 為了避免多人合作樣式?jīng)_突,系統(tǒng)對(duì) src 下的 less 文件啟用了 Css Module,css 文件沒(méi)有使用 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,通過(guò)樣式覆蓋來(lái)實(shí)現(xiàn)。

編寫(xiě)主題

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

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

參考

導(dǎo)航布局

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

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

更改方式

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

不需要導(dǎo)航

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

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

Tab 標(biāo)簽頁(yè)

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

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

Tab 操作 API

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

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

使用方式:

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

models(redux) 封裝

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

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

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

  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. 客戶(hù)端數(shù)據(jù)持久化(保存到 LocalStorage 中):syncStorage 配置;
  5. 異步數(shù)據(jù)處理:基于 promise 異步封裝;
  6. 請(qǐng)求錯(cuò)誤提示:error 處理封裝,errorTip 配置,自動(dòng)提示;
  7. 請(qǐng)求成功提示:successTip 配置,自動(dòng)提示;
  8. 簡(jiǎn)化寫(xiě)法:types actions reducers 可以在一個(gè)文件中編寫(xiě),較少?zèng)_突,方便多人協(xié)作,參見(jiàn)models/page.js中的寫(xiě)法;
  9. 業(yè)務(wù)代碼可集中歸類(lèi):在 models 目錄中統(tǒng)一編寫(xiě),或者在具體業(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 />;
};

對(duì) useSelector 的說(shuō)明

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

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

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

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

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

js 文件中使用

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

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

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

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

簡(jiǎn)化寫(xiě)法

action reducer 二合一,省去了 actionType,簡(jiǎn)化寫(xiě)法;

注意:

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

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

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

  • 調(diào)用 action 方法傳遞的數(shù)據(jù)將不會(huì)做任何處理,會(huì)直接傳遞給 reducer
  • 只能用第一個(gè)參數(shù)接收傳遞過(guò)來(lái)的數(shù)據(jù),如果多個(gè)數(shù)據(jù),需要通過(guò)對(duì)象方式傳遞,如果不需要傳遞數(shù)據(jù),但是要使用 state,也需要定義一個(gè)參數(shù)占位
  • 第二個(gè)參數(shù)固定為 state,第三個(gè)參數(shù)固定為 action,不需要可以缺省(一般都是缺省的)
  • 函數(shù)的返回值為一個(gè)對(duì)象或者 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ù)同步

通過(guò)配置的方式,可以讓 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: { // 支持對(duì)象指定字段,任意層次
            name: true,
            address: {
                city: true,
            },
        },
        job: true,
        users: [{name: true, age: true}], // 支持?jǐn)?shù)組
    },
}

action reducer 合并寫(xiě)法

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

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 寫(xiě)法

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

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

        // 基于promise 異步reducer寫(xiě)法;
        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è)對(duì)象,各個(gè)屬性說(shuō)明如下:

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

單獨(dú)定義 action 和 reducer

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

import {createAction} from 'redux-actions';

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

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能對(duì)應(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)通過(guò)登錄用戶(hù)返回用戶(hù)的菜單權(quán)限;頁(yè)面只顯示獲取到的菜單;

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

功能權(quán)限

可以通過(guò)src/components/permission組件對(duì)功能的權(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>添加用戶(hù)</Button>
                </Permission>
            </div>
        );
    }
}
?

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

角色

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

開(kāi)發(fā)代理

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

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]: "", // 如果后端接口無(wú)前綴,可以通過(guò)這種方式去掉
            },
            changeOrigin: true,
            secure: false, // 是否驗(yàn)證證書(shū)
            ws: true, // 啟用websocket
        })
    );
};

注:更多代理配置請(qǐng)參考http-proxy-middleware

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

nginx 配置參考

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

一個(gè)域名對(duì)應(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 防止頁(yè)面刷新出現(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請(qǐng)求 前端ajax請(qǐng)求以 /api 開(kāi)頭
    location ^~/api {
       rewrite ^/api/(.*)$ /$1 break; # 如果后端接口不是統(tǒng)一以api開(kāi)頭,去掉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è)域名對(duì)應(yīng)多個(gè)項(xiàng)目

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

比如,如下地址各對(duì)應(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)目錄 名稱(chēng)與 location /project1 location ~ ^/project1/static/.*  配置對(duì)應(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)目配置,訪(fǎng)問(wèn)地址 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請(qǐng)求 前端ajax請(qǐng)求以/api開(kāi)頭
    location ^~/api {
       rewrite ^/api/(.*)$ /$1 break; # 如果后端接口不是統(tǒng)一以api開(kāi)頭,去掉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)目配置 訪(fǎng)問(wèn)地址 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請(qǐng)求 前端ajax請(qǐng)求以 /project1_api 開(kāi)頭
    location ^~/project1_api {
       rewrite ^/api/(.*)$ /$1 break; # 如果后端接口不是統(tǒng)一以api開(kāi)頭,去掉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è)面打印

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

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

ESLint 說(shuō)明

如果前端項(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
  • 方便路徑書(shū)寫(xiě),不必關(guān)心相對(duì)路徑結(jié)構(gòu)
  • 復(fù)制粘貼到其他文件,不必修改路徑

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

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

form 表單

  1. FormElement:類(lèi)型有:
'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'
?

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

評(píng)論

查看更多

下載排行

本周

  1. 1山景DSP芯片AP8248A2數(shù)據(jù)手冊(cè)
  2. 1.06 MB  |  532次下載  |  免費(fèi)
  3. 2RK3399完整板原理圖(支持平板,盒子VR)
  4. 3.28 MB  |  339次下載  |  免費(fèi)
  5. 3TC358743XBG評(píng)估板參考手冊(cè)
  6. 1.36 MB  |  330次下載  |  免費(fèi)
  7. 4DFM軟件使用教程
  8. 0.84 MB  |  295次下載  |  免費(fèi)
  9. 5元宇宙深度解析—未來(lái)的未來(lái)-風(fēng)口還是泡沫
  10. 6.40 MB  |  227次下載  |  免費(fèi)
  11. 6迪文DGUS開(kāi)發(fā)指南
  12. 31.67 MB  |  194次下載  |  免費(fèi)
  13. 7元宇宙底層硬件系列報(bào)告
  14. 13.42 MB  |  182次下載  |  免費(fèi)
  15. 8FP5207XR-G1中文應(yīng)用手冊(cè)
  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開(kāi)關(guān)電源設(shè)計(jì)實(shí)例指南
  8. 未知  |  21549次下載  |  免費(fèi)
  9. 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書(shū))
  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ì)》 溫德?tīng)栔?/a>
  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語(yǔ)言視頻教程 下載
  14. 158M  |  183279次下載  |  免費(fèi)
  15. 8proe5.0野火版下載(中文版免費(fèi)下載)
  16. 未知  |  138040次下載  |  免費(fèi)
主站蜘蛛池模板: 97久久国产露脸精品国产| 亚洲另类欧美综合在线| 幻女FREE性俄罗斯学生| 越南美女内射BBWXZ| 欧美成人无码视频午夜福利| 公主纯肉高H文| 亚洲深夜在线| 暖暖免费 高清 日本社区中文| 国产AV亚洲精品久久久久| 亚洲色欲国产免费视频| 欧美高清xxx| 国产午夜在线精品三级a午夜电影 国产午夜在线观看视频播放 | 欧美国产影院| 国产精品久久久久久亚洲毛片| 一个人免费播放高清在线观看| 欧美日韩亚洲一区视频二区| 国产人妻系列无码专区97SS| 最新亚洲一区二区三区四区 | 艳鉧动漫片1~6全集在线| 欧美 亚洲 另类 综合网| 国产精品婷婷五月久久久久| 洲精品无码高潮喷水A片| 色呦呦导航| 久久女婷五月综合色啪| 高清欧美videos sexo| 野花香在线观看免费观看大全动漫 | 欧美亚洲国产手机在线有码| 国产婷婷午夜精品无码A片| 99国内精品久久久久久久清纯| 午夜理论片日本中文在线| 美女脱了内裤张开腿让男人桶到爽 | 少妇内射视频播放舔大片| 久久久久久88色偷偷| 国产69精品久久久久乱码韩国 | 国产福利不卡在线视频| 伊人影院综合网| 亚洲精品国偷拍电影自产在线| 日日碰狠狠躁久久躁综合网| 嫩草影院未满十八岁禁止入内| 99视频精品全部免费 在线| 亚洲春色AV无码专区456|