Skip to content

Commit

Permalink
Feature : docs desc can support markdown files (#85)
Browse files Browse the repository at this point in the history
* fix: fix TinyPro-vue list page btn css

* feat: docs desc can support markdown files
  • Loading branch information
aliceDxr authored Aug 18, 2023
1 parent 43f2202 commit 5ecc607
Show file tree
Hide file tree
Showing 12 changed files with 2,027 additions and 686 deletions.
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

0 comments on commit 5ecc607

Please sign in to comment.