Initial rearrangment of vuetom site source

This commit is contained in:
2024-10-09 08:23:08 +00:00
parent dbd456a517
commit e9d08bd263
233 changed files with 22841 additions and 1 deletions

View File

@@ -0,0 +1,112 @@
import fs from 'fs'
import path from 'path'
import chalk from 'chalk'
import consola from 'consola'
import { docRoot } from '../utils/paths'
import { errorAndExit } from '../utils/log'
// NB: this file is only for generating files that enables developers to develop the website.
const componentLocaleRoot = path.resolve(docRoot, '.vitepress/crowdin')
const exists = 'File already exists'
async function main() {
const localeOutput = path.resolve(docRoot, '.vitepress/i18n')
if (fs.existsSync(localeOutput)) {
throw new Error(exists)
}
consola.trace(chalk.cyan('Starting for build doc for developing'))
// all language should be identical since it is mirrored from crowdin.
const dirs = await fs.promises.readdir(componentLocaleRoot, {
withFileTypes: true
})
const languages = dirs.map((dir) => dir.name)
const langWithoutEn = languages.filter((l) => l !== '.DS_Store')
await fs.promises.mkdir(localeOutput)
// build lang.json for telling `header>language-select` how many languages are there
await fs.promises.writeFile(
path.resolve(localeOutput, 'lang.json'),
JSON.stringify(languages),
'utf-8'
)
// loop through en-US
const enUS = path.resolve(componentLocaleRoot, 'en-US')
// we do not include en-US since we are currently using it as template
const languagePaths = langWithoutEn.map((l) => ({
name: l,
pathname: path.resolve(componentLocaleRoot, l)
}))
consola.debug(languagePaths)
await traverseDir(enUS, languagePaths, localeOutput)
}
async function traverseDir(
dir: string,
paths: { name: string; pathname: string }[],
targetPath: string
) {
const contents = await fs.promises.readdir(dir, { withFileTypes: true })
await Promise.all(
contents.map(async (c) => {
if (c.isDirectory()) {
await fs.promises.mkdir(path.resolve(targetPath, c.name), {
recursive: true
})
return traverseDir(
path.resolve(dir, c.name),
paths.map((p) => ({
...p,
pathname: path.resolve(p.pathname, c.name)
})),
path.resolve(targetPath, c.name)
)
} if (c.isFile()) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const content = require(path.resolve(dir, c.name))
const contentToWrite = {
'en-US': content
}
await Promise.all(
paths.map(async (p) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const content = require(path.resolve(p.pathname, c.name))
contentToWrite[p.name] = content
})
)
return fs.promises.writeFile(
path.resolve(targetPath, c.name),
JSON.stringify(contentToWrite, null, 2),
{
encoding: 'utf-8'
}
)
}
})
)
}
main()
.then(() => {
consola.success(chalk.green('Locale for website development generated'))
})
.catch((err) => {
if (err.message === exists) {
// do nothing
} else {
errorAndExit(err)
}
})

View File

@@ -0,0 +1,57 @@
import { build, defineConfigWithTheme, useData } from 'vitepress'
import { VuetomThemeConfig } from 'vitepress-theme-vuetom'
import pkg from '../package.json'
import { getNav, getSidebar, head, locales } from './menus'
const nav = getNav('en-US')
const sidebar = getSidebar('en-US')
export default defineConfigWithTheme<VuetomThemeConfig>({
base: `/vt/`,
ignoreDeadLinks: true,
lastUpdated: true,
head,
locales: locales.vitepressConfig,
themeConfig: {
nav,
sidebar,
localeLinks: {
text: '',
items: [
{
text: 'English',
link: '/lang/enus'
}
]
},
socialLinks: [
{ icon: 'github', link: pkg.repository }
],
footer: {
message: 'Using the Vuetom Vitepress Theme.',
copyright: 'Copyright © 2021-present Lauset'
},
logoImg: '/imgs/Avatar.jpg',
bgImg: '/imgs/homg-bg01-200k.jpg',
bgColor: '0,0,0',
bgOpacity: 0.6,
flashEnable: true,
flashColor: ['238,17,17', '0,98,255'],
parallaxEnable: true,
pageBgEnable: true,
pageBgOpacity: 0.8,
featuresColor: ['#06cdff30', 'rgba(223,7,107,.3)']
},
markdown: {
lineNumbers: true
},
appearance: true,
vite: {
ssr: {
noExternal: ["vitepress-theme-vuetom"]
}
}
})

View File

@@ -0,0 +1,14 @@
---
title: What's the Vitepress
head:
- - meta
- name: description
content: vuetom info
- - meta
- name: keywords
content: vuetom theme
---
# {{ $frontmatter.title }}
developing...

View File

@@ -0,0 +1,149 @@
---
layout: home
title: Vuetom
titleTemplate: Vite & Vue Powered Static Site Generator
hero:
name: Vuetom
text:
tagline: vitepress flat theme
actions:
- theme: brand big
text: Quick Start
link: /en-US/guide/info
- theme: alt big
text: Config
link: /en-US/guide/config
features:
- title: 📦 优化的构建
details: 可选 “多页应用” 或 “库” 模式的预配置 Rollup 构建
- title: 🔩 通用的插件
details: 在开发和构建之间共享 Rollup-superset 插件接口。
- title: 🔑 完全类型化的API
details: 灵活的 API 和完整 TypeScript 类型。
---
<div class="frontpage sponsors">
<h2>Thanks</h2>
<div class="platinum-sponsors">
<a v-for="{ href, src, name, id } of sponsors.filter(s => s.tier === 'platinum')" :href="href" target="_blank" rel="noopener" aria-label="sponsor-img">
<img :src="src" :alt="name" :id="`sponsor-${id}`">
<p>{{ name }}</p>
</a>
</div>
<div class="gold-sponsors">
<a v-for="{ href, src, name, id } of sponsors.filter(s => s.tier !== 'platinum')" :href="href" target="_blank" rel="noopener" aria-label="sponsor-img">
<img :src="src" :alt="name" :id="`sponsor-${id}`">
<p>{{ name }}</p>
</a>
</div>
</div>
<script setup>
import { onMounted } from 'vue'
const sponsors = [
{
"id": "vue",
"name": "Vue",
"href": "https://v3.cn.vuejs.org/",
"src": "https://v3.cn.vuejs.org/logo.png",
"tier": "platinum"
},
{
"id": "vite",
"name": "Vite",
"href": "https://vitejs.cn/",
"src": "https://vitejs.cn/logo.svg"
},
{
"id": "vitepress",
"name": "Vitepress",
"href": "https://fttp.jjf-tech.cn/vitepress/",
"src": "https://v3.cn.vuejs.org/logo.png"
},
{
"id": "elementplus",
"name": "Element Plus",
"href": "https://element-plus.gitee.io/zh-CN/",
"src": "https://element-plus.gitee.io/images/element-plus-logo.svg"
}
]
function fetchReleaseTag() {
onMounted(() => {
fetch('https://api.github.com/repos/vitejs/docs-cn/releases/latest')
.then((res) => res.json())
.then((json) => {
const dom = document.getElementsByClassName('name')
const mainTitle = dom[0]
mainTitle.style.position = 'relative'
const docsReleaseTag = document.createElement('span')
docsReleaseTag.classList.add('release-tag')
const releaseTagName = json.tag_name
docsReleaseTag.innerText = releaseTagName
if (releaseTagName !== undefined) {
mainTitle.appendChild(docsReleaseTag)
}
})
})
}
fetchReleaseTag()
</script>
<style>
.sponsors {
padding: 0 1.5rem 2rem;
font-size: 0.8rem;
}
.sponsors a {
color: #999;
margin: 1em;
display: block;
}
.sponsors img {
max-width: 160px;
max-height: 40px;
}
.sponsors.frontpage {
text-align: center;
}
.sponsors.frontpage img {
display: inline-block;
vertical-align: middle;
}
.sponsors.frontpage h2 {
color: #999;
font-size: 1.2rem;
border: none;
}
.sponsors.sidebar a img {
max-height: 36px;
}
.platinum-sponsors {
margin-bottom: 1.5em;
}
.platinum-sponsors a img {
max-width: 240px;
max-height: 60px;
}
.gold-sponsors {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: center;
}
</style>

View File

@@ -0,0 +1,17 @@
---
title: 留言反馈
---
# {{ $frontmatter.title }}
> 该页面是在 Markdown 中引入 Vue 组件简单的展示
页面文件: `.vitepress/views/vt/vt-feedback.vue`
<ClientOnly>
<VtFeedback />
</ClientOnly>
::: tip
正在开发中,你以后可以把想说的话写在这里
:::

View File

@@ -0,0 +1,182 @@
---
title: 主题配置
head:
- - meta
- name: description
content: vuetom 主题配置项
- - meta
- name: keywords
content: vuetom theme config
---
# {{ $frontmatter.title }}
主题的配置在 `.vitepress/config.ts` 文件中的 themeConfig 属性中配置
下面是一些简要的配置项一览:
其中 head、sidebar、nav 对应的分别是 head脚本、侧边栏菜单、头部导航栏都可以默认为 []
<br>
以下是版本号满足 `vitepress >= 1.x.x` `vuetom-theme >= 2.x.x` 的配置
```js macos
// .vitepress/config.ts
export default defineConfigWithTheme<VuetomThemeConfig>({
lang: 'en-US',
base: '/vt',
title: 'Vuetom Theme',
description: 'Theme For Vitepress',
// head,
themeConfig: {
nav: nav(),
sidebar: {
'zh-CN/guide/': sidebarGuide(),
'zh-CN/mdshow/': sidebarMdShow()
},
socialLinks: [
{ icon: 'github', link: pkg.repository }
],
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2021-present Lauset'
},
logoImg: '/logo/vuetom-logo-m.png',
bgImg: '/imgs/homg-bg01.jpg',
bgColor: '0,0,0',
bgOpacity: 0.6,
flashEnable: true,
flashColor: ['238,17,17', '0,98,255'],
parallaxEnable: true,
pageBgEnable: true,
pageBgOpacity: 0.8,
featuresColor: ['#06cdff30', 'rgba(223,7,107,.3)']
},
markdown: {
lineNumbers: false,
config: (md) => mdPlugin(md)
},
lastUpdated: false
})
```
<br>
以下是版本号满足 `vitepress = 0.x.x` `vuetom-theme = 1.x.x` 的配置
```js macos
import { defineConfigWithTheme } from 'vitepress'
import type { VuetomThemeConfig } from 'vitepress-theme-vuetom'
// .vitepress/config.ts
// 部分配置项
export default defineConfigWithTheme<VuetomThemeConfig>({
title: 'Vuetom',
base: '/',
head,
themeConfig: {
repo: 'GIT地址',
docsDir: 'docs',
sidebar,
nav,
bgImg: '/imgs/homg-bg01.jpg',
bgColor: '0,0,0',
bgOpacity: 0.6,
flashEnable: true,
flashColor: ['238,17,17', '0,98,255'],
pageBgEnable: true,
pageBgOpacity: 0.8,
featuresColor: ['#06cdff30', 'rgba(223,7,107,.3)']
},
// ...
})
```
以下是对配置项的简要说明
## 首页LOGO
**logoImg**
- 类型:`string`
- 默认值:`''`
首页上方LOGO路径中的首个 `/` 表示 `public` 目录
例如:`'/logo/homg-logo.jpg'`
## 首页背景图
**bgImg**
- 类型:`string`
- 默认值:`undefined`
首页全屏背景图,路径中的首个 `/` 表示 `public` 目录
例如:`'/imgs/homg-bg01.jpg'` 等同于 `/public/imgs/home-bg01.jpg`
**bgColor**
- 类型:`string`
- 默认值:`'0,0,0'`
背景图边缘的覆盖颜色,值是 `rgb` 的颜色值 `rgb(0,0,0)` 则写为 `'0,0,0'`,默认为黑色
**bgOpacity**
- 类型:`0 - 1`
- 默认值:`0.6`
覆盖颜色的透明度,搭配上面的覆盖颜色使用,图片中间透明度要比图片边缘透明度要小
图片中间透明度为 `当前bgOpacity - 0.3`,也就是说默认为 `0.3`
## 文章页背景图
注意:文章页背景图片与首页一致
**pageBgEnable**
- 类型:`boolean`
- 默认值:`true`
文章页面背景图是否开启,默认开启
**pageBgOpacity**
- 类型:`0 - 1`
- 默认值:`0.8`
文章页背景图透明度1将看不到背景图0能清晰看到背景图
**featuresColor**
- 类型:`string | Array`
- 默认值:`rgba(255,255,255,0.8)`
首页功能面板背景色,可以是单个颜色字符串,也可以是两个字符串组成的数组
**flashEnable**
- 类型:`boolean`
- 默认值:`false`
是否开启首页背景图闪烁功能,效果类似于朋克风故障
**flashColor**
- 类型:`string | Array`
- 默认值:`['0,0,0','0,0,0']`
首页背景闪烁时附加的色彩0: Top位置的颜色1: Right位置的颜色默认都是黑色
**parallaxEnable**
- 类型:`boolean`
- 默认值:`false`
是否开启首页部分元素视觉差效果

View File

@@ -0,0 +1,49 @@
---
title: 夜间模式
lang: en-US
---
# {{ $frontmatter.title }}
默认的话夜间模式切换按钮是一直有的,右上角那个太阳图标
## 原理
开关操作修改的是 HTML 根标签的样式,会加上 dark 样式
```html
<html class="dark" lang="zh-CN"></html>
```
我们可以事先定义一些 css变量来完成不同语言下或者不同模式下的样式变换
## 主题色覆盖
修改 theme/custom.scss 文件即可
简单展示部分
```css
:root {
// 重写主题色
// 主色
--vp-c-brand: var(--vp-c-blue);
--vp-c-brand-light: var(--vp-c-blue-light);
--vp-c-brand-lighter: var(--vp-c-blue-lighter);
--vp-c-brand-dark: var(--vp-c-blue-dark);
--vp-c-brand-darker: var(--vp-c-blue-darker);
// 副色
--vp-c-second: var(--vp-c-pink);
--vp-c-second-light: var(--vp-c-pink-light);
--vp-c-second-lighter: var(--vp-c-pink-lighter);
--vp-c-second-dark: var(--vp-c-pink-dark);
--vp-c-second-darker: var(--vp-c-pink-darker);
}
.dark {
}
```

View File

@@ -0,0 +1,17 @@
# 指引
`info` : 介绍
`start` : 快速开始
`question` : 问题一览
`prodir` : 主题项目结构
`config` : 主题配置
`lang` : 国际化配置
::: tip
这里展示的是指引菜单下所有的子菜单
:::

View File

@@ -0,0 +1,50 @@
---
title: 什么是Vitepress
head:
- - meta
- name: description
content: vuetom 介绍
- - meta
- name: keywords
content: vuetom theme
---
# {{ $frontmatter.title }}
VitePress 是 VuePress 的升级,以 Vite 为基础构建的。是一款快速搭建文档静态网站的框架。
Vite法语意为 "快速的",发音 `/vit/` ,发音同 "veet"是一种新型前端构建工具能够显著提升前端开发体验。Vite 意在提供开箱即用的配置,同时它的 插件 API 和 JavaScript API 带来了高度的可扩展性,并有完整的类型支持。它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新HMR
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
## vuetom 呢?
是的,是一款主题,因 vitepress 而诞生。建立在 Vue3 与 Vite 之上的一款文档框架的主题。含有 `文档``博客` 两种风格模板。其推出目的是为了让大家体验并使用到更多美丽而又有趣的 vitepress 主题,进而大家可以发挥想象展示出更多优美的文档。
## 有什么特点
使用现代扁平化的设计风格,部分 UI 尽量接近于 macos 界面风格
该主题包含了以下相关技术
- [nodejs](http://nodejs.cn/)
- [vite](https://vitejs.cn/)
- [vue3](https://v3.vuejs.org/)
- [vitepress](https://vitejs.cn/vitepress/)
- [tailwindcss](https://www.tailwindcss.cn/docs)
该主题包含了一下功能模块
**内置UI组件:** 扁平化数据组织,方便编写。含有按钮,弹框,卡片等基础组件
**主题与样式:** 主要包含布局,间距,排版,颜色,边框
**API与指令:** 可搭配组件使用,实现不同组件不同指令效果
## 推荐开发工具?
这就无所谓了吧哈哈。
[vscode编辑器](https://code.visualstudio.com/) => [下载地址](https://blog.csdn.net/bielaiwuyang1999/article/details/117814237)

View File

@@ -0,0 +1,109 @@
---
title: 多国语言配置
lang: en-US
---
# {{ $frontmatter.title }}
:::warning
适配于 vitepress 1.x.x 版本的功能正在制作,下面是 0.x.x 版本的国际化方案
:::
你也可以配置多国语言,以首页为例,先在配置文件里配置下 locales然后创建对应的语言文件夹与文件
## 改配置文件
需要在 config.ts 中配置 locales注意 themeConfig 属性里也要配,两个 locales 内容是不一样的
themeConfig 中的 locales 配置是为了展示下拉菜单的展示内容
根Config 中的 locales 配置是为了展示头部标题内容description属性可有可无
```js
// .vitepress/config.ts
export default defineConfigWithTheme<VuetomThemeConfig>({
// ...
themeConfig: {
// ...
locales: {
'/zh-CN/': {
label: '简体中文',
selectText: '多国语言'
},
'/en-US/': {
label: 'English',
selectText: 'Languages'
}
}
},
locales: {
'/zh-CN/': {
lang: 'zh-CN',
title: 'Vuetom 主题',
description: '为 Vitepress 提供的一款主题'
},
'/en-US/': {
lang: 'en-US',
title: 'Vuetom Theme',
description: 'Theme For Vitepress'
}
}
})
```
**locales** 中的属性介绍
<b>lang</b>: 会直接设置给 `<html>` 标签
<b>title</b>: 不同语言时网站的标题,会替换之前定义的 title
<b>description</b>: 不同语言时网站的描述
<b>label</b>: 语言选择时展示出来的文本内容例如中文或English
<b>selectText</b>: 语言选择时下拉菜单的文本例如多国语言或者Languages
## 改首页
在 .vitepress 同级目录新建 `zh-CN``en-US` 文件夹,然后在这个文件夹中分别创建一个 `index.md` 文件
**zh-CN/index.md** 中写入 **中文首页** 要展示的内容
**en-US/index.md** 中写入 **英文首页** 要展示的内容
原来与 .vitepress 同级的 `index.md` 文件中可以改为转发至 `zh-CN/index` 或者 `en-US/index`
例如下方的代码,会直接将 `/` 转发到 `/zh-CN/` 这样就会直接前往中文首页了
```markdown
---
title: 'Vuetom Theme'
lang: en-US
page: true
---
<script setup>
if (typeof window !== 'undefined') {
const preferredLang = 'zh-CN'
window.location.pathname = `/${preferredLang}/`
}
</script>
```
::: warning
其实原理就是路径前加了一个语言标识,那么就在页面文件外加个语言文件夹就好了<br>
需要处理的就是书写导航栏和菜单栏时记得要在 link 属性前加上语言标识
:::
下面试试访问一下:
访问 localhost:3000/zh-CN/ 会前往中文首页
访问 localhost:3000/en-US/ 会前往英文首页
访问 localhost:3000 会前往 localhost:3000/zh-CN/

View File

@@ -0,0 +1,57 @@
---
title: 框架目录
head:
- - meta
- name: description
content: Vuetom 主题目录结构
- - meta
- name: keywords
content: project dir.
---
# 主题目录
在使用一个框架,其实也要简单了解下该框架的项目文件结构,请向下看吧。
```bash
vuetom
├─ blog 博客主题Vue组件 文件夹
├─ doc 新版文档适配vp1.x 文件夹
├─ docs 旧版文档主题组件 文件夹
│ ├─ components 主题Vue组件 文件夹
│ ├─ composables 组件脚本 文件夹
│ ├─ layouts 布局组件 文件夹
│ └─ index.ts 主题入口 文件
├─ icons 共用图标Vue组件 文件夹
├─ styles 全局SCSS样式 文件夹
├─ support 供支持脚本 文件夹
├─ types 规范描述 文件夹
├─ constant.ts 常量定义 文件
└─ index.ts 主题入口文件 文件
```
接下来介绍本文档**docs**文件夹
```bash
docs
├─ .vitepress
│ └─ config.ts 主题主要配置文件
├─ public 静态资源文件
├─ zh-CN 中文页面
│ ├─ feedback 留言反馈
│ ├─ guide 指引
│ ├─ mdshow Markdown示例
│ ├─ menu UI组件
│ ├─ styl 主题与样式
│ └─ index.md 中文首页
├─ crowdin.yml 多国语言配置
├─ index.md 项目首页
├─ CHANGELOG.md 更新日志
├─ package.json 包配置
├─ README.md 项目说明
├─ tsconfig.json ts配置
└─ vite.config.ts vite配置
```

View File

@@ -0,0 +1,53 @@
---
title: 常见问题
head:
- - meta
- name: description
content: 常见问题汇总
- - meta
- name: keywords
content: question
---
# {{ $frontmatter.title }}
是否经常卡在一些莫名其妙的问题上?让我们来汇总一下问题并给出相应的解决方案吧!
## 问题列表
- [版本多久更新一次?](#q01)
- [我的样式没有起作用。](#q02)
- [我不想要背景图咋办啊?](#q03)
## 问题解答
以下是针对于近期问题所作的答复
- <h3 id="q01">版本多久更新一次?</h3>
目前进度较慢,但是每周都会更新主要内容的
- <h3 id="q02">我的样式没有起作用。</h3>
查看 `.vitepress/config.js` 文件进中 `theme` 项是否配置正确
- <h3 id="q03">我不想要背景图咋办啊?</h3>
你可以用一张纯白色的图片作为背景图啊嘿嘿嘿
## 开发进度
| 功能组件 | 开发进度 | 预估 |
| - | - | - |
| 文档风格主题 vitepress 0.x.x | 开发中80% | 2022.5 |
| 文档风格主题 vitepress 1.x.x | 开发中80% | 2022.8 |
| 博客风格主题 vitepress 1.x.x | 开发中50% | 2022.11 |
## 需要帮助?
可点击 [留言反馈](/zh-CN/feedback/) 前往问题反馈界面对问题进行简单的描述
::: tip
目前 vitepress 版本已进入 1.0.0 , vuetom-theme 版本已进入 2.0.0
:::

View File

@@ -0,0 +1,158 @@
---
title: 快速使用
head:
- - meta
- name: description
content: 教你如何掌握框架的工作流程,快速上手。
- - meta
- name: keywords
content: 开始使用
---
# {{ $frontmatter.title }}
请确保你已经用过 Vitepress 框架,因为主题是建立在框架的基础上使用的。
请确保你使用的 vue 版本是 3+ 且 vitepress 是 1.x 哦。
## 最方便的方式
直接拉取本项目至本地packages/docs 和 packages/blog 目录下分别是文档和博客示例,修改内容打包编译即可
## 其他方式
### **1.** 脚手架初始化
可以使用脚手架 vuetom-cli 脚手架来进行主题的初始化,会在你本地初始化一个项目
网速慢可以再次尝试或者直接前往模版仓库拉取 [模版仓库](https://github.com/lauset/vuetom-cli)
1. 首先安装脚手架NPM安装前请确保开启管理员身份运行保证有权限
```js light
npm i -g vuetom-cli
```
2. 查看是否安装成功,黑窗口运行一下命令,返回版本号 x.x.x 则表示安装成功
```js light
vuetom-cli -v
```
1. 初始化模版,可以选择是否新建目录、仓息、作者、模版仓库等
```js light
vuetom-cli init
```
1. 然后便会下载模版,下载完成后,执行以下命令安装依赖并运行文档网站
```js light
pnpm i
pnpm dev
```
::: warning
模版是从 github 上拉取的,可能有时候会有网速的困惑,也可手动前往拉取下载本地启用
文档模版:<https://github.com/lauset/vuetom-cli/tree/temp-docs>
博客模版:<https://github.com/lauset/vuetom-cli/tree/temp-blog>
:::
<br/>
### **2.** 通过NPM下载安装主题已弃用
:::danger ⚠️ 已弃用
上传至远程仓库,作为三方依赖使用打包时会出现样式引入问题,暂时放弃使用
从 vitepress-theme-vuetom v2.2.x 开始不再上传远程仓库而是作为目录加至模版项目中
:::
使用这个方式首先你要搭建一个 vitepress 项目,主题只是会覆盖默认样式而已,所以项目还是得搭起来的,可以前往下面 vitepress 官网链接查看并开始搭建
[vitepress搭建文档](https://vitepress.vuejs.org/guide/getting-started.html)
搭建完后最简单的样子就是项目目录里有个 index.md 文件,那么开始下一步
1. 安装主题依赖
使用 NPM 安装最新版本的 vitepress-theme-vuetom 主题依赖包,在你的 vitepress 项目下安装主题包,打开 `终端DOS` 输入
```js light
npm i -D vitepress-theme-vuetom
```
2. 开始引入主题
先在 .vitepress (这个文件夹和首页 index.md 是同级目录,没有的自己创建或者去 vitepress 官网看文档)中新建一个名为 theme 的文件夹,在该文件下新建一个 index.ts 文件,当然用 js 还是 ts 文件就看你自己项目的决定了。
大致内容如下:
`VuetomTheme` 是主要的主题布局
`VuetomUI` 是内置的UI组件
```javascript light
// .vitepress/theme/index.ts
// 默认导出文档类型的主题
import VuetomTheme from 'vitepress-theme-vuetom'
export default {
...VuetomTheme,
enhanceApp({ app, router, siteData }) {
// app.use(VuetomUI)
}
}
```
引入了主题,然后你的 index.md 里写上一些内容应该就可以看出效果了
```html light
---
home: true
heroImage: /logo/vuetom-logo-m.png
heroAlt: LOGO
heroText: Vuetom
tagline: vitepress flat theme
actionText: 快 速 开 始
actionLink: /zh-CN/guide/info
altActionText: 配 置
altActionLink: /zh-CN/guide/config
features:
- title: 📦 优化的构建
details: 可选 “多页应用” 或 “库” 模式的预配置 Rollup 构建
- title: 🔩 通用的插件
details: 在开发和构建之间共享 Rollup-superset 插件接口。
- title: 🔑 完全类型化的API
details: 灵活的 API 和完整 TypeScript 类型。
footer: MIT Licensed
---
<div class="frontpage sponsors">
<h2>{{ data.text }}</h2>
</div>
<script setup>
import { onMounted, reactive } from 'vue'
const data = reactive({
text: '自定义内容'
})
onMounted(() => {
})
</script>
<style>
</style>
```
运行项目后,在 [localhost:3000] 中进入首页
至少到这里主题已经安装完成了,下一步就是主题的配置了,主题什么样还是要看你配的什么样子哦。

View File

@@ -0,0 +1,160 @@
---
layout: home
title: Vuetom
titleTemplate: Vite & Vue Powered Static Site Generator
hero:
name: Vuetom
text:
tagline: vitepress flat theme
actions:
- theme: brand big
text: 快 速 开 始
link: /zh-CN/guide/info
- theme: alt big
text: 配 置
link: /zh-CN/guide/config
features:
- title: 📦 优化的构建
details: 可选 “多页应用” 或 “库” 模式的预配置 Rollup 构建
- title: 🔩 通用的插件
details: 在开发和构建之间共享 Rollup-superset 插件接口。
- title: 🔑 完全类型化的API
details: 灵活的 API 和完整 TypeScript 类型。
---
<div class="frontpage sponsors">
<h2>感 谢</h2>
<div class="platinum-sponsors">
<a v-for="{ href, src, name, id } of sponsors.filter(s => s.tier === 'platinum')" :href="href" target="_blank" rel="noopener" aria-label="sponsor-img">
<img :src="src" :alt="name" :id="`sponsor-${id}`">
<p>{{ name }}</p>
</a>
</div>
<div class="gold-sponsors">
<a v-for="{ href, src, name, id } of sponsors.filter(s => s.tier !== 'platinum')" :href="href" target="_blank" rel="noopener" aria-label="sponsor-img">
<img :src="src" :alt="name" :id="`sponsor-${id}`">
<p>{{ name }}</p>
</a>
</div>
</div>
<script setup>
import { onMounted } from 'vue'
import pk from 'vitepress-theme-vuetom/package.json'
const sponsors = [
{
"id": "vue",
"name": "Vue",
"href": "https://v3.cn.vuejs.org/",
"src": "https://v3.cn.vuejs.org/logo.png",
"tier": "platinum"
},
{
"id": "vite",
"name": "Vite",
"href": "https://vitejs.cn/",
"src": "https://vitejs.cn/logo.svg"
},
{
"id": "vitepress",
"name": "Vitepress",
"href": "https://fttp.jjf-tech.cn/vitepress/",
"src": "https://v3.cn.vuejs.org/logo.png"
},
{
"id": "elementplus",
"name": "Element Plus",
"href": "https://element-plus.gitee.io/zh-CN/",
"src": "https://element-plus.gitee.io/images/element-plus-logo.svg"
}
]
function fetchReleaseTag() {
onMounted(() => {
const dom = document.getElementsByClassName('name')
const mainTitle = dom[0]
const docsReleaseTag = document.createElement('span')
docsReleaseTag.classList.add('release-tag')
const releaseTagName = `v${pk.version}`
docsReleaseTag.innerText = releaseTagName
if (releaseTagName !== undefined) {
mainTitle.appendChild(docsReleaseTag)
}
// fetch('https://api.github.com/repos/vitejs/docs-cn/releases/latest')
// .then((res) => res.json())
// .then((json) => {
// const mainTitle = document.getElementById('main-title')
// mainTitle.style.position = 'relative'
// const docsReleaseTag = document.createElement('span')
// docsReleaseTag.classList.add('release-tag')
// const releaseTagName = json.tag_name
// docsReleaseTag.innerText = releaseTagName
// if (releaseTagName !== undefined) {
// mainTitle.appendChild(docsReleaseTag)
// }
// })
})
}
fetchReleaseTag()
</script>
<style>
.sponsors {
padding: 0 1.5rem 2rem;
font-size: 0.8rem;
}
.sponsors a {
color: #999;
margin: 1em;
display: block;
}
.sponsors img {
max-width: 160px;
max-height: 40px;
}
.sponsors.frontpage {
text-align: center;
}
.sponsors.frontpage img {
display: inline-block;
vertical-align: middle;
}
.sponsors.frontpage h2 {
color: #999;
font-size: 1.2rem;
border: none;
}
.sponsors.sidebar a img {
max-height: 36px;
}
.platinum-sponsors {
margin-bottom: 1.5em;
}
.platinum-sponsors a img {
max-width: 240px;
max-height: 60px;
}
.gold-sponsors {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: center;
}
</style>

View File

@@ -0,0 +1,102 @@
---
title: 代码块展示
head:
- - meta
- name: description
content: 代码块展示
- - meta
- name: keywords
content: code
---
# {{ $frontmatter.title }}
## MD语法展示
使用三个 ` 符号将代码包裹其中便是展示代码块
<br/>
**亮/暗主题切换**
根据文档主题模式切换
**代码块主题 macos**
可以在 language_key(语言标识) 后加入 `macos`
输入内容:
<div>
```java macos
</div>
String language = "Java";
<div>
```
</div>
展示效果:
```java macos
String language = "Java";
```
<br/>
**默认主题**
默认主题是以暗色系为主的
输入内容:
<div>
```js
</div>
String language = "JS";
<div>
```
</div>
输出内容:
```java
String language = "JS";
```
<br/>
**示例展示**
- javascript ( js macos )
```js macos
function fun(){
echo "Hello, World!";
}
fun();
```
- Java ( java )
```java
System.out.print(1);
```
- Python ( py macos )
```py macos
#!/usr/bin/env python3
print("Hello, World!");
```
- SQL ( sql )
```sql
select user_name from user_info
```
- Shell ( bash, shell )
```bash
echo '1'
```

View File

@@ -0,0 +1,58 @@
---
title: 自定义语法
head:
- - meta
- name: description
content: 自定义 Markdown 语法
- - meta
- name: keywords
content: markdown custom
---
# {{ $frontmatter.title }}
## 信息框
```md
::: tip 使用TIPS代替
提示信息
:::
::: info
信息消息
:::
::: warning
警告消息
:::
::: danger
危险消息
:::
::: details Details
详细信息
:::
```
效果如下:
::: tip 使用TIPS代替
提示内容
:::
::: info
INFO消息
:::
::: warning
WARNING消息 <a>a链接</a>
:::
::: danger
DANGER消息 [md链接](./example.md)
:::
::: details Details
详细信息
:::

View File

@@ -0,0 +1,72 @@
---
title: 效果示例
head:
- - meta
- name: description
content: 来看看 MD 会变成什么样子
- - meta
- name: keywords
content: markdown example
---
# Markdown 效果示例
-------------------- 手动分割线 --------------------
# This is an h1 tag
## This is an h2 tag
### This is an h3 tag
#### This is an h4 tag
##### This is an h5 tag
###### This is an h6 tag
*这是斜体*
_这是斜体_
**这是黑体**
__这是黑体__
*斜体里加**黑体***
**黑体里加*斜体***
* Item 1
* Item 2
* Item 2a
* Item 2b
1. Item 1
1. Item 2
1. Item 3
1. Item 3a
1. Item 3b
![Yaktocat的图片](/logo/vuetom-logo.png)
http://github.com - automatic!
[GitHub](http://github.com)
As Kanye West said:
> We're living the future so
> the present is our past.
I think you should use an
`<addr>` element here instead.
First Header | Second Header
------------ | -------------
Content from cell 1 | Content from cell 2
Content in the first column | Content in the second column
~~this~~
😝🌟🐫✨🚶

View File

@@ -0,0 +1,31 @@
---
title: UI组件
head:
- - meta
- name: description
content: 各种各样的扁平化UI组件
- - meta
- name: keywords
content: components
---
# {{ $frontmatter.title }}
### `Vuetom UI`
[ui 文档](http://ui.tomhub.cn)
[ui github](https://github.com/lauset/vuetom-ui)
::: tip
UI 文档正在不断完善中请客观耐心等待一下吧T-T
:::
### Markdown 语法示例
这里将会展示在该文档中markdown所呈现的效果
`Example` : Markdown 语法示例
`Custom md` : 自定义语法

View File

@@ -0,0 +1,7 @@
{
"view-source": "Code",
"edit-on-github": "Edit on Github",
"copy-code": "Copy Code",
"copy-success": "Copy Success",
"copy-error": "Copy Error"
}

View File

@@ -0,0 +1,30 @@
[
{
"text": "Guide",
"link": "/guide/info",
"activeMatch": "/guide/info"
},
{
"text": "Components",
"link": "/mdshow/",
"activeMatch": "/mdshow/"
},
{
"text": "Links",
"items": [
{
"text": "FeedBack",
"link": "/feedback/",
"activeMatch": "/feedback/"
},
{
"text": "Gitee",
"link": "https://gitee.com/lauset/vitepress-theme-vuetom"
},
{
"text": "Vitepress",
"link": "https://vitepress.vuejs.org/"
}
]
}
]

View File

@@ -0,0 +1,34 @@
[
{
"text": "Basic",
"items": [
{ "text": "What's the Vitepress?", "link": "/guide/info" },
{ "text": "Quick Start", "link": "/guide/start" },
{
"text": "Questions",
"link": "/guide/question"
},
{
"text": "Project Dir",
"link": "/guide/prodir"
}
]
},
{
"text": "Advanced",
"items": [
{
"text": "Theme Config",
"link": "/guide/config"
},
{
"text": "Languages",
"link": "/guide/lang"
},
{
"text": "Dark Mode",
"link": "/guide/dark"
}
]
}
]

View File

@@ -0,0 +1,19 @@
{
"langshow": {
"text": "MD Show",
"items": [
{
"link": "/mdshow/example",
"text": "Example"
},
{
"link": "/mdshow/custom",
"text": "Custom"
},
{
"link": "/comp/codeblock",
"text": "Code Block"
}
]
}
}

View File

@@ -0,0 +1,20 @@
{
"label": "English",
"selectText": "Languages",
"editLinkText": "Suggest changes to this page",
"lastUpdatedText": "Last Updated",
"localeLinks": {
"text": "",
"items": [
{
"text": "Chinese",
"link": "/zh-CN/"
},
{
"text": "English",
"link": "/en-US/"
}
]
},
"socialLinks": [{ "icon": "github", "link": "" }]
}

View File

@@ -0,0 +1,5 @@
{
"title": "Vuetom Theme",
"lang": "en-US",
"description": "Theme for Vitepress"
}

View File

@@ -0,0 +1,7 @@
{
"view-source": "代码",
"edit-on-github": "前往 Github 编辑",
"copy-code": "复制",
"copy-success": "复制成功",
"copy-error": "复制出现了一些问题"
}

View File

@@ -0,0 +1,30 @@
[
{
"text": "指引",
"link": "/guide/info",
"activeMatch": "/guide/"
},
{
"text": "组件",
"link": "/mdshow/",
"activeMatch": "/mdshow/"
},
{
"text": "链接",
"items": [
{
"text": "反馈",
"link": "/feedback/",
"activeMatch": "/feedback/"
},
{
"text": "码云",
"link": "https://gitee.com/lauset/vitepress-theme-vuetom"
},
{
"text": "Vitepress官网",
"link": "https://vitepress.vuejs.org/"
}
]
}
]

View File

@@ -0,0 +1,34 @@
[
{
"text": "基础",
"items": [
{ "text": "什么是Vitepress?", "link": "/guide/info" },
{ "text": "快速开始", "link": "/guide/start" },
{
"text": "问题一览",
"link": "/guide/question"
},
{
"text": "主题目录",
"link": "/guide/prodir"
}
]
},
{
"text": "进阶",
"items": [
{
"text": "主题配置",
"link": "/guide/config"
},
{
"text": "国际化",
"link": "/guide/lang"
},
{
"text": "夜间模式",
"link": "/guide/dark"
}
]
}
]

View File

@@ -0,0 +1,19 @@
[
{
"text": "语法",
"items": [
{
"link": "/mdshow/example",
"text": "MD语法示例"
},
{
"link": "/mdshow/custom",
"text": "自定义语法"
},
{
"link": "/mdshow/codeblock",
"text": "代码块展示"
}
]
}
]

View File

@@ -0,0 +1,20 @@
{
"label": "简体中文",
"selectText": "选择语言",
"editLinkText": "对本页提出修改建议",
"lastUpdatedText": "最后更新",
"localeLinks": {
"text": "",
"items": [
{
"text": "简体中文",
"link": "/zh-CN/"
},
{
"text": "English",
"link": "/en-US/"
}
]
},
"socialLinks": [{ "icon": "github", "link": "" }]
}

View File

@@ -0,0 +1,5 @@
{
"title": "Vuetom 主题",
"lang": "zh-CN",
"description": "一款为 Vitepress 而生的主题"
}

View File

@@ -0,0 +1,16 @@
{
"en-US": {
"view-source": "Code",
"edit-on-github": "Edit on Github",
"copy-code": "Copy Code",
"copy-success": "Copy Success",
"copy-error": "Copy Error"
},
"zh-CN": {
"view-source": "代码",
"edit-on-github": "前往 Github 编辑",
"copy-code": "复制",
"copy-success": "复制成功",
"copy-error": "复制出现了一些问题"
}
}

View File

@@ -0,0 +1 @@
["en-US","zh-CN"]

View File

@@ -0,0 +1,33 @@
{
"en-US": [
{
"text": "Resume",
"link": "https://docs.google.com/document/d/1MTTfAx80EzWvtgmxFADdMrCS9R72ZwZPTLuBAaqtGNo/edit?usp=sharing",
},
{
"text": "Gitea",
"link": "https://git.vertinext.com/roryejinn",
},
{
"text": "Github",
"link": "https://github.com/ccubed"
}
{
"text": "Links",
"items": [
{
"text": "Linkedin",
"link": "/feedback/",
},
{
"text": "Twitch.tv",
"link": "https://gitee.com/lauset/vitepress-theme-vuetom"
},
{
"text": "Anilist Profile",
"link": "https://anilist.co/user/TakeshiKO/"
}
]
}
]
}

View File

@@ -0,0 +1,82 @@
{
"en-US": [
{
"text": "Basic",
"items": [
{
"text": "What's the Vitepress?",
"link": "/guide/info"
},
{
"text": "Quick Start",
"link": "/guide/start"
},
{
"text": "Questions",
"link": "/guide/question"
},
{
"text": "Project Dir",
"link": "/guide/prodir"
}
]
},
{
"text": "Advanced",
"items": [
{
"text": "Theme Config",
"link": "/guide/config"
},
{
"text": "Languages",
"link": "/guide/lang"
},
{
"text": "Dark Mode",
"link": "/guide/dark"
}
]
}
],
"zh-CN": [
{
"text": "基础",
"items": [
{
"text": "什么是Vitepress?",
"link": "/guide/info"
},
{
"text": "快速开始",
"link": "/guide/start"
},
{
"text": "问题一览",
"link": "/guide/question"
},
{
"text": "主题目录",
"link": "/guide/prodir"
}
]
},
{
"text": "进阶",
"items": [
{
"text": "主题配置",
"link": "/guide/config"
},
{
"text": "国际化",
"link": "/guide/lang"
},
{
"text": "夜间模式",
"link": "/guide/dark"
}
]
}
]
}

View File

@@ -0,0 +1,40 @@
{
"en-US": {
"langshow": {
"text": "MD Show",
"items": [
{
"link": "/mdshow/example",
"text": "Example"
},
{
"link": "/mdshow/custom",
"text": "Custom"
},
{
"link": "/comp/codeblock",
"text": "Code Block"
}
]
}
},
"zh-CN": [
{
"text": "语法",
"items": [
{
"link": "/mdshow/example",
"text": "MD语法示例"
},
{
"link": "/mdshow/custom",
"text": "自定义语法"
},
{
"link": "/mdshow/codeblock",
"text": "代码块展示"
}
]
}
]
}

View File

@@ -0,0 +1,52 @@
{
"en-US": {
"label": "English",
"selectText": "Languages",
"editLinkText": "Suggest changes to this page",
"lastUpdatedText": "Last Updated",
"localeLinks": {
"text": "",
"items": [
{
"text": "Chinese",
"link": "/zh-CN/"
},
{
"text": "English",
"link": "/en-US/"
}
]
},
"socialLinks": [
{
"icon": "github",
"link": ""
}
]
},
"zh-CN": {
"label": "简体中文",
"selectText": "选择语言",
"editLinkText": "对本页提出修改建议",
"lastUpdatedText": "最后更新",
"localeLinks": {
"text": "",
"items": [
{
"text": "简体中文",
"link": "/zh-CN/"
},
{
"text": "English",
"link": "/en-US/"
}
]
},
"socialLinks": [
{
"icon": "github",
"link": ""
}
]
}
}

View File

@@ -0,0 +1,12 @@
{
"en-US": {
"title": "Vuetom Theme",
"lang": "en-US",
"description": "Theme for Vitepress"
},
"zh-CN": {
"title": "Vuetom 主题",
"lang": "zh-CN",
"description": "一款为 Vitepress 而生的主题"
}
}

View File

@@ -0,0 +1,150 @@
import type { HeadConfig } from 'vitepress'
import { languages } from './utils/lang'
import navJson from './i18n/nav.json'
import guideSidebarJson from './i18n/sidebars/guide.json'
import mdshowSidebarJson from './i18n/sidebars/mdshow.json'
import vitepressConfigJson from './i18n/vitepress-config.json'
import themeConfigJson from './i18n/theme-config.json'
type langType = 'zh-CN' | 'en-US'
const getVitepressConfigLocales = (lang: langType = 'zh-CN') => (vitepressConfigJson[lang])
const getThemeConfigLocales = (lang: langType = 'zh-CN') => {
const sidebar = {}
sidebar[`/${lang}/guide/`] = guideSidebarJson[lang]
sidebar[`/${lang}/mdshow/`] = mdshowSidebarJson[lang]
return {
nav: navJson[lang],
sidebar,
...themeConfigJson[lang]
}
}
const getLocales = (langs: langType[] = []) => {
const config = {
vitepressConfig: {},
themeConfig: {}
}
langs.forEach(lang => {
config.vitepressConfig[`/${lang}/`] = getVitepressConfigLocales(lang)
config.themeConfig[`/${lang}/`] = getThemeConfigLocales(lang)
})
return config
}
const getNav = (lang: 'zh-CN' | 'en-US' = 'zh-CN') => (navJson[lang])
const getSidebar = (lang: 'zh-CN' | 'en-US' = 'zh-CN') => ({
'/guide/': guideSidebarJson[lang],
'/mdshow/': mdshowSidebarJson[lang]
})
const head: HeadConfig[] = [
[
'meta',
{
name: 'viewport',
content: 'width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no'
}
],
['link', { rel: 'icon', href: '/logo/vuetom-logo-m.png' }],
[
'script', {}, ';(() => { })()'
],
[
'script',
{},
`;(() => {
const supportedLangs = ${JSON.stringify(languages)}
const cacheKey = 'vuetom_langs'
localStorage.setItem(cacheKey, supportedLangs)
})()`
]
]
// const nav = (lang: '/zh-CN' | '/en-US' | '' = '') => [
// { text: '指引', link: `${lang}/guide/info`, activeMatch: '/guide/' },
// { text: '组件', link: `${lang}/mdshow/`, activeMatch: '/mdshow/' },
// {
// text: '链接',
// items: [
// {
// text: '反馈',
// link: `${lang}/feedback/`,
// activeMatch: '/feedback/',
// },
// {
// text: '码云',
// link: 'https://gitee.com/lauset/vitepress-theme-vuetom',
// },
// {
// text: 'Vitepress官网',
// link: 'https://vitepress.vuejs.org/',
// },
// ],
// },
// ]
// const sidebarGuide = (lang: '/zh-CN' | '/en-US' | '' = '') => [
// {
// text: '基础',
// collapsible: true,
// items: [
// { text: '什么是Vitepress?', link: `${lang}/guide/info` },
// { text: '快速开始', link: `${lang}/guide/start` },
// {
// text: '问题一览',
// link: `${lang}/guide/question`,
// },
// {
// text: '主题目录',
// link: `${lang}/guide/prodir`,
// },
// ],
// },
// {
// text: '进阶',
// collapsible: true,
// items: [
// {
// text: '主题配置',
// link: `${lang}/guide/config`,
// },
// {
// text: '国际化',
// link: `${lang}/guide/lang`,
// },
// {
// text: '夜间模式',
// link: `${lang}/guide/dark`,
// },
// ],
// },
// ]
// const sidebarMdShow = (lang: '/zh-CN' | '/en-US' | '' = '') => [
// {
// text: '语法',
// collapsible: true,
// items: [
// {
// link: `${lang}/mdshow/example`,
// text: 'MD语法示例',
// },
// {
// link: `${lang}/mdshow/custom`,
// text: '自定义语法',
// },
// {
// link: `${lang}/mdshow/codeblock`,
// text: '代码块展示',
// },
// ],
// },
// ]
const nav = getNav()
const sidebar = getSidebar()
const locales = getLocales(['zh-CN', 'en-US'])
export { nav, sidebar, head, locales, getNav, getSidebar }

View File

@@ -0,0 +1,17 @@
:root{
// 重写主题色
// 主色
--vp-c-brand: var(--vp-c-blue);
--vp-c-brand-light: var(--vp-c-blue-light);
--vp-c-brand-lighter: var(--vp-c-blue-lighter);
--vp-c-brand-dark: var(--vp-c-blue-dark);
--vp-c-brand-darker: var(--vp-c-blue-darker);
// 副色
--vp-c-second: var(--vp-c-pink);
--vp-c-second-light: var(--vp-c-pink-light);
--vp-c-second-lighter: var(--vp-c-pink-lighter);
--vp-c-second-dark: var(--vp-c-pink-dark);
--vp-c-second-darker: var(--vp-c-pink-darker);
}

View File

@@ -0,0 +1,24 @@
// .vitepress/theme/index.ts
// theme
import VuetomTheme from 'vitepress-theme-vuetom/docs'
// components
import { globals } from '../views'
// rewrite css
import './custom.scss'
export default {
...VuetomTheme,
// NotFound,
// Layout,
enhanceApp({ app, router, siteData }) {
// app.use(VuetomUI)
globals.forEach(([name, Comp]) => {
app.component(name, Comp)
})
},
}

View File

@@ -0,0 +1,8 @@
import fs from 'fs'
import path from 'path'
export const languages = fs.readdirSync(
path.resolve(__dirname, '../crowdin')
)
export const changeLang = (lang: string) => `/${lang}`

View File

@@ -0,0 +1,23 @@
import process from 'process'
import chalk from 'chalk'
export function cyan(str: string) {
console.log(chalk.cyan(str))
}
export function yellow(str: string) {
console.log(chalk.yellow(str))
}
export function green(str: string) {
console.log(chalk.green(str))
}
export function red(str: string) {
console.error(chalk.red(str))
}
export function errorAndExit(e: Error): never {
red(e.stack ?? e.message)
process.exit(1)
}

View File

@@ -0,0 +1,6 @@
import path from 'path'
export const vpRoot = path.resolve(__dirname, '..')
export const docRoot = path.resolve(vpRoot, '..')
export const pkgsRoot = path.resolve(docRoot, '..')
export const projRoot = path.resolve(pkgsRoot, '..')

View File

@@ -0,0 +1,18 @@
import { computed } from 'vue'
import { useRoute } from 'vitepress'
export const useLang = () => {
const route = useRoute()
return computed(() => {
// the first part of the first slash
const path = route.data?.relativePath
let lang: string
if (path?.includes('/')) {
lang = path.split('/').shift()
} else {
lang = 'zh-CN'
}
return lang
})
}

View File

@@ -0,0 +1,18 @@
import { isRef, ref } from 'vue'
import { isBoolean } from '@vueuse/core'
import type { MaybeRef } from '@vueuse/core'
export const useToggle = (getToggled?: MaybeRef<boolean>) => {
const val = isRef(getToggled)
? getToggled
: ref(isBoolean(getToggled) ? getToggled : false)
return [
val,
(toggle?: boolean) => {
val.value = isBoolean(toggle) ? toggle : !val.value
}
] as const
}
export default {}

View File

@@ -0,0 +1,8 @@
import VtFeedback from './vt/vt-feedback.vue'
// export default VtApp
export const globals = [
['VtFeedback', VtFeedback],
]
export default {}

View File

@@ -0,0 +1,426 @@
<template>
<div class="box" :style="{}" :class="getExtBoxClasses()">
<div class="box-center">
<div class="box-center-left"></div>
<div class="box-center-center loader">
<div class="app-bar" :style="{ backgroundColor: '#fff' }">
<div class="controll">
<div class="close"></div>
<div class="min"></div>
<div class="full"></div>
</div>
<div class="title" :style="{ color: '#000' }">标题</div>
</div>
<div class="app-body">
<div class="feedback-container">
<div class="row">
<input
class="txt"
type="text"
v-model="email"
placeholder="邮箱"
/>
</div>
<textarea
class="txt"
v-model="txt"
cols="30"
rows="3"
placeholder="留言内容"
></textarea>
<div class="btns row">
<button class="btn" @click="goFeeback">留言</button>
<button class="rest btn" @click="reset">重置</button>
</div>
<h3 class="row">留言列表</h3>
<ul>
<li v-for="item in list" :key="item.fd_id">
<div class="title">
来自-{{ item.f_address || '未知地区' }}
<span>{{ item.time }}</span>
</div>
<div class="content" v-text="item.f_context"></div>
<div
class="replay"
v-text="item.f_back"
v-if="item.f_back"
></div>
</li>
</ul>
<div class="nolist" v-if="list.length === 0">暂无留言~</div>
<br/>
<button class="more btn" @click="getList" :disabled="isDown">
加载更多
</button>
</div>
</div>
</div>
<div class="box-center-right"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import axios from 'axios'
// import { ElMessage } from "element-plus";
// import "element-plus/lib/theme-chalk/el-message.css";
// import "element-plus/lib/theme-chalk/el-icon.css";
const postUrl = ''
const getListUrl = ''
const isBoxResizing = ref(false)
const isMaxShowing = ref(false)
const isFullScreen = ref(false)
const txt = ref('')
const email = ref('')
const page = ref(1)
const pagesize = ref(10)
const isDown = ref(false)
const list = ref([])
const reset = () => {
txt.value = ''
email.value = ''
}
const goFeeback = () => {
alert('开发中!')
return
if (!txt.value || !email.value) {
alert("你还未填写内容");
return
}
axios
.post(postUrl, { text: txt.value.trim(), email: email.value })
.then((res) => {
const { status, msg } = res.data
if (status === 0) {
reset()
}
})
.catch((err) => {
})
}
const getExtBoxClasses = () => {
let str = ''
if (isMaxShowing) {
str += 'isMaxShowing '
}
if (isFullScreen) {
str += 'isFullScreen '
}
return str
}
const getList = (tip = false) => {
if (isDown.value) {
alert("没有更多数据了");
return null
}
axios
.post(getListUrl, { page: page.value, pagesize: pagesize.value })
.then((res) => {
const {
data: { status, msg, data },
} = res
if (status === 0) {
list.value.push(...data)
page.value += 1
if (data.length < pagesize.value) {
isDown.value = true
}
return
}
isDown.value = true
})
.catch((err) => {
})
}
onMounted(getList)
</script>
<style lang="scss" scoped>
.row {
margin-bottom: 20px;
}
.txt {
max-width: 100%;
height: auto;
min-height: 32px;
line-height: 1.5715;
vertical-align: bottom;
transition: all 0.3s, height 0s;
box-sizing: border-box;
margin: 0;
font-variant: tabular-nums;
list-style: none;
font-feature-settings: "tnum";
position: relative;
display: inline-block;
width: 100%;
min-width: 0;
padding: 4px 11px;
color: #000000d9;
font-size: 14px;
line-height: 1.5715;
background-color: #fff;
background-image: none;
border: 1px solid #d9d9d9;
border-radius: 2px;
transition: all 0.3s;
}
.txt:focus {
border-color: #40a9ff;
border-right-width: 1px !important;
outline: 0;
box-shadow: 0 0 0 2px #1890ff33;
}
.btns {
margin-top: 20px;
}
.btn {
line-height: 1.5715;
position: relative;
display: inline-block;
font-weight: 400;
white-space: nowrap;
text-align: center;
background-image: none;
border: 1px solid transparent;
box-shadow: 0 2px #00000004;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
touch-action: manipulation;
height: 32px;
padding: 4px 15px;
font-size: 14px;
border-radius: 2px;
color: #000000d9;
background: #fff;
border-color: #d9d9d9;
color: #fff;
background: #1890ff;
border-color: #1890ff;
text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
box-shadow: 0 2px #0000000b;
}
.btn:active {
outline: 0;
box-shadow: none;
color: #fff;
background: #096dd9;
border-color: #096dd9;
text-decoration: none;
}
.btn:focus,
.btn:hover {
color: #fff;
background: #40a9ff;
border-color: #40a9ff;
text-decoration: none;
}
.rest {
margin-left: 10px;
}
.title span {
margin-left: 10px;
font-size: 12px;
color: #d9d9d9;
}
.title {
color: #000;
}
.replay,.content {
padding: 10px;
white-space: pre-line;
color: #00000073;
}
.btn.more {
width: 100%;
}
.btn[disabled] {
color: #00000040;
background: #f5f5f5;
border-color: #d9d9d9;
text-shadow: none;
box-shadow: none;
cursor: not-allowed;
}
.replay{
padding:0 15px 10px;
color: red;
}
.box {
--resize: 5px;
--resize-bg: transparent;
--resize-main: transparent;
--resize-bg-main: transparent;
}
.box {
display: flex;
flex-direction: column;
position: relative;
pointer-events: auto;
.box-top {
display: flex;
flex-direction: row;
.box-top-left {
width: var(--resize);
height: var(--resize);
background: var(--resize-bg);
cursor: nw-resize;
}
.box-top-center {
height: var(--resize);
background: var(--resize-bg-main);
cursor: n-resize;
flex-grow: 1;
}
.box-top-right {
width: var(--resize);
height: var(--resize);
background: var(--resize-bg);
cursor: ne-resize;
}
}
.box-center {
display: flex;
flex-direction: row;
flex-grow: 1;
.loader {
display: flex;
flex-grow: 1;
flex-direction: column;
width: 100%;
}
.box-center-left {
width: var(--resize);
height: 100%;
background: var(--resize-bg-main);
cursor: w-resize;
}
.box-center-center {
height: 100%;
flex-grow: 1;
display: flex;
flex-direction: column;
border-radius: 10px;
box-shadow: 0px 0px 3px rgb(201, 196, 196);
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(20px);
overflow: hidden;
// filter: grayscale(1) brightness(0.9);
.app-bar {
height: 40px;
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(20px);
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
.title {
flex-grow: 1;
text-align: center;
margin-right: 84px;
font-weight: 500;
text-shadow: none;
font-size: 13px;
cursor: move;
color: #333;
}
.controll {
display: flex;
justify-content: center;
align-items: center;
margin-left: 15px;
div {
border-radius: 100%;
height: 14px;
width: 14px;
margin-right: 8px;
cursor: pointer;
}
.close {
background: #fc605c;
}
.close:hover {
background: #cc2c26;
}
.min {
background: #fcbb40;
}
.min:hover {
background: #c28719;
}
.full {
background: #34c648;
}
.full:hover {
background: #1f942e;
}
.full-disabled {
background: #ccc !important;
}
}
}
.app-body {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 50px 0;
}
}
.box-center-right {
width: var(--resize);
height: 100%;
background: var(--resize-bg-main);
cursor: e-resize;
}
}
.box-bottom {
display: flex;
flex-direction: row;
.box-bottom-left {
width: var(--resize);
height: var(--resize);
background: var(--resize-bg);
cursor: sw-resize;
}
.box-bottom-center {
height: var(--resize);
background: var(--resize-bg-main);
cursor: s-resize;
flex-grow: 1;
}
.box-bottom-right {
width: var(--resize);
height: var(--resize);
background: var(--resize-bg);
cursor: se-resize;
}
}
}
</style>