mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
4.2.0
This commit is contained in:
@@ -155,10 +155,6 @@ export default {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
uri: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
urlProd: {
|
||||
type: String,
|
||||
default: ''
|
||||
|
@@ -1,31 +1,61 @@
|
||||
<template>
|
||||
<div class="doc-debug">
|
||||
<h2>{{ docInfo.summary }}</h2>
|
||||
<el-table
|
||||
:data="[{ methodLabel: '接口名(method)', methodValue: docInfo.name, versionLabel: '版本号(version)', versionValue: docInfo.version }]"
|
||||
border
|
||||
:cell-style="baseInfoCellStyle"
|
||||
:show-header="false"
|
||||
<el-form
|
||||
ref="configForm"
|
||||
size="mini"
|
||||
:model="configFormData"
|
||||
:rules="configFormRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-table-column prop="methodLabel" align="center" width="130">
|
||||
<template slot-scope="scope"><span class="api-info">{{ scope.row.methodLabel }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="methodValue" />
|
||||
<el-table-column prop="versionLabel" align="center" width="130">
|
||||
<template slot-scope="scope"><span class="api-info">{{ scope.row.versionLabel }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="versionValue" width="120" />
|
||||
</el-table>
|
||||
<h3>接口描述</h3>
|
||||
<div class="doc-overview">{{ docInfo.description || docInfo.title }}</div>
|
||||
<h3>请求方法</h3>
|
||||
<div class="doc-request-method">
|
||||
{{ docInfo.httpMethodList && docInfo.httpMethodList.join(' / ').toUpperCase() }}
|
||||
</div>
|
||||
<el-form-item prop="url" label="网关地址">
|
||||
<el-input v-model="configFormData.url" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="appKey" label="AppId">
|
||||
<el-input v-model="configFormData.appKey" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="privateKey" label="应用私钥">
|
||||
<el-input v-model="configFormData.privateKey" clearable @change="onPrivateKeyChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="token">
|
||||
<el-input v-model="configFormData.token" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<h2>请求参数</h2>
|
||||
<parameter-table :data="docInfo.requestParameters" :editable="true" />
|
||||
<parameter-table-edit ref="paramTableRef" :data="docInfo.requestParameters" />
|
||||
<!-- 多文件选择 -->
|
||||
<el-upload
|
||||
v-show="docInfo.multiple"
|
||||
action=""
|
||||
:multiple="true"
|
||||
:auto-upload="false"
|
||||
style="width: 500px;margin-top: 10px"
|
||||
:on-remove="(file, fileList) => onSelectMultiFile(file, fileList)"
|
||||
:on-change="(file, fileList) => onSelectMultiFile(file, fileList)"
|
||||
>
|
||||
<el-button slot="trigger" type="primary" size="mini">上传多个文件</el-button>
|
||||
</el-upload>
|
||||
<br/>
|
||||
<el-form
|
||||
size="mini"
|
||||
>
|
||||
<el-form-item label="HttpMethod">
|
||||
<el-radio-group v-model="httpMethod">
|
||||
<el-radio v-for="method in docInfo.httpMethodList" :key="method" :label="method">{{ method.toUpperCase() }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button type="primary" @click="send">发送请求</el-button>
|
||||
<el-tabs v-show="resultShow">
|
||||
<el-tabs v-model="resultActive" type="card">
|
||||
<el-tab-pane label="请求信息" name="reqInfo">
|
||||
<el-input v-model="reqInfo" type="textarea" :rows="10" readonly />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="请求结果" name="resultContent">
|
||||
<el-input v-model="resultContent" type="textarea" :rows="16" readonly />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
@@ -36,20 +66,21 @@
|
||||
.doc-debug .cell .el-form-item {margin-bottom: 0;}
|
||||
</style>
|
||||
<script>
|
||||
import ParameterTable from '@/components/ParameterTable'
|
||||
import ParameterTableEdit from '@/components/ParameterTableEdit'
|
||||
const privateKeyStoreKey = 'sop.sendbox.privateKey'
|
||||
export default {
|
||||
name: 'Docdebug',
|
||||
components: { ParameterTable },
|
||||
components: { ParameterTableEdit },
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
uri: {
|
||||
appId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
urlProd: {
|
||||
gatewayUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
@@ -63,31 +94,192 @@ export default {
|
||||
return { padding: '5px 0' }
|
||||
}
|
||||
},
|
||||
configFormRules: {
|
||||
appKey: [
|
||||
{ required: true, message: '请填写AppId', trigger: 'blur' }
|
||||
],
|
||||
privateKey: [
|
||||
{ required: true, message: '请填写应用私钥', trigger: 'blur' }
|
||||
],
|
||||
url: [
|
||||
{ required: true, message: '请填写URL', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
configFormData: {
|
||||
url: '',
|
||||
appKey: '',
|
||||
privateKey: '',
|
||||
token: ''
|
||||
},
|
||||
httpMethod: '',
|
||||
docInfo: {
|
||||
summary: '',
|
||||
name: '',
|
||||
version: '',
|
||||
multiple: false,
|
||||
uploadRequest: false,
|
||||
httpMethodList: [],
|
||||
requestParameters: [],
|
||||
responseParameters: [],
|
||||
bizCodes: []
|
||||
}
|
||||
},
|
||||
uploadFiles: [],
|
||||
resultActive: 'resultContent',
|
||||
resultShow: false,
|
||||
reqInfo: '',
|
||||
resultContent: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
item(newVal) {
|
||||
this.initItem(newVal)
|
||||
},
|
||||
appId(newVal) {
|
||||
this.configFormData.appKey = newVal
|
||||
},
|
||||
gatewayUrl(url) {
|
||||
this.configFormData.url = url
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const privateKey = this.getAttr(privateKeyStoreKey)
|
||||
if (privateKey) {
|
||||
this.configFormData.privateKey = privateKey
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
send() {
|
||||
|
||||
this.$refs.configForm.validate(valid => {
|
||||
if (valid) {
|
||||
// 验证表格参数
|
||||
const promiseRequestArr = this.validateTable(this.docInfo.requestParameters, ['req_form_example_'])
|
||||
Promise.all(promiseRequestArr).then(validArr => {
|
||||
// 到这里来表示全部内容校验通过
|
||||
this.doSend()
|
||||
}).catch((e) => {
|
||||
// this.tipError('请完善表单内容')
|
||||
}) // 加上这个控制台不会报Uncaught (in promise)
|
||||
}
|
||||
})
|
||||
},
|
||||
doSend() {
|
||||
const bizContent = this.buildParamData(this.docInfo.requestParameters)
|
||||
const data = {
|
||||
gatewayUrl: this.configFormData.url,
|
||||
appId: this.configFormData.appKey,
|
||||
privateKey: this.configFormData.privateKey,
|
||||
token: this.configFormData.token,
|
||||
method: this.docInfo.name,
|
||||
version: this.docInfo.version,
|
||||
httpMethod: this.httpMethod,
|
||||
bizContent: JSON.stringify(bizContent)
|
||||
}
|
||||
const files = this.buildFiles(this.docInfo.requestParameters)
|
||||
const isForm = this.httpMethod.toUpperCase() === 'POST'
|
||||
this.request(this.httpMethod, '/sandbox/test_v2', data, {}, false, isForm, files, function(error, response) {
|
||||
this.resultShow = true
|
||||
this.resultActive = 'resultContent'
|
||||
const status = response.statusCode || response.status
|
||||
if (!error && status === 200) {
|
||||
this.successHandler(response)
|
||||
} else {
|
||||
console.log(error)
|
||||
this.$message.error('请求异常,请查看日志')
|
||||
}
|
||||
})
|
||||
},
|
||||
validateTable: function(arr, refPrefixArr) {
|
||||
const $refs = this.$refs.paramTableRef.$refs
|
||||
let promiseArr = []
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const row = arr[i]
|
||||
const id = row.id
|
||||
refPrefixArr.forEach(refPrefix => {
|
||||
const ref = $refs[refPrefix + id]
|
||||
if (ref) {
|
||||
promiseArr.push(ref.validate())
|
||||
}
|
||||
})
|
||||
const children = arr[i].children
|
||||
if (children && children.length > 0) {
|
||||
const childrenPromiseArr = this.validateTable(children, refPrefixArr)
|
||||
promiseArr = promiseArr.concat(childrenPromiseArr)
|
||||
}
|
||||
}
|
||||
return promiseArr
|
||||
},
|
||||
successHandler(response) {
|
||||
this.setReqInfo(response)
|
||||
this.setRespInfo(response)
|
||||
},
|
||||
setReqInfo(response) {
|
||||
const headers = response.headers
|
||||
if (headers) {
|
||||
const html = []
|
||||
html.push('【请求参数】:' + decodeURIComponent(headers['sendbox-params']))
|
||||
html.push('【待签名内容】:' + decodeURIComponent(headers['sendbox-beforesign']))
|
||||
html.push('【签名(sign)】:' + decodeURIComponent(headers['sendbox-sign']))
|
||||
this.reqInfo = html.join('\r\n')
|
||||
}
|
||||
},
|
||||
setRespInfo(response) {
|
||||
const headers = response.headers
|
||||
const targetHeadersString = headers['target-response-headers'] || '{}'
|
||||
const targetHeaders = JSON.parse(targetHeadersString)
|
||||
const contentType = targetHeaders['content-type'] || ''
|
||||
const contentDisposition = targetHeaders['content-disposition'] || ''
|
||||
if (contentType.indexOf('stream') > -1 || contentDisposition.indexOf('attachment') > -1) {
|
||||
const filename = this.getDispositionFilename(contentDisposition)
|
||||
this.downloadFile(filename, response.raw)
|
||||
} else {
|
||||
const body = response.body || response.data
|
||||
this.resultContent = JSON.stringify(body, null, 4)
|
||||
}
|
||||
},
|
||||
downloadFile(filename, buffer) {
|
||||
const url = window.URL.createObjectURL(new Blob([buffer]))
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', filename)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
},
|
||||
getDispositionFilename(disposition) {
|
||||
const dispositionArr = disposition.split(';')
|
||||
for (let i = 0; i < dispositionArr.length; i++) {
|
||||
const item = dispositionArr[i].trim()
|
||||
// filename="xx"
|
||||
if (item.toLowerCase().startsWith('filename')) {
|
||||
const result = item.match(new RegExp('filename="(.*?)"', 'i'))
|
||||
return result ? result[1] : ''
|
||||
}
|
||||
}
|
||||
},
|
||||
resetResult() {
|
||||
this.reqInfo = ''
|
||||
this.resultContent = ''
|
||||
this.resultShow = false
|
||||
},
|
||||
initItem(item) {
|
||||
this.setData(item)
|
||||
if (item) {
|
||||
this.setData(item)
|
||||
}
|
||||
},
|
||||
setData: function(data) {
|
||||
this.resetResult()
|
||||
this.docInfo = data
|
||||
this.httpMethod = this.docInfo.httpMethodList[0]
|
||||
},
|
||||
onSelectMultiFile(file, fileList) {
|
||||
const files = []
|
||||
fileList.forEach(file => {
|
||||
const rawFile = file.raw
|
||||
files.push(rawFile)
|
||||
})
|
||||
this.uploadFiles = files
|
||||
},
|
||||
onPrivateKeyChange() {
|
||||
this.setAttr(privateKeyStoreKey, this.configFormData.privateKey)
|
||||
},
|
||||
buildParamData: function(params) {
|
||||
const responseJson = {}
|
||||
@@ -117,6 +309,22 @@ export default {
|
||||
return Object.values(responseJson)[0]
|
||||
}
|
||||
return responseJson
|
||||
},
|
||||
buildFiles(params) {
|
||||
const files = []
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
const row = params[i]
|
||||
// 处理文件上传
|
||||
const fileConfig = row.__file__
|
||||
if (fileConfig) {
|
||||
files.push(fileConfig)
|
||||
}
|
||||
}
|
||||
// 全局上传
|
||||
if (this.uploadFiles.length > 0) {
|
||||
files.push({ name: 'file', files: this.uploadFiles })
|
||||
}
|
||||
return files
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -50,16 +50,11 @@
|
||||
label="示例值"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<div v-if="editable">
|
||||
|
||||
<div v-if="scope.row.type === 'enum'">
|
||||
{{ (scope.row.enums || []).join('、') }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="scope.row.type === 'enum'">
|
||||
{{ (scope.row.enums || []).join('、') }}
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ scope.row.paramExample }}
|
||||
</div>
|
||||
{{ scope.row.paramExample }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -74,10 +69,6 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tree: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
|
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<el-table
|
||||
:data="data"
|
||||
border
|
||||
row-key="id"
|
||||
default-expand-all
|
||||
:tree-props="{ children: 'refs', hasChildren: 'hasChildren' }"
|
||||
:cell-style="cellStyleSmall()"
|
||||
:header-cell-style="headCellStyleSmall()"
|
||||
empty-text="无参数"
|
||||
>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="名称"
|
||||
width="250"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span :class="{ 'required': scope.row.required}">{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="type"
|
||||
label="类型"
|
||||
width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.type }}</span>
|
||||
<span v-show="scope.row.type === 'array' && scope.row.elementType">
|
||||
<el-tooltip effect="dark" :content="`元素类型:${scope.row.elementType}`" placement="top">
|
||||
<i class="el-icon-info"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="paramExample"
|
||||
label="参数值"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-form
|
||||
v-if="scope.row.type !== 'object'"
|
||||
:ref="'req_form_example_' + scope.row.id"
|
||||
:model="scope.row"
|
||||
:rules="buildParamRules(scope.row)"
|
||||
size="mini"
|
||||
style="display: inline-block"
|
||||
>
|
||||
<el-form-item
|
||||
prop="paramExample"
|
||||
label-width="0"
|
||||
class="table-control"
|
||||
>
|
||||
<el-upload
|
||||
v-if="scope.row.type === 'file' || scope.row.elementType === 'file'"
|
||||
action=""
|
||||
:multiple="false"
|
||||
:auto-upload="false"
|
||||
:on-change="(file, fileList) => onSelectFile(file, fileList, scope.row)"
|
||||
:on-remove="(file, fileList) => onSelectFile(file, fileList, scope.row)"
|
||||
>
|
||||
<el-button slot="trigger" class="choose-file" type="primary">选择文件</el-button>
|
||||
</el-upload>
|
||||
<el-input v-else v-model="scope.row.paramExample" placeholder="参数值" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="description"
|
||||
label="描述"
|
||||
/>
|
||||
</el-table>
|
||||
</template>
|
||||
<style>
|
||||
.table-control .el-form-item__error {
|
||||
position: inherit;
|
||||
}
|
||||
span.required:before {
|
||||
content: '*';
|
||||
color: #F56C6C;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
name: 'ParameterTableEdit',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
tree: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buildParamRules(row) {
|
||||
const rules = []
|
||||
if (row.required && row.type !== 'file') {
|
||||
rules.push({ required: true, message: '请填写参数值', trigger: 'blur' })
|
||||
}
|
||||
const max = parseInt(row.maxLength)
|
||||
if (max) {
|
||||
rules.push({ max: max, message: `长度不超过 ${max} 个字符`, trigger: 'blur' })
|
||||
}
|
||||
return {
|
||||
paramExample: rules
|
||||
}
|
||||
},
|
||||
onSelectFile(f, fileList, row) {
|
||||
const files = []
|
||||
fileList.forEach(file => {
|
||||
const rawFile = file.raw
|
||||
files.push(rawFile)
|
||||
})
|
||||
row.__file__ = { name: row.name, files: files }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -5,9 +5,6 @@ Vue.use(Router)
|
||||
|
||||
/* Layout */
|
||||
import Layout from '@/layout'
|
||||
const _import = require('@/router/_import_' + process.env.NODE_ENV)
|
||||
|
||||
const menuKey = 'route-menus'
|
||||
|
||||
/**
|
||||
* Note: sub-menus only appear when route children.length >= 1
|
||||
|
@@ -55,6 +55,43 @@ Object.assign(Vue.prototype, {
|
||||
that.doResponse(error, response, callback, errorCallback)
|
||||
})
|
||||
},
|
||||
request(method, uri, data, headers, isJson, isForm, files, callback) {
|
||||
// 如果是文件上传,使用axios,needle上传文件不完美,不支持一个name对应多个文件
|
||||
if (files && files.length > 0) {
|
||||
this.doMultipart(uri, data, files, headers, callback)
|
||||
return
|
||||
}
|
||||
const that = this
|
||||
if (isForm) {
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||
}
|
||||
needle.request(method, baseURL + uri, data, {
|
||||
// 设置header
|
||||
headers: headers,
|
||||
json: isJson
|
||||
}, (error, response) => {
|
||||
callback.call(that, error, response)
|
||||
})
|
||||
},
|
||||
doMultipart(uri, data, files, headers, callback) {
|
||||
const that = this
|
||||
const formData = new FormData()
|
||||
files.forEach(fileConfig => {
|
||||
fileConfig.files.forEach(file => {
|
||||
formData.append(fileConfig.name, file)
|
||||
})
|
||||
})
|
||||
for (const name in data) {
|
||||
formData.append(name, data[name])
|
||||
}
|
||||
client.post(uri, formData, {
|
||||
headers: headers
|
||||
}).then(function(response) {
|
||||
callback.call(that, null, response)
|
||||
}).catch(function(error) {
|
||||
callback.call(that, error, null)
|
||||
})
|
||||
},
|
||||
doResponse(error, response, callback, errorCallback) {
|
||||
// 成功
|
||||
if (!error && response.statusCode === 200) {
|
||||
@@ -226,9 +263,6 @@ Object.assign(Vue.prototype, {
|
||||
goLogin() {
|
||||
removeToken()
|
||||
this.$router.replace({ path: `/login` })
|
||||
setTimeout(function() {
|
||||
location.reload()
|
||||
}, 200)
|
||||
},
|
||||
goRoute: function(path) {
|
||||
this.$router.push({ path: path })
|
||||
|
@@ -24,12 +24,25 @@
|
||||
/>
|
||||
</el-aside>
|
||||
<el-main style="padding-top: 0">
|
||||
<doc-view
|
||||
v-show="item"
|
||||
:item="item"
|
||||
:url-prod="docVO.urlProd"
|
||||
uri="/portal/isv/getDocItem"
|
||||
/>
|
||||
<el-tabs>
|
||||
<el-tabs v-show="item" v-model="active" type="card">
|
||||
<el-tab-pane name="info">
|
||||
<span slot="label"><i class="el-icon-document"></i> 接口信息</span>
|
||||
<doc-view
|
||||
:item="item"
|
||||
:url-prod="docVO.urlProd"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="debug">
|
||||
<span slot="label"><i class="el-icon-position"></i> 接口调试</span>
|
||||
<docdebug
|
||||
:item="item"
|
||||
:app-id="docVO.appId"
|
||||
:gateway-url="docVO.gatewayUrl"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-tabs>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
@@ -38,13 +51,16 @@
|
||||
<script>
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
import docView from '@/components/DocView'
|
||||
import docdebug from '@/components/Docdebug'
|
||||
|
||||
export default {
|
||||
components: { docView },
|
||||
components: { docView, docdebug },
|
||||
data() {
|
||||
return {
|
||||
active: 'info',
|
||||
docVO: {
|
||||
appId: '',
|
||||
gatewayUrl: '',
|
||||
urlProd: '',
|
||||
urlTest: '',
|
||||
menuProjects: []
|
||||
|
Reference in New Issue
Block a user