From 9ff8e37ba4551dffd1e00309ce106acc74c66d60 Mon Sep 17 00:00:00 2001 From: sakurayinfei <970412446@qq.com> Date: Wed, 25 Jun 2025 11:47:33 +0800 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20opendesign=E6=96=87=E6=A1=A3-?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=96=87=E4=BB=B6=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/_components/in-input/__docs__/index.zh-CN.md | 8 ++++++++ packages/opendesign/src/index.zh-CN.md | 3 +++ .../opendesign/src/virtual-list/__docs__/index.zh-CN.md | 7 +++++++ packages/portal/scripts/generateComponentRouter.ts | 0 4 files changed, 18 insertions(+) create mode 100644 packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md create mode 100644 packages/opendesign/src/index.zh-CN.md create mode 100644 packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md create mode 100644 packages/portal/scripts/generateComponentRouter.ts diff --git a/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md b/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md new file mode 100644 index 000000000..80dc27c7e --- /dev/null +++ b/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md @@ -0,0 +1,8 @@ +--- +sidebar: InInput _输入框 +emit: false +--- + +# _输入框 + +_输入框 diff --git a/packages/opendesign/src/index.zh-CN.md b/packages/opendesign/src/index.zh-CN.md new file mode 100644 index 000000000..92f578080 --- /dev/null +++ b/packages/opendesign/src/index.zh-CN.md @@ -0,0 +1,3 @@ +# 首页 + +通用 diff --git a/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md b/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md new file mode 100644 index 000000000..bb67d59aa --- /dev/null +++ b/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md @@ -0,0 +1,7 @@ +--- +sidebar: OVirtualList 虚拟滚动 +--- + +# 虚拟滚动 + +虚拟滚动 diff --git a/packages/portal/scripts/generateComponentRouter.ts b/packages/portal/scripts/generateComponentRouter.ts new file mode 100644 index 000000000..e69de29bb -- Gitee From a1c05fd960e6ce88463ce7602690f55ec84f7b1e Mon Sep 17 00:00:00 2001 From: AC-0308 Date: Wed, 25 Jun 2025 15:51:04 +0800 Subject: [PATCH 02/17] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=BB=84=E4=BB=B6=E8=B7=AF=E7=94=B1=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/portal/.gitignore | 3 +- packages/portal/package.json | 5 +- .../portal/scripts/generateComponentRouter.ts | 84 +++++++++++++++++++ packages/portal/src/vite-env.d.ts | 6 ++ pnpm-lock.yaml | 9 ++ 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/packages/portal/.gitignore b/packages/portal/.gitignore index abcfe591f..9c59933ec 100644 --- a/packages/portal/.gitignore +++ b/packages/portal/.gitignore @@ -25,4 +25,5 @@ dist-ssr icon-components es -lib \ No newline at end of file +lib +src/router/components.ts \ No newline at end of file diff --git a/packages/portal/package.json b/packages/portal/package.json index d234d06c1..57d104e39 100644 --- a/packages/portal/package.json +++ b/packages/portal/package.json @@ -15,9 +15,12 @@ "@opensig/open-analytics": "catalog:" }, "devDependencies": { + "fs-extra": "catalog:", + "glob": "catalog:", "sass": "catalog:", "typescript": "catalog:", "vite": "catalog:", - "vue-tsc": "catalog:" + "vue-tsc": "catalog:", + "@types/fs-extra": "catalog:" } } diff --git a/packages/portal/scripts/generateComponentRouter.ts b/packages/portal/scripts/generateComponentRouter.ts index e69de29bb..979d36432 100644 --- a/packages/portal/scripts/generateComponentRouter.ts +++ b/packages/portal/scripts/generateComponentRouter.ts @@ -0,0 +1,84 @@ +import {glob} from 'glob'; +import {resolve} from 'path' +import {fileURLToPath} from 'url' +import fse from 'fs-extra' + +const __fileName = fileURLToPath(import.meta.url); +const searchBase = resolve(__fileName, '../../../opendesign') +const output = resolve(__fileName, '../../src/router/components.ts') + +function metaTextToJson(metaText: string): Record { + return metaText.split('\n').reduce((previousValue, currentValue) => { + const [key, value] = currentValue.split(':').map(v => v.trim()); + if (['true', 'false'].includes(value)) { + previousValue[key] = value === 'true' + } else if (Number(value).toString() === value) { + previousValue[key] = Number(value) + } else { + previousValue[key] = value + } + return previousValue + }, {}) +} + +glob('**/__docs__/index.zh-CN.md', {cwd: searchBase, posix: true}).then(files => { + return files.map(file => { + const fullPath = resolve(searchBase, file) + const content = fse.readFileSync(fullPath).toString(); + return {content, file, fullPath, name: file.match(/([^\/]+)\/__docs__\/?/)?.[1]} + }) +}).then(fileContents => { + const headCommentRegex = /^\s*---\s*([\s\S]*?)\s*---\s*/; + return fileContents.map((info) => { + const match = info.content.match(headCommentRegex) + return { + ...info, + meta: match?.[1] ? metaTextToJson(match[1]) : {} + } + }) +}).then(res => { + return `import { createRouter, createWebHistory } from 'vue-router'; +import TheHome from '../pages/TheHome.vue'; +import { oa, OpenEventKeys } from '@/analytics'; + +export const routes = [ +${ + res.map(info => ` { + path: '/${info.name}', + name: '${info.name}', + component: () => import('@component${info.file.replace(/^src/, '')}'), + meta: ${JSON.stringify(info.meta)} + }`) + .join(',') + } +]; + +export const router = createRouter({ + history: createWebHistory(), + routes, + scrollBehavior(to, from, savePosition) { + if (savePosition) { + return savePosition; + } else { + return { + top: 0, + behavior: 'smooth', + }; + } + }, +}); + +router.afterEach((to, from) => { + oa.report(OpenEventKeys.PV, { + prev_page: from.fullPath, + url: to.fullPath, + title: to.meta.title, + }); +}); + + `; +}).then(res => { + console.log(res); + fse.writeFileSync(output, res) +}) + diff --git a/packages/portal/src/vite-env.d.ts b/packages/portal/src/vite-env.d.ts index af36df349..2f3fd327f 100644 --- a/packages/portal/src/vite-env.d.ts +++ b/packages/portal/src/vite-env.d.ts @@ -5,3 +5,9 @@ declare module '*.vue' { const component: DefineComponent<{}, {}, any>; export default component; } + +declare module '*.md' { + import type { DefineComponent } from 'vue'; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34bee73d8..88ce64e18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,6 +152,15 @@ importers: specifier: workspace:^ version: link:../opendesign devDependencies: + '@types/fs-extra': + specifier: 'catalog:' + version: 9.0.13 + fs-extra: + specifier: 'catalog:' + version: 11.3.0 + glob: + specifier: 'catalog:' + version: 11.0.1 sass: specifier: 'catalog:' version: 1.84.0 -- Gitee From 08ed622485ea8159559dcdfe5017390677729765 Mon Sep 17 00:00:00 2001 From: sakurayinfei <970412446@qq.com> Date: Wed, 25 Jun 2025 19:47:47 +0800 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8vite=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=94=9F=E6=88=90=E7=BB=84=E4=BB=B6=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E4=BB=A5=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../portal/plugins/generateComponentRouter.ts | 99 ++++ .../portal/scripts/generateComponentRouter.ts | 84 ---- packages/portal/src/components/TheAside.vue | 4 +- packages/portal/src/main.ts | 2 +- packages/portal/src/router.ts | 447 ------------------ packages/portal/src/router/index.ts | 32 ++ packages/portal/tsconfig.node.json | 2 +- 7 files changed, 135 insertions(+), 535 deletions(-) create mode 100644 packages/portal/plugins/generateComponentRouter.ts delete mode 100644 packages/portal/scripts/generateComponentRouter.ts delete mode 100644 packages/portal/src/router.ts create mode 100644 packages/portal/src/router/index.ts diff --git a/packages/portal/plugins/generateComponentRouter.ts b/packages/portal/plugins/generateComponentRouter.ts new file mode 100644 index 000000000..ce01ec4ca --- /dev/null +++ b/packages/portal/plugins/generateComponentRouter.ts @@ -0,0 +1,99 @@ +import { createFilter, type Plugin } from 'vite'; +import { glob } from 'glob'; +import { resolve } from 'path'; +import { fileURLToPath } from 'url'; +import fse from 'fs-extra'; + +const __fileName = fileURLToPath(import.meta.url); +const searchBase = resolve(__fileName, '../../../opendesign/src'); +const output = resolve(__fileName, '../../src/router/components.ts'); + +function metaTextToJson(metaText: string): Record { + return metaText.split('\n').reduce((previousValue, currentValue) => { + const [key, value] = currentValue.split(':').map((v) => v.trim()); + if (['true', 'false'].includes(value)) { + previousValue[key] = value === 'true'; + } else if (Number(value).toString() === value) { + previousValue[key] = Number(value); + } else { + previousValue[key] = value; + } + return previousValue; + }, {}); +} + +function debounce) => any>(fn: T, wait: number = 0, runFirst: boolean = true) { + let handler = null; + return (...args: Array) => { + if (runFirst) { + if (handler === 0) { + fn(...args); + } + } + clearTimeout(handler); + handler = setTimeout(() => { + if (!runFirst) { + fn(...args); + } + handler = 0; + }, wait); + }; +} + +const emit = debounce(() => { + glob('**/__docs__/index.zh-CN.md', { cwd: searchBase, posix: true }) + .then((files) => { + return files.map((file) => { + const fullPath = resolve(searchBase, file); + const content = fse.readFileSync(fullPath).toString(); + return { content, file, fullPath, name: file.match(/([^\/]+)\/__docs__\/?/)?.[1] }; + }); + }) + .then((fileContents) => { + const headCommentRegex = /^\s*---\s*([\s\S]*?)\s*---\s*/; + return fileContents.map((info) => { + const match = info.content.match(headCommentRegex); + return { + ...info, + meta: match?.[1] ? metaTextToJson(match[1]) : {}, + }; + }); + }) + .then((res) => { + return `import { type RouteRecordRaw } from 'vue-router'; +export const routes: Array = [ +${res + .map( + (info) => ` { + path: '/${info.name}', + name: '${info.name}', + component: () => import('@components/${info.file}'), + meta: ${JSON.stringify(info.meta)} + }` + ) + .join(',')} +]; + `; + }) + .then((res) => { + return fse.writeFile(output, res); + }); +}, 1000); + +export default function generateComponentRouter(): Plugin { + const filter = createFilter(/opendesign\/src\/.*?\/__docs__\/index\..*?\.md$/); + return { + name: 'generate-component-router', + configureServer(server) { + server.watcher.add(searchBase); + server.watcher.on('all', (event, path) => { + if (filter(path.replace(/\\/g, '/')) && ['add', 'unlink', 'change'].includes(event)) { + emit(); + } + }); + }, + buildStart() { + emit(); + }, + }; +} diff --git a/packages/portal/scripts/generateComponentRouter.ts b/packages/portal/scripts/generateComponentRouter.ts deleted file mode 100644 index 979d36432..000000000 --- a/packages/portal/scripts/generateComponentRouter.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {glob} from 'glob'; -import {resolve} from 'path' -import {fileURLToPath} from 'url' -import fse from 'fs-extra' - -const __fileName = fileURLToPath(import.meta.url); -const searchBase = resolve(__fileName, '../../../opendesign') -const output = resolve(__fileName, '../../src/router/components.ts') - -function metaTextToJson(metaText: string): Record { - return metaText.split('\n').reduce((previousValue, currentValue) => { - const [key, value] = currentValue.split(':').map(v => v.trim()); - if (['true', 'false'].includes(value)) { - previousValue[key] = value === 'true' - } else if (Number(value).toString() === value) { - previousValue[key] = Number(value) - } else { - previousValue[key] = value - } - return previousValue - }, {}) -} - -glob('**/__docs__/index.zh-CN.md', {cwd: searchBase, posix: true}).then(files => { - return files.map(file => { - const fullPath = resolve(searchBase, file) - const content = fse.readFileSync(fullPath).toString(); - return {content, file, fullPath, name: file.match(/([^\/]+)\/__docs__\/?/)?.[1]} - }) -}).then(fileContents => { - const headCommentRegex = /^\s*---\s*([\s\S]*?)\s*---\s*/; - return fileContents.map((info) => { - const match = info.content.match(headCommentRegex) - return { - ...info, - meta: match?.[1] ? metaTextToJson(match[1]) : {} - } - }) -}).then(res => { - return `import { createRouter, createWebHistory } from 'vue-router'; -import TheHome from '../pages/TheHome.vue'; -import { oa, OpenEventKeys } from '@/analytics'; - -export const routes = [ -${ - res.map(info => ` { - path: '/${info.name}', - name: '${info.name}', - component: () => import('@component${info.file.replace(/^src/, '')}'), - meta: ${JSON.stringify(info.meta)} - }`) - .join(',') - } -]; - -export const router = createRouter({ - history: createWebHistory(), - routes, - scrollBehavior(to, from, savePosition) { - if (savePosition) { - return savePosition; - } else { - return { - top: 0, - behavior: 'smooth', - }; - } - }, -}); - -router.afterEach((to, from) => { - oa.report(OpenEventKeys.PV, { - prev_page: from.fullPath, - url: to.fullPath, - title: to.meta.title, - }); -}); - - `; -}).then(res => { - console.log(res); - fse.writeFileSync(output, res) -}) - diff --git a/packages/portal/src/components/TheAside.vue b/packages/portal/src/components/TheAside.vue index 1c5bbb76e..7b9b5ff6d 100644 --- a/packages/portal/src/components/TheAside.vue +++ b/packages/portal/src/components/TheAside.vue @@ -1,5 +1,5 @@ ` + } + return newCode; + }, + }; +} diff --git a/packages/portal/src/assets/style/style.scss b/packages/portal/src/assets/style/style.scss index 01c58942d..71b2d7714 100644 --- a/packages/portal/src/assets/style/style.scss +++ b/packages/portal/src/assets/style/style.scss @@ -100,3 +100,7 @@ section { border-color: #aaa; } } + +.markdown-body { + padding: 8px 24px; +} diff --git a/packages/portal/vite.config.ts b/packages/portal/vite.config.ts index 5a4bdf87e..e2212554a 100644 --- a/packages/portal/vite.config.ts +++ b/packages/portal/vite.config.ts @@ -1,6 +1,10 @@ import path from 'path'; import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; +import Markdown from 'unplugin-vue-markdown/vite'; +import { injectDemoAndApi } from './plugins/injectDemoAndApi'; +import generateComponentRouter from './plugins/generateComponentRouter'; +import Inspect from 'vite-plugin-inspect'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,7 +13,15 @@ export default defineConfig({ target: ['chrome74'], outDir: '../../output/o', }, - plugins: [vue()], + plugins: [ + vue({ + include: [/\.vue$/, /\.md$/], + }), + injectDemoAndApi(), // injectDemoAndApi should be placed before Markdown + Markdown({}), + generateComponentRouter(), + Inspect(), + ], resolve: { alias: { '@/': `${path.resolve(__dirname, './src')}/`, -- Gitee From 30899c904f1fc6b916095563cf673d9ef5981be4 Mon Sep 17 00:00:00 2001 From: sakurayinfei <970412446@qq.com> Date: Wed, 25 Jun 2025 19:49:29 +0800 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=E6=92=B0=E5=86=99=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=BB=84=E4=BB=B6=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/_components/in-input/__docs__/index.zh-CN.md | 4 ++++ .../opendesign/src/button/__docs__/index.zh-CN.md | 11 +++++++++++ packages/opendesign/src/card/__docs__/index.zh-CN.md | 11 +++++++++++ .../opendesign/src/carousel/__docs__/index.zh-CN.md | 11 +++++++++++ .../opendesign/src/cascader/__docs__/index.zh-CN.md | 11 +++++++++++ .../opendesign/src/dialog/__docs__/index.zh-CN.md | 11 +++++++++++ .../opendesign/src/divider/__docs__/index.zh-CN.md | 11 +++++++++++ .../src/virtual-list/__docs__/index.zh-CN.md | 4 ++++ 8 files changed, 74 insertions(+) create mode 100644 packages/opendesign/src/button/__docs__/index.zh-CN.md create mode 100644 packages/opendesign/src/card/__docs__/index.zh-CN.md create mode 100644 packages/opendesign/src/carousel/__docs__/index.zh-CN.md create mode 100644 packages/opendesign/src/cascader/__docs__/index.zh-CN.md create mode 100644 packages/opendesign/src/dialog/__docs__/index.zh-CN.md create mode 100644 packages/opendesign/src/divider/__docs__/index.zh-CN.md diff --git a/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md b/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md index 80dc27c7e..fcd28dd22 100644 --- a/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md +++ b/packages/opendesign/src/_components/in-input/__docs__/index.zh-CN.md @@ -6,3 +6,7 @@ emit: false # _输入框 _输入框 + +## 示例 + + diff --git a/packages/opendesign/src/button/__docs__/index.zh-CN.md b/packages/opendesign/src/button/__docs__/index.zh-CN.md new file mode 100644 index 000000000..c0ec9090c --- /dev/null +++ b/packages/opendesign/src/button/__docs__/index.zh-CN.md @@ -0,0 +1,11 @@ +--- +sidebar: OButton 按钮 +--- + +# 按钮 + +按钮 + +## 示例 + + diff --git a/packages/opendesign/src/card/__docs__/index.zh-CN.md b/packages/opendesign/src/card/__docs__/index.zh-CN.md new file mode 100644 index 000000000..ae0b7c7cf --- /dev/null +++ b/packages/opendesign/src/card/__docs__/index.zh-CN.md @@ -0,0 +1,11 @@ +--- +sidebar: OCard 卡片 +--- + +# 卡片 + +卡片 + +## 示例 + + diff --git a/packages/opendesign/src/carousel/__docs__/index.zh-CN.md b/packages/opendesign/src/carousel/__docs__/index.zh-CN.md new file mode 100644 index 000000000..205ef9e64 --- /dev/null +++ b/packages/opendesign/src/carousel/__docs__/index.zh-CN.md @@ -0,0 +1,11 @@ +--- +sidebar: OCarousel 幻灯片 +--- + +# 幻灯片 + +幻灯片 + +## 示例 + + diff --git a/packages/opendesign/src/cascader/__docs__/index.zh-CN.md b/packages/opendesign/src/cascader/__docs__/index.zh-CN.md new file mode 100644 index 000000000..4e98cf788 --- /dev/null +++ b/packages/opendesign/src/cascader/__docs__/index.zh-CN.md @@ -0,0 +1,11 @@ +--- +sidebar: OCascader 级联选择器 +--- + +# 级联选择器 + +级联选择器 + +## 示例 + + diff --git a/packages/opendesign/src/dialog/__docs__/index.zh-CN.md b/packages/opendesign/src/dialog/__docs__/index.zh-CN.md new file mode 100644 index 000000000..d4190118f --- /dev/null +++ b/packages/opendesign/src/dialog/__docs__/index.zh-CN.md @@ -0,0 +1,11 @@ +--- +sidebar: ODialog 对话框 +--- + +# 对话框 + +对话框 + +## 示例 + + diff --git a/packages/opendesign/src/divider/__docs__/index.zh-CN.md b/packages/opendesign/src/divider/__docs__/index.zh-CN.md new file mode 100644 index 000000000..331ff74a5 --- /dev/null +++ b/packages/opendesign/src/divider/__docs__/index.zh-CN.md @@ -0,0 +1,11 @@ +--- +sidebar: ODivider 分割线 +--- + +# 分割线 + +分割线 + +## 示例 + + diff --git a/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md b/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md index bb67d59aa..6847599f9 100644 --- a/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md +++ b/packages/opendesign/src/virtual-list/__docs__/index.zh-CN.md @@ -5,3 +5,7 @@ sidebar: OVirtualList 虚拟滚动 # 虚拟滚动 虚拟滚动 + +## 示例 + + -- Gitee From 432c9f651833d63a62be895f1895661fa860671f Mon Sep 17 00:00:00 2001 From: sakurayinfei <970412446@qq.com> Date: Fri, 27 Jun 2025 09:01:37 +0800 Subject: [PATCH 06/17] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E7=94=9F=E6=88=90=E6=8F=92=E4=BB=B6bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/portal/plugins/generateComponentRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/portal/plugins/generateComponentRouter.ts b/packages/portal/plugins/generateComponentRouter.ts index ce01ec4ca..d14507e13 100644 --- a/packages/portal/plugins/generateComponentRouter.ts +++ b/packages/portal/plugins/generateComponentRouter.ts @@ -26,7 +26,7 @@ function debounce) => any>(fn: T, wait: number = let handler = null; return (...args: Array) => { if (runFirst) { - if (handler === 0) { + if (!handler) { fn(...args); } } -- Gitee From 742e12e78c86352b23f1912f6f5a4d28161bf6c4 Mon Sep 17 00:00:00 2001 From: sakurayinfei <970412446@qq.com> Date: Fri, 27 Jun 2025 09:05:43 +0800 Subject: [PATCH 07/17] =?UTF-8?q?feat:=20=E6=96=87=E6=A1=A3-=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E4=BB=A3=E7=A0=81=E9=AB=98=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/button/__docs__/index.zh-CN.md | 33 +++++++++++++++++++ packages/portal/package.json | 3 +- .../portal/src/components/CodeContainer.vue | 7 ++++ packages/portal/src/components/TheHeader.vue | 4 ++- packages/portal/src/main.ts | 2 ++ .../portal/src/utils/useHighLightTheme.ts | 21 ++++++++++++ packages/portal/vite.config.ts | 21 +++++++++++- 7 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 packages/portal/src/components/CodeContainer.vue create mode 100644 packages/portal/src/utils/useHighLightTheme.ts diff --git a/packages/opendesign/src/button/__docs__/index.zh-CN.md b/packages/opendesign/src/button/__docs__/index.zh-CN.md index c0ec9090c..7ab97abb8 100644 --- a/packages/opendesign/src/button/__docs__/index.zh-CN.md +++ b/packages/opendesign/src/button/__docs__/index.zh-CN.md @@ -9,3 +9,36 @@ sidebar: OButton 按钮 ## 示例 + +## 示例代码 + +```vue + + + +``` diff --git a/packages/portal/package.json b/packages/portal/package.json index 2bf968138..dd8a254a1 100644 --- a/packages/portal/package.json +++ b/packages/portal/package.json @@ -12,7 +12,8 @@ "dependencies": { "@opensig/opendesign": "workspace:^", "@opensig/open-scripts": "workspace:^", - "@opensig/open-analytics": "catalog:" + "@opensig/open-analytics": "catalog:", + "highlight.js": "catalog:" }, "devDependencies": { "fs-extra": "catalog:", diff --git a/packages/portal/src/components/CodeContainer.vue b/packages/portal/src/components/CodeContainer.vue new file mode 100644 index 000000000..d3bd45638 --- /dev/null +++ b/packages/portal/src/components/CodeContainer.vue @@ -0,0 +1,7 @@ + + + diff --git a/packages/portal/src/components/TheHeader.vue b/packages/portal/src/components/TheHeader.vue index 399d34abd..44fa2c090 100644 --- a/packages/portal/src/components/TheHeader.vue +++ b/packages/portal/src/components/TheHeader.vue @@ -7,6 +7,7 @@ import enUS from '@opensig/opendesign/src/locale/lang/en-us'; import '../../../opendesign/src/dropdown/style'; import { useTheme } from '@opensig/opendesign/src'; +import { useHighLightTheme } from '@/utils/useHighLightTheme'; const themeInfo = [ { @@ -36,6 +37,7 @@ const themeInfo = [ ]; const { theme } = useTheme(); +useHighLightTheme(theme); if (!theme.value) { theme.value = themeInfo[0].key; } @@ -81,7 +83,7 @@ const currentLocale = computed(() => { const changeLocale = (l: (typeof locales)[0]) => { useLocale(l.value); }; -window.setLocale = changeLocale; +(window as any).setLocale = changeLocale; diff --git a/packages/portal/package.json b/packages/portal/package.json index dd8a254a1..a3dc91758 100644 --- a/packages/portal/package.json +++ b/packages/portal/package.json @@ -10,20 +10,23 @@ "gen:icon": "open-scripts gen:icon --config __test_scripts/icons/icon.config.ts" }, "dependencies": { - "@opensig/opendesign": "workspace:^", - "@opensig/open-scripts": "workspace:^", "@opensig/open-analytics": "catalog:", - "highlight.js": "catalog:" + "@opensig/open-scripts": "workspace:^", + "@opensig/opendesign": "workspace:^" }, "devDependencies": { + "@types/fs-extra": "catalog:", + "@vue/compiler-sfc": "catalog:", "fs-extra": "catalog:", "glob": "catalog:", + "gray-matter": "catalog:", + "highlight.js": "catalog:", + "markdown-it-async": "catalog:", "sass": "catalog:", "typescript": "catalog:", "unplugin-vue-markdown": "catalog:", "vite": "catalog:", "vite-plugin-inspect": "catalog:", - "vue-tsc": "catalog:", - "@types/fs-extra": "catalog:" + "vue-tsc": "catalog:" } } diff --git a/packages/portal/plugins/injectDemoDocs.ts b/packages/portal/plugins/injectDemoDocs.ts new file mode 100644 index 000000000..09047c03f --- /dev/null +++ b/packages/portal/plugins/injectDemoDocs.ts @@ -0,0 +1,56 @@ +import { type Plugin } from 'vite'; +import { MarkdownItAsync } from 'markdown-it-async'; +import { markdownItPlugins, markdownItOptions } from './markdown/common'; +import matter from 'gray-matter'; + +const parseVueQuery = (id: string) => { + let [file, query] = id.split('?', 2); + const queryExtension = query.match(/\.([a-zA-Z0-9]+)$/)?.[1]; + query = queryExtension ? query.slice(0, query.length - queryExtension.length - 1) : query; + const queryObj = query + ? query.split('&').reduce((prev, curr) => { + const [key, value] = curr.split('='); + prev[key] = value || true; + return prev; + }, {} as Record) + : {}; + return { file, query: queryObj, queryExtension }; +}; +export function injectDemoDocs(): Plugin { + const md = new MarkdownItAsync(markdownItOptions); + markdownItPlugins.forEach((plugin) => md.use(plugin)); + const matterReg = /^---[\s\S]*?---/; + return { + name: 'portal:inject-demo-docs', + transform(code, id) { + if (id.endsWith('.docs')) { + const { query } = parseVueQuery(id); + if (!query.vue || query.type !== 'docs') { + return; + } + code = code.trimStart(); + const matterMatch = code.match(matterReg); + const matterData = matterMatch ? matter(matterMatch[0]) : null; + const h1Reg = /^#\s+(.+)/gm; + const h1MatchList = Array.from(code.matchAll(h1Reg)); + const __docs: { + title: Record; + description: Record; + } = { title: {}, description: {} } + if (matterData?.data?.title) { + __docs.title = matterData.data.title; + } + for(let i = 0; i < h1MatchList.length; i++) { + const h1Match = h1MatchList[i]; + const [text, title] = h1Match; + const nextIndex = i + 1 < h1MatchList.length ? h1MatchList[i + 1].index : Infinity; + const desRaw = code.slice(h1Match.index + text.length, nextIndex); + __docs.description[title] = md.render(desRaw); + } + return `export default function (_sfc_main) { + _sfc_main.__docs = ${JSON.stringify(__docs)}; +}`; + } + }, + }; +} diff --git a/packages/portal/plugins/injectDemoSource.ts b/packages/portal/plugins/injectDemoSource.ts new file mode 100644 index 000000000..d005747ec --- /dev/null +++ b/packages/portal/plugins/injectDemoSource.ts @@ -0,0 +1,70 @@ +import fsp from 'node:fs/promises'; +import { createFilter, type Plugin } from 'vite'; +import { parse, type SFCBlock } from '@vue/compiler-sfc'; +import { md } from './markdown/common'; + +const virtualModules = new Map(); + +const generateCode = (block: SFCBlock) => { + return `<${block.type} ${Object.entries(block.attrs) + .map(([key, value]) => { + if (typeof value === 'string') { + return `${key}="${value}"`; + } else { + return `${key}`; + } + }) + .join(' ')}>${block.content}\n`; +}; +export function injectDemoSource(): Plugin { + const filter = createFilter(/opendesign\/src\/.*?\/__demo__\/.+\.vue$/); + return { + name: 'portal:inject-demo-source', + resolveId(id) { + if (virtualModules.has(id)) { + return id; + } + }, + load(id) { + return virtualModules.get(id); + }, + async transform(code, id) { + if (!filter(id) || id.startsWith('portal-virtual:')) { + return; + } + if (await fsp.stat(id).then((stat) => stat.isFile())) { + const source = await fsp.readFile(id, 'utf-8'); + const { descriptor } = parse(source); + let cleanedSource = ''; + + if (descriptor.script) { + cleanedSource = generateCode(descriptor.script); + } + if (descriptor.scriptSetup) { + cleanedSource += generateCode(descriptor.scriptSetup); + } + if (descriptor.template) { + cleanedSource += generateCode(descriptor.template); + } + if (descriptor.styles) { + descriptor.styles.forEach((style) => { + cleanedSource += generateCode(style); + }); + } + cleanedSource = cleanedSource.trimEnd(); + let result = `${md.render(`\`\`\`vue:line-numbers\n${cleanedSource}\n\`\`\``)}`.replace( + /()([\s\S]*?)<\/code><\/pre>/, + (_, pre, codeAttr, codeContent) => { + return `${pre}${codeContent}`; + } + ); + result = ``; + const virtualId = `portal-virtual:${id}`; + virtualModules.set(virtualId, result); + return `${code} +;import _DemoSource from ${JSON.stringify(virtualId)}; +_sfc_main.DemoSource = _DemoSource;`; + } + }, + }; +} diff --git a/packages/portal/plugins/markdown/common.ts b/packages/portal/plugins/markdown/common.ts new file mode 100644 index 000000000..6f77d16eb --- /dev/null +++ b/packages/portal/plugins/markdown/common.ts @@ -0,0 +1,22 @@ +import { MarkdownItAsync, type MarkdownItAsyncOptions } from 'markdown-it-async'; +import hljs from 'highlight.js'; +import lineNumber from './lineNumber'; +import wrapCodeContainer from './wrapCodeContainer'; + +export function highlight(str: string, lang: string, attrs: string) { + let highlightLang = lang === 'vue' ? 'html' : lang; + const hasLang = hljs.getLanguage(highlightLang); + if (!hasLang) { + highlightLang = 'bash'; + } + return hljs.highlight(str, { language: highlightLang }).value; +} +export const markdownItOptions: MarkdownItAsyncOptions = { + html: true, + linkify: true, + typographer: true, + highlight, +}; +export const markdownItPlugins = [lineNumber, wrapCodeContainer]; +export const md = new MarkdownItAsync(markdownItOptions); +markdownItPlugins.forEach((plugin) => md.use(plugin)); diff --git a/packages/portal/plugins/markdown/lineNumber.ts b/packages/portal/plugins/markdown/lineNumber.ts new file mode 100644 index 000000000..7b3cda6ea --- /dev/null +++ b/packages/portal/plugins/markdown/lineNumber.ts @@ -0,0 +1,31 @@ +import { type MarkdownItAsync } from 'markdown-it-async'; + +export default function highlightLine(md: MarkdownItAsync) { + const fence = md.renderer.rules.fence; + md.renderer.rules.fence = (tokens, idx, options, env, self) => { + const lineNumberReg = /:line-numbers(=\d+)?/; + const lineNumberMatch = tokens[idx].info.match(lineNumberReg); + let originalCode = ''; + if (lineNumberMatch) { + let start = parseInt(lineNumberMatch[1]?.slice(1) || '1'); + start = Number.isNaN(start) ? 1 : start; + tokens[idx].info = tokens[idx].info.replace(lineNumberMatch[0], ''); + originalCode = fence(tokens, idx, options, env, self); + const codeMatch = originalCode.match(/()([\s\S]*?)(<\/code><\/pre>)/); + if (codeMatch) { + const [, pre, code, post] = codeMatch; + const codeLines = code.split('\n'); + if (codeLines[codeLines.length - 1] === '') { + codeLines.pop(); + } + if (env.portal) { + env.portal.lineNumbers = true; + } else { + env.portal = { lineNumbers: true }; + } + return `${pre}${codeLines.map((line, index) => `${start + index}${line}`).join('\n')}\n${post}`; + } + } + return fence(tokens, idx, options, env, self); + }; +} diff --git a/packages/portal/plugins/markdown/vueMdTranslate.ts b/packages/portal/plugins/markdown/vueMdTranslate.ts new file mode 100644 index 000000000..88fab1074 --- /dev/null +++ b/packages/portal/plugins/markdown/vueMdTranslate.ts @@ -0,0 +1,27 @@ +import Markdown from 'unplugin-vue-markdown/vite'; +import hljs from 'highlight.js'; +import lineNumber from './lineNumber'; +import wrapCodeContainer from './wrapCodeContainer'; +import { MarkdownItAsync } from 'markdown-it-async'; + +export const markdownItOptions = { + highlight(str: string, lang: string, attrs: string) { + let highlightLang = lang === 'vue' ? 'html' : lang; + const hasLang = hljs.getLanguage(highlightLang); + if (!hasLang) { + highlightLang = 'bash'; + } + return hljs.highlight(str, { language: highlightLang }).value; + }, +}; +export const markdownItPlugins = [ + lineNumber, + wrapCodeContainer, +]; +export const markdownItSetup = function (md: MarkdownItAsync) { + markdownItPlugins.forEach((plugin) => md.use(plugin)); +}; +export const plugin = Markdown({ + markdownItOptions, + markdownItSetup, +}); diff --git a/packages/portal/plugins/markdown/wrapCodeContainer.ts b/packages/portal/plugins/markdown/wrapCodeContainer.ts new file mode 100644 index 000000000..9edd147a9 --- /dev/null +++ b/packages/portal/plugins/markdown/wrapCodeContainer.ts @@ -0,0 +1,14 @@ +import { MarkdownItAsync } from 'markdown-it-async'; + +export default function wrapCodeContainer(md: MarkdownItAsync) { + const fence = md.renderer.rules.fence; + md.renderer.rules.fence = (tokens, idx, options, env, self) => { + const token = tokens[idx]; + const preCode = fence(tokens, idx, options, env, self); + + return ` + + ${preCode} +`; + }; +} diff --git a/packages/portal/src/assets/style/markdown.scss b/packages/portal/src/assets/style/markdown.scss new file mode 100644 index 000000000..61a42f389 --- /dev/null +++ b/packages/portal/src/assets/style/markdown.scss @@ -0,0 +1,6 @@ +:not(pre) > code { + border-radius: 4px; + padding: 0.1em 0.2em; + margin: 0 0.2em; + background-color: var(--o-color-control1-light); +} diff --git a/packages/portal/src/assets/style/style.scss b/packages/portal/src/assets/style/style.scss index 71b2d7714..5e9414f46 100644 --- a/packages/portal/src/assets/style/style.scss +++ b/packages/portal/src/assets/style/style.scss @@ -1,3 +1,5 @@ +@use './markdown.scss'; + body { font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; diff --git a/packages/portal/src/components/CodeContainer.vue b/packages/portal/src/components/CodeContainer.vue index 49a6420b1..c1777c462 100644 --- a/packages/portal/src/components/CodeContainer.vue +++ b/packages/portal/src/components/CodeContainer.vue @@ -1,30 +1,34 @@