<template>
<div id="detail">
<DetailNavbar />
<div class="app-container">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="接口" name="interface">接口</el-tab-pane>
<el-tab-pane label="数据管理" name="data">
<el-form ref="dataForm" :model="dataForm" :rules="dataRules" label-width="100px" label-position="left">
<el-form-item>
<h3>数据导入</h3>
</el-form-item>
<el-form-item>
Swagger
</el-form-item>
<el-form-item>
<el-select ref="groupId" v-model="dataForm.groupId" style="width: 26%" placeholder="请选择所属分组">
<el-option
v-for="item in groupOptions"
:key="item.id"
:value="item.id"
:label="item.name"
/>
</el-select>
</el-form-item>
<el-form-item>
开启url导入
<el-tooltip class="item" effect="light" content="swagger url导入" placement="top">
<i class="el-icon-question" />
</el-tooltip>:
<el-switch
v-model="enableUrlImport"
style="margin-left: 20px;"
@change="changeEnable"
/>
</el-form-item>
<el-form-item>
<el-upload
v-show="!enableUrlImport"
style="width: 360px"
drag
action=""
:on-change="readFile"
>
<i class="el-icon-upload" />
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div slot="tip" class="el-upload__tip">只能上传Swagger文件,且不超过500kb</div>
</el-upload>
<el-input v-show="enableUrlImport" ref="url" v-model="dataForm.url" style="width: 26%" placeholder="http://demo.swagger.io/avatar-service/v2/api-docs" />
</el-form-item>
<el-form-item v-show="enableUrlImport">
<el-button type="primary" @click="importSwagger">上传</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="成员管理" name="members">成员管理</el-tab-pane>
<el-tab-pane label="设置" name="setting">
<div class="app-container">
<el-tabs v-model="settingActiveName" type="card" @tab-click="settingHandleClick">
<el-tab-pane label="项目配置" name="projectSetting">
<el-form ref="projectForm" :model="projectForm" :rules="projectRules" label-width="250px" label-position="right">
<el-form-item prop="id" label="项目ID">
{{ projectForm.id }}
</el-form-item>
<el-form-item prop="name" label="项目名称">
<el-input ref="name" v-model="projectForm.name" style="width: 95%" placeholder="请输入项目名称" />
</el-form-item>
<el-form-item ref="groupId" prop="groupId" label="所属分组">
<el-select v-model="projectForm.groupId" style="width: 95%" placeholder="请选择所属分组">
<el-option
v-for="item in groupOptions"
:key="item.id"
:value="item.id"
:label="item.name"
/>
</el-select>
</el-form-item>
<el-form-item prop="path" label="基本路径">
<label slot="label">
基本路径
<el-tooltip class="item" effect="light" content="接口基本路径,为空是根路径" placement="top">
<i class="el-icon-question" />
</el-tooltip>
</label>
<el-input ref="path" v-model="projectForm.path" style="width: 95%" placeholder="请输入基本路径" />
</el-form-item>
<el-form-item prop="description" label="描述">
<el-input
ref="description"
v-model="projectForm.description"
:autosize="{ minRows: 2, maxRows: 4}"
type="textarea"
style="width: 95%"
placeholder="请输入描述"
/>
</el-form-item>
<el-form-item prop="access" label="权限">
<el-radio v-model="projectForm.access" label="1">
<i class="el-icon-lock" style="font-weight: 700; color: #5d5d5d;" />
<span style="font-weight: 700; color: #5d5d5d;">私有</span>
</el-radio>
<br>
<span style="margin-left: 24px; color: #919191">只有组长和项目开发者可以索引并查看项目信息</span>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('projectForm')">保存</el-button>
<div style="margin-top: 50px;">
<h2><i class="el-icon-warning">危险操作</i></h2>
</div>
<el-button type="danger" plain @click="deleteProject()">删除</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="环境配置" name="envSetting">
<el-tabs v-model="editableTabsValue" type="card" editable @tab-click="tabClick" @edit="handleTabsEdit">
<el-tab-pane
v-for="item in enviromentList"
:key="item.id"
:label="item.title"
:name="item.name"
>
<el-form ref="dynamicValidateForm" :model="dynamicValidateForm" label-width="100px" class="demo-dynamic">
<el-form-item label="环境名称">
<el-input v-model="item.title" />
</el-form-item>
<el-form-item label="环境域名">
<el-select v-model="item.firstHost" style="width: 9%">
<el-option
v-for="host in hostOptions"
:key="host.key"
:value="host.value"
:label="host.label"
/>
</el-select>
<el-input v-model="item.lastHost" style="width: 90%" />
</el-form-item>
<div style="margin-left: 40px; margin-bottom: -25px;">Header</div>
<el-form-item v-for="(header, index) in dynamicValidateForm.header" :key="header.key" :prop="'header.' + index + '.value'">
<el-select v-model="names[index]" allow-create filterable clearable placeholder="请输入header名称">
<el-option v-for="option in options" :key="option.key" :label="option.label" :value="option.value" />
</el-select>
<el-input v-model="values[index]" placeholder="请输入参数内容" style="width: 30%;" clearable />
<el-button @click.prevent="removeDomain(header)">删除</el-button>
</el-form-item>
<el-form-item>
<el-button @click="addHeader">新增属性</el-button>
</el-form-item>
<div style="margin-left: 40px; margin-bottom: -25px;">Cookie</div>
<el-form-item v-for="(cookie, index) in dynamicValidateForm.cookies" :key="cookie.key" :prop="'cookie.' + index + '.value'">
<el-input v-model="cookieNames[index]" placeholder="请输入cookie Name" style="width: 30%;" clearable />
<el-input v-model="cookieValues[index]" placeholder="请输入参数内容" style="width: 30%;" clearable />
<el-button @click.prevent="removeCookie(cookie)">删除</el-button>
</el-form-item>
<el-form-item>
<el-button @click="addCookie">新增属性</el-button>
</el-form-item>
<el-form-item>
<el-button @click="saveEnv">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
</el-tabs>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import DetailNavbar from '@/views/components/DetailNavbar'
import { getProject, getEnvironmentList, addEnvironment, updateEnvironment, deleteEnvironment, importSwagger } from '@/api/project-manager'
import { getGroupList, updateProject, deleteProject } from '@/api/group-manager'
import { deepClone } from '@/utils'
import { trimStr } from '@/utils/validate'
const HTTP_REQUEST_HEADER = ['Accept', 'Accept-Charset', 'Accept-Encoding', 'Accept-Language', 'Accept-Datetime', 'Authorization', 'Cache-Control', 'Connection', 'Cookie', 'Content-Disposition', 'Content-Length', 'Content-MD5', 'Content-Type', 'Date', 'Expect', 'From', 'Host', 'If-Match', 'If-Modified-Since', 'If-None-Match', 'If-Range', 'If-Unmodified-Since', 'Max-Forwards', 'Origin', 'Pragma', 'Proxy-Authorization', 'Range', 'Referer', 'TE', 'User-Agent', 'Upgrade', 'Via', 'Warning', 'X-Requested-With', 'DNT', 'X-Forwarded-For', 'X-Forwarded-Host', 'X-Forwarded-Proto', 'Front-End-Https', 'X-Http-Method-Override', 'X-ATT-DeviceId', 'X-Wap-Profile', 'Proxy-Connection', 'X-UIDH', 'X-Csrf-Token']
const state = {
result: [],
currentKey: -2
}
export default {
components: {
DetailNavbar
},
data() {
const validateName = (rule, value, callback) => {
const name = value && trimStr(value)
if (name === '' || name === undefined) {
callback(new Error('不能为空'))
} else if (name.length > 64) {
callback(new Error('不能超过64个字符'))
} else {
callback()
}
}
const validateDescription = (rule, value, callback) => {
const description = value && trimStr(value)
if (description && description.length > 512) {
callback(new Error('不能超过512个字符'))
} else {
callback()
}
}
return {
groupId: '',
itemId: '',
activeName: 'interface',
settingActiveName: 'projectSetting',
groupOptions: [],
defaultProject: {},
projectForm: {
id: '',
name: '',
groupId: '',
path: '',
description: '',
access: '1'
},
projectRules: {
name: [{ required: true, trigger: 'blur', validator: validateName }],
groupId: [{ required: true, trigger: 'blur' }],
path: [{ required: false, trigger: 'blur' }],
description: [{ required: false, trigger: 'blur', validator: validateDescription }],
access: [{ required: true }]
},
enviromentList: [],
currentEnv: {},
editableTabsValue: '',
options: [],
tabIndex: 1,
dynamicValidateForm: {
header: [
{
value: ''
}
],
cookies: [
{
value: ''
}
]
},
names: [],
values: [],
cookieNames: [],
cookieValues: [],
hostOptions: [
{
value: 'http://',
label: 'http://'
},
{
value: 'https://',
label: 'https://'
}
],
dataRules: {
groupId: [{ required: true, trigger: 'blur' }],
jsonStr: [{ required: true, trigger: 'blur' }],
url: [{ required: true, trigger: 'blur' }]
},
dataForm: {
groupId: '',
itemId: '',
jsonStr: '',
url: ''
},
enableUrlImport: false
}
},
created() {
this.groupId = this.$route.query.groupId
const path = this.$route.path
const params = path && path.split('\/')
if (params.length) {
this.itemId = params[2]
this.activeIndex = params[3]
}
this.initOptions()
},
methods: {
initImportData() {
getGroupList().then(response => {
this.groupOptions = response.result
})
this.dataForm.itemId = this.itemId
this.dataForm.groupId = this.groupId
},
changeEnable(state) {
this.enableUrlImport = state
},
importSwagger() {
this.$refs.dataForm.validate((valid) => {
if (valid) {
if (this.enableUrlImport) {
this.dataForm.jsonStr = ''
} else {
this.dataForm.url = ''
}
importSwagger(this.dataForm).then(response => {
if (response) {
this.$message({
type: 'success',
message: '导入成功!'
})
}
})
} else {
return false
}
})
},
readFile(file) {
const index = file.name.split('.')
if (index[1] === 'json') {
this.dataForm.jsonStr = ''
const reader = new FileReader()
reader.readAsText(file.raw)
reader.onload = (e) => {
// 读取文件内容
const cont = e.target.result
// 接下来可对文件内容进行处理
this.dataForm.jsonStr += cont
}
if (trimStr(this.dataForm.jsonStr) !== '' ) {
this.importSwagger()
}
} else {
this.$message({
type: 'error',
message: '只支持Swagger json文件!'
})
}
},
initProject() {
getGroupList().then(response => {
this.groupOptions = response.result
})
this.getProject()
},
getProject() {
getProject(this.itemId).then(response => {
this.projectForm = deepClone(response.result)
this.projectForm.access = '1'
this.defaultProject = response.result
})
},
submitForm(formName) {
this.$refs[formName].validate(async(valid) => {
if (valid) {
await updateProject(this.projectForm).then(async(response) => {
if (response) {
this.$message({
type: 'success',
message: '修改成功!'
})
}
})
} else {
return false
}
})
},
deleteProject() {
this.$confirm('确定要删除项目:' + this.defaultProject.name + '吗?', 'Warning', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async() => {
const response = await deleteProject(this.itemId)
if (response) {
this.$router.push({ path: '/dashboard' })
this.$message({
type: 'success',
message: '删除成功!'
})
}
})
.catch(err => { console.error(err) })
},
initOptions() {
this.options = []
for (let i = 0; i < HTTP_REQUEST_HEADER.length; i++) {
const option = {
value: HTTP_REQUEST_HEADER[i],
label: HTTP_REQUEST_HEADER[i]
}
this.options.push(option)
}
},
handleClick(tab, event) {
let route = ''
this.activeName = tab.name
const path = this.$route.path
const params = path && path.split('\/')
if (params.length) {
this.itemId = params[2]
route = { path: '/projectDetail/' + this.itemId + '/' + this.activeName, query: { groupId: this.groupId }}
if (this.activeName === 'setting') {
this.initProject()
} else if (this.activeName === 'data') {
this.initImportData()
}
}
this.$router.push(route)
},
settingHandleClick(tab, event) {
if (tab.name === 'envSetting') {
this.initEnvironment()
} else {
this.initProject()
}
},
envHandleClick(key, data) {
this.currentEnv = data
this.currentKey = key
this.initEnvParams()
},
initState(curdata) {
const newValue = {}
newValue['result'] = [].concat(curdata)
this.setState(newValue, newValue['result'][0], 0)
},
setState(result, currentEnv, currentKey) {
this.result = result
this.currentKey = currentKey
this.currentEnv = currentEnv
this.initEnvParams()
},
initEnvironment() {
getEnvironmentList(this.itemId).then(response => {
this.enviromentList = response.result || []
if (this.enviromentList && this.enviromentList.length) {
for (const enviroment of this.enviromentList) {
// 新增title属性显示页签
this.$set(enviroment, 'title', enviroment.name)
if (enviroment.host) {
const hosts = enviroment.host.split(':\/\/')
if (hosts.length === 2) {
this.$set(enviroment, 'firstHost', hosts[0] + '://')
this.$set(enviroment, 'lastHost', hosts[1])
} else {
this.$set(enviroment, 'firstHost', 'http://')
this.$set(enviroment, 'lastHost', enviroment.host)
}
}
}
this.editableTabsValue = this.enviromentList[0].title
this.currentEnv = this.enviromentList[0]
this.initEnvParams()
this.initState(this.enviromentList)
this.envHandleClick(0, this.enviromentList[0])
} else {
// 没有环境配置时,默认新增
this.handleTabsEdit(null, 'add')
}
})
},
initEnvParams() {
this.dynamicValidateForm.header = [{ value: '', key: '' }]
this.names = []
this.values = []
const header = this.currentEnv && this.currentEnv.header
for (const key in header) {
if (key && header[key]) {
this.dynamicValidateForm.header.push({
value: header[key],
key: key
})
this.names.push(key)
this.values.push(header[key])
}
}
this.dynamicValidateForm.cookies = [{ value: '', key: '' }]
this.cookieNames = []
this.cookieValues = []
const cookies = this.currentEnv && this.currentEnv.cookies
for (const key in cookies) {
if (key && cookies[key]) {
this.dynamicValidateForm.cookies.push({
value: cookies[key],
key: key
})
this.cookieNames.push(key)
this.cookieValues.push(cookies[key])
}
}
},
addEnvParams(name, data) {
// 增加新环境变量项
const newValue = {}
data = { name: '', firstHost: 'http://', lastHost: '', header: {}, cookies: {}}
newValue[name] = [].concat(data, state[name])
this.setState(newValue)
this.envHandleClick(0, data)
},
tabClick(tab, event) {
this.envHandleClick(tab.index, this.enviromentList[tab.index])
},
handleTabsEdit(targetName, action) {
if (action === 'add') {
this.addEnvParams('result')
const newTabName = 'local' + new Date().getTime() + ''
this.enviromentList.push({
name: newTabName,
title: newTabName,
firstHost: 'http://',
lastHost: '',
header: {},
cookies: {}
})
this.editableTabsValue = newTabName
this.currentEnv = this.enviromentList[this.enviromentList.length - 1]
}
if (action === 'remove') {
// 删除页签
this.$confirm('确定要删除环境:' + targetName + '吗?', 'Warning', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async() => {
const tabs = this.enviromentList
let activeName = this.editableTabsValue
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
this.editableTabsValue = activeName
this.enviromentList = tabs.filter(tab => tab.name !== targetName)
if (this.currentEnv && this.currentEnv.id) {
const queryParam = { itemId: this.itemId }
const response = await deleteEnvironment(queryParam, this.currentEnv.id)
if (response) {
this.$message({
type: 'success',
message: '删除成功!'
})
this.initEnvironment()
}
} else {
this.$message({
type: 'success',
message: '删除成功!'
})
}
})
.catch(err => { console.error(err) })
}
},
removeDomain(item) {
var index = this.dynamicValidateForm.header.indexOf(item)
if (index !== -1) {
this.dynamicValidateForm.header.splice(index, 1)
this.names.splice(index, 1)
this.values.splice(index, 1)
}
},
addHeader() {
this.dynamicValidateForm.header.push({
value: '',
key: Date.now()
})
},
removeCookie(item) {
var index = this.dynamicValidateForm.cookies.indexOf(item)
if (index !== -1) {
this.dynamicValidateForm.cookies.splice(index, 1)
this.cookieNames.splice(index, 1)
this.cookieValues.splice(index, 1)
}
},
addCookie() {
this.dynamicValidateForm.cookies.push({
value: '',
key: Date.now()
})
},
saveEnv() {
if (trimStr(this.currentEnv.title) === '') {
this.$message({
type: 'error',
message: '请输入环境名称!'
})
return false
}
if (trimStr(this.currentEnv.lastHost) === '') {
this.$message({
type: 'error',
message: '请输入环境域名!'
})
return false
}
const arryKey = [...this.names, ...this.cookieNames]
const arrySet = new Set(arryKey)
if (arryKey.length !== arrySet.size) {
this.$message({
type: 'error',
message: '存在重复key,请检查重新输入'
})
return false
}
if (this.names.length !== this.values.length) {
this.$message({
type: 'error',
message: 'Header 属性必需成对出现且不能为空,请检查重新输入'
})
return false
} else {
for (let i = 0; i < this.names.length; i++) {
if (trimStr(this.names[i]) === '' || trimStr(this.values[i]) === '') {
this.$message({
type: 'error',
message: 'Header 属性必需成对出现且不能为空,请检查重新输入'
})
return false
}
}
}
if (this.cookieNames.length !== this.cookieValues.length) {
this.$message({
type: 'error',
message: 'Cookie 属性必需成对出现且不能为空,请检查重新输入'
})
return false
} else {
for (let i = 0; i < this.cookieNames.length; i++) {
if (trimStr(this.cookieNames[i]) !== '') {
if (trimStr(this.cookieValues[i]) === '') {
this.$message({
type: 'error',
message: 'Cookie 属性必需成对出现且不能为空,请检查重新输入'
})
return false
}
}
}
}
const header = {}
for (let i = 0; i < this.names.length; i++) {
if (trimStr(this.names[i]) !== '') {
const headerKey = trimStr(this.names[i])
const headValue = trimStr(this.values[i])
header[headerKey] = headValue
}
}
const cookies = {}
for (let i = 0; i < this.cookieNames.length; i++) {
if (trimStr(this.cookieNames[i]) !== '') {
const cookieKey = trimStr(this.cookieNames[i])
const cookieValue = trimStr(this.cookieValues[i])
cookies[cookieKey] = cookieValue
}
}
let params = {
host: this.currentEnv.firstHost + this.currentEnv.lastHost,
name: this.currentEnv.title,
cookies: cookies,
header: header
}
const queryParam = { itemId: this.itemId }
if (this.currentEnv.id) {
params = Object.assign(params,
{
id: this.currentEnv.id
})
updateEnvironment(queryParam, params).then(response => {
if (response) {
this.$message({
type: 'success',
message: '修改成功!'
})
this.initEnvironment()
}
})
} else {
addEnvironment(queryParam, params).then(response => {
if (response) {
this.$message({
type: 'success',
message: '新增环境成功!'
})
this.initEnvironment()
}
})
}
}
}
}
</script>
<style lang="scss" scoped>
</style>