feat: 文档导出

test
bigfengyu 5 years ago
parent b086c486da
commit 80267598f5

@ -29,6 +29,7 @@
"@types/json5": "^0.0.30",
"animate.css": "3.7.2",
"awesome-debounce-promise": "^2.1.0",
"buc-client": "^3.12.5",
"chart.js": "^2.8.0",
"classnames": "^2.2.6",
"clipboard-copy": "^3.1.0",

@ -40,11 +40,3 @@ export const fetchJoinedRepositoryList = (
) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH', user, name })
export const fetchJoinedRepositoryListSucceeded = (repositories: any) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_SUCCEEDED', repositories })
export const fetchJoinedRepositoryListFailed = (message: any) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_FAILED', message })
export const fetchForeignInterfaces = ({ id, itf } = {id: '', itf: ''}) => ({ type: 'FOREIGN_INTERFACE_FETCH', id, itf })
export const fetchForeignInterfacesSuccessed = (data: any) => ({ type: 'FOREIGN_INTERFACE_FETCH_SUCCEEDED', data })
export const fetchForeignInterfacesFailed = (message: any) => ({ type: 'FOREIGN_INTERFACE_FETCH_FAILED', message })
export const addForeignRoomCase = ({ id, itf, name } = { id: '', itf: '', name: ''}) => ({ type: 'ADD_FOREIGN_ROOM_CASE', id, itf, name })
export const addForeignRoomCaseSuccessed = ({ id, itf } = { id: '', itf: ''}) => ({ type: 'ADD_FOREIGN_ROOM_CASE_SUCCEEDED', id, itf })
export const addForeignRoomCaseFailed = ({ id, itf, message }: any) => ({ type: 'ADD_FOREIGN_ROOM_CASE_FAILED', id, itf, message })

@ -15,7 +15,6 @@ export interface RootState {
counter: any
logs: any
foreign: any
loading: boolean
message: IMessage
}
@ -56,6 +55,13 @@ export interface INumItem {
label: string
}
export interface IConfig {
serve: string
keys: string[]
session: {
key: string
}
}
export interface Repository {
id: number

@ -21,11 +21,17 @@ type Props = {
// 模拟数据
const mockUser = () =>
Mock.mock({
fullname: '@CNAME',
email: '@email',
password: '@string(6)',
})
process.env.NODE_ENV === 'development'
? Mock.mock({
fullname: '@CNAME',
email: '@email',
password: '@string(6)',
})
: {
fullname: '',
email: '',
password: '',
}
// 展示组件
class RegisterForm extends Component<Props, State> {

@ -12,7 +12,6 @@ import { METHODS, STATUS_LIST } from './InterfaceForm'
import { CopyToClipboard } from '../utils/'
import { getRelativeUrl } from '../../utils/URLUtils'
import './InterfaceSummary.css'
import { RootState } from 'actions/types'
import { showMessage, MSG_TYPE } from 'actions/common'
import {
TextField,
@ -109,7 +108,6 @@ class InterfaceSummary extends Component<
InterfaceSummaryState
> {
static contextTypes = {
// onAddForeignRoomCase: PropTypes.func.isRequired,
onDeleteInterface: PropTypes.func.isRequired,
}
constructor(props: any) {
@ -490,20 +488,8 @@ class InterfaceSummary extends Component<
}
}
handleUpdate = () => { /** empty */ }
// createCase = (e: any, repositoryId: any, interfaceId: any, name: any) => {
// e.preventDefault()
// this.setState({ name })
// const { onAddForeignRoomCase } = this.context
// onAddForeignRoomCase({
// id: repositoryId,
// itf: interfaceId,
// name,
// })
// // itf.repositoryId
// }
}
const mapStateToProps = (state: RootState) => ({
room: state.foreign,
const mapStateToProps = () => ({
})
const mapDispatchToProps = {
replace,

@ -84,15 +84,17 @@ interface ModuleListProps {
mod?: Module
repository: Repository
}
function ModuleList(props: ModuleListProps, context: any) {
function ModuleList(props: ModuleListProps) {
const [open, setOpen] = useState(false)
const dispatch = useDispatch()
const auth = useSelector((state: RootState) => state.auth)
const { repository, mods = [], mod } = props
const isOwned = repository.owner!.id === auth.id
const isJoined = repository.members!.find((item: any) => item.id === auth.id)
const handleSort = (_: any, sortable: any) => {
const { onSortModuleList } = context
onSortModuleList(sortable.toArray())
dispatch(sortModuleList(sortable.toArray(), () => {
/** empty */
}))
}
return (
<RSortable onChange={handleSort} disabled={!isOwned && !isJoined}>
@ -136,7 +138,6 @@ const mapStateToProps = (state: RootState) => ({
})
const mapDispatchToProps = ({
replace,
onSortModuleList: sortModuleList,
})
export default connect(
mapStateToProps,

@ -1,7 +1,7 @@
import React, { Component } from 'react'
import { PropTypes, connect, Link, replace, _ } from '../../family'
import { serve } from '../../relatives/services/constant'
import { RModal, Spin } from '../utils'
import { Spin } from '../utils'
import RepositoryForm from '../repository/RepositoryForm'
import RepositorySearcher from './RepositorySearcher'
import ModuleList from './ModuleList'
@ -25,7 +25,7 @@ import {
updateInterface,
deleteInterface,
lockInterface,
unlockInterface,
unlockInterface
} from '../../actions/interface'
import {
addProperty,
@ -39,7 +39,6 @@ import {
GoPlug,
GoDatabase,
GoJersey,
GoChecklist,
GoLinkExternal,
GoPencil
} from 'react-icons/go'
@ -100,7 +99,6 @@ class RepositoryEditor extends Component<any, any> {
render() {
const {
location: { params },
room,
auth,
} = this.props
let { repository } = this.props
@ -161,17 +159,15 @@ class RepositoryEditor extends Component<any, any> {
<GoPencil />
</span>
) : null}
<RModal when={this.state.update} onResolve={this.handleUpdate}>
<RepositoryForm
open={this.state.update}
onClose={ok => {
ok && this.handleUpdate()
this.setState({ update: false })
}}
title="编辑仓库"
repository={repository}
/>
</RModal>
<RepositoryForm
open={this.state.update}
onClose={ok => {
ok && this.handleUpdate()
this.setState({ update: false })
}}
title="编辑仓库"
repository={repository}
/>
<a
href={`${serve}/app/plugin/${repository.id}`}
target="_blank"
@ -196,31 +192,19 @@ class RepositoryEditor extends Component<any, any> {
>
<GoJersey />
</a>
{room &&
room[repository.id] &&
typeof room[repository.id].coverage !== 'undefined' && (
<a
href={`http://room.daily.taobao.net/index.html#/detail?projectId=${room[repository.id].roomProjectId}`}
target="_blank"
rel="noopener noreferrer"
>
<GoChecklist /> Room:{' '}
{Math.round(room[repository.id].coverage * 100)}%
</a>
)}
<span
className="fake-link edit"
onClick={() => this.setState({ exportPostman: true })}
>
<GoLinkExternal /> Postman Collection
<GoLinkExternal />
</span>
<RModal
when={this.state.exportPostman}
<ExportPostmanForm
title="导出"
open={this.state.exportPostman}
repoId={repository.id}
onClose={() => this.setState({ exportPostman: false })}
onResolve={() => this.setState({ exportPostman: false })}
>
<ExportPostmanForm title="导出到Postman" repoId={repository.id} />
</RModal>
/>
</div>
<RepositorySearcher repository={repository} />
<div className="desc">{repository.description}</div>

@ -1,70 +1,94 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { RParsley } from '../utils'
import React from 'react'
import config from '../../config'
import {
Button,
Dialog,
DialogTitle,
DialogContent,
Slide
} from '@material-ui/core'
import { TransitionProps } from '@material-ui/core/transitions/transition'
class ExportPostmanForm extends Component<any, any> {
static contextTypes = {
rmodal: PropTypes.object.isRequired,
}
static propTypes = {
repoId: PropTypes.number,
}
rparsley: any
render() {
const { rmodal } = this.context
const { repoId } = this.props
return (
<section className="RepositoryForm">
<div className="rmodal-header">
<span className="rmodal-title">{this.props.title}</span>
</div>
<RParsley ref={rparsley => { this.rparsley = rparsley }}>
<form className="form-horizontal" onSubmit={() => false} >
<div className="rmodal-body">
<div>
PostmanImportImport From Link
</div>
<div className="alert alert-info" role="alert" style={{ margin: '8px 0' }}> {config.serve}/postman/export?id={repoId} </div>
const Transition = React.forwardRef<unknown, TransitionProps>((props, ref) => {
return <Slide direction="up" ref={ref} {...props} />
})
export default function ExportPostmanForm(props: {
repoId: number;
open: boolean;
onClose: () => void;
title: string;
}) {
const { repoId, open, onClose, title } = props
const markdownLink = `${config.serve}/export/markdown?id=${repoId}&origin=${window.location.origin}`
const docxLink = `${config.serve}/export/docx?id=${repoId}&origin=${window.location.origin}`
// const pdfLink = `${config.serve}/export/pdf?id=${repoId}&origin=${window.location.origin}`
return (
<Dialog
open={open}
onClose={() => onClose()}
TransitionComponent={Transition}
>
<DialogTitle>{title}</DialogTitle>
<DialogContent dividers={true}>
<form className="form-horizontal" onSubmit={() => false}>
<div>
<div>
Postman: </div>
<div
className="alert alert-info"
role="alert"
style={{ margin: '8px 0' }}
>
{' '}
{config.serve}/export/postman?id={repoId}{' '}
</div>
<div> Postman ImportImport
From Link</div>
</div>
<div>
<div>Markdown:</div>
<div
className="alert alert-info"
role="alert"
style={{ margin: '8px 0' }}
>
<a href={markdownLink} target="_blank">
{markdownLink}
</a>
</div>
<div className="rmodal-footer">
<div className="form-group row mb0">
<label className="col-sm-2 control-label" />
<div className="col-sm-10">
<button onClick={e => { e.preventDefault(); rmodal.close() }} className="mr10 btn btn-secondary"></button>
</div>
</div>
</div>
<div>
<div>Docx:</div>
<div
className="alert alert-info"
role="alert"
style={{ margin: '8px 0' }}
>
<a href={docxLink}>{docxLink}</a>
</div>
</form>
</RParsley>
</section>
)
}
componentDidUpdate() {
this.context.rmodal.reposition()
}
handleSubmit = (e: any) => {
e.preventDefault()
</div>
if (!this.rparsley.isValid()) { return }
{/* <div>
<div>PDF:</div>
<div
className="alert alert-info"
role="alert"
style={{ margin: '8px 0' }}
>
<a href={pdfLink}>{pdfLink}</a>
</div>
</div> */}
const { onAddRepository, onUpdateRepository } = this.context
const onAddOrUpdateRepository = this.state.id ? onUpdateRepository : onAddRepository
const { organization } = this.props
const repository: any = {
...this.state,
// ownerId: owner.id, // DONE 2.2 支持转移仓库
organizationId: organization ? organization.id : null,
memberIds: (this.state.members || []).map((user: any) => user.id),
}
const { owner, newOwner } = this.state
if (newOwner && newOwner.id !== owner.id) { repository.ownerId = newOwner.id }
const { rmodal } = this.context
rmodal.close()
onAddOrUpdateRepository(repository, () => {
if (rmodal) { rmodal.resolve() }
})
}
<div className="mt10">
<Button variant="outlined" onClick={onClose}>
</Button>
</div>
</form>
</DialogContent>
</Dialog>
)
}
export default ExportPostmanForm

@ -401,39 +401,6 @@ export default {
return state
}
},
foreign(state: any = null, action: any) {
switch (action.type) {
case 'FOREIGN_INTERFACE_FETCH':
return {
...state,
[action.itf ? action.id + '_' + action.itf : action.id]: 'pending',
}
case 'FOREIGN_INTERFACE_FETCH_SUCCEEDED':
return {
...state,
[action.data.interfaceId
? action.data.repositoryId + '_' + action.data.interfaceId
: action.data.repositoryId]: action.data,
}
case 'ADD_FOREIGN_ROOM_CASE':
return {
...state,
['+' + action.id + '_' + action.itf]: 'pending',
}
case 'ADD_FOREIGN_ROOM_CASE_SUCCEEDED':
return {
...state,
['+' + action.id + '_' + action.itf]: 'success',
}
case 'ADD_FOREIGN_ROOM_CASE_FAILED':
return {
...state,
['+' + action.id + '_' + action.itf]: 'failed',
}
default:
return state
}
},
},
sagas: {
[RepositoryAction.addRepository(undefined, undefined).type]:

@ -1,120 +0,0 @@
import { CREDENTIALS, serve } from './constant'
export default {
getInterfaces({ id, itf }: any) {
let roomProjectId: any
let hostname: any
let interfaces: any[] = []
const caseMap: any = {}
const roomCases = new Set()
return fetch(`${serve}/foreign/room?repositoryId=${id}`,
{ ...CREDENTIALS }
)
.then(res => res.json())
.then(json => {
if (json.error) {
throw new Error('接口请求失败: ' + json.error)
}
roomProjectId = json.data.roomProjectId
interfaces = json.data.interfaces
hostname = json.data.hostname
if (itf) {
interfaces = interfaces.filter(({ id }) => id === +itf)
}
return requestRoom('module/moduledetail?projectId=' + roomProjectId, undefined)
})
.then(roomCaseBatches => {
roomCaseBatches.reduce(
(prev: any, curr: any) => prev.concat(curr.cases), []
).filter((item: any) => !!item).forEach((theCase: any) => {
const anchor = document.createElement('a')
anchor.href = theCase.path
const { pathname } = anchor
if (!caseMap[pathname]) {
caseMap[pathname] = []
}
caseMap[pathname].push(theCase.caseId)
roomCases.add(caseMap[pathname])
})
const arr = interfaces.map( (item: any) => {
const { url } = item
const newItem: any = { url }
newItem.id = item.id
newItem.cases = item.url in caseMap ? caseMap[item.url] : []
return newItem
})
return {
list: arr,
coverage: arr.length && arr.reduce((prev, curr) => prev + (!!curr.cases.length), 0) / arr.length,
hostname,
roomProjectId,
repositoryId: id,
interfaceId: itf,
}
})
},
addForeignRoomCase({ id, itf, name }: any) {
let module
let cases: any = []
return fetch(`${serve}/foreign/room/params?repositoryId=${id}&interfaceId=${itf}&name=${encodeURIComponent(name)}`)
.then(res => res.json())
.then(json => {
if (json.error) {
throw new Error('接口请求失败: ' + json.error)
}
module = json.data.module
cases = json.data.cases
return requestRoom('module', module, 'post')
})
.then(moduleId => {
cases.forEach((theCase: any) => {
theCase.moduleId = moduleId
})
return Promise.all(cases.map((theCase: any) => requestRoom('case', theCase, 'post')))
})
},
}
async function requestRoom(path: any, body: any, method: any = 'get') {
if (typeof body === 'object' && method === 'get') {
path = path + '?' + formatKV(body)
body = null
}
if (path[0] !== '/') {
path = '/' + path
}
const resp = await fetch('http://room.daily.taobao.net/api' + path, {
method: method,
body: body && formatKV(body),
// @ts-ignore
headers: new window.Headers({
'Content-Type': 'application/x-www-form-urlencoded',
}),
})
const { status } = resp
if (status !== 200) {
throw new Error(`Request Failed for ${path}\nStatus Code: ${status}`)
}
const json = await resp.json()
if (!json || !json.info || !json.info.ok) {
throw new Error('Error from room remote: ' + json.info.message)
}
return (json && json.data && json.data.result) || json.info.message
function formatKV(obj: any) {
let str = ''
for (const key of Object.keys(obj)) {
str && (str += '&')
str += key + '=' + encodeURIComponent(obj[key])
}
return str
}
}
Loading…
Cancel
Save