前端採用螞蟻金服的前端集成框架:ant-design-pro, 官網:https://pro.ant.design/
前端採用ant-design,其中是用react寫的,所以針對後端開發人員而言,學習也不是那麼快,該文章用於能夠快速的讓後端人員上手。
一、主要文件
這裏主要介紹,平常開發中可能用到的一些文件,這裏分爲兩類:
配置文件
界面文件
1.配置文件
該文件用於非界面文件,用於項目中的配置的介紹
-
後端配置文件
-
菜單中文設置文件
-
菜單層級設置文件
1.後端配置文件
這裏在當前的ant-design版本中文件爲
/src/config.js
// 其中最重要的是這裏
proxy: {
// 下面是在遇到這種路徑的時候進行替換
'/like/tina/api/v1/': {
// 自己的路徑,其中8084是後端的端口號
target: 'http://localhost:8084/like/tina/api/v1/',
changeOrigin: true,
pathRewrite: { '^/like/tina/api/v1': '' },
},
},
其中該配置文件主要用於 xxxxApi.js 文件針對後端的url的替換
注意:這裏面最後請求的路徑會自動修改掉,但是在瀏覽器下面查看的ip是不會變的,參考(這裏),但是實際的鏈接已經被代理成,而且能夠獲取數據,請注意
在console中展示的可能還是Http://localhost:8000/xxxxx,但是實際上已經替換爲我們自己的這個了,比如:http://localhost:8080/like/tina/api/v1//user/table
2.菜單中文設置文件
文件爲(其中我們只考慮中文的轉換,其他的暫時不考慮):
/src/locales/zh-CN/menu.js
文件介紹,該文件主要是給左側菜單中的中文展示使用
export default {
'menu.home': '首頁',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': '分析頁',
'menu.dashboard.monitor': '監控頁',
'menu.dashboard.workplace': '工作臺',
'menu.form': '表單頁',
'menu.form.basicform': '基礎表單',
'menu.form.stepform': '分步表單',
'menu.form.stepform.info': '分步表單(填寫轉賬信息)',
'menu.form.stepform.confirm': '分步表單(確認轉賬信息)',
'menu.form.stepform.result': '分步表單(完成)',
'menu.form.advancedform': '高級表單',
'menu.list': '列表頁',
'menu.list.searchtable': '查詢表格',
'menu.list.basiclist': '標準列表',
'menu.list.cardlist': '卡片列表',
'menu.list.searchlist': '搜索列表',
'menu.list.searchlist.articles': '搜索列表(文章)',
'menu.list.searchlist.projects': '搜索列表(項目)',
'menu.list.searchlist.applications': '搜索列表(應用)',
'menu.config': '配置',
'menu.config.configgrouplist': '配置組',
'menu.config.configitemlist': '配置項',
'menu.profile': '詳情頁',
'menu.profile.basic': '基礎詳情頁',
'menu.profile.advanced': '高級詳情頁',
'menu.result': '結果頁',
'menu.result.success': '成功頁',
'menu.result.fail': '失敗頁',
'menu.exception': '異常頁',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': '觸發錯誤',
'menu.account': '個人頁',
'menu.account.center': '個人中心',
'menu.account.settings': '個人設置',
'menu.account.trigger': '觸發報錯',
'menu.account.logout': '退出登錄',
'menu.task': '調度',
'menu.task.tasklist': '任務調度',
};
比如其中的一條數據:menu.task.tasklist:‘任務調度’,該條數據用於在菜單層級設置中,比如如下,其中就是在界面展示的
export default [
...
{
path: '/task',
icon: 'project',
name: 'task',
routes: [
{
path: '/task/task-list',
// 這裏就是跟中文對應的文件名
name: 'tasklist',
component: './like/TaskList',
},
],
},
...
];
3.菜單層級設置文件
文件爲:
/config/router.config.js
該文件用於顯示菜單,還是比較重要的,但是要展示自己的菜單的話,還是要自己進行單獨設置,不過一鍵式工具中我們已經可以直接生成了,下面主要針對文件進行介紹下,全量文件如下
export default [
// user
{
path: '/user',
component: '../layouts/UserLayout',
routes: [
{ path: '/user', redirect: '/user/login' },
{ path: '/user/login', component: './User/Login' },
{ path: '/user/register', component: './User/Register' },
{ path: '/user/register-result', component: './User/RegisterResult' },
],
},
// app
{
path: '/',
component: '../layouts/BasicLayout',
Routes: ['src/pages/Authorized'],
authority: ['admin', 'user'],
routes: [
// dashboard
{ path: '/', redirect: '/dashboard/analysis' },
{
path: '/config',
icon: 'setting',
name: 'config',
routes: [
{
path: '/config/config-group-list',
name: 'configgrouplist',
component: './config/ConfigGroupList',
},
{
path: '/config/config-item-list',
name: 'configitemlist',
component: './config/ConfigItemList',
},
],
},
{
path: '/task',
icon: 'project',
name: 'task',
routes: [
{
path: '/task/task-list',
name: 'tasklist',
component: './like/TaskList',
},
],
},
{
component: '404',
},
],
},
];
其中如果有routes則就表示是父級菜單,可以進行測試下,其中內部的爲子菜單,而子菜單還是可以繼續向下拆分,其中component就是指向我們自己的component,就是界面xxxList.js文件
2.界面文件
1.界面展示文件(默認用List結尾)
自動生成工具會直接生成對應的表名+List.js,該文件就是直接在界面上展示的文件,下面主要介紹下這文件的結構
import React, { PureComponent } from 'react';
import { connect } from 'dva';
// 引入antdesign的組件
import {
Row,
Col,
Card,
Badge,
Form,
Input,
Button,
Table,
Icon,
Select,
Divider,
Drawer,
Pagination,
InputNumber,
Tabs,
Modal,
} from 'antd';
// 引用第三方文件
import 'codemirror/theme/solarized.css';
// 下面@connect 這個是將Model文件中的數據和當前界面List關聯起來
/* eslint react/no-multi-comp:0 */
@connect(({ taskModel, loading }) => ({
// 這裏是Model文件的namespace(注意:一定要保持一致)
taskModel,
loading: loading.models.taskModel,
}))
// @Form.create() 是一個註解,就簡化了xxx = Form.create(xxx);export xxx
@Form.create()
class TaskList extends PureComponent {
// 這個是List的一些數據,也可以放到Model中
state = {
addModalVisible: false,
editModalVisible: false,
item: {},
};
// 界面展示的列
columns = [
{
name: 'id',
title: 'index',
dataIndex: 'id',
width: '8%',
},
];
// 界面初始化
componentDidMount() {
...
}
// 普通的函數
onClose = () => {
// 重要:當我們要調用Model中的函數的時候,應該通過dispatch來調用
const { dispatch } = this.props;
// 其中
dispatch({
type: 'configItem/getListCount',
payload: {
paneIndex: index,
searchParam: param,
},
});
};
// 界面加載,每次數據有更新,該函數都會執行
render() {
// 變量引用:props方式,props是外部數據引用過來的變量,比如Model文件,我們這裏引入,Model中的數據就應該這樣使用
const {
taskModel: { selectState, groupAllCodeList, drawerRecord, resultOfRun, drawerVisible },
} = this.props;
// 變量引用:state方式,這個其中的變量都是自己內部的
const { addModalVisible, editModalVisible, item } = this.state;
// 定義的一些定量
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
// 封裝的表格
const tabPanes = panes.map(pane => (
<Tabs.TabPane tab={pane.title} key={pane.name}>
<Card bordered={false}>
<div className={styles.tableList}>
{/*這裏是搜索框部分*/}
<div className={styles.tableListForm}>{this.renderSearchForm()}</div>
<div className={styles.tableListOperator} />
{/*這是表格*/}
<Table
rowKey={record => record.id}
dataSource={pane.content.tableList}
columns={this.columns}
loading={pane.content.tableLoading}
pagination={false}
expandedRowRender={this.expandedRowRender}
/>
{/*這裏是頁面分頁部分*/}
<Pagination
showQuickJumper
onChange={this.onChange}
defaultCurrent={1}
total={pane.content.totalNumber}
current={pane.content.pager.pageNo}
defaultPageSize={pane.content.pager.pageSize}
/>
</div>
</Card>
</Tabs.TabPane>
));
// 這裏返回界面元素
return (
// 該標籤爲antdesign的的標籤,用於菜單和界面顯示
<PageHeaderWrapper>
<Tabs
onChange={this.onTabChange}
activeKey={activePaneName}
defaultActiveKey="1"
type="editable-card"
onEdit={this.onEdit}
>
{tabPanes}
</Tabs>
</PageHeaderWrapper>
);
}
}
export default TaskList;java
2.界面數據文件(默認用Model結尾)
// 引入Api文件
import {
pageList,
add,
deleteData,
update,
pageCount,
getCodeList,
getAllCodeList,
disable,
enable,
run,
handRun,
} from '@/services/taskApi';
export default {
namespace: 'taskModel', // 這個是標示當前model的,用於跟List文件關聯
// 下面是定義的數據模型
state: {
maxTabIndex: 1, // 最大的標籤頁索引,用於標籤新增計數用
activePaneName: '1', // tabPane 的激活的key
tabIndexList: ['1'], // 當前存在的標籤的列表
panes: [
{
name: '1',
title: '任務調度1',
content: {
tableList: [],
tableLoading: false,
searchParam: {},
totalNumber: 0,
pager: {
pageNo: 1,
pageSize: 20,
},
},
},
],
},
// 異步處理函數
effects: {
// 增加組配置,payload爲List調度過來的參數,call爲調用Api函數的方法,put爲調用其他異步或者同步的方法
*add({ payload }, { call, put }) {
console.log('task.add 參數:');
// 打印複雜函數
console.log(JSON.stringify(payload));
// 調用Api文件中的某個函數add,這裏
const response = yield call(add, payload);
yield put({
type: 'handleAddResult',
payload: response,
});
// 調用其他的函數,同步或者異步函數均可
yield put({
type: 'tableFresh',
payload: {
paneIndex: payload.paneIndex,
},
});
},
// 其他函數
...
},
// 同步函數
reducers: {
// 同步函數xxx
setTableLoading(state) {
const newPanes = state.panes;
const index = newPanes.findIndex(pane => pane.name === state.activePaneName);
newPanes[index].content.tableLoading = true;
// 下面這個retuen 在同步函數中必須存在
return {
...state, // 這裏...是不修改除了下面這個panes之外的其他數據,注意:...state必須有,否則其他的數據都會被清空
panes: newPanes, // 這裏是更新state中的panes字段
};
},
// 其他函數
...
},
};
3.跟後端交互文件(默認用Api結尾)
該文件是跟後端發送url的地方
import request from '@/utils/request';
// 這裏我用一個變量統一表示,這個正好可以給config.js文件中的代理進行替換
const path = '/like/tina/api/v1';
export async function add(params) {
console.log('taskApi.add 發送的參數');
console.log(JSON.stringify(params));
// request是框架封裝的函數,body爲對應的參數,method不填寫,則默認爲GET
return request(`${path}/task/add`, {
method: 'PUT',
body: {
...params,
},
});
}
export async function deleteData(params) {
console.log('taskApi.deleteData 發送的參數');
console.log(JSON.stringify(params));
return request(`${path}/task/delete/${params}`, {
method: 'DELETE',
});
}
//
export async function getCodeList() {
console.log('taskApi.getCodeList 發送');
return request(`${path}/task/codeList`);
}
export async function update(params) {
console.log('taskApi.update 發送的參數');
console.log(JSON.stringify(params));
return request(`${path}/task/update`, {
method: 'POST',
body: {
...params,
},
});
}
二、頁面加載流程
整體的流程是這樣的:
List->Model→Api——>後端
其中Model中放置的是數據,發起方是List模塊的dispatch,一旦數據獲取到更新到Model中,List界面會自動刷新,整體的流程圖是這樣的解釋:
connect :對應@connect這個註解
dispatch:對應的是dispatch函數
action:新的版本中已經將action這個模塊刪除掉了,早期的寫法中是還有一層Action,List那邊dispatch的其實是Action這一層中的函數,而這一層再調用Model,現在新的版本中已經直接採用dispatch調用Model中的函數了,Action層已經嵌入到內核中
Model:對應的就是我們的Model文件
Effect:這個是Model中的異步函數,用於界面中調用獲取數據比較耗時的操作,一般用於調用後端數據
Reducer:同步函數,用於界面中的數據,一般用於在界面處理後可以直接更新,並展示到界面中,是阻塞性函數
Subscriptioin:這個函數我們這邊用的不多,主要是一種訂閱,詳細可參考這裏:https://dvajs.com/guide/concepts.html#effect
Server:就是我們的後端
三、文件數據用法
其中主要介紹下List怎麼調用Model中的數據,其實在上面的List文件中已經介紹了,下面簡單介紹下
// 變量引用:props方式,props是外部數據引用過來的變量,比如Model文件,我們這裏引入,Model中的數據就應該這樣使用
const {
taskModel: { selectState, groupAllCodeList, drawerRecord, resultOfRun, drawerVisible },
} = this.props;
要這樣寫纔行,引用某個變量的數據
四、注意事項(重要)
主要介紹一些語法糖,很多都是小細節,小坑,網上資料也較少,建議關注下
1.變量的…含義
// 同步函數xxx
setTableLoading(state) {
const newPanes = state.panes;
const index = newPanes.findIndex(pane => pane.name === state.activePaneName);
newPanes[index].content.tableLoading = true;
// 下面這個retuen 在同步函數中必須存在
return {
...state, // 這裏...是不修改除了下面這個panes之外的其他數據,注意:...state必須有,否則其他的數據都會被清空
panes: newPanes, // 這裏是更新state中的panes字段
};
},
在代碼中經常會見到這種“…”三個點的變量,這種表示是將變量的key展示出來
比如如果一個類型是這種:tem:{“key1”:123,“key2”:222},這個變量爲tem,…tem 這個就表示:“key1”:123, “key2”:222,其實就是將外層剝離
2.變量,這種寫法
在代碼中有時候會見到這種寫法,如下
dispatch({
type: 'configItem/getPageList',
payload: {
paneIndex: index,
pager, // 這裏其實就是pager:pager, 這個的簡化版,就是key和value的名字相同,就可以這樣寫,而且建議這樣寫,否則覈查會有告警
searchParam: param,
},
});
3.變量的打印
在日誌中打印平常都是使用console.log(xxxx) 即可,但是對於複雜類型,這個打印就顯示有點複雜,這裏建議使用JSON打印,如下這樣
console.log(JSON.stringify(item));
注意:
其中不能這麼寫,如下:
console.log("變量爲:"+JSON.stringify(item));
不能將JSON的解析和前面的字符鏈接,這樣後來發現,就是這個JSON不生效
4.函數的寫法
函數的寫法跟舊的js寫法不一樣,沒有function關鍵字,平常函數都這麼寫
// 沒有參數
renderSearchForm = () => {
};
// 一個參數
enable = record => {
};
// 多個參數
contain = (object1, object2) => {
};
5.列中屬性函數調用寫法
在column中可能會添加Button或者Icon等控件,進行函數的跳轉,在寫的時候一定要注意使用如下的方式
{
key: 'delete',
title: '刪除',
dataIndex: 'delete',
editable: false,
width: '5%',
render: (text, row) => (
<span>
<Button type="danger" icon="delete" onClick={() => this.showDeleteConfirm(row)} />
</span>
),
},
注意:其中函數調用一定要寫成
onClick={() => this.fun()}
而不能寫成如下: 下面這個會自動調用函數fun()
onClick={this.fun()}
6.js文件中的註釋
其中"//"之後要添加一個空格,否則會報錯
7.List頁面調用
這個頁面可以調用異步也可以調用同步函數,但是異步一般用於獲取數據耗時比較長的,一般用於從後端獲取數據並覆蓋前端數據,同步函數用於直接將進行數據操作
8.Model頁面函數調用
Model頁面中有兩種函數,異步函數和同步函數,但是異步函數可以調用同步函數也可以調用異步函數,而同步函數不可以調用其他的函數。
五、參考
dva概念:https://dvajs.com/guide/concepts.html#effectreact語法:https://www.jianshu.com/p/7e872afeae42antdesign官網:https://pro.ant.design/docs/getting-started-cnantdesign組件庫:https://ant.design/docs/react/introduce-cnantdesign目錄解釋:https://pro.ant.design/docs/getting-started-cnhttp://www.cnblogs.com/bjlhx/p/8973658.htmlantdesignpro詳細介紹:https://www.ctolib.com/topics-134955.html
六、學習參考:
https://blog.csdn.net/luo1055120207/article/details/78953381https://blog.csdn.net/qietingfengdeyanse/article/details/81206292https://zhuanlan.zhihu.com/p/36134136https://pro.ant.design/index-cnhttp://www.cnblogs.com/bjlhx/p/9009056.htmlhttps://www.ru23.com/note/ab0115f0.html