This commit is contained in:
2024-03-01 20:28:14 +08:00
commit 076c21dc36
491 changed files with 84482 additions and 0 deletions

159
common/HandleImportData.js Normal file
View File

@@ -0,0 +1,159 @@
const _ = require('underscore');
const axios = require('axios');
const isNode = typeof global == 'object' && global.global === global;
async function handle(
res,
projectId,
selectCatid,
menuList,
basePath,
dataSync,
messageError,
messageSuccess,
callback,
token,
port
) {
const taskNotice = _.throttle((index, len)=>{
messageSuccess(`正在导入,已执行任务 ${index+1} 个,共 ${len}`)
}, 3000)
const handleAddCat = async cats => {
let catsObj = {};
if (cats && Array.isArray(cats)) {
for (let i = 0; i < cats.length; i++) {
let cat = cats[i];
let findCat = _.find(menuList, menu => menu.name === cat.name);
catsObj[cat.name] = cat;
if (findCat) {
cat.id = findCat._id;
} else {
let apipath = '/api/interface/add_cat';
if (isNode) {
apipath = 'http://127.0.0.1:' + port + apipath;
}
let data = {
name: cat.name,
project_id: projectId,
desc: cat.desc,
token
};
let result = await axios.post(apipath, data);
if (result.data.errcode) {
messageError(result.data.errmsg);
callback({ showLoading: false });
return false;
}
cat.id = result.data.data._id;
}
}
}
return catsObj;
};
const handleAddInterface = async info => {
const cats = await handleAddCat(info.cats);
if (cats === false) {
return;
}
const res = info.apis;
let len = res.length;
let count = 0;
let successNum = len;
let existNum = 0;
if (len === 0) {
messageError(`解析数据为空`);
callback({ showLoading: false });
return;
}
if(info.basePath){
let projectApiPath = '/api/project/up';
if (isNode) {
projectApiPath = 'http://127.0.0.1:' + port + projectApiPath;
}
await axios.post(projectApiPath, {
id: projectId,
basepath: info.basePath,
token
})
}
for (let index = 0; index < res.length; index++) {
let item = res[index];
let data = Object.assign(item, {
project_id: projectId,
catid: selectCatid
});
if (basePath) {
data.path =
data.path.indexOf(basePath) === 0 ? data.path.substr(basePath.length) : data.path;
}
if (
data.catname &&
cats[data.catname] &&
typeof cats[data.catname] === 'object' &&
cats[data.catname].id
) {
data.catid = cats[data.catname].id;
}
data.token = token;
if (dataSync !== 'normal') {
// 开启同步功能
count++;
let apipath = '/api/interface/save';
if (isNode) {
apipath = 'http://127.0.0.1:' + port + apipath;
}
data.dataSync = dataSync;
let result = await axios.post(apipath, data);
if (result.data.errcode) {
successNum--;
callback({ showLoading: false });
messageError(result.data.errmsg);
} else {
existNum = existNum + result.data.data.length;
}
} else {
// 未开启同步功能
count++;
let apipath = '/api/interface/add';
if (isNode) {
apipath = 'http://127.0.0.1:' + port + apipath;
}
let result = await axios.post(apipath, data);
if (result.data.errcode) {
successNum--;
if (result.data.errcode == 40022) {
existNum++;
}
if (result.data.errcode == 40033) {
callback({ showLoading: false });
messageError('没有权限');
break;
}
}
}
if (count === len) {
callback({ showLoading: false });
messageSuccess(`成功导入接口 ${successNum} 个, 已存在的接口 ${existNum}`);
return;
}
taskNotice(index, res.length)
}
};
return await handleAddInterface(res);
}
module.exports = handle;

29
common/config.js Normal file
View File

@@ -0,0 +1,29 @@
module.exports = {
exts: [{
name: 'import-postman'
},{
name: 'import-har'
},{
name: 'advanced-mock'
},{
name: 'import-swagger'
},{
name: 'statistics'
},{
name: 'export-data'
},{
name: 'gen-services'
},{
name: 'export-swagger2-data'
},{
name: 'import-yapi-json'
},{
name: 'wiki'
}, {
name: 'swagger-auto-sync'
}
// {
// name: 'test'
// }
]
}

14
common/createContext.js Normal file
View File

@@ -0,0 +1,14 @@
module.exports = function (uid, projectId,interfaceId) {
if(!uid || !projectId || !interfaceId){
console.error('uid projectId interfaceId 不能为空', uid, projectId,interfaceId)
}
/**
* 统一转换为number
*/
return {
uid: +uid,
projectId: +projectId,
interfaceId: +interfaceId
}
}

190
common/diff-view.js Normal file
View File

@@ -0,0 +1,190 @@
// const json5_parse = require('../client/common.js').json5_parse;
const json5 = require('json5');
module.exports = function(jsondiffpatch, formattersHtml, curDiffData) {
const json5_parse = json => {
if (typeof json === 'object' && json) return json;
try {
return json5.parse(json);
} catch (err) {
return json;
}
};
const diffText = (left, right) => {
left = left || '';
right = right || '';
if (left == right) {
return null;
}
var delta = jsondiffpatch.diff(left, right);
let result = formattersHtml.format(delta, left);
return result;
};
const diffJson = (left, right) => {
left = json5_parse(left);
right = json5_parse(right);
let delta = jsondiffpatch.diff(left, right);
return formattersHtml.format(delta, left);
// return '';
};
const valueMaps = {
'1': '必需',
'0': '非必需',
text: '文本',
file: '文件',
undone: '未完成',
done: '已完成'
};
const handleParams = item => {
let newItem = Object.assign({}, item);
newItem._id = undefined;
Object.keys(newItem).forEach(key => {
switch (key) {
case 'required':
newItem[key] = valueMaps[newItem[key]];
break;
case 'type':
newItem[key] = valueMaps[newItem[key]];
break;
}
});
return newItem;
};
const diffArray = (arr1, arr2) => {
arr1 = arr1 || [];
arr2 = arr2 || [];
arr1 = arr1.map(handleParams);
arr2 = arr2.map(handleParams);
return diffJson(arr1, arr2);
};
let diffView = [];
if (curDiffData && typeof curDiffData === 'object' && curDiffData.current) {
const { current, old, type } = curDiffData;
// wiki 信息的diff 输出
if (type === 'wiki') {
if (current != old) {
diffView.push({
title: 'wiki更新',
content: diffText(old, current)
});
}
return (diffView = diffView.filter(item => item.content));
}
if (current.path != old.path) {
diffView.push({
title: 'Api 路径',
content: diffText(old.path, current.path)
});
}
if (current.title != old.title) {
diffView.push({
title: 'Api 名称',
content: diffText(old.title, current.title)
});
}
if (current.method != old.method) {
diffView.push({
title: 'Method',
content: diffText(old.method, current.method)
});
}
if (current.catid != old.catid) {
diffView.push({
title: '分类 id',
content: diffText(old.catid, current.catid)
});
}
if (current.status != old.status) {
diffView.push({
title: '接口状态',
content: diffText(valueMaps[old.status], valueMaps[current.status])
});
}
if (current.tag !== old.tag) {
diffView.push({
title: '接口tag',
content: diffText(old.tag, current.tag)
});
}
diffView.push({
title: 'Request Path Params',
content: diffArray(old.req_params, current.req_params)
});
diffView.push({
title: 'Request Query',
content: diffArray(old.req_query, current.req_query)
});
diffView.push({
title: 'Request Header',
content: diffArray(old.req_headers, current.req_headers)
});
let oldValue = current.req_body_type === 'form' ? old.req_body_form : old.req_body_other;
if (current.req_body_type !== old.req_body_type) {
diffView.push({
title: 'Request Type',
content: diffText(old.req_body_type, current.req_body_type)
});
oldValue = null;
}
if (current.req_body_type === 'json') {
diffView.push({
title: 'Request Body',
content: diffJson(oldValue, current.req_body_other)
});
} else if (current.req_body_type === 'form') {
diffView.push({
title: 'Request Form Body',
content: diffArray(oldValue, current.req_body_form)
});
} else {
diffView.push({
title: 'Request Raw Body',
content: diffText(oldValue, current.req_body_other)
});
}
let oldResValue = old.res_body;
if (current.res_body_type !== old.res_body_type) {
diffView.push({
title: 'Response Type',
content: diffText(old.res_body_type, current.res_body_type)
});
oldResValue = '';
}
if (current.res_body_type === 'json') {
diffView.push({
title: 'Response Body',
content: diffJson(oldResValue, current.res_body)
});
} else {
diffView.push({
title: 'Response Body',
content: diffText(oldResValue, current.res_body)
});
}
}
return (diffView = diffView.filter(item => item.content));
};

101
common/formats.js Normal file
View File

@@ -0,0 +1,101 @@
const formats = [
{
name: 'url',
title: 'url'
},
{
name: 'domain',
title: '域名'
},
{
name: 'ip',
title: 'ipv4 地址'
},
{
name: 'id'
},
{
name: 'guid'
},
{
name: 'now'
},
{
name: 'timestamp'
},
{
name: 'date'
},
{
name: 'time'
},
{
name: 'datetime'
},
{
name: 'image',
title: '图片链接'
},
{
name: 'imageData',
title: '图片'
},
{
name: 'email',
title: '邮箱'
},
{
name: 'paragraph',
title: '段落'
},
{
name: 'sentence',
title: '句子'
},
{
name: 'word',
title: '单词'
},
{
name: 'title',
title: '标题'
},
{
name: 'name',
title: '姓名'
},
{
name: 'region',
title: '地区'
},
{
name: 'province',
title: '省份'
},
{
name: 'city',
title: '城市名'
},
{
name: 'county',
title: '国家'
},
{
name: 'mobile',
title: '手机号'
},
{
name: 'cparagraph',
title: '中文本'
},
{
name: 'cname',
title: '中文姓名'
},
{
title: '中文标题',
name: 'ctitle'
}
];
module.exports = formats;

97
common/lib.js Normal file
View File

@@ -0,0 +1,97 @@
const _ = require('underscore');
function isObj(object) {
return (
object &&
typeof object == 'object' &&
Object.prototype.toString.call(object).toLowerCase() == '[object object]'
);
}
function isArray(object) {
return object && typeof object == 'object' && object.constructor == Array;
}
function getLength(object) {
return Object.keys(object).length;
}
function Compare(objA, objB) {
if (!isObj(objA) && !isObj(objB)) {
if (isArray(objA) && isArray(objB)) {
return CompareArray(objA, objB, true);
}
return objA == objB;
}
if (!isObj(objA) || !isObj(objB)) return false;
if (getLength(objA) != getLength(objB)) return false;
return CompareObj(objA, objB, true);
}
function CompareArray(objA, objB, flag) {
if (objA.length != objB.length) return false;
for (let i in objB) {
if (!Compare(objA[i], objB[i])) {
flag = false;
break;
}
}
return flag;
}
function CompareObj(objA, objB, flag) {
for (var key in objA) {
if (!flag) break;
if (!objB.hasOwnProperty(key)) {
flag = false;
break;
}
if (!isArray(objA[key])) {
if (objB[key] != objA[key]) {
flag = false;
break;
}
} else {
if (!isArray(objB[key])) {
flag = false;
break;
}
var oA = objA[key],
oB = objB[key];
if (oA.length != oB.length) {
flag = false;
break;
}
for (var k in oA) {
if (!flag) break;
flag = CompareObj(oA[k], oB[k], flag);
}
}
}
return flag;
}
exports.jsonEqual = Compare;
exports.isDeepMatch = function(obj, properties) {
if (!properties || typeof properties !== 'object' || Object.keys(properties).length === 0) {
return true;
}
if (!obj || typeof obj !== 'object' || Object.keys(obj).length === 0) {
return false;
}
let match = true;
let keys = Object.keys(properties)
for (let index=0; index< keys.length; index++) {
let i = keys[index];
if (!Compare(obj[i], properties[i])) {
match = false;
break;
}
}
return match;
};

325
common/markdown.js Normal file
View File

@@ -0,0 +1,325 @@
const schema = require('./schema-transformTo-table.js');
const _ = require('underscore');
const json_parse = function(json) {
try {
return JSON.parse(json);
} catch (err) {
return {};
}
};
// 处理字符串换行
const handleWrap = str => {
return _.isString(str) ? str.replace(/\n/gi, '<br/>') : str;
};
const messageMap = {
desc: '备注',
default: '实例',
maximum: '最大值',
minimum: '最小值',
maxItems: '最大数量',
minItems: '最小数量',
maxLength: '最大长度',
minLength: '最小长度',
uniqueItems: '元素是否都不同',
itemType: 'item 类型',
format: 'format',
enum: '枚举',
enumDesc: '枚举备注',
mock: 'mock'
};
const columns = [
{
title: '名称',
dataIndex: 'name',
key: 'name'
},
{
title: '类型',
dataIndex: 'type',
key: 'type'
},
{
title: '是否必须',
dataIndex: 'required',
key: 'required'
},
{
title: '默认值',
dataIndex: 'default',
key: 'default'
},
{
title: '备注',
dataIndex: 'desc',
key: 'desc'
},
{
title: '其他信息',
dataIndex: 'sub',
key: 'sub'
}
];
function escapeStr(str, isToc) {
return isToc ? escape(str) : str;
}
function createBaseMessage(basepath, inter) {
// 基本信息
let baseMessage = `### 基本信息\n\n**Path** ${basepath + inter.path}\n\n**Method** ${
inter.method
}\n\n**接口描述:**\n${_.isUndefined(inter.desc) ? '' : inter.desc}\n`;
return baseMessage;
}
function createReqHeaders(req_headers) {
// Request-headers
if (req_headers && req_headers.length) {
let headersTable = `**Headers**\n\n`;
headersTable += `| 参数名称 | 参数值 | 是否必须 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ | ------------ |\n`;
for (let j = 0; j < req_headers.length; j++) {
headersTable += `| ${req_headers[j].name || ''} | ${req_headers[j].value || ''} | ${
req_headers[j].required == 1 ? '是' : '否'
} | ${handleWrap(req_headers[j].example) || ''} | ${handleWrap(req_headers[j].desc) ||
''} |\n`;
}
return headersTable;
}
return '';
}
function createPathParams(req_params) {
if (req_params && req_params.length) {
let paramsTable = `**路径参数**\n\n`;
paramsTable += `| 参数名称 | 示例 | 备注 |\n| ------------ | ------------ | ------------ |\n`;
for (let j = 0; j < req_params.length; j++) {
paramsTable += `| ${req_params[j].name || ''} | ${handleWrap(req_params[j].example) ||
''} | ${handleWrap(req_params[j].desc) || ''} |\n`;
}
return paramsTable;
}
return '';
}
function createReqQuery(req_query) {
if (req_query && req_query.length) {
let headersTable = `**Query**\n\n`;
headersTable += `| 参数名称 | 是否必须 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ |\n`;
for (let j = 0; j < req_query.length; j++) {
headersTable += `| ${req_query[j].name || ''} | ${
req_query[j].required == 1 ? '是' : '否'
} | ${handleWrap(req_query[j].example) || ''} | ${handleWrap(req_query[j].desc) ||
''} |\n`;
}
return headersTable;
}
return '';
}
function createReqBody(req_body_type, req_body_form, req_body_other, req_body_is_json_schema) {
if (req_body_type === 'form' && req_body_form.length) {
let bodyTable = `**Body**\n\n`;
bodyTable += `| 参数名称 | 参数类型 | 是否必须 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ | ------------ |\n`;
let req_body = req_body_form;
for (let j = 0; j < req_body.length; j++) {
bodyTable += `| ${req_body[j].name || ''} | ${req_body[j].type || ''} | ${
req_body[j].required == 1 ? '是' : '否'
} | ${req_body[j].example || ''} | ${req_body[j].desc || ''} |\n`;
}
return `${bodyTable}\n\n`;
} else if (req_body_other) {
if (req_body_is_json_schema) {
let reqBody = createSchemaTable(req_body_other);
return `**Body**\n\n` + reqBody;
} else {
//other
return `**Body**\n\n` + '```javascript' + `\n${req_body_other || ''}` + '\n```';
}
}
return '';
}
function tableHeader(columns) {
let header = ``;
columns.map(item => {
header += `<th key=${item.key}>${item.title}</th>`;
});
return header;
}
function handleObject(text) {
if (!_.isObject(text)) {
return text;
}
let tpl = ``;
Object.keys(text || {}).map((item, index) => {
let name = messageMap[item];
let value = text[item];
tpl += _.isUndefined(text[item])
? ''
: `<p key=${index}><span style="font-weight: '700'">${name}: </span><span>${value.toString()}</span></p>`;
});
return tpl;
}
function tableCol(col, columns, level) {
let tpl = ``;
columns.map((item, index) => {
let dataIndex = item.dataIndex;
let value = col[dataIndex];
value = _.isUndefined(value) ? '' : value;
let text = ``;
switch (dataIndex) {
case 'sub':
text = handleObject(value);
break;
case 'type':
text =
value === 'array'
? `<span>${col.sub ? col.sub.itemType || '' : 'array'} []</span>`
: `<span>${value}</span>`;
break;
case 'required':
text = value ? '必须' : '非必须';
break;
case 'desc':
text = _.isUndefined(col.childrenDesc)
? `<span style="white-space: pre-wrap">${value}</span>`
: `<span style="white-space: pre-wrap">${col.childrenDesc}</span>`;
break;
case 'name':
text = `<span style="padding-left: ${20 * level}px"><span style="color: #8c8a8a">${
level > 0 ? '├─' : ''
}</span> ${value}</span>`;
break;
default:
text = value;
}
tpl += `<td key=${index}>${text}</td>`;
});
return tpl;
}
function tableBody(dataSource, columns, level) {
// 按照columns的顺序排列数据
let tpl = ``;
dataSource.map(col => {
let child = null;
tpl += `<tr key=${col.key}>${tableCol(col, columns, level)}</tr>`;
if (!_.isUndefined(col.children) && _.isArray(col.children)) {
let index = level + 1;
child = tableBody(col.children, columns, index);
}
tpl += child ? `${child}` : ``;
});
return tpl;
}
function createSchemaTable(body) {
let template = ``;
let dataSource = schema.schemaTransformToTable(json_parse(body));
template += `<table>
<thead class="ant-table-thead">
<tr>
${tableHeader(columns)}
</tr>
</thead>`;
template += `<tbody className="ant-table-tbody">${tableBody(dataSource, columns, 0)}
</tbody>
</table>
`;
return template;
}
function createResponse(res_body, res_body_is_json_schema, res_body_type) {
let resTitle = `\n### 返回数据\n\n`;
if (res_body) {
if (res_body_is_json_schema && res_body_type === 'json') {
let resBody = createSchemaTable(res_body);
return resTitle + resBody;
} else {
let resBody = '```javascript' + `\n${res_body || ''}\n` + '```';
return resTitle + resBody;
}
}
return '';
}
function createInterMarkdown(basepath, listItem, isToc) {
let mdTemplate = ``;
const toc = `[TOC]\n\n`;
// 接口名称
mdTemplate += `\n## ${escapeStr(`${listItem.title}\n<a id=${listItem.title + listItem.catid}> </a>`, isToc)}\n`;
isToc && (mdTemplate += toc);
// 基本信息
mdTemplate += createBaseMessage(basepath, listItem);
// Request
mdTemplate += `\n### 请求参数\n`;
// Request-headers
mdTemplate += createReqHeaders(listItem.req_headers);
// Request-params
mdTemplate += createPathParams(listItem.req_params);
// Request-query
mdTemplate += createReqQuery(listItem.req_query);
// Request-body
mdTemplate += createReqBody(
listItem.req_body_type,
listItem.req_body_form,
listItem.req_body_other,
listItem.req_body_is_json_schema
);
// Response
// Response-body
mdTemplate += createResponse(
listItem.res_body,
listItem.res_body_is_json_schema,
listItem.res_body_type
);
return mdTemplate;
}
function createProjectMarkdown(curProject, wikiData) {
let mdTemplate = ``;
// 项目名、项目描述
let title = `<h1 class="curproject-name"> ${curProject.name} </h1>`;
mdTemplate += `\n ${title} \n ${curProject.desc || ''}\n\n`;
// 增加公共wiki信息展示
mdTemplate += wikiData ? `\n### 公共信息\n${wikiData.desc || ''}\n` : '';
return mdTemplate;
}
function createClassMarkdown(curProject, list, isToc) {
let mdTemplate = ``;
const toc = `[TOC]\n\n`;
list.map(item => {
// 分类名称
mdTemplate += `\n# ${escapeStr(item.name, isToc)}\n`;
isToc && (mdTemplate += toc);
for (let i = 0; i < item.list.length; i++) {
//循环拼接 接口
// 接口内容
mdTemplate += createInterMarkdown(curProject.basepath, item.list[i], isToc);
}
});
return mdTemplate;
}
let r = {
createInterMarkdown,
createProjectMarkdown,
createClassMarkdown
};
module.exports = r;

34
common/mergeJsonSchema.js Normal file
View File

@@ -0,0 +1,34 @@
function isPlainObject(obj) {
return obj ? typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.prototype : false;
}
function handleProperties(sourceProperties, mergeProperties){
if(!isPlainObject(mergeProperties)){
return mergeProperties
}
if(! isPlainObject(sourceProperties)){
return mergeProperties
}
Object.keys(mergeProperties).forEach(key=>{
mergeProperties[key]= handleSchema(sourceProperties[key], mergeProperties[key])
})
return mergeProperties;
}
function handleSchema(source, merge){
if(!isPlainObject(source)) return merge;
if(!isPlainObject(merge)) return merge;
let result = {}
Object.assign(result, source, merge)
if(merge.type === 'object'){
result.properties = handleProperties(source.properties, merge.properties);
}else if(merge.type === 'array'){
result.items = handleSchema(source.items, merge.items);
}
return result;
}
module.exports = function(sourceJsonSchema, mergeJsonSchema){
return handleSchema(sourceJsonSchema, mergeJsonSchema)
}

95
common/mock-extra.js Normal file
View File

@@ -0,0 +1,95 @@
/**
* @author suxiaoxin
* @info mockJs 功能增强脚本
*/
var strRegex = /\${([a-zA-Z]+)\.?([a-zA-Z0-9_\.]*)\}/i;
var varSplit = '.';
var mockSplit = '|';
var Mock = require('mockjs');
Mock.Random.extend({
timestamp: function(){
var time = new Date().getTime() + '';
return +time.substr(0, time.length - 3)
}
})
function mock(mockJSON, context) {
context = context || {};
var filtersMap = {
regexp: handleRegexp
};
if(!mockJSON || typeof mockJSON !== 'object'){
return mockJSON;
}
return parse(mockJSON);
function parse(p, c) {
if(!c){
c = Array.isArray(p) ? [] : {}
}
for (var i in p) {
if (!p.hasOwnProperty(i)) {
continue;
}
if (p[i] && typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
parse(p[i], c[i]);
} else if(p[i] && typeof p[i] === 'string'){
p[i] = handleStr(p[i]);
var filters = i.split(mockSplit), newFilters = [].concat(filters);
c[i] = p[i];
if (filters.length > 1) {
for (var f = 1, l = filters.length, index; f < l; f++) {
filters[f] = filters[f].toLowerCase();
if (filters[f] in filtersMap) {
if ((index = newFilters.indexOf(filters[f])) !== -1) {
newFilters.splice(index, 1);
}
delete c[i];
c[newFilters.join(mockSplit)] = filtersMap[filters[f]].call(p, p[i]);
}
}
}
}else{
c[i] = p[i];
}
}
return c;
}
function handleRegexp(item) {
return new RegExp(item);
}
function handleStr(str) {
if (typeof str !== 'string' || str.indexOf('{') === -1 || str.indexOf('}') === -1 || str.indexOf('$') === -1) {
return str;
}
let matchs = str.match(strRegex);
if(matchs){
let name = matchs[1] + (matchs[2]? '.' + matchs[2] : '');
if(!name) return str;
var names = name.split(varSplit);
var data = context;
if(typeof context[names[0]] === undefined){
return str;
}
names.forEach(function (n) {
if (data === '') return '';
if (n in data) {
data = data[n];
} else {
data = '';
}
});
return data;
}
return str;
}
}
module.exports = mock;

54
common/plugin.js Normal file
View File

@@ -0,0 +1,54 @@
const _ = require('underscore');
function getPluginConfig(name, type) {
let pluginConfig;
if (type === 'ext') {
pluginConfig = require('../exts/yapi-plugin-' + name);
} else {
pluginConfig = require('yapi-plugin-' + name);
}
if (!pluginConfig || typeof pluginConfig !== 'object') {
throw new Error(`Plugin ${name} Config 配置错误,请检查 yapi-plugin-${name}/index.js`);
}
return {
server: pluginConfig.server,
client: pluginConfig.client
}
}
/**
* type @string enum[plugin, ext] plugin是外部插件ext是内部插件
*/
exports.initPlugins = function (plugins, type) {
if (!plugins) {
return [];
}
if (typeof plugins !== 'object' || !Array.isArray(plugins)) {
throw new Error('插件配置有误,请检查', plugins);
}
plugins = plugins.map(item => {
let pluginConfig;
if (item && typeof item === 'string') {
pluginConfig = getPluginConfig(item, type);
return Object.assign({}, pluginConfig, { name: item, enable: true })
} else if (item && typeof item === 'object') {
pluginConfig = getPluginConfig(item.name, type);
return Object.assign({},
pluginConfig,
{
name: item.name,
options: item.options,
enable: item.enable === false ? false : true
})
}
})
plugins = plugins.filter(item => {
return item.enable === true && (item.server || item.client)
})
return _.uniq(plugins, item => item.name)
}

484
common/postmanLib.js Normal file
View File

@@ -0,0 +1,484 @@
const { isJson5, json_parse, handleJson, joinPath, safeArray } = require('./utils');
const constants = require('../client/constants/variable.js');
const _ = require('underscore');
const URL = require('url');
const utils = require('./power-string.js').utils;
const HTTP_METHOD = constants.HTTP_METHOD;
const axios = require('axios');
const qs = require('qs');
const CryptoJS = require('crypto-js');
const jsrsasign = require('jsrsasign');
const https = require('https');
const isNode = typeof global == 'object' && global.global === global;
const ContentTypeMap = {
'application/json': 'json',
'application/xml': 'xml',
'text/xml': 'xml',
'application/html': 'html',
'text/html': 'html',
other: 'text'
};
const getStorage = async (id)=>{
try{
if(isNode){
let storage = global.storageCreator(id);
let data = await storage.getItem();
return {
getItem: (name)=> data[name],
setItem: (name, value)=>{
data[name] = value;
storage.setItem(name, value)
}
}
}else{
return {
getItem: (name)=> window.localStorage.getItem(name),
setItem: (name, value)=> window.localStorage.setItem(name, value)
}
}
}catch(e){
console.error(e)
return {
getItem: (name)=>{
console.error(name, e)
},
setItem: (name, value)=>{
console.error(name, value, e)
}
}
}
}
async function httpRequestByNode(options) {
function handleRes(response) {
if (!response || typeof response !== 'object') {
return {
res: {
status: 500,
body: isNode
? '请求出错, 内网服务器自动化测试无法访问到,请检查是否为内网服务器!'
: '请求出错'
}
};
}
return {
res: {
header: response.headers,
status: response.status,
body: response.data
}
};
}
function handleData() {
let contentTypeItem;
if (!options) return;
if (typeof options.headers === 'object' && options.headers) {
Object.keys(options.headers).forEach(key => {
if (/content-type/i.test(key)) {
if (options.headers[key]) {
contentTypeItem = options.headers[key]
.split(';')[0]
.trim()
.toLowerCase();
}
}
if (!options.headers[key]) delete options.headers[key];
});
if (
contentTypeItem === 'application/x-www-form-urlencoded' &&
typeof options.data === 'object' &&
options.data
) {
options.data = qs.stringify(options.data);
}
}
}
try {
handleData(options);
let response = await axios({
method: options.method,
url: options.url,
headers: options.headers,
timeout: 10000,
maxRedirects: 0,
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
data: options.data
});
return handleRes(response);
} catch (err) {
if (err.response === undefined) {
return handleRes({
headers: {},
status: null,
data: err.message
});
}
return handleRes(err.response);
}
}
function handleContentType(headers) {
if (!headers || typeof headers !== 'object') return ContentTypeMap.other;
let contentTypeItem = 'other';
try {
Object.keys(headers).forEach(key => {
if (/content-type/i.test(key)) {
contentTypeItem = headers[key]
.split(';')[0]
.trim()
.toLowerCase();
}
});
return ContentTypeMap[contentTypeItem] ? ContentTypeMap[contentTypeItem] : ContentTypeMap.other;
} catch (err) {
return ContentTypeMap.other;
}
}
function checkRequestBodyIsRaw(method, reqBodyType) {
if (
reqBodyType &&
reqBodyType !== 'file' &&
reqBodyType !== 'form' &&
HTTP_METHOD[method].request_body
) {
return reqBodyType;
}
return false;
}
function checkNameIsExistInArray(name, arr) {
let isRepeat = false;
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
if (item.name === name) {
isRepeat = true;
break;
}
}
return isRepeat;
}
function handleCurrDomain(domains, case_env) {
let currDomain = _.find(domains, item => item.name === case_env);
if (!currDomain) {
currDomain = domains[0];
}
return currDomain;
}
function sandboxByNode(sandbox = {}, script) {
const vm = require('vm');
script = new vm.Script(script);
const context = new vm.createContext(sandbox);
script.runInContext(context, {
timeout: 10000
});
return sandbox;
}
async function sandbox(context = {}, script) {
if (isNode) {
try {
context.context = context;
context.console = console;
context.Promise = Promise;
context.setTimeout = setTimeout;
context = sandboxByNode(context, script);
} catch (err) {
err.message = `Script: ${script}
message: ${err.message}`;
throw err;
}
} else {
context = sandboxByBrowser(context, script);
}
if (context.promise && typeof context.promise === 'object' && context.promise.then) {
try {
await context.promise;
} catch (err) {
err.message = `Script: ${script}
message: ${err.message}`;
throw err;
}
}
return context;
}
function sandboxByBrowser(context = {}, script) {
if (!script || typeof script !== 'string') {
return context;
}
let beginScript = '';
for (var i in context) {
beginScript += `var ${i} = context.${i};`;
}
try {
eval(beginScript + script);
} catch (err) {
let message = `Script:
----CodeBegin----:
${beginScript}
${script}
----CodeEnd----
`;
err.message = `Script: ${message}
message: ${err.message}`;
throw err;
}
return context;
}
/**
*
* @param {*} defaultOptions
* @param {*} preScript
* @param {*} afterScript
* @param {*} commonContext 负责传递一些业务信息crossRequest 不关注具体传什么,只负责当中间人
*/
async function crossRequest(defaultOptions, preScript, afterScript, commonContext = {}) {
let options = Object.assign({}, defaultOptions);
const taskId = options.taskId || Math.random() + '';
let urlObj = URL.parse(options.url, true),
query = {};
query = Object.assign(query, urlObj.query);
let context = {
isNode,
get href() {
return urlObj.href;
},
set href(val) {
throw new Error('context.href 不能被赋值');
},
get hostname() {
return urlObj.hostname;
},
set hostname(val) {
throw new Error('context.hostname 不能被赋值');
},
get caseId() {
return options.caseId;
},
set caseId(val) {
throw new Error('context.caseId 不能被赋值');
},
method: options.method,
pathname: urlObj.pathname,
query: query,
requestHeader: options.headers || {},
requestBody: options.data,
promise: false,
storage: await getStorage(taskId)
};
Object.assign(context, commonContext)
context.utils = Object.freeze({
_: _,
CryptoJS: CryptoJS,
jsrsasign: jsrsasign,
base64: utils.base64,
md5: utils.md5,
sha1: utils.sha1,
sha224: utils.sha224,
sha256: utils.sha256,
sha384: utils.sha384,
sha512: utils.sha512,
unbase64: utils.unbase64,
axios: axios
});
let scriptEnable = true;
if (preScript && scriptEnable) {
context = await sandbox(context, preScript);
defaultOptions.url = options.url = URL.format({
protocol: urlObj.protocol,
host: urlObj.host,
query: context.query,
pathname: context.pathname
});
defaultOptions.headers = options.headers = context.requestHeader;
defaultOptions.data = options.data = context.requestBody;
}
let data;
if (isNode) {
data = await httpRequestByNode(options);
data.req = options;
} else {
data = await new Promise((resolve, reject) => {
options.error = options.success = function(res, header, data) {
let message = '';
if (res && typeof res === 'string') {
res = json_parse(data.res.body);
data.res.body = res;
}
if (!isNode) message = '请求异常,请检查 chrome network 错误信息... https://juejin.im/post/5c888a3e5188257dee0322af 通过该链接查看教程"';
if (isNaN(data.res.status)) {
reject({
body: res || message,
header,
message
});
}
resolve(data);
};
window.crossRequest(options);
});
}
if (afterScript && scriptEnable) {
context.responseData = data.res.body;
context.responseHeader = data.res.header;
context.responseStatus = data.res.status;
context.runTime = data.runTime;
context = await sandbox(context, afterScript);
data.res.body = context.responseData;
data.res.header = context.responseHeader;
data.res.status = context.responseStatus;
data.runTime = context.runTime;
}
return data;
}
function handleParams(interfaceData, handleValue, requestParams) {
let interfaceRunData = Object.assign({}, interfaceData);
function paramsToObjectWithEnable(arr) {
const obj = {};
safeArray(arr).forEach(item => {
if (item && item.name && (item.enable || item.required === '1')) {
obj[item.name] = handleValue(item.value, currDomain.global);
if (requestParams) {
requestParams[item.name] = obj[item.name];
}
}
});
return obj;
}
function paramsToObjectUnWithEnable(arr) {
const obj = {};
safeArray(arr).forEach(item => {
if (item && item.name) {
obj[item.name] = handleValue(item.value, currDomain.global);
if (requestParams) {
requestParams[item.name] = obj[item.name];
}
}
});
return obj;
}
let { case_env, path, env, _id } = interfaceRunData;
let currDomain,
requestBody,
requestOptions = {};
currDomain = handleCurrDomain(env, case_env);
interfaceRunData.req_params = interfaceRunData.req_params || [];
interfaceRunData.req_params.forEach(item => {
let val = handleValue(item.value, currDomain.global);
if (requestParams) {
requestParams[item.name] = val;
}
path = path.replace(`:${item.name}`, val || `:${item.name}`);
path = path.replace(`{${item.name}}`, val || `{${item.name}}`);
});
const urlObj = URL.parse(joinPath(currDomain.domain, path), true);
const url = URL.format({
protocol: urlObj.protocol || 'http',
host: urlObj.host,
pathname: urlObj.pathname,
query: Object.assign(urlObj.query, paramsToObjectWithEnable(interfaceRunData.req_query))
});
let headers = paramsToObjectUnWithEnable(interfaceRunData.req_headers);
requestOptions = {
url,
caseId: _id,
method: interfaceRunData.method,
headers,
timeout: 82400000
};
// 对 raw 类型的 form 处理
try {
if (interfaceRunData.req_body_type === 'raw') {
if (headers && headers['Content-Type']) {
if (headers['Content-Type'].indexOf('application/x-www-form-urlencoded') >= 0) {
interfaceRunData.req_body_type = 'form';
let reqData = json_parse(interfaceRunData.req_body_other);
if (reqData && typeof reqData === 'object') {
interfaceRunData.req_body_form = [];
Object.keys(reqData).forEach(key => {
interfaceRunData.req_body_form.push({
name: key,
type: 'text',
value: JSON.stringify(reqData[key]),
enable: true
});
});
}
} else if (headers['Content-Type'].indexOf('application/json') >= 0) {
interfaceRunData.req_body_type = 'json';
}
}
}
} catch (e) {
console.error('err', e);
}
if (HTTP_METHOD[interfaceRunData.method].request_body) {
if (interfaceRunData.req_body_type === 'form') {
requestBody = paramsToObjectWithEnable(
safeArray(interfaceRunData.req_body_form).filter(item => {
return item.type == 'text';
})
);
} else if (interfaceRunData.req_body_type === 'json') {
let reqBody = isJson5(interfaceRunData.req_body_other);
if (reqBody === false) {
requestBody = interfaceRunData.req_body_other;
} else {
if (requestParams) {
requestParams = Object.assign(requestParams, reqBody);
}
requestBody = handleJson(reqBody, val => handleValue(val, currDomain.global));
}
} else {
requestBody = interfaceRunData.req_body_other;
}
requestOptions.data = requestBody;
if (interfaceRunData.req_body_type === 'form') {
requestOptions.files = paramsToObjectWithEnable(
safeArray(interfaceRunData.req_body_form).filter(item => {
return item.type == 'file';
})
);
} else if (interfaceRunData.req_body_type === 'file') {
requestOptions.file = 'single-file';
}
}
return requestOptions;
}
exports.checkRequestBodyIsRaw = checkRequestBodyIsRaw;
exports.handleParams = handleParams;
exports.handleContentType = handleContentType;
exports.crossRequest = crossRequest;
exports.handleCurrDomain = handleCurrDomain;
exports.checkNameIsExistInArray = checkNameIsExistInArray;

202
common/power-string.js Normal file
View File

@@ -0,0 +1,202 @@
/**
* @author suxiaoxin
*/
const aUniqueVerticalStringNotFoundInData = '___UNIQUE_VERTICAL___';
const aUniqueCommaStringNotFoundInData = '___UNIQUE_COMMA___';
const segmentSeparateChar = '|';
const methodAndArgsSeparateChar = ':';
const argsSeparateChar = ',';
const md5 = require('md5');
const sha = require('sha.js');
const Base64 = require('js-base64').Base64;
const stringHandles = {
md5: function(str) {
return md5(str);
},
sha: function(str, arg) {
return sha(arg)
.update(str)
.digest('hex');
},
/**
* type: sha1 sha224 sha256 sha384 sha512
*/
sha1: function(str) {
return sha('sha1')
.update(str)
.digest('hex');
},
sha224: function(str) {
return sha('sha224')
.update(str)
.digest('hex');
},
sha256: function(str) {
return sha('sha256')
.update(str)
.digest('hex');
},
sha384: function(str) {
return sha('sha384')
.update(str)
.digest('hex');
},
sha512: function(str) {
return sha('sha512')
.update(str)
.digest('hex');
},
base64: function(str) {
return Base64.encode(str);
},
unbase64: function(str) {
return Base64.decode(str);
},
substr: function(str, ...args) {
return str.substr(...args);
},
concat: function(str, ...args) {
args.forEach(item => {
str += item;
});
return str;
},
lconcat: function(str, ...args) {
args.forEach(item => {
str = item + this._string;
});
return str;
},
lower: function(str) {
return str.toLowerCase();
},
upper: function(str) {
return str.toUpperCase();
},
length: function(str) {
return str.length;
},
number: function(str) {
return !isNaN(str) ? +str : str;
}
};
let handleValue = function(str) {
return str;
};
const _handleValue = function(str) {
if (str[0] === str[str.length - 1] && (str[0] === '"' || str[0] === "'")) {
str = str.substr(1, str.length - 2);
}
return handleValue(
str
.replace(new RegExp(aUniqueVerticalStringNotFoundInData, 'g'), segmentSeparateChar)
.replace(new RegExp(aUniqueCommaStringNotFoundInData, 'g'), argsSeparateChar)
);
};
class PowerString {
constructor(str) {
this._string = str;
}
toString() {
return this._string;
}
}
function addMethod(method, fn) {
PowerString.prototype[method] = function(...args) {
args.unshift(this._string + '');
this._string = fn.apply(this, args);
return this;
};
}
function importMethods(handles) {
for (let method in handles) {
addMethod(method, handles[method]);
}
}
importMethods(stringHandles);
function handleOriginStr(str, handleValueFn) {
if (!str) return str;
if (typeof handleValueFn === 'function') {
handleValue = handleValueFn;
}
str = str
.replace('\\' + segmentSeparateChar, aUniqueVerticalStringNotFoundInData)
.replace('\\' + argsSeparateChar, aUniqueCommaStringNotFoundInData)
.split(segmentSeparateChar)
.map(handleSegment)
.reduce(execute, null)
.toString();
return str;
}
function execute(str, curItem, index) {
if (index === 0) {
return new PowerString(curItem);
}
return str[curItem.method].apply(str, curItem.args);
}
function handleSegment(str, index) {
str = str.trim();
if (index === 0) {
return _handleValue(str);
}
let method,
args = [];
if (str.indexOf(methodAndArgsSeparateChar) > 0) {
str = str.split(methodAndArgsSeparateChar);
method = str[0].trim();
args = str[1].split(argsSeparateChar).map(item => _handleValue(item.trim()));
} else {
method = str;
}
if (typeof stringHandles[method] !== 'function') {
throw new Error(`This method name(${method}) is not exist.`);
}
return {
method,
args
};
}
module.exports = {
utils: stringHandles,
PowerString,
/**
* 类似于 angularJs的 filter 功能
* @params string
* @params fn 处理参数值函数,默认是一个返回原有参数值函数
*
* @expamle
* filter('string | substr: 1, 10 | md5 | concat: hello ')
*/
filter: handleOriginStr
};

View File

@@ -0,0 +1,197 @@
const _ = require('underscore');
let fieldNum = 1;
exports.schemaTransformToTable = schema => {
try {
schema = checkJsonSchema(schema);
let result = Schema(schema, 0);
result = _.isArray(result) ? result : [result];
return result;
} catch (err) {
console.log(err);
}
};
// 自动添加type
function checkJsonSchema(json) {
let newJson = Object.assign({}, json);
if (_.isUndefined(json.type) && _.isObject(json.properties)) {
newJson.type = 'object';
}
return newJson;
}
const mapping = function(data, index) {
switch (data.type) {
case 'string':
return SchemaString(data);
case 'number':
return SchemaNumber(data);
case 'array':
return SchemaArray(data, index);
case 'object':
return SchemaObject(data, index);
case 'boolean':
return SchemaBoolean(data);
case 'integer':
return SchemaInt(data);
default:
return SchemaOther(data);
}
};
const ConcatDesc = (title, desc) => {
return [title, desc].join('\n').trim();
};
const Schema = (data, key) => {
let result = mapping(data, key);
if (data.type !== 'object') {
let desc = result.desc;
let d = result.default;
let children = result.children;
delete result.desc;
delete result.default;
delete result.children;
let item = {
type: data.type,
key,
desc,
default: d,
sub: result
};
if (_.isArray(children)) {
item = Object.assign({}, item, { children });
}
return item;
}
return result;
};
const SchemaObject = (data, key) => {
let { properties, required } = data;
properties = properties || {};
required = required || [];
let result = [];
Object.keys(properties).map((name, index) => {
let value = properties[name];
let copiedState = checkJsonSchema(JSON.parse(JSON.stringify(value)));
let optionForm = Schema(copiedState, key + '-' + index);
let item = {
name,
key: key + '-' + index,
desc: ConcatDesc(copiedState.title, copiedState.description),
required: required.indexOf(name) != -1
};
if (value.type === 'object' || (_.isUndefined(value.type) && _.isArray(optionForm))) {
item = Object.assign({}, item, { type: 'object', children: optionForm });
delete item.sub;
} else {
item = Object.assign({}, item, optionForm);
}
result.push(item);
});
return result;
};
const SchemaString = data => {
let item = {
desc: ConcatDesc(data.title, data.description),
default: data.default,
maxLength: data.maxLength,
minLength: data.minLength,
enum: data.enum,
enumDesc: data.enumDesc,
format: data.format,
mock: data.mock && data.mock.mock
};
return item;
};
const SchemaArray = (data, index) => {
data.items = data.items || { type: 'string' };
let items = checkJsonSchema(data.items);
let optionForm = mapping(items, index);
// 处理array嵌套array的问题
let children =optionForm ;
if (!_.isArray(optionForm) && !_.isUndefined(optionForm)) {
optionForm.key = 'array-' + fieldNum++;
children = [optionForm];
}
let item = {
desc: ConcatDesc(data.title, data.description),
default: data.default,
minItems: data.minItems,
uniqueItems: data.uniqueItems,
maxItems: data.maxItems,
itemType: items.type,
children
};
if (items.type === 'string') {
item = Object.assign({}, item, { itemFormat: items.format });
}
return item;
};
const SchemaNumber = data => {
let item = {
desc: ConcatDesc(data.title, data.description),
maximum: data.maximum,
minimum: data.minimum,
default: data.default,
format: data.format,
enum: data.enum,
enumDesc: data.enumDesc,
mock: data.mock && data.mock.mock
};
return item;
};
const SchemaInt = data => {
let item = {
desc: ConcatDesc(data.title, data.description),
maximum: data.maximum,
minimum: data.minimum,
default: data.default,
format: data.format,
enum: data.enum,
enumDesc: data.enumDesc,
mock: data.mock && data.mock.mock
};
return item;
};
const SchemaBoolean = data => {
let item = {
desc: ConcatDesc(data.title, data.description),
default: data.default,
enum: data.enum,
mock: data.mock && data.mock.mock
};
return item;
};
const SchemaOther = data => {
let item = {
desc: ConcatDesc(data.title, data.description),
default: data.default,
mock: data.mock && data.mock.mock
};
return item;
};

BIN
common/tui-editor/dist/tui-editor-2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
@charset "UTF-8";.CodeMirror{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.tui-editor-contents :not(table){line-height:160%;box-sizing:content-box}.tui-editor-contents address,.tui-editor-contents cite,.tui-editor-contents dfn,.tui-editor-contents em,.tui-editor-contents i,.tui-editor-contents var{font-style:italic}.tui-editor-contents strong{font-weight:700}.tui-editor-contents p{margin:10px 0;color:#555}.tui-editor-contents>div>div:first-of-type h1,.tui-editor-contents>h1:first-of-type{margin-top:14px}.tui-editor-contents h1,.tui-editor-contents h2,.tui-editor-contents h3,.tui-editor-contents h5{font-weight:700}.tui-editor-contents h1{font-size:1.6rem;line-height:28px;border-bottom:3px double #999;margin:52px 0 15px 0;padding-bottom:7px;color:#000}.tui-editor-contents h2{font-size:1.3rem;line-height:23px;border-bottom:1px solid #dbdbdb;margin:30px 0 13px 0;padding-bottom:7px;color:#333}.tui-editor-contents h3,.tui-editor-contents h4{font-size:1.2rem;line-height:18px;margin:20px 0 2px;color:#333}.tui-editor-contents h5,.tui-editor-contents h6{font-size:1rem;line-height:17px;margin:10px 0 -4px;color:#333}.tui-editor-contents blockquote{margin:15px 0}.tui-editor-contents blockquote{border-left:4px solid #ddd;padding:0 15px;color:#777}.tui-editor-contents blockquote>:first-child{margin-top:0}.tui-editor-contents blockquote>:last-child{margin-bottom:0}.tui-editor-contents code,.tui-editor-contents pre{font-family:Consolas,Courier,"Apple SD 산돌고딕 Neo",-apple-system,"Lucida Grande","Apple SD Gothic Neo","맑은 고딕","Malgun Gothic","Segoe UI","돋움",dotum,sans-serif;border:0;border-radius:0}.tui-editor-contents pre{margin:2px 0 8px;padding:18px;background-color:#f5f7f8}.tui-editor-contents code{color:#c1788b;padding:4px 4px 2px 0;letter-spacing:-.3px}.tui-editor-contents pre code{padding:0;color:inherit;white-space:pre-wrap;background-color:transparent}.tui-editor-contents pre.addon{border:1px solid #e8ebed;background-color:#fff}.tui-editor-contents img{margin:4px 0 10px;box-sizing:border-box;vertical-align:top;max-width:100%}.tui-editor-contents table{margin:2px 0 14px;color:#555;width:auto;border-collapse:collapse;box-sizing:border-box}.tui-editor-contents table td,.tui-editor-contents table th{height:32px;padding:5px 14px 5px 12px}.tui-editor-contents table td{border:1px solid #eaeaea}.tui-editor-contents table th{border:1px solid #72777b;border-top:0;background-color:#7b8184;font-weight:300;color:#fff;padding-top:6px}.tui-editor-contents dir,.tui-editor-contents menu,.tui-editor-contents ol,.tui-editor-contents ul{display:block;list-style-type:disc;padding-left:17px;margin:6px 0 10px;color:#555}.tui-editor-contents ol{list-style-type:decimal}.tui-editor-contents ol ol,.tui-editor-contents ol ul,.tui-editor-contents ul ol,.tui-editor-contents ul ul{margin-top:0!important;margin-bottom:0!important}.tui-editor-contents ul li{position:relative}.tui-editor-contents ul p,ol p{margin:0}.tui-editor-contents pre ul li:before,.tui-editor-contents ul li.task-list-item:before{content:""}.tui-editor-contents hr{border-top:1px solid #eee;margin:16px 0}.tui-editor-contents a{text-decoration:underline;color:#5286bc}.tui-editor-contents a:hover{color:#007cff}.tui-editor-contents{font-size:13px;margin:0;padding:0}.tui-editor-contents .task-list-item{border:0;list-style:none;padding-left:22px;margin-left:-22px;background-repeat:no-repeat;background-size:16px 16px;background-position:0 2px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADdJREFUKBVjvHv37n8GMgALSI+SkhJJWu/du8fARJIOJMWjGpECA505GjjoIYLEB6dVUNojFQAA/1MJUFWet/4AAAAASUVORK5CYII=)}.tui-editor-contents .task-list-item.checked{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAMpJREFUKBVjjJ/64D8DGYCJDD1gLbTVyM3OxJDiJMzAxcYIdyALnIWDAdJU7i/OICfCxsDMxMgwc88bwk5F1vTs/W+GFUffwY2H+1FBlI2hLliCQYCbGSyJrqlzwwuGj9//YWoMtRBgUBJnZ6gMEGeQFWaFOw9kE7omkG5GWDyCPF7mJ86gIMbO8P//fwZGRkYGXJpAGuFO/fbrP0PXppcMD179JKgJRSOIA9N8/NZXrM4DqYEBjOgAaYYFOUwRNhruVGyS+MTI1ggAx8NTGcUtFVQAAAAASUVORK5CYII=)}.tui-editor-contents .task-list-item .task-list-item-checkbox,.tui-editor-contents .task-list-item input[type=checkbox]{margin-left:-17px;margin-right:3.8px;margin-top:3px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
common/tui-editor/dist/tui-editor.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

288
common/utils.js Normal file
View File

@@ -0,0 +1,288 @@
const Mock = require('mockjs');
const filter = require('./power-string.js').filter;
const stringUtils = require('./power-string.js').utils;
const json5 = require('json5');
const Ajv = require('ajv');
/**
* 作用:解析规则串 key ,然后根据规则串的规则以及路径找到在 json 中对应的数据
* 规则串:$.{key}.{body||params}.{dataPath} 其中 body 为返回数据params 为请求数据datapath 为数据的路径
* 数组:$.key.body.data.arr[0]._id (获取 key 所指向请求的返回数据的 arr 数组的第 0 项元素的 _id 属性)
* 对象:$.key.body.data.obj._id ((获取 key 所指向请求的返回数据的 obj 对象的 _id 属性))
*
* @param String key 规则串
* @param Object json 数据
* @returns
*/
function simpleJsonPathParse(key, json) {
if (!key || typeof key !== 'string' || key.indexOf('$.') !== 0 || key.length <= 2) {
return null;
}
let keys = key.substr(2).split('.');
keys = keys.filter(item => {
return item;
});
for (let i = 0, l = keys.length; i < l; i++) {
try {
let m = keys[i].match(/(.*?)\[([0-9]+)\]/);
if (m) {
json = json[m[1]][m[2]];
} else {
json = json[keys[i]];
}
} catch (e) {
json = '';
break;
}
}
return json;
}
// 全局变量 {{ global.value }}
// value 是在环境变量中定义的字段
function handleGlobalWord(word, json) {
if (!word || typeof word !== 'string' || word.indexOf('global.') !== 0) return word;
let keys = word.split('.');
keys = keys.filter(item => {
return item;
});
return json[keys[0]][keys[1]] || word;
}
function handleMockWord(word) {
if (!word || typeof word !== 'string' || word[0] !== '@') return word;
return Mock.mock(word);
}
/**
*
* @param {*} data
* @param {*} handleValueFn 处理参数值函数
*/
function handleJson(data, handleValueFn) {
if (!data) {
return data;
}
if (typeof data === 'string') {
return handleValueFn(data);
} else if (typeof data === 'object') {
for (let i in data) {
data[i] = handleJson(data[i], handleValueFn);
}
} else {
return data;
}
return data;
}
function handleValueWithFilter(context) {
return function(match) {
if (match[0] === '@') {
return handleMockWord(match);
} else if (match.indexOf('$.') === 0) {
return simpleJsonPathParse(match, context);
} else if (match.indexOf('global.') === 0) {
return handleGlobalWord(match, context);
} else {
return match;
}
};
}
function handleFilter(str, match, context) {
match = match.trim();
try {
let a = filter(match, handleValueWithFilter(context));
return a;
} catch (err) {
return str;
}
}
function handleParamsValue(val, context = {}) {
const variableRegexp = /\{\{\s*([^}]+?)\}\}/g;
if (!val || typeof val !== 'string') {
return val;
}
val = val.trim();
let match = val.match(/^\{\{([^\}]+)\}\}$/);
if (!match) {
// val ==> @name 或者 $.body
if (val[0] === '@' || val[0] === '$') {
return handleFilter(val, val, context);
}
} else {
return handleFilter(val, match[1], context);
}
return val.replace(variableRegexp, (str, match) => {
return handleFilter(str, match, context);
});
}
exports.handleJson = handleJson;
exports.handleParamsValue = handleParamsValue;
exports.simpleJsonPathParse = simpleJsonPathParse;
exports.handleMockWord = handleMockWord;
exports.joinPath = (domain, joinPath) => {
let l = domain.length;
if (domain[l - 1] === '/') {
domain = domain.substr(0, l - 1);
}
if (joinPath[0] !== '/') {
joinPath = joinPath.substr(1);
}
return domain + joinPath;
};
// exports.safeArray = arr => {
// return Array.isArray(arr) ? arr : [];
// };
function safeArray(arr) {
return Array.isArray(arr) ? arr : [];
}
exports.safeArray = safeArray;
exports.isJson5 = function isJson5(json) {
if (!json) return false;
try {
json = json5.parse(json);
return json;
} catch (e) {
return false;
}
};
function isJson(json) {
if (!json) return false;
try {
json = JSON.parse(json);
return json;
} catch (e) {
return false;
}
}
exports.isJson = isJson;
exports.unbase64 = function(base64Str) {
try {
return stringUtils.unbase64(base64Str);
} catch (err) {
return base64Str;
}
};
exports.json_parse = function(json) {
try {
return JSON.parse(json);
} catch (err) {
return json;
}
};
exports.json_format = function(json) {
try {
return JSON.stringify(JSON.parse(json), null, ' ');
} catch (e) {
return json;
}
};
exports.ArrayToObject = function(arr) {
let obj = {};
safeArray(arr).forEach(item => {
obj[item.name] = item.value;
});
return obj;
};
exports.timeago = function(timestamp) {
let minutes, hours, days, seconds, mouth, year;
const timeNow = parseInt(new Date().getTime() / 1000);
seconds = timeNow - timestamp;
if (seconds > 86400 * 30 * 12) {
year = parseInt(seconds / (86400 * 30 * 12));
} else {
year = 0;
}
if (seconds > 86400 * 30) {
mouth = parseInt(seconds / (86400 * 30));
} else {
mouth = 0;
}
if (seconds > 86400) {
days = parseInt(seconds / 86400);
} else {
days = 0;
}
if (seconds > 3600) {
hours = parseInt(seconds / 3600);
} else {
hours = 0;
}
minutes = parseInt(seconds / 60);
if (year > 0) {
return year + '年前';
} else if (mouth > 0 && year <= 0) {
return mouth + '月前';
} else if (days > 0 && mouth <= 0) {
return days + '天前';
} else if (days <= 0 && hours > 0) {
return hours + '小时前';
} else if (hours <= 0 && minutes > 0) {
return minutes + '分钟前';
} else if (minutes <= 0 && seconds > 0) {
if (seconds < 30) {
return '刚刚';
} else {
return seconds + '秒前';
}
} else {
return '刚刚';
}
};
// json schema 验证器
exports.schemaValidator = function(schema, params) {
try {
const ajv = new Ajv({
format: false,
meta: false
});
let metaSchema = require('ajv/lib/refs/json-schema-draft-04.json');
ajv.addMetaSchema(metaSchema);
ajv._opts.defaultMeta = metaSchema.id;
ajv._refs['http://json-schema.org/schema'] = 'http://json-schema.org/draft-04/schema';
var localize = require('ajv-i18n');
schema = schema || {
type: 'object',
title: 'empty object',
properties: {}
};
const validate = ajv.compile(schema);
let valid = validate(params);
let message = '';
if (!valid) {
localize.zh(validate.errors);
message += ajv.errorsText(validate.errors, { separator: '\n' });
}
return {
valid: valid,
message: message
};
} catch (e) {
return {
valid: false,
message: e.message
};
}
};