From 2cc78dd3297b1d1bbb518efd83e78e874d6c355e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=8E=AB=E5=94=90=E5=B0=BC?= Date: Mon, 15 Jun 2026 16:36:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=8D=9A=E5=AE=A2tok?= =?UTF-8?q?en=E9=85=8D=E7=BD=AE=EF=BC=8C=E5=AE=8C=E5=96=84=E6=96=87?= =?UTF-8?q?=E7=AB=A0=E8=AF=A6=E6=83=85=E9=A1=B5=E4=B8=8Emarkdown=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增IUniHaloConfig的token配置项并关联环境变量 2. 调整文章跳转路径到subpkg分包目录 3. 重构markdown配置使用主题色替换硬编码值 4. 升级代码块样式为深色主题并添加圆角 5. 新增完整的文章详情页面实现 6. 删除冗余的markdown样式scss文件 7. 添加项目claude开发指南文档 --- CLAUDE.md | 118 +++++++ src/api/halo-plugin/uni-halo.ts | 2 + .../mp-html/components/mp-html/node/node.vue | 2 +- .../static/app-plus/mp-html/local.html | 2 +- src/components/uh-post-card/uh-post-card.vue | 2 +- src/config/markdown/config.ts | 6 +- src/config/markdown/style.scss | 155 ---------- src/store/config.ts | 1 + src/subpkg-blog/post-detail/post-detail.vue | 287 +++++++++++++++++- 9 files changed, 412 insertions(+), 163 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 src/config/markdown/style.scss diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e37a919 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,118 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +uni-halo v3.0 is a multi-platform blog client built on the **Halo 2.x** headless CMS API. It targets WeChat mini-program (primary), H5, Alipay, and native apps. Built on the **unibest** scaffold with Vue 3 + TypeScript + Vite + UnoCSS. + +## Commands + +| Task | Command | +|------|---------| +| Install dependencies | `pnpm install` | +| Dev (H5) | `pnpm dev` or `pnpm dev:h5` | +| Dev (WeChat mini-program) | `pnpm dev:mp` or `pnpm dev:mp-weixin` | +| Dev (Alipay mini-program) | `pnpm dev:mp-alipay` | +| Dev (native app) | `pnpm dev:app` | +| Build (H5) | `pnpm build` or `pnpm build:h5` | +| Build (WeChat) | `pnpm build:mp` | +| Build (app) | `pnpm build:app` | +| Lint | `pnpm lint` | +| Lint + auto-fix | `pnpm lint:fix` | +| Type check | `pnpm type-check` | +| Run tests (watch) | `pnpm test` | +| Run tests (single) | `pnpm test:run` | +| Generate API client from OpenAPI | `pnpm openapi` | +| Upload to WeChat platform | `pnpm upload:mp` | + +## Architecture + +### Entry Flow +`src/main.ts` → creates Vue app → installs Pinia → route interceptor → request interceptor → i18n. On launch, `src/App.vue` fetches the uni-halo plugin configuration from the Halo server and stores it in the `uniHaloConfig` Pinia store. + +### Routing (Convention-Based) +Pages in `src/pages/` are auto-registered by `@uni-helper/vite-plugin-uni-pages`. Use the `definePage()` macro at the top of page files to set metadata (navigation style, title, etc.). Sub-packages live in `src/subpkg-blog/` and `src/subpkg-demo/`. + +Route guards in `src/router/interceptor.ts` intercept `navigateTo`, `reLaunch`, `redirectTo`, `switchTab`. Supports whitelist (default: login required) and blacklist strategies, configured in `src/router/config.ts`. + +### HTTP Layer — Three Coexisting Strategies +1. **Custom `uni.request` wrapper** (`src/http/http.ts`) — GET/POST/PUT/DELETE helpers with transparent 401 token refresh (queues pending requests during refresh). Used by `src/api/login.ts`. +2. **Alova** (`src/http/alova.ts`) — Alova 3.3+ with `@alova/adapter-uniapp`, Halo-specific response handling. Used by `src/api/halo-base/*` and `src/api/halo-plugin/*`. +3. **vue-query** (`src/http/vue-query.ts`) — skeleton/option, not actively used. + +Request interceptor (`src/http/interceptor.ts`) attaches `Authorization: Bearer ` to all requests. + +### Authentication +Dual token mode support (single access token OR access+refresh pair), controlled by `VITE_AUTH_MODE` env var. Token store (`src/store/token.ts`) handles login (standard + WeChat mini-program), logout, and automatic refresh with computed expiry checks. + +### API Layer +- `src/api/halo-base/` — Core Halo APIs: posts, categories, tags, comments, stats, trackers +- `src/api/halo-plugin/` — uni-halo plugin APIs: plugin config, moments, links, gallery, votes, douban, muyin, data stats +- `src/api/login.ts` — Auth APIs (login, register, WeChat login, token refresh) +- `src/api/types/` — TypeScript interfaces for API responses +- Types from `@halo-dev/api-client` (e.g., `ListedPostVo`, `PostVo`) + +### State Management +Pinia with `pinia-plugin-persistedstate` using `uni.getStorageSync`/`uni.setStorageSync`. All stores use Composition API style (`defineStore` with setup function): +- `src/store/token.ts` — Authentication +- `src/store/user.ts` — User profile +- `src/store/config.ts` — uni-halo plugin configuration + +### Custom Tabbar +Fully custom tabbar in `src/tabbar/` with its own Pinia store. Configurable strategy: no tabbar, native tabbar, or custom tabbar. Currently set to `CUSTOM_TABBAR` with 5 tabs. + +## Coding Conventions + +### Vue Components +- Use ` -
\ No newline at end of file +
\ No newline at end of file diff --git a/src/components/uh-post-card/uh-post-card.vue b/src/components/uh-post-card/uh-post-card.vue index 32bee30..e03e391 100644 --- a/src/components/uh-post-card/uh-post-card.vue +++ b/src/components/uh-post-card/uh-post-card.vue @@ -96,7 +96,7 @@ const textFooterClass = computed(() => { function handleToPostDetail() { uni.navigateTo({ - url: `/pages-blog/post-detail/post-detail?metadataName=${props.post.metadata.name}`, + url: `/subpkg-blog/post-detail/post-detail?metadataName=${props.post.metadata.name}`, }) } diff --git a/src/config/markdown/config.ts b/src/config/markdown/config.ts index 6243201..e7e90b2 100644 --- a/src/config/markdown/config.ts +++ b/src/config/markdown/config.ts @@ -1,5 +1,7 @@ import { useAppConfig } from '../useAppConfig' +const primaryColor = '#111827' + export const markdownConfig = { domain: useAppConfig().appBaseUrl.value, tagStyle: { @@ -28,7 +30,7 @@ border-bottom: 1px solid var(--classE); padding: 8px 15px; color: #606266; background: #f2f6fc; -border-left: 5px solid #50bfff; +border-left: 5px solid ${primaryColor}; border-radius: 4px; line-height: 26px; margin-bottom: 18px; @@ -87,7 +89,7 @@ font-size: 14px; strong: 'font-weight: 700;color: rgb(248, 57, 41);', video: 'width: 100%', }, - containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding:12px;font-size: 14px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;', + containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;', loadingGif: '', emptyGif: '', diff --git a/src/config/markdown/style.scss b/src/config/markdown/style.scss deleted file mode 100644 index 28078a2..0000000 --- a/src/config/markdown/style.scss +++ /dev/null @@ -1,155 +0,0 @@ -:root { - --main: #303133; - --theme: #fb6c28; - --code-background: #e8f3ff; - --radius-inner: 4px; - --classA: #dcdfe6; - --classB: #e4e7ed; - --classC: #ebeef5; - --classD: #f2f6fc; - --classE: #dcdcdc; - --classF: #333; - --classG: #dcdcdc; - --classH: #e9f2ff; - --classI: #5a3713; - --classJ: #f9e5fb; - --classK: #e4e7ed; - --classL: #666; - --classM: #2d2e37; - --quote: #50bfff; - --code: #409eff; -} -.uh-markdown { - ::v-deep { - h1::before, - h2::before, - h3::before, - h4::before, - h5::before, - h6::before { - position: relative; - display: inline-block; - vertical-align: middle; - content: ''; - margin-right: 6px; - background-position: center; - } - h1::before { - position: relative; - display: inline-block; - vertical-align: middle; - content: '¶'; - top: -4px; - margin-right: 12px; - font-size: 24px; - color: var(--theme); - } - h2::before { - top: -2px; - left: 0; - width: 20px; - height: 20px; - background-size: auto 100%; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAVJJREFUWEftl7FKw1AUhv9rbmf1PRw6utkHEOoi6WPoE1ifQJ8gySKCOtRFRBKsDuIiDdZAURBFF50M2sFIzpGogVK03JA2cbh3S/i55zvfvYETgRyLj5erIKOO949FyKm5eHvvHEBV2jyruq1QDQ7n2DNXALEx+D7e2vl6lBYp76scHCzErtmCEPVhqEIAfus8BZk4wM+Zd/46tskDeI0mgLUyAdoAFsoDcM0XCDFdHoDX4FGfbRF3QANoA9qANqANaAPawD82EEWId1sAOJQWz6iO+5nGch41kDw9I3bbYOaTis21wgG4G4AuAzCwXrEoGV6V1ngMJPoPjoB+PzSIa8KBr1QdwFgA6KID7t1AgFYNC5uqxZNcbgB+eASdngHM+9LmpSzF8wFEEagbgHvXoQA3s3aegmYz8P1f4NNVME+39z5e3w4lkSMc3GXtPM1/AjYDFjDGddN5AAAAAElFTkSuQmCC); - } - h3::before { - top: -3px; - left: 0; - width: 20px; - height: 20px; - background-size: auto 100%; - background-repeat: none; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAH1JREFUWEft1qENgDAQheH/JANg60DAJIgmrMFCzEEYgTFgDyRFIClp0yBf9evl8pl3RsYbp7BjNLHoMptljPiMZH3WAhKQgAQkIIGXQAXUT79cniHaNMa5draliqqojIID86nRHEtvbSqlBSQgAQlIQAISkECRAA746SC5Ad6XpiGnnOGPAAAAAElFTkSuQmCC); - } - h4::before { - top: -2px; - width: 22px; - height: 22px; - color: var(--theme); - background-size: auto 100%; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAndJREFUWEftVkty2kAQfT1SBLtwg6AqwzbxCQIniG8QvAmwCp8kRqvgFa5KwHjlkI3NCeIbhJwgXptUgW9AdiDQdGoITqUsjcZyuYqNZ6WSXs88vel+3YQtL9ry+Xgk8KhApALNAe8x5PPbCUrgabdsD2/e+1edEpN4FsIRps7OwT9cXKKHCLw75QIL+V0XRFIUP1dptBx3ChKkxQlw8UnOG5mqLESgdsYZy5dTAE8jg5lH3Ypd5Mlxxl8ttDgGRulcq5iYgApofgn6ILzVBQsSu5/e0OXiqnMOotdatVjsOvkPl4muQIFrp5y1hJzEBA67ZavEk07WX5EexzxM5b1SYgJrFQbBOQDt3wVSuP0qTRfjowsAr3SHODa75HrqqiKX1gdMycjAYa9stU3JCPBhKue1ExNQAY3BakSgl1HBDMykI9z+Ps0WV0eXIITKdhM3c+yUS259FrVPrBM2TlclEnSmY8+S93tV+/yvH+hxxLzv5D11paFltOLmIFD3FzIbtRMzpr2K5arn+bgzJZAWl8631rjby0zAUJI3xnTfkjQTiFEAwHW3bGWNCoCv0zlvjUukgOoJgPymzQGg3itbfVMOMKiezh30ExOIqwIAvwNHZFUVzMcdbbUonGOnsomr4P1XfiFZ/tS6GOOkW7FqJh8gwomz06ol9oE7O6GhH9zLCbfeCxqDoE3AR61sm5nA/3XUZ47pmneYCSLnAeHLCQGZaAvmH72yXdjMA6oTanHpnFfQ5tDmQ+KJ6MZ+jckXY7//k4pWYB7sQVCUccxU3a9teHKcWS7ne0wI4Rhipqv7REZkku8hvhut+CEOidvjkcDWFfgD9RMzMKE7f80AAAAASUVORK5CYII=); - } - h5::before { - top: -1px; - left: 0; - width: 18px; - height: 18px; - background-size: 100% 100%; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAC8klEQVRYR+3WP2gTURwH8O/vKnVRRHKXP52cBO3g4p/BqYNIhy4muajUQRBFKjQV1En6ZxJBcmlRCoJDRe3FDiJVwamLS3FwqbgIgss1l2ZQF5XeT3I1Z3NJ7u5dLlAwN9699/t97vfe7/EIO/yhHe5DD9jpCv3fFVTu856+Xzi62Y/35hj9CFPNrlUwWeRJAJfBGADwBcBNI0/PRZFdAdo4xlQLjCqKjBzogat7hZCRAgPghJGBgbFnxglAOvS7b/fLb+q+qnv5BHBCyEBAWTdnANy2IxOtWSC1qsY+1jOFwAVG+gIVvXyHQbcaKuZCxmf5iMQogXFQtEsBeO5JT6BcMu+Bcb1lUhcyqfExACUAB6JEtgUqi+U5JrrmmcyFHCjwSYtQO+tSUSFbAmXdnAdwJVASdyWLPAS2kbFA8xsHNS13EzCmlx8R6KJQcBcypfFpho3cKxRna3ADsgEo6+ZjAKMhgjZ1d2KWR2gTSyD0h4jnIB2gXDIXwciFCPZvSnMl0wwshYrJGDImaMUGKovlLBPVOrDjh8APzFx8zDkjNa7FzYoGZsKT9XEarQOHmei1aJA246cqOWW6/i2l8VMGzoWIPW/k6eq2Ja6UwCz8pw2JGZ8sS8pUz8fWau/jGp+SgLchcGBgZD1Py41NUuoI+ZloV8ZU93+ogZKzfBiWfXAPCgMJU8Y42avQdMzI4ZBfmSizocqrUeJaAmsvBZGGBCtTziXeRY1rCxRAViEhXckqK93AeQJ9kYTvlmVlqmcTdhNEtefc+9X3utVmuX+CkK6oyqtu4nwrWP8bF5IZdGYjJ79wDuMCz4D+XmhFWnZbt7ab5ltBB6mbkyAaBPFCJassuwMmNb4L4EZgXwBc4AoGTZrUeA6A9x1yK6tzzvnFDlxBv0D176kCP2TCpbbjBXCRV9DZk0VeAONCE1IQ1zWg3dlF1sFQHWQIXFeBNrLAw5BwHBZWjQl6E3SbbB8X+R4Mg/Ca0wN2WtFeBTut4B84mFI4VpekyAAAAABJRU5ErkJggg==); - } - h6::before { - top: -1px; - left: 0; - width: 16px; - height: 16px; - background-size: auto 100%; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEI0lEQVRYR+3Xb2wTZRwH8G/vL22RPy5GW8fK6rJpGeFFY9RO3TRRE1HfmEAammEyjZmOSEg1RrPZaTD6xjhU/Ndlf0CZgwmD+qcgZBRIETeqY0Vcnc7pGonhRY2l3m2t5upqjq693l2vcy+8N81zz/NcPvf75fd7rjos8ku3yH34HyjK0PqlDLPzD56vMNL0VHxmpgXAoUIZXKgI3sMSpK+cNTAbylaj/9IkprnLM3+mkg8XQi4EsJ4lSL+ZNbAfVd+JG/XLcSERg3M8IAtZauBtDEkeNdN6fX9NfRqXueQiSwm00wQRMDMGw94snBJkqYBrSYIIXs/ojftqGq6IXHZRFIpkKYC2JSw9SJE663W0nthlqcMawwrJYpVCag1M48pNy1YNvLWRffGNIYSCv6Cnog7rjCtVIbUEXoG7qeqaNMj51F58dWoKPZY62JeWyUJG+ctcIplcIizWCpgTl9EIyODJn9BtceDWq/6B57tO/H4R688fFaZ7AWzWAiiJEyMDx39Ed6UDdyy7Ni/w1ekxbP95VJi/F8CRYoGycGLksaEJdFU6cPdy0zxkR/RbtE6FhPvHATQUm2JFODHSfyyCLmsd7lth/hf53q/jcE8OC+NTAG7PTKiNoCqcGPnpF+PotDrwwNXl+OC3H9A8cVqYPgPgFnFo1QCLwomRg4cvoPMGBzZHTgq3zwKwZ+ddKVATXAZhf+gdnPvuojAUqmJdrspRAtQU93pXEM+8clgwhQHU5itruUBNcTt6TsP9sl8weQC0S/VFOUBNcW/2folt2z+XhZPTZh6kSKJ79aqVRuFszRxfUm8sNbdz9xlsfekz2bhCQBvDMMM8z+ubN92Mjrb71brS+97dM4wtnk8U4SSBBEGErVarzeVywePxoHVLA1pb6lUh3+8bwZMv+BTjpIAVACa9Xq+uqakJ7e3tqpGd/WfR3Jr+81awIJS0mY0A+sLhMGw2W3qfGmTXvhAef/6gapxUBDuqqqoejUQiBvFbKUH2DHyNx54bLAqXF0jT9HBjY6Pd6/XOi7oc5K7936Dp2QNF4/IBrTqdLuLxeIi2tracRSGF/PDgKB55er8muHzATQB2m81m+P1+1NbmPoVyIfsOnUOj+2PNcPmAO1iWbeE4TmexWODz+WQhayrL4No2oCkuJ5Bl2VGO49ZmcisXObdeVSuRaq7ZZ3G10KBTqRQl3pQPGY1GEQgE4HQ6heVDAO5S1cklNmUDXQRB9KZSqXkfESaTCW63G7FYDKFQKDEyMoJoNKqfe/bbAJ7QGpcrxa8xDLOV5/k0kGXZv2ZnZ5FMJtNjmqYvURR1IpFInAcwAeD7ud/pUuDmAY1G41g8Hl9DkmScoqggx3FHAIyJMMlSQfI9V5zKagDC93dsoRFKimQx2dIWOV/U/yn6bx0WyDj8vgLOAAAAAElFTkSuQmCC); - } - blockquote > p { - margin-bottom: 0 !important; - margin-top: 0 !important; - font-size: 0.9em !important; - } - code[class='md-code'], - code:not([class]) { - display: inline-block; - font-size: 13px; - color: #409eff; - margin: 2px 5px; - padding: 0 8px; - white-space: normal; - text-indent: 0; - -webkit-user-select: auto; - -moz-user-select: auto; - -ms-user-select: auto; - user-select: auto; - vertical-align: baseline; - word-break: break-word; - background: #e8f3ff; - border-radius: 4px; - } - code[class*='language-'] { - display: block; - overflow-x: auto; - // border-radius: 0 0 8px 8px; - white-space: pre-wrap; - word-break: break-all; - user-select: auto; - padding: 12px 12px 14px 18px; - margin-bottom: 16px; - background: #282c34; - color: #abb2bf; - border-radius: 4px; - text-shadow: 0 1px rgba(0, 0, 0, 0.3); - font-family: 'Fira Code', 'Fira Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - line-height: 1.5; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; - } - table { - td { - padding: 8px; - border-right: 1px solid var(--classE); - border-bottom: 1px solid var(--classE); - } - thead th { - font-weight: 500; - background: var(--classC); - } - - tbody tr { - transition: background 0.35s; - } - } - } -} diff --git a/src/store/config.ts b/src/store/config.ts index 3f1bdd0..f9515cb 100644 --- a/src/store/config.ts +++ b/src/store/config.ts @@ -6,6 +6,7 @@ import { deepMerge } from '@/utils/base/deepMerge' const defaultConfig: IUniHaloConfig = { base: { domain: import.meta.env.VITE_UNI_HALO_DOMAIN, + token: import.meta.env.VITE_UNI_HALO_TOKEN, isIndividual: false, }, home: { diff --git a/src/subpkg-blog/post-detail/post-detail.vue b/src/subpkg-blog/post-detail/post-detail.vue index 676836b..b02a0c7 100644 --- a/src/subpkg-blog/post-detail/post-detail.vue +++ b/src/subpkg-blog/post-detail/post-detail.vue @@ -1,5 +1,19 @@ + +