├── composables ├── index.ts └── useGithubFetch.ts ├── .npmrc ├── .eslintrc ├── public └── favicon.ico ├── utils ├── index.ts ├── readTime.ts ├── url.ts └── date.ts ├── tsconfig.json ├── api ├── query.d.ts ├── index.ts └── type.d.ts ├── app.vue ├── .env.example ├── .editorconfig ├── pages ├── [...404].vue ├── p │ └── [id].vue ├── index.vue ├── search.vue └── blog │ └── [[catelog]].vue ├── components ├── markdown │ ├── DocFooter.vue │ ├── DocRender.vue │ ├── DocToc.vue │ ├── DocBack.vue │ ├── index.vue │ ├── markdown-it.ts │ └── DocTitle.vue ├── AppFooter.vue ├── SearchBar.vue ├── IssueCell.vue ├── SubNav.vue ├── Comment.vue ├── AppHeader.vue └── DarkMode.vue ├── layouts └── default.vue ├── middleware └── app.global.ts ├── assets └── styles │ ├── var.css │ ├── global.css │ ├── transition.css │ └── markdown.scss ├── .gitignore ├── .vscode └── settings.json ├── package.json ├── LICENSE ├── uno.config.ts ├── nuxt.config.ts ├── README.md └── README_en.md /composables/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useGithubFetch' 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@chansee97/eslint-config-vue" 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chansee97/issue-nuxt-blog/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './url' 2 | export * from './date' 3 | export * from './readTime' 4 | -------------------------------------------------------------------------------- /utils/readTime.ts: -------------------------------------------------------------------------------- 1 | export function getReadTime(post: string) { 2 | return Math.round(post.length / 700) + 1 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /api/query.d.ts: -------------------------------------------------------------------------------- 1 | export interface IssueQuery { 2 | /* issue分类 */ 3 | milestone?: string 4 | /* issue标签 */ 5 | labels?: string 6 | } 7 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # gh 用户名 2 | VITE_OWNER = chansee97 3 | # gh blog 仓库 4 | VITE_BLOGS_REPO = my-blogs 5 | # gh token 6 | VITE_GITHUB_TOKEN = github_pat_xxxxxx 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /pages/[...404].vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /components/markdown/DocFooter.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /middleware/app.global.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtRouteMiddleware((_to, _from) => { 2 | useHead({ 3 | titleTemplate: (productCategory) => { 4 | return productCategory 5 | ? `${productCategory} - Rock Chen` 6 | : 'Rock Chen' 7 | }, 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /assets/styles/var.css: -------------------------------------------------------------------------------- 1 | :root { 2 | } 3 | 4 | html { 5 | --primary: #0d0d0d; 6 | --c-bg: #fafafa; 7 | --c-scrollbar: #ccc; 8 | --c-scrollbar-hover: #aaa; 9 | } 10 | 11 | html.dark { 12 | --c-text: #ebebeb; 13 | --c-bg: #141414; 14 | --c-scrollbar: #555; 15 | --c-scrollbar-hover: #777; 16 | } 17 | -------------------------------------------------------------------------------- /components/markdown/DocRender.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | .env.local 25 | !.env.example 26 | 27 | #Lock files 28 | *-lock.yaml 29 | -------------------------------------------------------------------------------- /pages/p/[id].vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 20 | -------------------------------------------------------------------------------- /components/markdown/DocToc.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | -------------------------------------------------------------------------------- /composables/useGithubFetch.ts: -------------------------------------------------------------------------------- 1 | const { VITE_GITHUB_TOKEN } = import.meta.env 2 | 3 | export async function useGithubFetch(url: string) { 4 | const { data, error } = await useFetch(url, { 5 | onRequest({ options }) { 6 | options.headers = { 7 | Authorization: `Bearer ${VITE_GITHUB_TOKEN}`, 8 | } 9 | }, 10 | }) 11 | 12 | if (error.value) { 13 | throw createError({ 14 | ...error.value, 15 | statusMessage: `Could not fetch data from ${url}`, 16 | }) 17 | } 18 | 19 | return { 20 | data, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.formatOnSave": false, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": "explicit", 6 | "source.fixAll.stylelint": "explicit", 7 | "source.organizeImports": "never" 8 | }, 9 | "prettier.enable": false, 10 | "eslint.format.enable": true, 11 | "eslint.validate": [ 12 | "javascript", 13 | "javascriptreact", 14 | "typescript", 15 | "typescriptreact", 16 | "vue", 17 | "html", 18 | "markdown", 19 | "json", 20 | "jsonc", 21 | "yaml" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /components/markdown/DocBack.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | 16 | 28 | -------------------------------------------------------------------------------- /assets/styles/global.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | width: 6px; 3 | } 4 | 5 | ::-webkit-scrollbar:horizontal { 6 | height: 6px; 7 | } 8 | 9 | ::-webkit-scrollbar-track, 10 | ::-webkit-scrollbar-corner { 11 | border-radius: 10px; 12 | } 13 | 14 | ::-webkit-scrollbar-thumb { 15 | background: var(--c-scrollbar); 16 | border-radius: 10px; 17 | } 18 | 19 | ::-webkit-scrollbar-thumb:hover { 20 | background: var(--c-scrollbar-hover); 21 | } 22 | 23 | html, 24 | body, 25 | #nuxt-root { 26 | height: 100%; 27 | line-height: 1.75; 28 | scroll-behavior: smooth; 29 | } 30 | 31 | html { 32 | background: var(--c-bg); 33 | color:var(--c-text); 34 | } 35 | -------------------------------------------------------------------------------- /components/SearchBar.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 30 | -------------------------------------------------------------------------------- /utils/url.ts: -------------------------------------------------------------------------------- 1 | export function addSearchParamsToURL(url: string, params?: Record) { 2 | if (!params) return url 3 | 4 | const searchParams = new URLSearchParams() 5 | 6 | // 将参数添加到 URLSearchParams 对象中 7 | for (const key in params) 8 | searchParams.append(key, params[key]) 9 | 10 | // 将 URLSearchParams 对象添加到 URL 中 11 | const urlObject = new URL(url) 12 | urlObject.search = searchParams.toString() 13 | 14 | return urlObject.toString() 15 | } 16 | 17 | export function buildQueryString(obj: any) { 18 | let queryString = '' 19 | 20 | for (const key in obj) 21 | queryString += `+${key}:${obj[key]}` 22 | 23 | return queryString 24 | } 25 | -------------------------------------------------------------------------------- /utils/date.ts: -------------------------------------------------------------------------------- 1 | import { format, parseISO } from 'date-fns' 2 | 3 | export function formattedDate(date: string) { 4 | if (!date) return '' 5 | const dateObject = parseISO(date) 6 | const formattedResult = format(dateObject, 'yyyy/MM/dd ') 7 | return formattedResult 8 | } 9 | 10 | export function insertYearToPosts(posts: any) { 11 | let currentYear = -1 12 | return posts.reduce( 13 | (posts: any, post: any) => { 14 | const year = new Date(post.created_at).getFullYear() 15 | if (year !== currentYear && !isNaN(year)) { 16 | posts.push({ 17 | isMarked: true, 18 | year, 19 | }) 20 | currentYear = year 21 | } 22 | posts.push(post) 23 | return posts 24 | }, 25 | [], 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /components/IssueCell.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | -------------------------------------------------------------------------------- /components/markdown/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 35 | -------------------------------------------------------------------------------- /components/SubNav.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "scripts": { 5 | "build": "nuxt build", 6 | "dev": "nuxt dev", 7 | "generate": "nuxt generate", 8 | "preview": "nuxt preview", 9 | "postinstall": "nuxt prepare" 10 | }, 11 | "dependencies": { 12 | "@iconify-json/icon-park-outline": "^1.1.14", 13 | "@iconify-json/simple-icons": "^1.1.82", 14 | "@unocss/reset": "^0.58.0", 15 | "date-fns": "^2.30.0", 16 | "highlight.js": "^11.9.0", 17 | "markdown-it": "^14.0.0", 18 | "markdown-it-anchor": "^8.6.7", 19 | "markdown-it-highlightjs": "^4.0.1", 20 | "markdown-it-toc-done-right": "^4.2.0" 21 | }, 22 | "devDependencies": { 23 | "@chansee97/eslint-config-vue": "^0.3.5", 24 | "@nuxt/devtools": "^1.0.5", 25 | "@types/markdown-it": "^13.0.7", 26 | "@types/node": "^18.17.3", 27 | "@unocss/nuxt": "^0.55.0", 28 | "@vueuse/core": "^10.3.0", 29 | "@vueuse/nuxt": "^10.3.0", 30 | "eslint": "^8.47.0", 31 | "feed": "^4.2.2", 32 | "nuxt": "^3.8.2", 33 | "sass": "^1.65.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /components/Comment.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 |