mirror of
https://github.com/YMFE/yapi.git
synced 2025-04-06 15:00:26 +08:00
fix: 增加mock点击数据查询
This commit is contained in:
parent
3b9d249280
commit
ef79b8be77
@ -10,20 +10,54 @@ import Header from './components/Header/Header';
|
||||
import Footer from './components/Footer/Footer';
|
||||
import Loading from './components/Loading/Loading';
|
||||
import MyPopConfirm from './components/MyPopConfirm/MyPopConfirm';
|
||||
import statisticsPage from '../exts/yapi-plugin-statistics/statisticsClientPage/index';
|
||||
// import statisticsPage from '../exts/yapi-plugin-statistics/statisticsClientPage/index';
|
||||
import { checkLoginState } from './reducer/modules/user';
|
||||
import { requireAuthentication } from './components/AuthenticatedComponent';
|
||||
const plugin = require('client/plugin.js');
|
||||
|
||||
const LOADING_STATUS = 0;
|
||||
|
||||
const alertContent = () => {
|
||||
const ua = window.navigator.userAgent,
|
||||
isChrome = ua.indexOf("Chrome") && window.chrome;
|
||||
isChrome = ua.indexOf("Chrome") && window.chrome;
|
||||
if (!isChrome) {
|
||||
return <Alert style={{zIndex: 99}} message={'YApi 的接口测试等功能仅支持 Chrome 浏览器,请使用 Chrome 浏览器获得完整功能。'} banner closable />
|
||||
return <Alert style={{ zIndex: 99 }} message={'YApi 的接口测试等功能仅支持 Chrome 浏览器,请使用 Chrome 浏览器获得完整功能。'} banner closable />
|
||||
}
|
||||
}
|
||||
|
||||
let AppRoute = {
|
||||
home: {
|
||||
path: '/',
|
||||
component: Home
|
||||
},
|
||||
group: {
|
||||
path: '/group',
|
||||
component: Group
|
||||
},
|
||||
project: {
|
||||
path: '/project/:id',
|
||||
component: Project
|
||||
},
|
||||
user: {
|
||||
path: '/user',
|
||||
component: User
|
||||
},
|
||||
follow: {
|
||||
path: '/follow',
|
||||
component: Follows
|
||||
},
|
||||
addProject: {
|
||||
path: '/add-project',
|
||||
component: AddProject
|
||||
},
|
||||
login: {
|
||||
path: '/login',
|
||||
component: Login
|
||||
}
|
||||
};
|
||||
// 增加路由钩子
|
||||
plugin.emitHook('app_route', AppRoute);
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
@ -72,6 +106,18 @@ export default class App extends Component {
|
||||
{alertContent()}
|
||||
{this.props.loginState !== 1 ? <Header /> : null}
|
||||
<div className="router-container">
|
||||
{Object.keys(AppRoute).map(key => {
|
||||
let item = AppRoute[key];
|
||||
return (
|
||||
key === 'login' ?
|
||||
<Route key={key} path={item.path} component={item.component} />
|
||||
: key === 'home' ? <Route key={key} exact path={item.path} component={item.component} />
|
||||
: <Route key={key} path={item.path} component={requireAuthentication(item.component)} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
{/* <div className="router-container">
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path="/group" component={requireAuthentication(Group)} />
|
||||
<Route path="/project/:id" component={requireAuthentication(Project)} />
|
||||
@ -79,12 +125,12 @@ export default class App extends Component {
|
||||
<Route path="/follow" component={requireAuthentication(Follows)} />
|
||||
<Route path="/add-project" component={requireAuthentication(AddProject)} />
|
||||
<Route path="/login" component={Login} />
|
||||
<Route path="/statistic" component={statisticsPage} />
|
||||
</div>
|
||||
{/* <Route path="/statistic" component={statisticsPage} /> */}
|
||||
{/* </div> */}
|
||||
</div>
|
||||
<Footer/>
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
</Router >
|
||||
|
||||
)
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import './Header.scss'
|
||||
import React, {Component} from 'react'
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {Link} from 'react-router-dom'
|
||||
import {Icon, Layout, Menu, Dropdown, message, Tooltip, Avatar, Popover, Tag} from 'antd'
|
||||
import {checkLoginState, logoutActions, loginTypeAction} from '../../reducer/modules/user'
|
||||
import {changeMenuItem} from '../../reducer/modules/menu'
|
||||
import {withRouter} from 'react-router';
|
||||
import { connect } from 'react-redux'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Icon, Layout, Menu, Dropdown, message, Tooltip, Avatar, Popover, Tag } from 'antd'
|
||||
import { checkLoginState, logoutActions, loginTypeAction } from '../../reducer/modules/user'
|
||||
import { changeMenuItem } from '../../reducer/modules/menu'
|
||||
import { withRouter } from 'react-router';
|
||||
import Srch from './Search/Search'
|
||||
const {Header} = Layout;
|
||||
import {logoSVG} from '../../common.js';
|
||||
const { Header } = Layout;
|
||||
import { logoSVG } from '../../common.js';
|
||||
import Breadcrumb from '../Breadcrumb/Breadcrumb.js'
|
||||
import GuideBtns from '../GuideBtns/GuideBtns.js';
|
||||
const plugin = require('client/plugin.js');
|
||||
@ -49,11 +49,11 @@ const MenuUser = (props) => (
|
||||
<Menu.Item key={key}>
|
||||
{
|
||||
item.name === '个人中心' ? <Link to={item.path + `/${props.uid}`}>
|
||||
<Icon type={item.icon}/>
|
||||
<Icon type={item.icon} />
|
||||
{item.name}
|
||||
</Link> : !item.adminFlag ? <Link to={item.path}><Icon type={item.icon}/>
|
||||
</Link> : !item.adminFlag ? <Link to={item.path}><Icon type={item.icon} />
|
||||
{item.name}
|
||||
</Link> : <Link to={item.path}><Icon type={item.icon}/>
|
||||
</Link> : <Link to={item.path}><Icon type={item.icon} />
|
||||
{item.name}
|
||||
</Link>
|
||||
}
|
||||
@ -73,17 +73,17 @@ const MenuUser = (props) => (
|
||||
{/*}*/}
|
||||
|
||||
<Menu.Item key="9">
|
||||
<a onClick={props.logout}><Icon type="logout"/>退出</a>
|
||||
<a onClick={props.logout}><Icon type="logout" />退出</a>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const tipFollow = (<div className="title-container">
|
||||
<h3 className="title"><Icon type="star"/> 关注</h3>
|
||||
<h3 className="title"><Icon type="star" /> 关注</h3>
|
||||
<p>这里是你的专属收藏夹,便于你找到自己的项目</p>
|
||||
</div>);
|
||||
const tipAdd = (<div className="title-container">
|
||||
<h3 className="title"><Icon type="plus-circle"/> 新建项目</h3>
|
||||
<h3 className="title"><Icon type="plus-circle" /> 新建项目</h3>
|
||||
<p>在任何页面都可以快速新建项目</p>
|
||||
</div>);
|
||||
const tipDoc = (<div className="title-container">
|
||||
@ -105,11 +105,11 @@ const ToolUser = (props) => {
|
||||
return (
|
||||
<ul>
|
||||
<li className="toolbar-li item-search">
|
||||
<Srch groupList={props.groupList}/>
|
||||
<Srch groupList={props.groupList} />
|
||||
</li>
|
||||
<Popover
|
||||
overlayClassName="popover-index"
|
||||
content={<GuideBtns/>}
|
||||
content={<GuideBtns />}
|
||||
title={tipFollow}
|
||||
placement="bottomRight"
|
||||
arrowPointAtCenter
|
||||
@ -118,14 +118,14 @@ const ToolUser = (props) => {
|
||||
<Tooltip placement="bottom" title={'我的关注'}>
|
||||
<li className="toolbar-li">
|
||||
<Link to="/follow">
|
||||
<Icon className="dropdown-link" style={{fontSize: 16}} type="star"/>
|
||||
<Icon className="dropdown-link" style={{ fontSize: 16 }} type="star" />
|
||||
</Link>
|
||||
</li>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
<Popover
|
||||
overlayClassName="popover-index"
|
||||
content={<GuideBtns/>}
|
||||
content={<GuideBtns />}
|
||||
title={tipAdd}
|
||||
placement="bottomRight"
|
||||
arrowPointAtCenter
|
||||
@ -134,14 +134,14 @@ const ToolUser = (props) => {
|
||||
<Tooltip placement="bottom" title={'新建项目'}>
|
||||
<li className="toolbar-li">
|
||||
<Link to="/add-project">
|
||||
<Icon className="dropdown-link" style={{fontSize: 16}} type="plus-circle"/>
|
||||
<Icon className="dropdown-link" style={{ fontSize: 16 }} type="plus-circle" />
|
||||
</Link>
|
||||
</li>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
<Popover
|
||||
overlayClassName="popover-index"
|
||||
content={<GuideBtns isLast={true}/>}
|
||||
content={<GuideBtns isLast={true} />}
|
||||
title={tipDoc}
|
||||
placement="bottomRight"
|
||||
arrowPointAtCenter
|
||||
@ -150,7 +150,7 @@ const ToolUser = (props) => {
|
||||
<Tooltip placement="bottom" title={'使用文档'}>
|
||||
<li className="toolbar-li">
|
||||
<a target="_blank" href="https://yapi.ymfe.org/" rel="noopener noreferrer"><Icon
|
||||
className="dropdown-link" style={{fontSize: 16}} type="question-circle"/></a>
|
||||
className="dropdown-link" style={{ fontSize: 16 }} type="question-circle" /></a>
|
||||
</li>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
@ -170,10 +170,10 @@ const ToolUser = (props) => {
|
||||
/>
|
||||
}>
|
||||
<a className="dropdown-link">
|
||||
<Avatar src={`/api/user/avatar?uid=${props.uid}`}/>
|
||||
<Avatar src={`/api/user/avatar?uid=${props.uid}`} />
|
||||
{/*<img style={{width:24,height:24}} src={`/api/user/avatar?uid=${props.uid}`} />*/}
|
||||
{/*<span className="name">{props.user}</span>*/}
|
||||
<span className="name"><Icon type="down"/></span>
|
||||
<span className="name"><Icon type="down" /></span>
|
||||
</a>
|
||||
</Dropdown>
|
||||
|
||||
@ -281,7 +281,7 @@ export default class HeaderCom extends Component {
|
||||
|
||||
|
||||
render() {
|
||||
const {login, user, msg, uid, role, studyTip, study} = this.props;
|
||||
const { login, user, msg, uid, role, studyTip, study } = this.props;
|
||||
return (
|
||||
<Header className="header-box m-header">
|
||||
<div className="content g-row">
|
||||
@ -293,12 +293,12 @@ export default class HeaderCom extends Component {
|
||||
</Link>
|
||||
<Breadcrumb />
|
||||
<div className="user-toolbar"
|
||||
style={{position: 'relative', zIndex: this.props.studyTip > 0 ? 3 : 1}}>
|
||||
style={{ position: 'relative', zIndex: this.props.studyTip > 0 ? 3 : 1 }}>
|
||||
{login ?
|
||||
<ToolUser
|
||||
{...{studyTip, study, user, msg, uid, role}}
|
||||
relieveLink={ this.relieveLink }
|
||||
logout={ this.logout }
|
||||
{...{ studyTip, study, user, msg, uid, role }}
|
||||
relieveLink={this.relieveLink}
|
||||
logout={this.logout}
|
||||
/>
|
||||
: ""}
|
||||
</div>
|
||||
|
@ -112,6 +112,50 @@ hooks = {
|
||||
type: 'listener',
|
||||
mulit: true,
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* Route路由列表钩子
|
||||
* @param AppRoute
|
||||
*
|
||||
* @info
|
||||
* 可参考 vendors/exts/yapi-plugin-statistics
|
||||
* 添加位置在Application.js 中
|
||||
* let AppRoute = {
|
||||
home: {
|
||||
path: '/',
|
||||
component: Home
|
||||
},
|
||||
group: {
|
||||
path: '/group',
|
||||
component: Group
|
||||
},
|
||||
project: {
|
||||
path: '/project/:id',
|
||||
component: Project
|
||||
},
|
||||
user: {
|
||||
path: '/user',
|
||||
component: User
|
||||
},
|
||||
follow: {
|
||||
path: '/follow',
|
||||
component: Follows
|
||||
},
|
||||
addProject: {
|
||||
path: '/add-project',
|
||||
component: AddProject
|
||||
},
|
||||
login: {
|
||||
path: '/login',
|
||||
component: Login
|
||||
}
|
||||
};
|
||||
};
|
||||
*/
|
||||
app_route: {
|
||||
type: 'listener',
|
||||
mulit: true,
|
||||
listener: []
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Created by gxl.gao on 2017/10/24.
|
||||
*/
|
||||
// import statisticsPage from './statisticsClientPage'
|
||||
import StatisticsPage from './statisticsClientPage/index'
|
||||
|
||||
module.exports = function () {
|
||||
this.bindHook('header_menu', function (menu) {
|
||||
@ -12,4 +12,12 @@ module.exports = function () {
|
||||
adminFlag: true
|
||||
}
|
||||
})
|
||||
this.bindHook('app_route', function (app) {
|
||||
app.statisticsPage = {
|
||||
path: '/statistic',
|
||||
component: StatisticsPage
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
@ -6,9 +6,11 @@ const statisMockModel = require('./statisMockModel.js');
|
||||
const groupModel = require('models/group.js');
|
||||
const projectModel = require('models/project.js');
|
||||
const interfaceModel = require('models/interface.js');
|
||||
const interfaceCaseModel = require('models/interfaceCase.js')
|
||||
|
||||
const yapi = require('yapi.js');
|
||||
const config = require('./index.js');
|
||||
const commons = require('./util.js');
|
||||
|
||||
class statisMockController extends baseController {
|
||||
constructor(ctx) {
|
||||
@ -17,20 +19,55 @@ class statisMockController extends baseController {
|
||||
this.groupModel = yapi.getInst(groupModel);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
this.interfaceModel = yapi.getInst(interfaceModel);
|
||||
this.interfaceCaseModel = yapi.getInst(interfaceCaseModel);
|
||||
}
|
||||
|
||||
async getStatisMock(ctx) {
|
||||
/**
|
||||
* 获取所有统计总数
|
||||
* @interface statismock/count
|
||||
* @method get
|
||||
* @category statistics
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
*/
|
||||
async getStatisCount(ctx) {
|
||||
// let id = ctx.query.interface_id;
|
||||
// let mockData = await this.Model.get(id);
|
||||
let groupCount = await this.groupModel.getGroupListCount();
|
||||
let projectCount = await this.projectModel.getProjectListCount();
|
||||
let interfaceCount = await this.interfaceModel.getInterfaceListCount();
|
||||
let interfaceCaseCount = await this.interfaceCaseModel.getInterfaceCaseListCount();
|
||||
|
||||
// if(!mockData){
|
||||
// return ctx.body = yapi.commons.resReturn(null, 408, 'mock脚本不存在');
|
||||
// }
|
||||
return ctx.body = yapi.commons.resReturn({groupCount, projectCount, interfaceCount});
|
||||
return ctx.body = yapi.commons.resReturn({ groupCount, projectCount, interfaceCount, interfaceCaseCount });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有mock接口数据信息
|
||||
* @interface statismock/get
|
||||
* @method get
|
||||
* @category statistics
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
*/
|
||||
async getMockDateList(ctx) {
|
||||
let mockCount = await this.Model.getTotalCount();
|
||||
// let list = await this.Model.list();
|
||||
let mockDateList = [];
|
||||
|
||||
if (!this.getRole() === 'admin') {
|
||||
return ctx.body = yapi.commons.resReturn(null, 405, '没有权限');
|
||||
}
|
||||
// 默认时间是30 天为一周期
|
||||
let dateInterval = commons.getDateRange();
|
||||
// console.log('interval', dateInterval);
|
||||
mockDateList = await this.Model.getDayCount(dateInterval);
|
||||
return ctx.body = yapi.commons.resReturn({ mockCount, mockDateList });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = statisMockController;
|
@ -4,8 +4,10 @@
|
||||
const yapi = require('yapi.js')
|
||||
const mongoose = require('mongoose');
|
||||
const controller = require('./controller');
|
||||
const statisModel = require('./statisMockModel.js');
|
||||
const commons = require('./util.js');
|
||||
|
||||
module.exports = function(){
|
||||
module.exports = function () {
|
||||
|
||||
yapi.connect.then(function () {
|
||||
let Col = mongoose.connection.db.collection('statis_mock');
|
||||
@ -15,16 +17,57 @@ module.exports = function(){
|
||||
Col.createIndex({
|
||||
project_id: 1
|
||||
})
|
||||
Col.createIndex({
|
||||
group_id: 1
|
||||
})
|
||||
Col.createIndex({
|
||||
time: 1
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
this.bindHook('add_router', function(addRouter) {
|
||||
this.bindHook('add_router', function (addRouter) {
|
||||
addRouter({
|
||||
controller: controller,
|
||||
method: 'get',
|
||||
path: 'statismock/count',
|
||||
action: 'getStatisCount'
|
||||
})
|
||||
|
||||
addRouter({
|
||||
controller: controller,
|
||||
method: 'get',
|
||||
path: 'statismock/get',
|
||||
action: 'getStatisMock'
|
||||
action: 'getMockDateList'
|
||||
})
|
||||
})
|
||||
|
||||
// MockServer生成mock数据后触发
|
||||
this.bindHook('mock_after', async function (context) {
|
||||
|
||||
// console.log('context', context);
|
||||
let interfaceId = context.interfaceData._id;
|
||||
let projectId = context.projectData._id;
|
||||
let groupId = context.projectData.group_id;
|
||||
let ip = context.ctx.originalUrl;
|
||||
// let date = commons.formatYMD(new Date());
|
||||
|
||||
let data = {
|
||||
interface_id: interfaceId,
|
||||
project_id: projectId,
|
||||
group_id: groupId,
|
||||
time: yapi.commons.time(),
|
||||
ip: ip,
|
||||
date: commons.formatYMD(new Date())
|
||||
};
|
||||
let inst = yapi.getInst(statisModel);
|
||||
|
||||
try {
|
||||
let result = await inst.save(data);
|
||||
result = yapi.commons.fieldSelect(result, ['interface_id', 'project_id', 'group_id', 'time', 'ip', 'date']);
|
||||
console.log('result', result);
|
||||
} catch (e) {
|
||||
console.log('mockStatisError', e);
|
||||
}
|
||||
})
|
||||
};
|
@ -8,6 +8,56 @@ class statisMockModel extends baseModel {
|
||||
getName() {
|
||||
return 'statis_mock';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
interface_id: { type: Number, required: true },
|
||||
project_id: { type: Number, required: true },
|
||||
group_id: { type: Number, required: true },
|
||||
time: Number, //'时间戳'
|
||||
ip: String,
|
||||
date: String
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
getTotalCount() {
|
||||
return this.model.count({});
|
||||
}
|
||||
|
||||
getDayCount(timeInterval) {
|
||||
// console.log(this.model.count({}));
|
||||
let end = timeInterval[1];
|
||||
let start = timeInterval[0];
|
||||
return this.model.aggregate([
|
||||
{
|
||||
$match: {
|
||||
date: { $gt: start, $lte: end }
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$date', //$region is the column name in collection
|
||||
count: { $sum: 1 }
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
list() {
|
||||
return this.model.find({}).select('date').exec();
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
data.up_time = yapi.commons.time();
|
||||
return this.model.update({
|
||||
_id: id
|
||||
}, data, { runValidators: true });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = statisMockModel;
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Created by gxl.gao on 2017/10/25.
|
||||
*/
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'
|
||||
// const data = [
|
||||
// { name: 'Page A', pv: 2400 },
|
||||
// { name: 'Page B', pv: 1398 },
|
||||
// { name: 'Page C', pv: 9800 },
|
||||
// { name: 'Page D', pv: 3908 },
|
||||
// { name: 'Page E', pv: 4800 },
|
||||
// { name: 'Page F', pv: 3800 },
|
||||
// { name: 'Page G', pv: 4300 }
|
||||
// ];
|
||||
|
||||
class StatisChart extends Component {
|
||||
|
||||
static propTypes = {
|
||||
data: PropTypes.object
|
||||
}
|
||||
|
||||
render() {
|
||||
// console.log('date', this.props.date);
|
||||
const { data } = this.props;
|
||||
return (
|
||||
<div className="statis-select">
|
||||
我是一个图表
|
||||
<LineChart width={600} height={300} data={data.mockDateList}
|
||||
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
|
||||
<XAxis dataKey="_id" />
|
||||
<YAxis />
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="count" stroke="#8884d8" activeDot={{ r: 8 }} />
|
||||
{/* <Line type="monotone" dataKey="uv" stroke="#82ca9d" /> */}
|
||||
</LineChart>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StatisChart;
|
@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Created by gxl.gao on 2017/10/25.
|
||||
*/
|
||||
import React, {Component} from 'react'
|
||||
|
||||
class StatisSelect extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="statis-select">
|
||||
我是一个select
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default StatisSelect;
|
@ -1,47 +1,57 @@
|
||||
/**
|
||||
* Created by gxl.gao on 2017/10/25.
|
||||
*/
|
||||
import React, {Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import React, { Component } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import axios from 'axios'
|
||||
import PropTypes from 'prop-types'
|
||||
import './index.scss'
|
||||
// import { withRouter } from 'react-router-dom';
|
||||
import {Row, Col, Tooltip, Icon} from 'antd';
|
||||
import {setBreadcrumb} from '../../../client/reducer/modules/user';
|
||||
import StatisSelect from './StatisSelect';
|
||||
import { Row, Col, Tooltip, Icon } from 'antd';
|
||||
import { setBreadcrumb } from 'client/reducer/modules/user';
|
||||
import StatisChart from './StatisChart';
|
||||
|
||||
const CountOverview = (props) => (
|
||||
<Row type="flex" justify="space-start" className="m-row">
|
||||
<Col className="gutter-row" span={8}>
|
||||
<Col className="gutter-row" span={6}>
|
||||
<span>
|
||||
分组总数
|
||||
<Tooltip placement="rightTop" title="统计yapi中一共开启了多少可见的公共分组">
|
||||
<Icon className="m-help" type="question-circle"/>
|
||||
<Icon className="m-help" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
<h2 className="gutter-box">{props.date.groupCount}</h2>
|
||||
|
||||
</Col>
|
||||
<Col className="gutter-row" span={8}>
|
||||
<Col className="gutter-row" span={6}>
|
||||
<span>
|
||||
项目总数
|
||||
<Tooltip placement="rightTop" title="统计yapi中建立的所有项目总数">
|
||||
<Icon className="m-help" type="question-circle"/>
|
||||
<Icon className="m-help" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
<h2 className="gutter-box">{props.date.projectCount}</h2>
|
||||
</Col>
|
||||
<Col className="gutter-row" span={8}>
|
||||
<Col className="gutter-row" span={6}>
|
||||
<span>
|
||||
接口总数
|
||||
<Tooltip placement="rightTop" title="统计yapi所有项目中的所有接口总数">
|
||||
{/*<a href="javascript:void(0)" className="m-a-help">?</a>*/}
|
||||
<Icon className="m-help" type="question-circle"/>
|
||||
<Icon className="m-help" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
<h2 className="gutter-box">{props.date.interfaceCount}</h2>
|
||||
</Col>
|
||||
<Col className="gutter-row" span={6}>
|
||||
<span>
|
||||
测试接口总数
|
||||
<Tooltip placement="rightTop" title="统计yapi所有项目中的所有测试接口总数">
|
||||
{/*<a href="javascript:void(0)" className="m-a-help">?</a>*/}
|
||||
<Icon className="m-help" type="question-circle" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
<h2 className="gutter-box">{props.date.interfaceCaseCount}</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
@ -68,37 +78,53 @@ class statisticsPage extends Component {
|
||||
count: {
|
||||
groupCount: 0,
|
||||
projectCount: 0,
|
||||
interfaceCount: 0
|
||||
interfaceCount: 0,
|
||||
interfactCaseCount: 0
|
||||
},
|
||||
chartDate: {
|
||||
mockCount: 0,
|
||||
mockDateList: []
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
this.props.setBreadcrumb([{name: '数据统计'}]);
|
||||
this.props.setBreadcrumb([{ name: '数据统计' }]);
|
||||
this.getStatisData();
|
||||
this.getMockData();
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
async getStatisData() {
|
||||
let result = await axios.get('/api/plugin/statismock/get');
|
||||
let result = await axios.get('/api/plugin/statismock/count');
|
||||
if (result.data.errcode === 0) {
|
||||
let statisData = result.data.data;
|
||||
this.setState({
|
||||
count: {...statisData}
|
||||
count: { ...statisData }
|
||||
});
|
||||
}
|
||||
}
|
||||
// 获取mock 请求次数信息
|
||||
async getMockData() {
|
||||
let result = await axios.get('/api/plugin/statismock/get');
|
||||
if (result.data.errcode === 0) {
|
||||
let mockStatisData = result.data.data;
|
||||
this.setState({
|
||||
chartDate: { ...mockStatisData }
|
||||
});
|
||||
|
||||
console.log('state', this.state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const {count} = this.state;
|
||||
const { count, chartDate } = this.state;
|
||||
// console.log('state', this.state)
|
||||
|
||||
return (
|
||||
<div className="g-statistic">
|
||||
<div className="statis-content">
|
||||
<CountOverview date={count}></CountOverview>
|
||||
<StatisSelect/>
|
||||
<StatisChart data={chartDate} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
139
exts/yapi-plugin-statistics/util.js
Normal file
139
exts/yapi-plugin-statistics/util.js
Normal file
@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 获取所需要的日期区间点
|
||||
* @param time {Number} Number是ele日期区间选择组件返回的结果
|
||||
* Number是之前时刻距离今天的间隔天数,默认是3天
|
||||
* @param start {String} 日期对象,日期区间的开始点 '2017-01-17 00:00:00'
|
||||
* @param withToday {Boolean} 是否包含今天
|
||||
* @return {Array} ['2017-01-17 00:00:00', '2017-01-20 23:59:59']
|
||||
*/
|
||||
exports.getDateRange = (time = 30, start = false, withToday = true) => {
|
||||
const gapTime = time * 24 * 3600 * 1000;
|
||||
if (!start) {
|
||||
// 没有规定start时间
|
||||
let endTime = getNowMidnightDate().getTime();
|
||||
if (!withToday) {
|
||||
endTime -= 86400000;
|
||||
}
|
||||
return [this.formatYMD(endTime - gapTime), this.formatYMD(endTime - 1000)];
|
||||
}
|
||||
const startTime = dateSpacialWithSafari(start);
|
||||
const endTime = startTime + (gapTime - 1000);
|
||||
return [start, this.formatYMD(endTime)];
|
||||
}
|
||||
|
||||
// 时间
|
||||
const convert2Decimal = num => (num > 9 ? num : `0${num}`)
|
||||
|
||||
/**
|
||||
* 获取距今天之前多少天的所有时间
|
||||
* @param time {Number} Number是ele日期区间选择组件返回的结果
|
||||
* Number是之前时刻距离今天的间隔天数,默认是30天
|
||||
* @return {Array} ['2017-01-17', '2017-01-28', '2017-10-29',...]
|
||||
*/
|
||||
|
||||
exports.getDateInterval = (time = 30) => {
|
||||
// const gapTime = time * 24 * 3600 * 1000;
|
||||
// 今天
|
||||
let endTime = new Date().getTime();
|
||||
let timeList = []
|
||||
for (let i = 0; i < time; i++) {
|
||||
const gapTime = i * 24 * 3600 * 1000;
|
||||
const time = this.formatYMD(endTime - gapTime);
|
||||
timeList.push(time);
|
||||
}
|
||||
return timeList;
|
||||
}
|
||||
|
||||
/**获取2017-10-27 00:00:00 和 2017-10-27 23:59:59的时间戳
|
||||
* @param date {String} "2017-10-27"
|
||||
* @return {Array} [ 1509033600000, 1509119999000 ]
|
||||
*/
|
||||
|
||||
exports.getTimeInterval = (date) => {
|
||||
const startTime = (getNowMidnightDate(date).getTime()-86400000)/1000;
|
||||
const endTime =(getNowMidnightDate(date).getTime()-1000)/1000;
|
||||
// console.log('start',formatDate(startTime))
|
||||
return [startTime, endTime];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间午夜0点的日期对象
|
||||
*/
|
||||
const getNowMidnightDate = (time) => {
|
||||
let date;
|
||||
if (time) {
|
||||
date = new Date(time);
|
||||
} else {
|
||||
date = new Date();
|
||||
}
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 年、月、日、时、分、秒
|
||||
* @param val {Object or String or Number} 日期对象 或是可new Date的对象或时间戳
|
||||
* @return {String} 2017-01-20 20:00:00
|
||||
*/
|
||||
const formatDate = val => {
|
||||
let date = val;
|
||||
if (typeof val !== 'object') {
|
||||
date = new Date(val);
|
||||
}
|
||||
return `${[
|
||||
date.getFullYear(),
|
||||
convert2Decimal(date.getMonth() + 1),
|
||||
convert2Decimal(date.getDate())
|
||||
].join('-')} ${[
|
||||
convert2Decimal(date.getHours()),
|
||||
convert2Decimal(date.getMinutes()),
|
||||
convert2Decimal(date.getSeconds())
|
||||
].join(':')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化年、月、日
|
||||
* @param val {Object or String or Number} 日期对象 或是可new Date的对象或时间戳
|
||||
* @return {String} 2017-01-20
|
||||
*/
|
||||
exports.formatYMD = (val, joinStr = '-') => {
|
||||
let date = val;
|
||||
if (typeof val !== 'object') {
|
||||
date = new Date(val);
|
||||
}
|
||||
return `${[
|
||||
date.getFullYear(),
|
||||
convert2Decimal(date.getMonth() + 1),
|
||||
convert2Decimal(date.getDate())
|
||||
].join(joinStr)}`;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取所需的时间差值,
|
||||
* tip:new Date('2017-01-17 00:00:00')在safari下不可用,需进行替换
|
||||
* @param Array ['2017-01-17 00:00:00', '2017-01-20 23:59:59']
|
||||
* @return {Number} 3
|
||||
*/
|
||||
exports.getDayGapFromRange = dateRange => {
|
||||
const startTime = dateSpacialWithSafari(dateRange[0]);
|
||||
const endTime = dateSpacialWithSafari(dateRange[1]);
|
||||
return Math.ceil((endTime - startTime) / 86400000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dateSpacialWithSafari 格式话safari下通用的格式
|
||||
* @param str {String} 2017-04-19T11:01:19.074+0800 or 2017-10-10 10:10:10
|
||||
* @return {number} date.getTime()
|
||||
*/
|
||||
const dateSpacialWithSafari = str => {
|
||||
if (str.indexOf('T') > -1) {
|
||||
let date;
|
||||
str.replace(/(\d{4})-(\d{2})-(\d{2})\w(\d{2}):(\d{2}):(\d{2})/, (match, p1, p2, p3, p4, p5, p6) => {
|
||||
date = new Date(p1, +p2 - 1, p3, p4, p5, p6);
|
||||
return;
|
||||
})
|
||||
return date.getTime();
|
||||
}
|
||||
return new Date(str.replace(/-/g, '/')).getTime();
|
||||
}
|
@ -97,6 +97,7 @@
|
||||
"react-scripts": "1.0.10",
|
||||
"reactabular-dnd": "^8.9.0",
|
||||
"reactabular-table": "^8.9.0",
|
||||
"recharts": "^1.0.0-beta.0",
|
||||
"redux": "^3.7.1",
|
||||
"redux-devtools": "^3.4.0",
|
||||
"redux-devtools-dock-monitor": "^1.1.2",
|
||||
|
@ -44,6 +44,11 @@ class interfaceCase extends baseModel {
|
||||
return m.save();
|
||||
}
|
||||
|
||||
//获取全部测试接口信息
|
||||
getInterfaceCaseListCount() {
|
||||
return this.model.count({});
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model.findOne({
|
||||
_id: id
|
||||
|
@ -37,7 +37,7 @@ var hooks = {
|
||||
* 客户端更新接口成功后触发
|
||||
* @param id 接口id
|
||||
*/
|
||||
'interface_update':{
|
||||
'interface_update': {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
@ -45,7 +45,7 @@ var hooks = {
|
||||
* 客户端获取接口数据列表
|
||||
* @param id project_id
|
||||
*/
|
||||
'interface_list':{
|
||||
'interface_list': {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
@ -53,7 +53,7 @@ var hooks = {
|
||||
* 客户端获取一条接口信息触发
|
||||
* @param id 接口id
|
||||
*/
|
||||
'interface_get':{
|
||||
'interface_get': {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
@ -61,7 +61,7 @@ var hooks = {
|
||||
* 客户端增加一个新项目
|
||||
* @param id 项目id
|
||||
*/
|
||||
'project_add':{
|
||||
'project_add': {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
@ -69,7 +69,7 @@ var hooks = {
|
||||
* 客户端删除删除一个项目
|
||||
* @param id 项目id
|
||||
*/
|
||||
'project_del':{
|
||||
'project_del': {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
@ -131,12 +131,12 @@ function emitHook(name) {
|
||||
if (hooks[name] && typeof hooks[name] === 'object') {
|
||||
let args = Array.prototype.slice.call(arguments, 1);
|
||||
if (hooks[name].type === 'single' && typeof hooks[name].listener === 'function') {
|
||||
return Promise.resolve(hooks[name].listener.apply(yapi, args));
|
||||
return Promise.resolve(hooks[name].listener.apply(yapi, args));
|
||||
}
|
||||
let promiseAll = [];
|
||||
if (Array.isArray(hooks[name].listener)) {
|
||||
let listenerList = hooks[name].listener;
|
||||
for(let i=0, l = listenerList.length; i< l; i++){
|
||||
for (let i = 0, l = listenerList.length; i < l; i++) {
|
||||
promiseAll.push(Promise.resolve(listenerList[i].apply(yapi, args)));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user