Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature : docs desc can support markdown files #85

Merged
merged 3 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/toolkits/docs/template/ng/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ let routes = [
name: 'components',
children: [{ path: '', component: Components }],
},
//单组件示例
{
path: import.meta.env.VITE_CONTEXT + 'demoPage/:cmpId/:demoId',
component: DemoPage,
name: 'demoPage',
},
// 未匹配到目标地址时,进行路由重定向
{
path: '/:pathMatch(.*)*',
Expand Down
355 changes: 34 additions & 321 deletions packages/toolkits/docs/template/ng/src/views/components/components.vue
Original file line number Diff line number Diff line change
@@ -1,348 +1,61 @@
<template>
<div>
<div class="f-r pt48 pl48 pr48">
<div class="fi-1 w0 rel cmp-container">
<!-- 一个组件的文档: 描述md + demos + apis -->
<n-space vertical>
<n-spin :show="cmpTopMdShow">
<div class="markdown-body" v-html="cmpTopMd"></div>
</n-spin>
</n-space>

<template v-if="currJson?.demos?.length > 0">
<n-space vertical>
<n-spin :show="currJsonShow">
<h2 class="f30 fw-normal !mb20">{{ $t('yan-shi') }}</h2>
<n-layout class="f-c f-wrap ofx-auto" :native-scrollbar="true">
<template v-if="currJson.column === '2'">
<div class="one-demo-col2">
<div>
<demo v-for="demo in evenDemo" :key="demo.name" :demo="demo" />
</div>
<div>
<demo v-for="demo in oddDemo" :key="demo.name" :demo="demo" />
</div>
</div>
</template>
<template v-else>
<demo v-for="demo in currJson.demos" :key="demo.name" :demo="demo" class="mb16" />
</template>
</n-layout>
</n-spin>
</n-space>
</template>
<template v-if="currJson.apis?.length > 0">
<div ref="apiDiv">
<h2 id="API" v-if="cmpId !== 'interfaces'" class="f30 fw-normal mt28">API</h2>
<!-- apis 是一个数组 {name,type,properties:[原table内容],events:[] ...........} -->
<div class="mt20" v-for="(oneGroup, idx) in currJson.apis" :key="oneGroup.name">
<div class="f-r f-pos-start fw-bold">
<div :id="oneGroup.name" class="f18">{{ oneGroup.name }}</div>
<div class="ml12 b-a-primary c-primary px8 py4">{{ oneGroup.type }}</div>
</div>
<div v-for="(oneApiArr, key) in oneGroup" :key="key">
<template v-if="key !== 'name' && key !== 'type' && oneApiArr.length > 0">
<div class="f18 py28">{{ key }}</div>
<n-data-table class="api-table" v-bind="getApiTableOpt(oneApiArr)" />
</template>
</div>
</div>
</div>
</template>
<!-- types表格 -->
<template v-if="currJson.types">
<n-data-table class="types-table mt20" v-bind="getTypesTableOpt(currJson.types)" />
</template>
<h2 id="FAQ" v-if="cmpFAQMd" class="f30 fw-normal mt28 mb20">FAQ</h2>
<div class="markdown-body" v-html="cmpFAQMd"></div>
<div v-if="currJson.owner" class="abs right24 top24" @click="copyText(currJson.owner)">{{ $t('doc-owner') }} : {{ currJson.owner }}</div>
</div>
<!-- 目录列表 -->
<div class="catalog w128 sticky top32" v-if="cmpId !== 'types'">
<n-anchor :show-rail="true" offset-target="#doc-layout" :ignore-gap="true" :show-background="true">
<n-anchor-link v-for="(demo, index) in currJson.demos || []" :key="demo.demoId" :title="demo.name[langKey]" :href="'#' + demo.demoId" />
<n-anchor-link v-if="cmpId !== 'interfaces'" title="API" href="#API" />
<n-anchor-link v-if="cmpFAQMd" title="FAQ" href="#FAQ" />
<!-- 接口特殊的导航 -->
<template v-if="cmpId === 'interfaces'">
<n-anchor-link v-for="(api, index) in currJson.apis || []" :key="api.name" :title="api.name" :href="'#' + api.name" />
</template>
</n-anchor>
</div>
<div class="f-r pt48 pl48 pr48 doc-container">
<template v-if="useTab">
<!-- 描述 包含基本描述 示例和规范页 -->
<components-detail-tab></components-detail-tab>
</template>
<template v-else>
<!-- 描述 包含基本描述及示例 -->
<components-detail></components-detail>
</template>
</div>
<div id="footer"></div>
</div>
</template>
<script lang="jsx">
import { defineComponent, reactive, computed, toRefs, watch, onMounted, onUnmounted, effectScope, nextTick, h } from 'vue';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import demo from '@demo';
import { $t, $t2, $clone, $split, fetchDemosFile } from '@/tools';
import { defineComponent, reactive, toRefs, onMounted } from 'vue';
import componentsDetail from './componentsDetail.vue';
import componentsDetailTab from './componentsDetailTab.vue';
import { fetchDemoFile } from '@/tools';
import { router } from '@/router.js';
import { faqMdConfig } from './cmpConfig';
import { watch } from 'fs';
const state = reactive({
langKey: $t2('zh-CN', 'en-US'),
cmpId: '',
currJson: { column: 1, demos: [], apis: [] },
currJsonShow: true,
cmpTopMd: null,
cmpTopMdShow: true,
cmpFAQMd: null,
evenDemo: computed(() => state.currJson.demos?.filter((d, i) => i % 2 === 0) || []),
oddDemo: computed(() => state.currJson.demos?.filter((d, i) => i % 2 === 1) || []),
apiDiv: null,
useTab: import.meta.env.VITE_USE_TAB || false, // 是否开启规范tab页,此变量需在.env生命,如不使用则删除对应.env变量
});
const getTypesTableOptFn = typesArr => {
return {
columns: [
{
key: 'name',
title: $t('name'),
width: '20%',
render: row => <div v-html={row.name}></div>,
},
{
key: 'value',
title: $t('typeValue'),
width: '40%',
render: row => <div v-html={row.value}></div>,
},
{
key: 'desc',
title: $t('desc'),
width: '40%',
render: row => <div v-html={row.desc[state.langKey]}> </div>,
},
],
data: typesArr,
};
};
const getApiTableOptFn = oneApiArr => {
return {
columns: [
{
key: 'name',
title: $t('name'),
width: '20%',
render: row => {
return row.demoId ? <a href={'#' + row.demoId}> {row.name} </a> : <span> {row.name} </span>;
},
},
{
key: 'type',
title: $t('propType'),
width: '25%',
render: row => <div class="route-anchor" v-html={row.type}></div>,
},
{
key: 'defaultValue',
title: $t('defValue'),
width: '20%',
render: row => <div class="route-anchor" v-html={row.defaultValue}></div>,
},
{
key: 'desc',
title: $t('desc'),
width: '35%',
render: row => <div class="route-anchor" v-html={row.desc[state.langKey]}></div>,
},
],
data: oneApiArr,
};
};

const loadPage = () => {
const lang = $t2('cn', 'en');
state.cmpTopMdShow = state.currJsonShow = true;
state.cmpId = router.currentRoute.value.params.cmpId;
fetchDemosFile(`@demos/app/${state.cmpId}/webdoc/${state.cmpId}.${lang}.md`)
.then(data => {
state.cmpTopMd = marked(DOMPurify.sanitize(data));
state.cmpTopMdShow = false;
})
.catch(error => {
state.cmpTopMdShow = false;
});
if (faqMdConfig[state.cmpId]) {
fetchDemosFile(`@demos/app/${state.cmpId}/webdoc/${state.cmpId}.faq.${lang}.md`)
.then(data => {
state.cmpFAQMd = marked(DOMPurify.sanitize(data));
})
.catch(error => {});
} else {
state.cmpFAQMd = null;
}
fetchDemosFile(`@demos/app/${state.cmpId}/webdoc/${state.cmpId}.js`)
.then(data => {
const json = eval('(' + data.slice(15) + ')');
state.currJson = {
...json,
demos: $clone(json['demos'] || []), // 克隆一下,避免保存上次的isOpen
column: json.column || '1', // columns可能为空
};
state.currJsonShow = false;
// 兼容#hashName和/#hashName两种模式
setTimeout(() => {
let hash = router.currentRoute.value.hash?.slice(1);
if (!hash) return;
if (hash.indexOf('/') > -1) {
hash = hash.slice(1);
}
const scrollTarget = document.querySelector(`#${hash}`);
if (scrollTarget) {
scrollTarget.scrollIntoView();
}
}, 0);
})
.catch(error => {
state.currJsonShow = false;
});
};
const handleApiAnchor = ev => {
if (ev.target.tagName === 'A' && ev.target.closest('.route-anchor')) {
ev.preventDefault();
const href = ev.target.getAttribute('href');
const hash = $split(href, '#', -1);
router.push(href);
setTimeout(() => {
const scrollTarget = document.querySelector('#' + hash);
if (scrollTarget) {
scrollTarget.scrollIntoView();
}
}, 16);
}
};
const fn = {
copyText: text => {
navigator.clipboard.writeText(text);
},
getApiTableOpt: oneApiArr => {
return getApiTableOptFn(oneApiArr);
},
getTypesTableOpt: typesArr => {
return getTypesTableOptFn(typesArr);
},
const loadCmp = cmpI => {
fetchDemoFile(`@demos/app/${cmpId}/webdoc/${cmpId}.js`).then(data => {
const json = eval(`(${data.slice(15)})`);
if (json['standard']) {
state.useTab = true;
} else {
state.useTab = false;
}
});
};
export default defineComponent({
name: 'CmpPage_vue',
components: { demo },
components: { componentsDetail, componentsDetailTab },
setup() {
const scope = effectScope();
scope.run(() => {
watch(
() => router.currentRoute.value.params.cmpId,
cmpId => {
if (!cmpId) {
state.currJson = {};
} else {
state.cmpFAQMdShow = state.cmpTopMdShow = state.currJsonShow = true;
loadPage();
}
}
);
});
loadCmp(router.currentRoute.value.params.cmpId);
watch(
() => router.currentRoute.value.params.cmpId,
cmpId => {
if (cmpId) loadCmp(cmpId);
}
);
onMounted(() => {
loadPage();
const common = new window.TDCommon(['#footer'], {});
common.renderFooter();
nextTick(() => {
state.apiDiv?.addEventListener('click', handleApiAnchor);
});
});
onUnmounted(() => {
scope.stop();
state.apiDiv?.removeEventListener('click', handleApiAnchor);
});
return {
...toRefs(state),
...fn,
};
},
});
</script>
<style lang="less">
.types-table a,
.api-table a {
text-decoration: none;
color: #5e7ce0;
cursor: pointer;
}
.n-data-table-td {
vertical-align: middle !important;
}
.catalog {
height: calc(100vh - 150px);
overflow: hidden;
}
.catalog:hover {
overflow-y: auto;
}
.catalog::-webkit-scrollbar {
width: 10px;
background-color: #f5f5f5;
}
.catalog::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color: #c1c1c1;
}
.n-anchor .n-anchor-link .n-anchor-link__title {
font-size: 12px;
}
.one-demo-col2 {
display: grid;
gap: 16px;
grid-template-columns: minmax(0px, 1fr) minmax(0px, 1fr);
align-items: flex-start;
> div {
display: grid;
gap: 16px;
grid-template-columns: 100%;
}
}
.cmp-container {
padding-right: 24px;
}
@media (max-width: 1279px) {
.catalog {
display: none;
}
.cmp-container {
padding-right: 0;
}
}
@media (max-width: 767px) {
.one-demo-col2 {
grid-template-columns: 100%;
}
}
</style>
<style lang="less"></style>
Loading