znt/module/api/js/index.ui.js

544 lines
20 KiB
JavaScript
Raw Permalink Normal View History

/**
* 获取列表类型
* Get the list type.
*
* @param {object} docApp
* @returns {string}
*/
function getListType(docApp)
{
docApp = docApp || getDocApp();
if(docApp.mode !== 'list' || docApp.module) return '';
const listType = docApp.signals.listType.value;
return listType;
}
/**
* 定义页面上的自定义渲染器
* Define the custom renderers on the page.
*/
const customRenders =
{
/**
* 落地页渲染根据 API 接口类型显示不同的内容
* Home page render, show different content according to the API interface type.
*/
home: function()
{
const docApp = this;
const filterType = docApp.location.filterType || 'nolink';
const pager = $.extend({page: 1, recPerPage: 20}, docApp.signals.pager.value);
const params = docApp.signals.params.value || 'notempty_unclosed';
const homeViewUrl = $.createLink('api', 'ajaxgethome', `type=${filterType}&params=${params}&recPerPage=${pager.recPerPage}&pageID=${pager.page}`);
return {fetcher: homeViewUrl, clearBeforeLoad: false, className: 'doc-api-home h-full', class: 'h-full col',htmlRender: (element, props) => $(element).morphInner(`<div class="lazy-content doc-api-home h-full">${props.html}</div>`)};
},
/**
* 定义 API 文档编辑器渲染包括查看编辑和创建
* Define the API doc editor render, including view, edit and create.
*/
editor: function()
{
const mode = this.mode;
if(mode === 'create')
{
const libID = this.libID;
const moduleID = this.moduleID;
return {fetcher: $.createLink('api', 'create', `libID=${libID}&moduleID=${moduleID}&space=api`), loadingText: getDocAppLang('loading')};
}
if(mode === 'edit')
{
return {fetcher: $.createLink('api', 'edit', `apiID=${this.docID}`), loadingText: getDocAppLang('loading')};
}
const docInfo = this.doc;
const doc = docInfo ? docInfo.data : {id: this.docID, lib: this.libID, module: this.moduleID};
if(!docAppHasPriv('view')) return {html: `<h1>${doc ? doc.title : ''}</h1><p>${getDocAppLang('accessDenied')}</p>`};
const release = this.signals.libReleaseMap.value[doc.lib] || 0;
const version = this.signals.docVersion.value || 0;
return {fetcher: $.createLink('api', 'view', `libID=${doc.lib}&apiID=${doc.id}&moduleID=${doc.module}&version=${version}&release=${release}`), loadingText: getDocAppLang('loadingDocsData'), className: 'doc-editor-content'};
},
/**
* 定义 API 文档列表渲染包括结构和版本
* Define the API doc list render, including structs and releases.
*/
list: function()
{
const listType = getListType(this);
const libID = this.libID;
const releaseID = this.signals.libReleaseMap.value[libID] || 0;
if(listType === 'structs') return {fetcher: $.createLink('api', 'struct', `libID=${this.libID}&releaseID=${releaseID}`), loadingText: getDocAppLang('loading'), className: 'api-struct-list'};
if(listType === 'releases') return {fetcher: $.createLink('api', 'releases', `libID=${this.libID}`), loadingText: getDocAppLang('loading'), className: 'api-release-list'};
},
/**
* 定义 API 文档列表筛选菜单渲染当展示结构和版本时不显示筛选菜单
* Define the API doc list filter menu render, not show the filter menu when showing structs and releases.
*/
filters: function()
{
const listType = getListType(this);
if(this.mode !== 'list' || !listType) return;
return null;
},
/**
* 定义 API 文档列表工具栏渲染
* Define the API doc list toolbar render.
*/
toolbar: function()
{
const listType = getListType(this);
if(this.mode === 'list' && listType)
{
const items = [];
if(docAppHasPriv('createStruct') && listType === 'structs') items.push({text: getDocAppLang('createStruct'), icon: 'plus', btnType: 'primary', 'data-toggle': 'modal', 'data-size': 'lg', url: $.createLink('api', 'createStruct', `libID=${this.libID}`)});
if(docAppHasPriv('createRelease') && listType === 'releases') items.push({text: getDocAppLang('createRelease'), icon: 'plus', btnType: 'primary', 'data-toggle': 'modal', url: $.createLink('api', 'createRelease', `libID=${this.libID}`)});
if(!items.length) return null;
return {component: 'toolbar', props: {items: items}};
}
},
/**
* 定义顶部面包屑导航渲染
* Define the top breadcrumb render.
*/
'app-nav': function(items)
{
const lib = this.lib;
if(!lib) return items;
const versions = lib.data.versions;
if(versions && versions.length)
{
const viewIndex = items.findIndex(item => item[0] === 'lib');
if(viewIndex >= 0)
{
const libView = items[viewIndex][1];
const libID = lib.data.id;
const release = this.signals.libReleaseMap.value[libID] || 0;
const options = [{text: getDocAppLang('latestVersion'), value: 0, selected: !release, command: `changeLibRelease/${libID}/0`}];
versions.forEach(version => options.push({selected: release === version.id, text: `v${version.version}`, value: version.id, command: `changeLibRelease/${libID}/${version.id}`}));
const currentVersion = versions.find(x => x.id === release);
const versionPicker = zui.renderCustomContent(
{
content:
{
component: 'DropdownButton',
props:
{
text : currentVersion ? currentVersion.version : getDocAppLang('latestVersion'),
size : 'xs',
type : 'gray-pale',
rounded : 'full',
className: 'h-4 gap-1 mr-2',
items : options,
}
}
});
items[viewIndex] = ['lib', [libView, versionPicker]];
}
}
const listType = getListType(this);
if(this.mode !== 'list' || !listType) return items;
if(listType === 'structs') items.push([listType, zui.renderCustomContent({className: 'mx-2', content: getDocAppLang('struct')})]);
if(listType === 'releases') items.push([listType, zui.renderCustomContent({className: 'mx-2', content: getDocAppLang('releases')})]);
return items;
},
/**
* 定义侧边栏渲染显示结构版本和模块
* Define the sidebar render, show structs, releases and modules.
*/
'sidebar-before': function()
{
if(!this.libID) return;
const canViewStructs = docAppHasPriv('struct');
const canViewReleases = docAppHasPriv('releases');
if(!canViewStructs && !canViewReleases) return;
const isListMode = this.mode === 'list';
const lang = getDocAppLang();
const listType = getListType(this);
const items = [];
if(canViewStructs) items.push({text: lang.struct, selected: listType === 'structs' && isListMode, icon: 'treemap muted', command: 'showStructs'}, {type: 'divider', className: 'my-1'});
if(canViewReleases) items.push({text: lang.releases, selected: listType === 'releases' && isListMode, icon: 'version muted', command: 'showReleases'}, {type: 'divider', className: 'my-1'});
items.push({text: lang.module, icon: 'list muted', command: 'showModules'});
return {
component: 'tree',
className: 'p-2 pb-0 api-lib-menu',
props: {items: items, itemProps: {className: 'state'}}
};
}
};
/**
* 获取查看视图的 URL用于更新浏览器地址栏
* Get the view mode URL for updating the browser address bar.
*/
function getViewModeUrl(options)
{
const doc = this.doc;
options = $.extend({release: (doc ? this.signals.libReleaseMap.value[doc.lib] : 0) || 0}, options);
let url;
if(this.mode === 'view')
{
const params = zui.formatString('libID={libID}&apiID={docID}&moduleID={moduleID}&version={docVersion}&release={release}', options).replace('&version=0&release=0', '');
url = $.createLink('api', 'view', params);
}
else
{
const params = zui.formatString('libID={libID}&moduleID={moduleID}&apiID={docID}&version={docVersion}&release={release}&browseType={filterType}&params={params}&orderBy={orderBy}&recTotal={recTotal}&recPerPage={recPerPage}&pageID={page}&mode={mode}&search={search}', options).replace('libID=0&moduleID=0&apiID=0&version=0&release=0&browseType=all&params=0&orderBy=order_asc&recTotal=0&recPerPage=20&pageID=1&mode=&search=', '').replace('&apiID=0&version=0&release=0&browseType=all&params=0&orderBy=order_asc&recTotal=0&recPerPage=20&pageID=1&mode=&search=', '').replace('&browseType=all&params=0&orderBy=order_asc&recTotal=0&recPerPage=20&pageID=1&mode=&search=', '');
url = $.createLink('api', 'index', params);
}
return url;
}
/**
* 获取文档详情侧边栏标签页定义
* Get the doc view sidebar tabs.
*
* @param {object} doc
*/
function getDocViewSidebarTabs(doc, info)
{
const lang = getDocAppLang();
if(info.isNewDoc || info.mode === 'create') return [];
return [
{key: 'info', icon: 'info', title: lang.docInfo},
info.mode === 'edit' ? null : {key: 'outline', icon: 'list-box', title: lang.docOutline},
{key: 'history', icon: 'file-log', title: lang.history},
].filter(Boolean);
}
function getSpaceFetcher(spaceType, spaceID)
{
const parts = String(spaceID).split('.');
const objectType = parts[0] || 'nolink';
const objectID = parts[1] || 0;
const libID = getDocApp().libID;
return $.createLink('api', 'ajaxGetDropMenu', `objectType=${objectType}&objectID=${objectID}&libID=${libID}`);
}
function handleClickSpaceMenu(event, value)
{
event.preventDefault();
if($(event.target).closest('.dropmenu-nav').length) return;
const parts = String(value).split('.');
const type = parts[0];
const id = parts[1] || 0;
const docApp = getDocApp();
if(type === 'product' || type === 'project') docApp.selectSpace(value, true, true);
else docApp.selectSpace('nolink', +id, true);
}
function getSpaceMenuText(text, state)
{
const spaceID = this.spaceID;
if(spaceID === 'nolink') return text;
const libTypeList = getDocAppLang('libTypeList');
return `${libTypeList[spaceID.split('.')[0]]} / ${text}`;
}
function getSpaceMenuOptions(spaceType, spaceID)
{
const libID = this.libID;
const lang = $('html').attr('lang');
return {
caret : true,
popWidth : ['zh-cn', 'zh-tw'].includes(lang) ? 350 : 400,
onClickItem : handleClickSpaceMenu,
defaultValue: spaceID === 'nolink' ? `lib.${libID}`: spaceID,
display : getSpaceMenuText.bind(this)
};
}
/* 扩展文档应用操作按钮生成定义。 Extend the doc app action definition. */
$.extend(window.docAppActions,
{
/**
* 定义文档编辑时的操作按钮
* Define the actions on toolbar of the doc editing page.
*/
'doc-edit': function(info)
{
const doc = info.data;
if(!doc) return;
const lang = getDocAppLang();
return [
{text: lang.save, size: 'md', className: 'btn-wide', type: 'primary', command: 'saveApiDoc'},
{text: lang.cancel, size: 'md', className: 'btn-wide', type: 'primary-outline', command: 'cancelEditDoc'},
];
},
/**
* 定义文档创建时的操作按钮
* Define the actions on toolbar of the doc editing page.
*/
'doc-create': function(info)
{
const doc = info.data;
if(!doc) return;
const lang = getDocAppLang();
return [
{text: lang.save, size: 'md', className: 'btn-wide', type: 'primary', command: 'saveApiDoc'},
{text: lang.cancel, size: 'md', className: 'btn-wide', type: 'primary-outline', command: 'cancelCreateDoc'},
];
},
});
/* 扩展文档应用命令定义。 Extend the doc app command definition. */
$.extend(window.docAppCommands,
{
/**
* 保存 API 文档数据
* Save the API doc data.
*/
saveApiDoc: function()
{
/* 触发 API 表单的提交事件。 */
$('#docApp .doc-view form').trigger('submit');
},
/**
* 取消编辑 API 文档库
* Create api lib.
*/
createLib: function(_, args)
{
const docApp = getDocApp();
const spaceID = args[0] || docApp.spaceID;
const parts = String(spaceID).split('.');
const url = $.createLink('api', 'createLib', `type=${parts[0] || 'nolink'}&objectID=${parts[1] || 0}`);
zui.Modal.open({size: 'sm', url: url});
},
/**
* 加载指定的 API 文档
* Load the specified API doc.
*/
loadApi: function(_, args)
{
const apiID = args[0] || this.docID;
const version = args[1] || 0;
const release = args[2] || 0;
const select = !!args[3];
$.getJSON($.createLink('api', 'ajaxGetApi', `apiID=${apiID}&version=${version}&release=${release}`), function(result)
{
if(!result || typeof result !== 'object') return;
const docApp = getDocApp();
docApp.update('doc', result);
if(select) docApp.selectDoc(apiID);
});
},
/**
* 选择 API 文档
* Select the specified API doc.
*/
selectApi: function(_, args)
{
const apiID = args[0];
getDocApp().selectDoc(apiID);
},
/**
* 显示结构列表
* Show the struct list.
*/
showStructs: function()
{
getDocApp().changeState(
{
mode: 'list',
moduleID: 0,
docID: 0,
listType: 'structs',
});
},
/**
* 显示版本列表
* Show the release list.
*/
showReleases: function()
{
getDocApp().changeState(
{
mode: 'list',
moduleID: 0,
docID: 0,
listType: 'releases',
});
},
/**
* 显示API 目录
* Show the api catalog.
*/
showModules: function()
{
getDocApp().changeState(
{
mode: 'list',
moduleID: 0,
docID: 0,
listType: '',
});
},
/**
* 获取库的 API 列表
* Get the API list of the library.
*/
loadLibApi: function(_, args)
{
const docApp = getDocApp();
const libReleaseMap = docApp.signals.libReleaseMap.value;
const libID = args[0] || docApp.libID;
if(!libID) return;
const releaseID = args[1] || libReleaseMap[libID] || 0;
const url = $.createLink('api', 'ajaxGetLibApiList', `libID=${libID}&releaseID=${releaseID}`);
$.getJSON(url, function(data)
{
const lib = docApp.getLib(libID);
if(lib && lib.docs && lib.docs.length) docApp.delete('doc', lib.docs.map(x => x.data.id));
docApp.update('doc', data);
});
},
/**
* 更改当前库的发布版本
* Change the release of the current library.
*/
changeLibRelease: function(_, args)
{
const docApp = getDocApp();
const libID = args[0];
const release = args[1];
const libReleaseMap = docApp.signals.libReleaseMap.value;
if(libReleaseMap[libID] === release) return;
libReleaseMap[libID] = release;
docApp.signals.libReleaseMap.value = $.extend({}, libReleaseMap);
docApp.executeCommand('loadLibApi', [libID, release]);
const doc = docApp.doc;
if(doc && doc.data.lib === libID) docApp.executeCommand('loadApi', [doc.data.id, 0, release]);
},
/**
* 加载懒加载内容
* Load the lazy content.
*/
loadLazyContent: function(_, args)
{
const selector = args[0];
if(!selector) return;
$(selector).closest('.lazy-content').trigger('loadContent');
},
/**
* 更新懒加载内容
* Update the lazy content.
*/
updateLazyContent: function(context, args)
{
const event = context.event;
const $element = $(event.currentTarget);
const selector = args[0] || $element.data('lazyTarget');
const $lazy = (selector ? $(selector) : $element).closest('.lazy-content');
const url = $element.data('url') || $element.attr('href');
if(url) $lazy.trigger('loadContent', url);
event.preventDefault();
event.stopPropagation();
},
/**
* 更新首页内容
* Update the home content.
*/
loadHome: function(context, args)
{
const event = context.event;
const $element = $(event.currentTarget);
const data = $element.data();
const type = args[0] !== undefined ? args[0] : data.type;
const params = args[1] !== undefined ? args[1] : data.params;
const recPerPage = args[2] !== undefined ? args[2] : data.recPerPage;
const pageID = args[3] !== undefined ? args[3] : data.pageID;
window.loadHome(type, params, recPerPage, pageID);
},
/**
* 显示首页菜单
* Show the home item menu.
*/
showHomeItemMenu: function(context, args)
{
const event = context.event;
const type = args[0];
const id = args[1];
const lang = getDocAppLang();
const items = [];
if(type === 'nolink')
{
if(docAppHasPriv('editLib')) items.push({text: lang.actions.editLib, command: `editLib/${id}`});
if(docAppHasPriv('deleteLib')) items.push({text: lang.actions.deleteLib, command: `deleteLib/${id}`});
}
else if(docAppHasPriv('createLib'))
{
items.push({text: lang.createLib, command: `createLib/${type}.${id}`});
}
if(items.length)
{
zui.ContextMenu.show({event: event, items: items, placement: 'bottom-end'});
}
event.preventDefault();
event.stopPropagation();
},
});
window.loadHome = function(type, params, recPerPage, pageID)
{
const location = {};
if(typeof type === 'object') $.extend(location, type);
else if(type !== undefined) location.filterType = type;
if(params !== undefined) location.params = params;
if(recPerPage !== undefined || pageID !== undefined)
{
location.pager = {};
if(recPerPage !== undefined) location.pager.recPerPage = recPerPage;
if(pageID !== undefined) location.pager.page = pageID;
}
if(Object.keys(location).length) getDocApp().switchView(location, 'home');
};
/**
* 重写文档应用的配置选项方法
* Override the method to set the doc app options.
*/
window._setDocAppOptions = window.setDocAppOptions; // Save the original method.
window.setDocAppOptions = function(_, options) // Override the method.
{
options = window._setDocAppOptions(_, options);
const oldIsMatchFilter = options.isMatchFilter; // Save the original isMatchFilter method.
return $.extend(options,
{
defaultState : {libReleaseMap: {}, listType: ''},
spaceMenuOptions : getSpaceMenuOptions,
customRenders : customRenders,
viewModeUrl : getViewModeUrl,
getDocViewSidebarTabs: getDocViewSidebarTabs,
getSpaceFetcher : getSpaceFetcher,
isMatchFilter : function(type, filterType, item)
{
if(type === 'api') return (item.objectType || 'nolink').toLowerCase() === filterType.toLowerCase();
return oldIsMatchFilter.call(this, type, filterType, item);
}
});
};