commit 6f2985e47a9593f24a360e93c0f91c76ebc74f9e Author: lhc Date: Wed Feb 3 17:01:33 2021 +0800 创建文档 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84e173c --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# npm +package-lock.json +node_modules + +# yarn +yarn.lock + +# vscode +.vscode + +# vuepress +docs/.vuepress/dist + +# 百度链接推送 +urls.txt + +# mac +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..16d6732 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-present gaoyi(Evan) Xu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..5585776 --- /dev/null +++ b/README.MD @@ -0,0 +1,100 @@ +

logo

+ +

+ CI + baiduPush + License + npm + GitHub stars + + + +

+ +

vuepress-theme-vdoing

+ +* [文档 (国内源)](https://doc.xugaoyi.com/) +* [文档 (github源)](https://xugaoyi.github.io/vuepress-theme-vdoing-doc/) +* [效果:Evan's blog](https://xugaoyi.com/) + + +## 介绍 +1. 这个主题的初衷是打造一个好用的、面向程序员的`知识管理工具` +2. 轻松构建一个`结构化`的知识库,让你的知识海洋像一本本书一样清晰易读。 +3. 博客功能提供一种知识的`碎片化`形态,并支持个性化博客配置。 +4. `简洁高效`,以 Markdown 为中心的项目结构。内置自动化工具,以更少的配置完成更多的事。配合多维索引快速定位每个知识点。 + +[**更新日志**](https://github.com/xugaoyi/vuepress-theme-vdoing/releases) + +## 这个主题可以做什么? +* 案例1:[知识库兼博客站](https://xugaoyi.com/) +* 案例2:[仅博客站](https://xugaoyi.github.io/vdoing-demo-blog/) +* 案例3:[仅知识库](https://xugaoyi.github.io/vdoing-demo-repository/) +* 案例4:[文档站](https://xugaoyi.github.io/vuepress-theme-vdoing-doc/) + + +## 快速上手 + +```bash +# clone the project +git clone https://github.com/xugaoyi/vuepress-theme-vdoing.git + +# enter the project directory +cd vuepress-theme-vdoing + +# install dependency +npm install # or yarn install + +# develop +npm run dev # or yarn dev +``` +## ⚡️未来... + +### [VuePress-next (VuePress v2.0)](https://github.com/vuepress/vuepress-next) +> VuePress-next with the power of Vue 3.0 and TypeScript + +### [VitePress](https://github.com/vuejs/vitepress) + +> * Uses Vue 3. +> * Uses vite under the hood: +> * Vue 3 tree-shaking + Rollup code splitting +> +> VuePress' little brother, built on top of vite. It's currently under a different name so that we don't over commit to the compatibility with the current VuePress ecosystem (mostly themes and plugins). We'll see how close we can get without compromising the design goals listed above. But the overall idea is that VitePress will have a drastically more minimal theming API (preferring JavaScript APIs instead of file layout conventions) and likely no plugins (all customization is done in themes). + +期待 [VuePress v2.0](https://github.com/vuepress/vuepress-next) 以及 [VitePress](https://github.com/vuejs/vitepress) 的正式发布... + +届时,VuePress 1.x 编译慢的缺点将得到极大的改善。我将会视情况把主题升级至 VuePress v2.0 或 VitePress,也可能两个都升级。目前(2020.10.29)来看还需要一段时间才能让大家使用上基于它们的新版本,还希望大家多多 [:sparkling_heart:支持](https://doc.xugaoyi.com/pages/1b12ed/) 哟,持续关注吧~ + +## :sparkling_heart:支持这个项目 + +如果你正在使用这个项目并感觉良好,或只是想要支持我继续开发,你可以通过如下*任意* 方式支持我: + +1. *Star* 并 分享这个项目 :rocket: +2. 保留主题 footer(页脚) 的主题链接 :D +3. 购买前端学习类 [电子书](https://github.com/xugaoyi/blog-gitalk-comment/wiki/Front-end-Study) Ⓤⓟ 💯 +4. 轻轻点击一次页面广告 ✨ +5. 通过以下二维码 一次性捐款。 我多半会买一杯 ~~咖啡~~ 茶。:tea: + +谢谢! :heart: + +| 微信赞赏 | 微信 | 支付宝 | +| :---: | :---: | :---: | +| 赞赏码 | Wechat QRcode| Alipay QRcode | + +二维码没有正常显示?点 [这里😎](https://doc.xugaoyi.com/pages/1b12ed/) + +## 致谢 +:heart:感谢支持这个项目的朋友 + +:heart:感谢为这个项目贡献代码的朋友 → [Contributors](https://github.com/xugaoyi/vuepress-theme-vdoing/graphs/contributors) + +## Vdoing官方交流群 +群号:694387113 + +群号:694387113 + +## 许可证 +[MIT](https://github.com/xugaoyi/vuepress-theme-vdoing/blob/master/LICENSE) + +Copyright (c) 2019-present Evan Xu diff --git a/baiduPush.sh b/baiduPush.sh new file mode 100644 index 0000000..88ffbcb --- /dev/null +++ b/baiduPush.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +# 确保脚本抛出遇到的错误 +set -e + +# 百度链接推送 +curl -H 'Content-Type:text/plain' --data-binary @urls.txt "http://data.zz.baidu.com/urls?site=https://xugaoyi.com&token=T5PEAzhGaPNbjQ2X" + +rm -rf urls.txt # 删除文件 diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..fdbfc17 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env sh + +# 确保脚本抛出遇到的错误 +set -e + +# 生成静态文件 +npm run build + +# 进入生成的文件夹 +cd docs/.vuepress/dist + +# deploy to github +echo 'b.xugaoyi.com' > CNAME +if [ -z "$GITHUB_TOKEN" ]; then + msg='deploy' + githubUrl=git@github.com:xugaoyi/vuepress-theme-vdoing.git +else + msg='来自github actions的自动部署' + githubUrl=https://xugaoyi:${GITHUB_TOKEN}@github.com/xugaoyi/vuepress-theme-vdoing.git + git config --global user.name "xugaoyi" + git config --global user.email "894072666@qq.com" +fi +git init +git add -A +git commit -m "${msg}" +git push -f $githubUrl master:gh-pages # 推送到github + +# deploy to coding +echo 'www.xugaoyi.com\nxugaoyi.com' > CNAME # 自定义域名 +echo 'google.com, pub-7828333725993554, DIRECT, f08c47fec0942fa0' > ads.txt # 谷歌广告相关文件 + +if [ -z "$CODING_TOKEN" ]; then # -z 字符串 长度为0则为true;$CODING_TOKEN来自于github仓库`Settings/Secrets`设置的私密环境变量 + codingUrl=git@e.coding.net:xgy/xgy.git +else + codingUrl=https://HmuzsGrGQX:${CODING_TOKEN}@e.coding.net/xgy/xgy.git +fi +git add -A +git commit -m "${msg}" +git push -f $codingUrl master # 推送到coding + +cd - # 退回开始所在目录 +rm -rf docs/.vuepress/dist diff --git a/docs/.vuepress/components/Bit.vue b/docs/.vuepress/components/Bit.vue new file mode 100644 index 0000000..0faaf43 --- /dev/null +++ b/docs/.vuepress/components/Bit.vue @@ -0,0 +1,33 @@ + + + diff --git a/docs/.vuepress/components/Bpmn/bpmn.vue b/docs/.vuepress/components/Bpmn/bpmn.vue new file mode 100644 index 0000000..f770582 --- /dev/null +++ b/docs/.vuepress/components/Bpmn/bpmn.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/docs/.vuepress/components/Bpmn/bpmnView.vue b/docs/.vuepress/components/Bpmn/bpmnView.vue new file mode 100644 index 0000000..d3350f8 --- /dev/null +++ b/docs/.vuepress/components/Bpmn/bpmnView.vue @@ -0,0 +1,38 @@ + + + diff --git a/docs/.vuepress/components/Bpmn/customTranslate/customTranslate.js b/docs/.vuepress/components/Bpmn/customTranslate/customTranslate.js new file mode 100644 index 0000000..a7caea6 --- /dev/null +++ b/docs/.vuepress/components/Bpmn/customTranslate/customTranslate.js @@ -0,0 +1,20 @@ +import translations from './translationsGerman' + +export default function customTranslate (template, replacements) { + replacements = replacements || {} + + // Translate + template = translations[template] || template + + // Replace + return template.replace(/{([^}]+)}/g, function (_, key) { + var str = replacements[key] + if ( + translations[replacements[key]] != null + && translations[replacements[key]] != 'undefined' + ) { + str = translations[replacements[key]] + } + return str || '{' + key + '}' + }) +} diff --git a/docs/.vuepress/components/Bpmn/customTranslate/translationsGerman.js b/docs/.vuepress/components/Bpmn/customTranslate/translationsGerman.js new file mode 100644 index 0000000..a4a9cee --- /dev/null +++ b/docs/.vuepress/components/Bpmn/customTranslate/translationsGerman.js @@ -0,0 +1,206 @@ +export default { + // Labels + 'Activate the global connect tool': '激活全局连接工具', + 'Append {type}': '添加 {type}', + 'Add Lane above': '在上面添加道', + 'Divide into two Lanes': '分割成两个道', + 'Divide into three Lanes': '分割成三个道', + 'Add Lane below': '在下面添加道', + 'Append compensation activity': '追加补偿活动', + 'Change type': '修改类型', + 'Connect using Association': '使用关联连接', + 'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接', + 'Connect using DataInputAssociation': '使用数据输入关联连接', + 'Remove': '移除', + 'Activate the hand tool': '激活抓手工具', + 'Activate the lasso tool': '激活套索工具', + 'Activate the create/remove space tool': '激活创建/删除空间工具', + 'Create expanded SubProcess': '创建扩展子过程', + 'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出事件/边界事件', + 'Create Pool/Participant': '创建池/参与者', + 'Parallel Multi Instance': '并行多重事件', + 'Sequential Multi Instance': '时序多重事件', + 'DataObjectReference': '数据对象参考', + 'DataStoreReference': '数据存储参考', + 'Loop': '循环', + 'Ad-hoc': '即席', + 'Create {type}': '创建 {type}', + 'Task': '任务', + 'Send Task': '发送任务', + 'Receive Task': '接收任务', + 'User Task': '用户任务', + 'Manual Task': '手工任务', + 'Business Rule Task': '业务规则任务', + 'Service Task': '服务任务', + 'Script Task': '脚本任务', + 'Call Activity': '调用活动', + 'Sub Process (collapsed)': '子流程(折叠的)', + 'Sub Process (expanded)': '子流程(展开的)', + 'Start Event': '开始事件', + 'StartEvent': '开始事件', + 'Intermediate Throw Event': '中间事件', + 'End Event': '结束事件', + 'EndEvent': '结束事件', + 'Create Gateway': '创建网关', + 'Create Intermediate/Boundary Event': '创建中间/边界事件', + 'Message Start Event': '消息开始事件', + 'Timer Start Event': '定时开始事件', + 'Conditional Start Event': '条件开始事件', + 'Signal Start Event': '信号开始事件', + 'Error Start Event': '错误开始事件', + 'Escalation Start Event': '升级开始事件', + 'Compensation Start Event': '补偿开始事件', + 'Message Start Event (non-interrupting)': '消息开始事件(非中断)', + 'Timer Start Event (non-interrupting)': '定时开始事件(非中断)', + 'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)', + 'Signal Start Event (non-interrupting)': '信号开始事件(非中断)', + 'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)', + 'Message Intermediate Catch Event': '消息中间捕获事件', + 'Message Intermediate Throw Event': '消息中间抛出事件', + 'Timer Intermediate Catch Event': '定时中间捕获事件', + 'Escalation Intermediate Throw Event': '升级中间抛出事件', + 'Conditional Intermediate Catch Event': '条件中间捕获事件', + 'Link Intermediate Catch Event': '链接中间捕获事件', + 'Link Intermediate Throw Event': '链接中间抛出事件', + 'Compensation Intermediate Throw Event': '补偿中间抛出事件', + 'Signal Intermediate Catch Event': '信号中间捕获事件', + 'Signal Intermediate Throw Event': '信号中间抛出事件', + 'Message End Event': '消息结束事件', + 'Escalation End Event': '定时结束事件', + 'Error End Event': '错误结束事件', + 'Cancel End Event': '取消结束事件', + 'Compensation End Event': '补偿结束事件', + 'Signal End Event': '信号结束事件', + 'Terminate End Event': '终止结束事件', + 'Message Boundary Event': '消息边界事件', + 'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)', + 'Timer Boundary Event': '定时边界事件', + 'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)', + 'Escalation Boundary Event': '升级边界事件', + 'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)', + 'Conditional Boundary Event': '条件边界事件', + 'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)', + 'Error Boundary Event': '错误边界事件', + 'Cancel Boundary Event': '取消边界事件', + 'Signal Boundary Event': '信号边界事件', + 'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)', + 'Compensation Boundary Event': '补偿边界事件', + 'Exclusive Gateway': '互斥网关', + 'Parallel Gateway': '并行网关', + 'Inclusive Gateway': '相容网关', + 'Complex Gateway': '复杂网关', + 'Event based Gateway': '事件网关', + 'Transaction': '转运', + 'Sub Process': '子流程', + 'Event Sub Process': '事件子流程', + 'Collapsed Pool': '折叠池', + 'Expanded Pool': '展开池', + // Errors + 'no parent for {element} in {parent}': '在{parent}里,{element}没有父类', + 'no shape type specified': '没有指定的形状类型', + 'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类', + 'out of bounds release': 'out of bounds release', + 'more than {count} child lanes': '子道大于{count} ', + 'element required': '元素不能为空', + 'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范', + 'no diagram to display': '没有可展示的流程图', + 'no process or collaboration to display': '没有可展示的流程/协作', + 'element {element} referenced by {referenced}#{property} not yet drawn': '由{referenced}#{property}引用的{element}元素仍未绘制', + 'already rendered {element}': '{element} 已被渲染', + 'failed to import {element}': '导入{element}失败', + // 属性面板的参数 + 'Id': '标识', + 'Name': '名称', + 'General': '常规', + 'Details': '详情', + 'Message Name': '消息名称', + 'Message': '消息', + 'Initiator': '创建者', + 'Asynchronous Continuations': '持续异步', + 'Asynchronous Before': '异步前', + 'Asynchronous After': '异步后', + 'Job Configuration': '工作配置', + 'Exclusive': '排除', + 'Job Priority': '工作优先级', + 'Retry Time Cycle': '重试时间周期', + 'Documentation': '文档', + 'Element Documentation': '元素文档', + 'History Configuration': '历史配置', + 'History Time To Live': '历史的生存时间', + 'Forms': '表单', + 'Form Key': '表单key', + 'Form Fields': '表单字段', + 'Business Key': '业务key', + 'Form Field': '表单字段', + 'ID': '编号', + 'Type': '类型', + 'Label': '名称', + 'Default Value': '默认值', + 'Validation': '校验', + 'Add Constraint': '添加约束', + 'Config': '配置', + 'Properties': '属性', + 'Add Property': '添加属性', + 'Value': '值', + 'Listeners': '监听器', + 'Execution Listener': '执行监听', + 'Event Type': '事件类型', + 'Listener Type': '监听器类型', + 'Java Class': 'Java类', + 'Expression': '表达式', + 'Must provide a value': '必须提供一个值', + 'Delegate Expression': '代理表达式', + 'Script': '脚本', + 'Script Format': '脚本格式', + 'Script Type': '脚本类型', + 'Inline Script': '内联脚本', + 'External Script': '外部脚本', + 'Resource': '资源', + 'Field Injection': '字段注入', + 'Extensions': '扩展', + 'Input/Output': '输入/输出', + 'Input Parameters': '输入参数', + 'Output Parameters': '输出参数', + 'Parameters': '参数', + 'Output Parameter': '输出参数', + 'Timer Definition Type': '定时器定义类型', + 'Timer Definition': '定时器定义', + 'Date': '日期', + 'Duration': '持续', + 'Cycle': '循环', + 'Signal': '信号', + 'Signal Name': '信号名称', + 'Escalation': '升级', + 'Error': '错误', + 'Link Name': '链接名称', + 'Condition': '条件名称', + 'Variable Name': '变量名称', + 'Variable Event': '变量事件', + 'Specify more than one variable change event as a comma separated list.': '多个变量事件以逗号隔开', + 'Wait for Completion': '等待完成', + 'Activity Ref': '活动参考', + 'Version Tag': '版本标签', + 'Executable': '可执行文件', + 'External Task Configuration': '扩展任务配置', + 'Task Priority': '任务优先级', + 'External': '外部', + 'Connector': '连接器', + 'Must configure Connector': '必须配置连接器', + 'Connector Id': '连接器编号', + 'Implementation': '实现方式', + 'Field Injections': '字段注入', + 'Fields': '字段', + 'Result Variable': '结果变量', + 'Topic': '主题', + 'Configure Connector': '配置连接器', + 'Input Parameter': '输入参数', + 'Assignee': '代理人', + 'Candidate Users': '候选用户', + 'Candidate Groups': '候选组', + 'Due Date': '到期时间', + 'Follow Up Date': '跟踪日期', + 'Priority': '优先级', + 'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', + 'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', + 'Variables': '变量' +} diff --git a/docs/.vuepress/components/Co/formCo.vue b/docs/.vuepress/components/Co/formCo.vue new file mode 100644 index 0000000..d6d304a --- /dev/null +++ b/docs/.vuepress/components/Co/formCo.vue @@ -0,0 +1,205 @@ + + + + diff --git a/docs/.vuepress/components/Co/searchCo.vue b/docs/.vuepress/components/Co/searchCo.vue new file mode 100644 index 0000000..84afdeb --- /dev/null +++ b/docs/.vuepress/components/Co/searchCo.vue @@ -0,0 +1,104 @@ + + + \ No newline at end of file diff --git a/docs/.vuepress/components/Co/tableHeadCo.vue b/docs/.vuepress/components/Co/tableHeadCo.vue new file mode 100644 index 0000000..77c71fa --- /dev/null +++ b/docs/.vuepress/components/Co/tableHeadCo.vue @@ -0,0 +1,127 @@ +ya + + + diff --git a/docs/.vuepress/components/Foo/Bar.vue b/docs/.vuepress/components/Foo/Bar.vue new file mode 100644 index 0000000..ab16351 --- /dev/null +++ b/docs/.vuepress/components/Foo/Bar.vue @@ -0,0 +1,15 @@ + + + diff --git a/docs/.vuepress/components/OtherComponent.vue b/docs/.vuepress/components/OtherComponent.vue new file mode 100644 index 0000000..4384204 --- /dev/null +++ b/docs/.vuepress/components/OtherComponent.vue @@ -0,0 +1,5 @@ + diff --git a/docs/.vuepress/components/UpgradePath.vue b/docs/.vuepress/components/UpgradePath.vue new file mode 100644 index 0000000..97b6aeb --- /dev/null +++ b/docs/.vuepress/components/UpgradePath.vue @@ -0,0 +1,35 @@ + + + diff --git a/docs/.vuepress/components/Web/Bpmn.vue b/docs/.vuepress/components/Web/Bpmn.vue new file mode 100644 index 0000000..aaee3e4 --- /dev/null +++ b/docs/.vuepress/components/Web/Bpmn.vue @@ -0,0 +1,17 @@ + + + diff --git a/docs/.vuepress/components/Web/BpmnNameForm.vue b/docs/.vuepress/components/Web/BpmnNameForm.vue new file mode 100644 index 0000000..3a296be --- /dev/null +++ b/docs/.vuepress/components/Web/BpmnNameForm.vue @@ -0,0 +1,20 @@ + + + diff --git a/docs/.vuepress/components/Web/EditBpmn.vue b/docs/.vuepress/components/Web/EditBpmn.vue new file mode 100644 index 0000000..d914155 --- /dev/null +++ b/docs/.vuepress/components/Web/EditBpmn.vue @@ -0,0 +1,65 @@ + + + diff --git a/docs/.vuepress/components/Web/Form.vue b/docs/.vuepress/components/Web/Form.vue new file mode 100644 index 0000000..af79339 --- /dev/null +++ b/docs/.vuepress/components/Web/Form.vue @@ -0,0 +1,168 @@ + + diff --git a/docs/.vuepress/components/Web/OnlyShowBpmn.vue b/docs/.vuepress/components/Web/OnlyShowBpmn.vue new file mode 100644 index 0000000..5f5fa5d --- /dev/null +++ b/docs/.vuepress/components/Web/OnlyShowBpmn.vue @@ -0,0 +1,81 @@ + + + diff --git a/docs/.vuepress/components/Web/SearchForm.vue b/docs/.vuepress/components/Web/SearchForm.vue new file mode 100644 index 0000000..8715afe --- /dev/null +++ b/docs/.vuepress/components/Web/SearchForm.vue @@ -0,0 +1,134 @@ + + + \ No newline at end of file diff --git a/docs/.vuepress/components/Web/Table.vue b/docs/.vuepress/components/Web/Table.vue new file mode 100644 index 0000000..8407c7a --- /dev/null +++ b/docs/.vuepress/components/Web/Table.vue @@ -0,0 +1,48 @@ + + + + diff --git a/docs/.vuepress/components/Web/TableCheckBox.vue b/docs/.vuepress/components/Web/TableCheckBox.vue new file mode 100644 index 0000000..756a016 --- /dev/null +++ b/docs/.vuepress/components/Web/TableCheckBox.vue @@ -0,0 +1,56 @@ + + + + diff --git a/docs/.vuepress/components/Web/TableSlot.vue b/docs/.vuepress/components/Web/TableSlot.vue new file mode 100644 index 0000000..658df70 --- /dev/null +++ b/docs/.vuepress/components/Web/TableSlot.vue @@ -0,0 +1,66 @@ + + + + diff --git a/docs/.vuepress/components/demo-1.vue b/docs/.vuepress/components/demo-1.vue new file mode 100644 index 0000000..f1dfb58 --- /dev/null +++ b/docs/.vuepress/components/demo-1.vue @@ -0,0 +1,15 @@ + + + diff --git a/docs/.vuepress/components/diagram-markdown-slot-relationship.vue b/docs/.vuepress/components/diagram-markdown-slot-relationship.vue new file mode 100644 index 0000000..5ffe821 --- /dev/null +++ b/docs/.vuepress/components/diagram-markdown-slot-relationship.vue @@ -0,0 +1,204 @@ + diff --git a/docs/.vuepress/components/svg-container.vue b/docs/.vuepress/components/svg-container.vue new file mode 100644 index 0000000..5aa846c --- /dev/null +++ b/docs/.vuepress/components/svg-container.vue @@ -0,0 +1,13 @@ + + + diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 0000000..0c3c57e --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,18 @@ +const head = require('./config/head.js'); +const plugins = require('./config/plugins.js'); +const themeConfig = require('./config/themeConfig.js'); + +module.exports = { + // theme: 'vdoing', // 使用依赖包主题 + theme: require.resolve('../../theme-vdoing'), // 使用本地主题 + + title: "HCFrame", + description: '通用框架组件', + // base: '/', // 格式:'/<仓库名>/', 默认'/' + markdown: { + lineNumbers: true, // 代码行号 + }, + head, + plugins, + themeConfig, +} diff --git a/docs/.vuepress/config/head.js b/docs/.vuepress/config/head.js new file mode 100644 index 0000000..a9032ee --- /dev/null +++ b/docs/.vuepress/config/head.js @@ -0,0 +1,22 @@ +// head +module.exports = [ + // 注入到页面 中的标签,格式[tagName, { attrName: attrValue }, innerHTML?] + ['link', { rel: 'icon', href: 'https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/favicon/favicon.ico' }], //favicons,资源放在public文件夹 + [ + 'meta', + { + name: 'keywords', + content: '通用框架组件', + }, + ], + // ['meta', { name: 'baidu-site-verification', content: '7F55weZDDc' }], // 百度统计的站长验证 + ['meta', { name: 'theme-color', content: '#11a8cd' }], // 移动浏览器主题颜色 + // [ + // 'script', + // { + // 'data-ad-client': 'ca-pub-7828333725993554', + // async: 'async', + // src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js', + // }, + // ], // 网站关联Google AdSense 与 html格式广告支持 +] diff --git a/docs/.vuepress/config/plugins.js b/docs/.vuepress/config/plugins.js new file mode 100644 index 0000000..f496434 --- /dev/null +++ b/docs/.vuepress/config/plugins.js @@ -0,0 +1,110 @@ +// 插件配置 +module.exports = [ + // 本地插件 + // [require('./plugins/love-me'), { // 鼠标点击爱心特效 + // color: '#11a8cd', // 爱心颜色,默认随机色 + // excludeClassName: 'theme-vdoing-content' // 要排除元素的class, 默认空'' + // }], + + 'vuepress-plugin-baidu-autopush', // 百度自动推送 + + // 可以添加第三方搜索链接的搜索框(原官方搜索框的参数仍可用) + [ + 'thirdparty-search', + { + thirdparty: [ + // 可选,默认 [] + { + title: '在MDN中搜索', + frontUrl: 'https://developer.mozilla.org/zh-CN/search?q=', // 搜索链接的前面部分 + behindUrl: '', // 搜索链接的后面部分,可选,默认 '' + }, + { + title: '在Runoob中搜索', + frontUrl: 'https://www.runoob.com/?s=', + }, + { + title: '在Vue API中搜索', + frontUrl: 'https://cn.vuejs.org/v2/api/#', + }, + { + title: '在Bing中搜索', + frontUrl: 'https://cn.bing.com/search?q=', + }, + { + title: '通过百度搜索本站的', + frontUrl: 'https://www.baidu.com/s?wd=site%3Axugaoyi.com%20', + }, + ], + }, + ], + + [ + 'one-click-copy', + { + // 代码块复制按钮 + copySelector: ['div[class*="language-"] pre', 'div[class*="aside-code"] aside'], // String or Array + copyMessage: '复制成功', // default is 'Copy successfully and then paste it for use.' + duration: 1000, // prompt message display time. + showInMobile: false, // whether to display on the mobile side, default: false. + }, + ], + [ + 'demo-block', + { + // demo演示模块 https://github.com/xiguaxigua/vuepress-plugin-demo-block + settings: { + // jsLib: ['http://xxx'], // 在线示例(jsfiddle, codepen)中的js依赖 + // cssLib: ['http://xxx'], // 在线示例中的css依赖 + // vue: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js', // 在线示例中的vue依赖 + jsfiddle: false, // 是否显示 jsfiddle 链接 + codepen: true, // 是否显示 codepen 链接 + horizontal: false, // 是否展示为横向样式 + }, + }, + ], + [ + 'vuepress-plugin-zooming', // 放大图片 + { + selector: '.theme-vdoing-content img:not(.no-zoom)', // 排除class是no-zoom的图片 + options: { + bgColor: 'rgba(0,0,0,0.6)', + }, + }, + ], + // [ + // 'vuepress-plugin-baidu-tongji', // 百度统计 + // { + // hm: '503f098e7e5b3a5b5d8c5fc2938af002', + // }, + // ], + // [ + // 'vuepress-plugin-comment', // 评论 + // { + // choosen: 'gitalk', + // options: { + // clientID: 'a6e1355287947096b88b', + // clientSecret: 'f0e77d070fabfcd5af95bebb82b2d574d7248d71', + // repo: 'blog-gitalk-comment', // GitHub 仓库 + // owner: 'xugaoyi', // GitHub仓库所有者 + // admin: ['xugaoyi'], // 对仓库有写权限的人 + // // distractionFreeMode: true, + // pagerDirection: 'last', // 'first'正序 | 'last'倒序 + // id: '<%- (frontmatter.permalink || frontmatter.to.path).slice(-16) %>', // 页面的唯一标识,长度不能超过50 + // title: '「评论」<%- frontmatter.title %>', // GitHub issue 的标题 + // labels: ['Gitalk', 'Comment'], // GitHub issue 的标签 + // body: + // '页面:<%- window.location.origin + (frontmatter.to.path || window.location.pathname) %>', // GitHub issue 的内容 + // }, + // }, + // ], + [ + '@vuepress/last-updated', // "上次更新"时间格式 + { + transformer: (timestamp, lang) => { + const dayjs = require('dayjs') // https://day.js.org/ + return dayjs(timestamp).format('YYYY/MM/DD, HH:mm:ss') + }, + }, + ], +] diff --git a/docs/.vuepress/config/themeConfig.js b/docs/.vuepress/config/themeConfig.js new file mode 100644 index 0000000..d072f88 --- /dev/null +++ b/docs/.vuepress/config/themeConfig.js @@ -0,0 +1,86 @@ +const nav = require('./themeConfig/nav.js'); +const sidebar = require('./themeConfig/sidebar.js'); +const htmlModules = require('./themeConfig/htmlModules.js'); + +// 主题配置 +module.exports = { + nav, + sidebarDepth: 2, // 侧边栏显示深度,默认1,最大2(显示到h3标题) + logo: 'https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203141030.png', // 导航栏logo + // repo: 'xugaoyi/vuepress-theme-vdoing', // 导航栏右侧生成Github链接 + searchMaxSuggestions: 10, // 搜索结果显示最大数 + lastUpdated: '上次更新', // 开启更新时间,并配置前缀文字 string | boolean (取值为git提交时间) + docsDir: 'docs', // 编辑的文件夹 + editLinks: true, // 启用编辑 + editLinkText: '编辑', + + //*** 以下配置是Vdoing主题改动和新增的配置 ***// + + category: false, // 是否打开分类功能,默认true。 如打开,会做的事情有:1. 自动生成的frontmatter包含分类字段 2.页面中显示与分类相关的信息和模块 3.自动生成分类页面(在@pages文件夹)。如关闭,则反之。 + tag: false, // 是否打开标签功能,默认true。 如打开,会做的事情有:1. 自动生成的frontmatter包含标签字段 2.页面中显示与标签相关的信息和模块 3.自动生成标签页面(在@pages文件夹)。如关闭,则反之。 + archive: false, // 是否打开归档功能,默认true。 如打开,会做的事情有:1.自动生成归档页面(在@pages文件夹)。如关闭,则反之。 + // categoryText: '随笔', // 碎片化文章(_posts文件夹的文章)预设生成的分类值,默认'随笔' + + // bodyBgImg: [ + // 'https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200507175828.jpeg', + // 'https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200507175845.jpeg', + // 'https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200507175846.jpeg' + // ], // body背景大图,默认无。 单张图片 String || 多张图片 Array, 多张图片时每隔15秒换一张。 + // bodyBgImgOpacity: 0.5, // body背景图透明度,选值 0 ~ 1.0, 默认0.5 + + // titleBadge: false, // 文章标题前的图标是否显示,默认true + // titleBadgeIcons: [ // 文章标题前图标的地址,默认主题内置图标 + // '图标地址1', + // '图标地址2' + // ], + // contentBgStyle: 1, // 文章内容块的背景风格,默认无. 1 => 方格 | 2 => 横线 | 3 => 竖线 | 4 => 左斜线 | 5 => 右斜线 | 6 => 点状 + + // updateBar: { // 最近更新栏 + // showToArticle: true, // 显示到文章页底部,默认true + // moreArticle: '/archives' // “更多文章”跳转的页面,默认'/archives' + // }, + // rightMenuBar: false, // 是否显示右侧文章大纲栏,默认true (屏宽小于1300px下无论如何都不显示) + // sidebarOpen: false, // 初始状态是否打开侧边栏,默认true + // pageButton: false, // 是否显示快捷翻页按钮,默认true + + sidebar: 'structuring', // 侧边栏 'structuring' | { mode: 'structuring', collapsable: Boolean} | 'auto' | 自定义 温馨提示:目录页数据依赖于结构化的侧边栏数据,如果你不设置为'structuring',将无法使用目录页 + + author: { + // 文章默认的作者信息,可在md文件中单独配置此信息 String | {name: String, link: String} + name: 'haocheng Liu', // 必需 + }, + // blogger: { + // // 博主信息,显示在首页侧边栏 + // avatar: 'https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200103123203.jpg', + // name: 'Evan Xu', + // slogan: '前端界的小学生', + // }, + social: { + // 社交图标,显示于博主信息栏和页脚栏 + // iconfontCssFile: '//at.alicdn.com/t/font_1678482_u4nrnp8xp6g.css', // 可选,阿里图标库在线css文件地址,对于主题没有的图标可自由添加 + icons: [ + { + iconClass: 'icon-youjian', + title: '发邮件', + link: 'mailto:894072666@qq.com', + }, + { + iconClass: 'icon-github', + title: 'GitHub', + link: 'https://github.com/xugaoyi', + }, + { + iconClass: 'icon-erji', + title: '听音乐', + link: 'https://music.163.com/#/playlist?id=755597173', + }, + ], + }, + footer: { + // 页脚信息 + createYear: 2019, // 博客创建年份 + copyrightInfo: + 'Haocheng Liu | MIT License', // 博客版权信息,支持a标签 + }, + // htmlModules // 插入hmtl(广告)模块 +} diff --git a/docs/.vuepress/config/themeConfig/htmlModules.js b/docs/.vuepress/config/themeConfig/htmlModules.js new file mode 100644 index 0000000..bb76eda --- /dev/null +++ b/docs/.vuepress/config/themeConfig/htmlModules.js @@ -0,0 +1,106 @@ +/** 插入自定义html模块 (可用于插入广告模块等) + * { + * homeSidebarB: htmlString, 首页侧边栏底部 + * + * sidebarT: htmlString, 全局左侧边栏顶部 + * sidebarB: htmlString, 全局左侧边栏底部 + * + * pageT: htmlString, 全局页面顶部 + * pageB: htmlString, 全局页面底部 + * pageTshowMode: string, 页面顶部-显示方式:未配置默认全局;'article' => 仅文章页①; 'custom' => 仅自定义页① + * pageBshowMode: string, 页面底部-显示方式:未配置默认全局;'article' => 仅文章页①; 'custom' => 仅自定义页① + * + * windowLB: htmlString, 全局窗口左下角② + * windowRB: htmlString, 全局窗口右下角② + * } + * + * ①注:在.md文件front matter配置`article: false`的页面是自定义页,未配置的默认是文章页(首页除外)。 + * ②注:windowLB 和 windowRB:1.展示区块宽高最大是200*200px。2.请给自定义元素定一个不超过200px的固定宽高。3.在屏宽小于960px时无论如何都不会显示。 + */ + +module.exports = { + homeSidebarB: + ` + + `, + // sidebarT: + // ` + // + // `, + sidebarB: + ` + + `, + pageT: + ` + + `, + // pageTshowMode: 'article', + pageB: + ` + + `, + // pageBshowMode: 'article', + // windowLB: // 会遮挡部分侧边栏 + // ` + // + // + // `, + windowRB: + ` + + + `, +} + + +// module.exports = { +// homeSidebarB: `
自定义模块测试
`, +// sidebarT: `
自定义模块测试
`, +// sidebarB: `
自定义模块测试
`, +// pageT: `
自定义模块测试
`, +// pageB: `
自定义模块测试
`, +// windowLB: `
自定义模块测试
`, +// windowRB: `
自定义模块测试
`, +// } diff --git a/docs/.vuepress/config/themeConfig/nav.js b/docs/.vuepress/config/themeConfig/nav.js new file mode 100644 index 0000000..7b7681a --- /dev/null +++ b/docs/.vuepress/config/themeConfig/nav.js @@ -0,0 +1,42 @@ +// nav +module.exports = [ + { text: '首页', link: '/' }, + { + text: '指南', + link: '/guide/', + items: [ + { + text: '介绍', + link: '/pages/a3c9a2/', + }, + { + text: '快速开始', + link: '/pages/introduce/', + }, + { + text: '打包部署', + link: '/pages/6fe261/', + }, + ] + }, + { + text: '前端', + link: '/web/', //目录页链接,此处link是vdoing主题新增的配置项,有二级导航时,可以点击一级导航跳到目录页 + items: [ + // 说明:以下所有link的值只是在相应md文件定义的永久链接(不是什么特殊生成的编码)。另外,注意结尾是有斜杠的 + { + text: '说明', + link: '/pages/introduce/', + },{ + text: '配置', + link: '/pages/b8a7ad/', + },{ + text: '代码示例', + link: '/pages/5c0128/', + },{ + text: '组件', + link: '/pages/1ab4ce/', + }, + ], + }, +] diff --git a/docs/.vuepress/config/themeConfig/sidebar.js b/docs/.vuepress/config/themeConfig/sidebar.js new file mode 100644 index 0000000..9c97278 --- /dev/null +++ b/docs/.vuepress/config/themeConfig/sidebar.js @@ -0,0 +1,105 @@ +// !!!注:此文件没有使用到,仅用于测试和侧边栏数据格式的参考。 + +// 侧边栏 +module.exports = { + '/01.前端/': [ + { + title: 'JavaScript', + collapsable: false, //是否可折叠,可选的,默认true + children: [ + ['01.JavaScript/01.JavaScript中的名词概念','JavaScript中的名词概念'], + ['01.JavaScript/02.数据类型转换','数据类型转换'], + ['01.JavaScript/03.ES5面向对象','ES5面向对象'], + ['01.JavaScript/04.ES6面向对象','ES6面向对象'], + ['01.JavaScript/05.new命令原理','new命令原理'], + ['01.JavaScript/06.多种数组去重性能对比','多种数组去重性能对比'], + ] + }, + ], + '/02.页面/': [ + { + title: 'html-css', + collapsable: false, + children: [ + ['01.html-css/00.flex布局语法','flex布局语法'], + ['01.html-css/01.flex布局案例-基础','flex布局案例-基础'], + ['01.html-css/02.flex布局案例-骰子','flex布局案例-骰子'], + ['01.html-css/03.flex布局案例-网格布局','flex布局案例-网格布局'], + ['01.html-css/04.flex布局案例-圣杯布局','flex布局案例-圣杯布局'], + ['01.html-css/05.flex布局案例-输入框布局','flex布局案例-输入框布局'], + ['01.html-css/06.CSS3之transform过渡','CSS3之transform过渡'], + ['01.html-css/07.CSS3之animation动画','CSS3之animation动画'], + ] + }, + ], + '/03.技术杂谈/': [ + { + title: '技术杂谈', + collapsable: false, //是否可折叠,可选的,默认true + sidebarDepth: 2, // 深度,可选的, 默认值是 1 + children: [ + ['01.Git使用手册','Git使用手册'], // 同 {path: '01.Git使用手册', title: 'Git使用文档'} + ['02.GitHub高级搜索技巧','GitHub高级搜索技巧'], + ['03.Markdown使用教程','Markdown使用教程'], + ['04.npm常用命令','npm常用命令'], + ['05.yaml语言教程','yaml语言教程'], + ['06.解决百度无法收录搭建在GitHub上的个人博客的问题','解决百度无法收录搭建在GitHub上的个人博客的问题'], + ['07.使用Gitalk实现静态博客无后台评论系统','使用Gitalk实现静态博客无后台评论系统'], + ] + } + ], + '/04.其他/': [ + { + title: '学习', + collapsable: false, + children: [ + ['01.学习/01.学习网站','学习网站'], + ['01.学习/02.学习效率低,忘性很大怎么办?','学习效率低,忘性很大怎么办?'], + ] + }, + { + title: '学习笔记', + collapsable: false, + children: [ + ['02.学习笔记/01.小程序笔记','小程序笔记'], + ] + }, + { + title: '面试', + collapsable: false, //是否可折叠,可选的,默认true + children: [ + ['03.面试/01.面试问题集锦','面试问题集锦'], + ] + }, + ['01.在线工具','在线工具'], + ['02.友情链接','友情链接'], + ], + // '/': [ // 在最后定义,在没有单独设置侧边栏时统一使用这个侧边栏 + // '', + // 'git', + // 'github', + // 'markdown', + // 'study', + // 'interview' + // // '/', + // // { + // // title: 'foo', // 标题,必要的 + // // path: '/foo/', // 标题的路径,可选的, 应该是一个绝对路径 + // // collapsable: false, // 是否可折叠,可选的,默认true + // // sidebarDepth: 1, // 深度,可选的, 默认值是 1 + // // children: [ + // // ['foo/', '子页1'], + // // 'foo/1', + // // 'foo/2', + // // ] + // // }, + // // { + // // title: 'bar', + // // children: [ + // // ['bar/', '子页2'], + // // 'bar/3', + // // 'bar/4', + // // ] + // // } + // ], +} diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js new file mode 100644 index 0000000..ed6d0d4 --- /dev/null +++ b/docs/.vuepress/enhanceApp.js @@ -0,0 +1,25 @@ +// import vue from 'vue/dist/vue.esm.browser' +import Element from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' + +// bpmn 相关依赖 +import 'bpmn-js/dist/assets/diagram-js.css' +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css' +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css' +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css' +// 左边工具栏以及编辑节点的样式 +import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' +export default ({ + Vue, // VuePress 正在使用的 Vue 构造函数 + options, // 附加到根实例的一些选项 + router, // 当前应用的路由实例 + siteData // 站点元数据 +}) => { + // if (!isServer) { + // import('vue-toasted' /* webpackChunkName: "notification" */).then((module) => { + // Vue.use(module.default) + // }) + // } + Vue.use(Element) + // window.Vue = vue // 使页面中可以使用Vue构造函数 (使页面中的vue demo生效) +} diff --git a/docs/.vuepress/plugins/love-me/index.js b/docs/.vuepress/plugins/love-me/index.js new file mode 100644 index 0000000..674294c --- /dev/null +++ b/docs/.vuepress/plugins/love-me/index.js @@ -0,0 +1,12 @@ +const path= require('path'); +const LoveMyPlugin = (options={}) => ({ + define () { + const COLOR = options.color || "rgb(" + ~~ (255 * Math.random()) + "," + ~~ (255 * Math.random()) + "," + ~~ (255 * Math.random()) + ")" + const EXCLUDECLASS = options.excludeClassName || '' + return {COLOR, EXCLUDECLASS} + }, + enhanceAppFiles: [ + path.resolve(__dirname, 'love-me.js') + ] +}); +module.exports = LoveMyPlugin; diff --git a/docs/.vuepress/plugins/love-me/love-me.js b/docs/.vuepress/plugins/love-me/love-me.js new file mode 100644 index 0000000..f93855e --- /dev/null +++ b/docs/.vuepress/plugins/love-me/love-me.js @@ -0,0 +1,62 @@ +export default () => { + if (typeof window !== "undefined") { + (function(e, t, a) { + function r() { + for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x + "px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e].scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999"); + requestAnimationFrame(r) + } + function n() { + var t = "function" == typeof e.onclick && e.onclick; + + e.onclick = function(e) { + // 过滤指定元素 + let mark = true; + EXCLUDECLASS && e.path && e.path.forEach((item) =>{ + if(item.nodeType === 1) { + typeof item.className === 'string' && item.className.indexOf(EXCLUDECLASS) > -1 ? mark = false : '' + } + }) + + if(mark) { + t && t(), + o(e) + } + } + } + function o(e) { + var a = t.createElement("div"); + a.className = "heart", + s.push({ + el: a, + x: e.clientX - 5, + y: e.clientY - 5, + scale: 1, + alpha: 1, + color: COLOR + }), + t.body.appendChild(a) + } + function i(e) { + var a = t.createElement("style"); + a.type = "text/css"; + try { + a.appendChild(t.createTextNode(e)) + } catch(t) { + a.styleSheet.cssText = e + } + t.getElementsByTagName("head")[0].appendChild(a) + } + // function c() { + // return "rgb(" + ~~ (255 * Math.random()) + "," + ~~ (255 * Math.random()) + "," + ~~ (255 * Math.random()) + ")" + // } + var s = []; + e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || + function(e) { + setTimeout(e, 1e3 / 60) + }, + i(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"), + n(), + r() + })(window, document) + } +} \ No newline at end of file diff --git a/docs/.vuepress/public/img/EB-logo.png b/docs/.vuepress/public/img/EB-logo.png new file mode 100644 index 0000000..8e1d567 Binary files /dev/null and b/docs/.vuepress/public/img/EB-logo.png differ diff --git a/docs/.vuepress/public/img/bg.jpeg b/docs/.vuepress/public/img/bg.jpeg new file mode 100644 index 0000000..85e53e7 Binary files /dev/null and b/docs/.vuepress/public/img/bg.jpeg differ diff --git a/docs/.vuepress/public/img/bg.jpg b/docs/.vuepress/public/img/bg.jpg new file mode 100644 index 0000000..f093e79 Binary files /dev/null and b/docs/.vuepress/public/img/bg.jpg differ diff --git a/docs/.vuepress/public/img/favicon.ico b/docs/.vuepress/public/img/favicon.ico new file mode 100644 index 0000000..3fc4d60 Binary files /dev/null and b/docs/.vuepress/public/img/favicon.ico differ diff --git a/docs/.vuepress/public/img/git.png b/docs/.vuepress/public/img/git.png new file mode 100644 index 0000000..82ba43f Binary files /dev/null and b/docs/.vuepress/public/img/git.png differ diff --git a/docs/.vuepress/public/img/more.png b/docs/.vuepress/public/img/more.png new file mode 100644 index 0000000..830613b Binary files /dev/null and b/docs/.vuepress/public/img/more.png differ diff --git a/docs/.vuepress/public/img/other.png b/docs/.vuepress/public/img/other.png new file mode 100644 index 0000000..87f8098 Binary files /dev/null and b/docs/.vuepress/public/img/other.png differ diff --git a/docs/.vuepress/public/img/panda-waving.png b/docs/.vuepress/public/img/panda-waving.png new file mode 100644 index 0000000..20246c6 Binary files /dev/null and b/docs/.vuepress/public/img/panda-waving.png differ diff --git a/docs/.vuepress/public/img/python.png b/docs/.vuepress/public/img/python.png new file mode 100644 index 0000000..c3ddebe Binary files /dev/null and b/docs/.vuepress/public/img/python.png differ diff --git a/docs/.vuepress/public/img/ui.png b/docs/.vuepress/public/img/ui.png new file mode 100644 index 0000000..617c56d Binary files /dev/null and b/docs/.vuepress/public/img/ui.png differ diff --git a/docs/.vuepress/public/img/web.png b/docs/.vuepress/public/img/web.png new file mode 100644 index 0000000..0a6e27c Binary files /dev/null and b/docs/.vuepress/public/img/web.png differ diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl new file mode 100644 index 0000000..756357c --- /dev/null +++ b/docs/.vuepress/styles/index.styl @@ -0,0 +1,59 @@ + +// 评论区颜色重置 +.gt-container + .gt-ico-tip + &::after + content: '。( Win + . ) 或 ( ⌃ + ⌘ + ␣ ) 打开表情' + color: #999 + font-size: .8rem + .gt-meta + border-color var(--borderColor)!important + .gt-comments-null + color var(--textColor) + opacity .5 + .gt-header-textarea + color var(--textColor) + background rgba(180,180,180,0.1)!important + .gt-btn + border-color $accentColor!important + background-color $accentColor!important + .gt-btn-preview + background-color rgba(255,255,255,0)!important + color $accentColor!important + a + color $accentColor!important + .gt-svg svg + fill $accentColor!important + .gt-comment-content,.gt-comment-admin .gt-comment-content + background-color rgba(150,150,150,0.1)!important + &:hover + box-shadow 0 0 25px rgba(150,150,150,.5)!important + .gt-comment-body + color var(--textColor)!important + + +// qq徽章 +.qq + position: relative; +.qq::after + content: "可撩"; + background: $accentColor; + color:#fff; + padding: 0 5px; + border-radius: 10px; + font-size:12px; + position: absolute; + top: -4px; + right: -35px; + transform:scale(0.85); + +// demo模块图标颜色 +body .vuepress-plugin-demo-block__wrapper + &,.vuepress-plugin-demo-block__display + border-color rgba(160,160,160,.3) + .vuepress-plugin-demo-block__footer:hover + .vuepress-plugin-demo-block__expand::before + border-top-color: $accentColor !important; + border-bottom-color: $accentColor !important; + svg + fill: $accentColor !important; diff --git a/docs/.vuepress/styles/palette.styl b/docs/.vuepress/styles/palette.styl new file mode 100644 index 0000000..f0b9389 --- /dev/null +++ b/docs/.vuepress/styles/palette.styl @@ -0,0 +1,68 @@ +//***vdoing主题-样式变量***// + +// 以下注释的变量仅供参考,主题使用的最新变量请查看:https://github.com/xugaoyi/vuepress-theme-vdoing/blob/master/theme-vdoing/styles/palette.styl +// 你可以在这个文件内修改这些变量的值 + +// // 颜色 + +// $bannerTextColor = #fff // 首页banner区(博客标题)文本颜色 +// $accentColor = #11A8CD +// $activeColor = #ff5722 +// $arrowBgColor = #ccc +// $badgeTipColor = #42b983 +// $badgeWarningColor = darken(#ffe564, 35%) +// $badgeErrorColor = #DA5961 + +// // 布局 +// $navbarHeight = 3.6rem +// $sidebarWidth = 18rem +// $contentWidth = 860px +// $homePageWidth = 1100px +// $rightMenuWidth = 230px // 右侧菜单 + +// // 代码块 +// $lineNumbersWrapperWidth = 2.5rem + +// // 浅色模式 +// .theme-mode-light +// --bodyBg: #f4f4f4 +// --mainBg: rgba(255,255,255,1) +// --sidebarBg: rgba(255,255,255,.8) +// --blurBg: rgba(255,255,255,.9) +// --textColor: #004050 +// --textLightenColor: #0085AD +// --borderColor: rgba(0,0,0,.15) +// // 代码块浅色主题 +// --codeBg: #f6f6f6 +// --codeColor: #525252 +// codeThemeLight() +// // // 代码块深色主题 +// // --codeBg: #252526 +// // --codeColor: #fff +// // codeThemeDark() + +// // 深色模式 +// .theme-mode-dark +// --bodyBg: rgb(39,39,43) +// --mainBg: rgba(30,30,34,1) +// --sidebarBg: rgba(30,30,34,.8) +// --blurBg: rgba(30,30,34,.8) +// --textColor: rgb(140,140,150) +// --textLightenColor: #0085AD +// --borderColor: #2C2C3A +// --codeBg: #252526 +// --codeColor: #fff +// codeThemeDark() + +// // 阅读模式 +// .theme-mode-read +// --bodyBg: rgb(240,240,208) +// --mainBg: rgba(245,245,213,1) +// --sidebarBg: rgba(245,245,213,.8) +// --blurBg: rgba(245,245,213,.9) +// --textColor: #004050 +// --textLightenColor: #0085AD +// --borderColor: rgba(0,0,0,.15) +// --codeBg: #282c34 +// --codeColor: #fff +// codeThemeDark() diff --git a/docs/00.目录页/01.指南.md b/docs/00.目录页/01.指南.md new file mode 100644 index 0000000..983c87c --- /dev/null +++ b/docs/00.目录页/01.指南.md @@ -0,0 +1,16 @@ +--- +pageComponent: + name: Catalogue + data: + key: 01.指南 + imgUrl: /img/web.png + description: 通用框架基本说明 +title: 指南 +date: 2020-03-11 21:50:53 +permalink: /guide +sidebar: false +article: false +comment: false +editLink: false +--- + diff --git a/docs/00.目录页/02.前端.md b/docs/00.目录页/02.前端.md new file mode 100644 index 0000000..76aed40 --- /dev/null +++ b/docs/00.目录页/02.前端.md @@ -0,0 +1,16 @@ +--- +pageComponent: + name: Catalogue + data: + key: 02.前端 + imgUrl: /img/web.png + description: 前端框架使用说明 +title: 前端 +date: 2020-03-11 21:50:53 +permalink: /web +sidebar: false +article: false +comment: false +editLink: false +--- + diff --git a/docs/01.指南/01.介绍.md b/docs/01.指南/01.介绍.md new file mode 100644 index 0000000..24d4a5e --- /dev/null +++ b/docs/01.指南/01.介绍.md @@ -0,0 +1,113 @@ +--- +title: 介绍 +date: 2021-02-03 09:26:32 +permalink: /pages/a3c9a2/ +--- +# 介绍 + +通用框架组件是由前端及后台两部分组成,通过对单表操作代码的封装,使使用者在较小的开发工作下,完成业务系统的开发。 + +## 前端用到的技术 + +1. [**Vue**](https://cn.vuejs.org/) Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。 + +2. [**Vuex**](https://vuex.vuejs.org/zh-cn/) 前端状态管理 + +3. [**Vue-router**](https://router.vuejs.org/zh-cn/) 管理页面路由跳转。 + +4. [**axios**](https://github.com/axios/axios) 前端请求后台获取数据的工具,类似于ajax。 + +5. [**Vue-cli**](https://github.com/vuejs/vue-cli) Vue项目的手脚架搭建客户端。 + +6. [**Typescript**](https://www.tslang.cn/) 是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。TypeScript添加了可选的静态类型系统、很多尚未正式发布的ECMAScript新特性(如装饰器 [1] )。2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript。 + +7. [**vue-typescript-admin-template**](https://github.com/Armour/vue-typescript-admin-template) 一套封装较为成熟的管理系统Vue模板,包含较多的实用性组件及示例。 + +8. [**elementUI**](https://element.eleme.cn/#/zh-CN) ,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库 + +9. [**vue-class-component**](https://github.com/vuejs/vue-class-component) 使用typescript语法及注解简化代码插件 + +10. [**vue-property-decorator**](https://github.com/kaorun343/vue-property-decorator) 使用 typescript 语法及注解简化代码的插件 + +## 后台用到的技术 + +1. [**SpringBoot**](https://spring.io/projects/spring-boot/) 快速开发,自动装配的Spring家族的框架 + +2. [**Mybatis**](https://mybatis.org/mybatis-3/zh/index.html) 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 + +3. [**Apache Shiro**](http://shiro.apache.org/) 是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。 + +4. [**Lombok**](https://projectlombok.org/) 项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。 + +5. [**Druid**](https://github.com/alibaba/druid/) Java语言中被广泛应用的的数据库连接池。Druid能够提供强大的监控和扩展功能。 + +6. [**knife4j**](https://doc.xiaominfo.com/) 是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍! + +7. [**tkMybaits**](https://github.com/abel533/Mapper) 通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。极其方便的使用MyBatis单表的增删改查。支持单表操作,不支持通用的多表联合查询。通用 Mapper 支持 Mybatis-3.2.4 及以上版本。 + +## 它是如何工作的? + +通用框架,通过前端及后台对单表操作代码的封装,省去了单表操作代码的开发工作量,仅通过配置,即可生成相应的功能。 +使开发者更加专注于需求、页面展示及关联查询等复杂需求的开发。 + + +首先,使用前需要将业务表的信息录入到配置表当中。在配置表中,我们设计了table表,field表,及select表。 +将表信息录入到配置表中以后,通过单表通用接口中的getTableInfo接口进行表信息的获取,即可获取当前业务表的全部信息。 + +**table表:** + +![An image](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203144029.png) + +**field表:** +![field](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203144046.png) + +**select表:** +![select](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203144058.png) + + +后台中如何做到单表操作通过一套接口进行封装?最初的项目中,我们通过将表别名设置成一个变量,由前端传入。 +同时摒弃了MVC设计模式,通过Map集合的方式进行传值,更加自由,且不限制具体字段。但是随即暴露出的问题就是表名会暴露在前端中,产生一系列安全问题。 +于是我们再配置表中加入表别名字段,并通过REST接口的方式,将表别名当做一个参数写入URL中,增加接口地址安全性。 +后台再通过表别名映射成真实表名,进行单表通用接口的操作。后台基本思路就是将表名当做参数,然后通过前端传值,进行SQL拼接完成通用接口。 + +**后台接口示例:** +``` java + /** + * 通用保存接口(不带保存日前) + * + * @param typeName --- 表名映射 + * @param map --- 保存参数 + * @return + */ + @ApiOperation(value = "保存接口(不带保存日期)") + @PostMapping("/{typeName}") + public ResultVO save(@PathVariable String typeName, @RequestParam Map map) { + return tableService.save(tableNameUtil.getTableName(typeName), map); + } + + /** + * 通用保存接口(带保存和更新日期) + * + * @param typeName --- 表名映射 + * @param map --- 保存参数 + * @return + */ + @ApiOperation(value = "保存接口(带保存日期)") + @PostMapping("/{typeName}/date") + public ResultVO saveWithDate(@PathVariable String typeName, @RequestParam Map map) { + return tableService.saveWithDate(tableNameUtil.getTableName(typeName), map); + } + +``` + + +在前端中,页面实例再创建后,首先需要通过getTableInfo接口获取当前页面需要渲染的表的表信息。 +然后前端组件通过读取表信息后,自动生成表格,搜索表单,新增及编辑表单,按钮等页面内容。 +最后通过读取单表通用接口数据,将数据加载入表格当中完成整个页面渲染。 + +**前端通用页面示例:** +![web](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203144128.png) + + + + diff --git a/docs/01.指南/02.快速开始.md b/docs/01.指南/02.快速开始.md new file mode 100644 index 0000000..3667e93 --- /dev/null +++ b/docs/01.指南/02.快速开始.md @@ -0,0 +1,125 @@ +--- +title: 快速开始 +date: 2021-02-03 09:26:32 +permalink: /pages/b0b86c/ +--- +# 快速上手 + +::: warning 前提条件 +编译器安装好 Lombok 插件,jdk环境1.8,需要最新的LTS版本的 [Node.js](https://nodejs.org/en/) + +运行工具推荐使用 [Yarn](https://yarn.bootcss.com/) + +请clone代码时尽量从Tags中选择,Tags中的代码皆为稳定版,存在bug较少。 +::: + +本文会帮助你从头搭建并运行简单版本的通用框架前后端。 + +## 后台搭建 + +1. 从git仓库clone后台代码 + + ``` bash + # 此处clone后台代码为dameng1.0版本的代码,mysql请下载mysql对应版本 + git clone -b dameng-1.0 http://taixingyiji.synology.me:1010/common-frame/common.git + ``` + +2. 引入编译器,等待maven下载好依赖 + +3. 创建数据库,并运行 `src>main>resources>common.sql` 文件 + +4. 修改yml文件,目录为 `src>main>resources>application.yml` + + **配置数据库数据源:** + ``` yaml + datasource: + type: com.alibaba.druid.pool.DruidDataSource + # 将此处修改成你的数据库地址 + url: jdbc:dm://192.168.10.3:5236/COMMON?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 + # 将此处修改你的数据库用户名 + username: COMMON + # 将此处修改成你的数据库密码 + password: 123456789 + # 指定数据库驱动 + driver-class-name: dm.jdbc.driver.DmDriver + ``` + **配置token存证是否存入redis:** + ``` yaml + spring: + # 若不使用redis需要注释掉此类信息 + redis: + database: 0 + # redis地址 + host: 192.168.100.145 + # redis端口号 + port: 6379 + # redis 密码 + password: root + lettuce: + pool: + # 连接池中的最大空闲连接 默认8 + max-idle: 8 + # 连接池中的最小空闲连接 默认0 + min-idle: 0 + # 连接池最大连接数 默认8 ,负数表示没有限制 + max-active: 8 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1 + max-wait: -1 + timeout: 30000 + # 是否开启redis 用户登录,若开启此项,需要配置redis节点及相关配置,若不开启,需要注释掉redis相关配置信息 + isRedisLogin: false + ``` + **配置分页插件:** + + ``` yaml + # 分页插件设置成达梦数据库支持,若不是达梦数据库需要注释掉此部分 + pagehelper: + helperDialect: dm + reasonable: true + supportMethodsArguments: true + pageSizeZero: true + params: count=countSql + ``` +5. 通过启动类启动后台项目 `src>main>java>com.commmon.common>CommonApplication.java` + +现在你的后台项目已经配置完成,且已经启动,接下来需要配置前端项目。 + +## 前端搭建 + +1. 通过git下载前端项目 + + ``` bash + # 此处clone前端代码为dameng1.0版本的代码,mysql请下载mysql对应版本 + git clone -b dameng-1.0 http://taixingyiji.synology.me:1010/common-frame/common-web.git + ``` +2. 安装依赖 + + ```bash + yarn install + ``` +3. 修改配置文件 `./vue.config.js` + + ``` js + '/common': { + // 此处修改成你的后台服务器地址及端口,其余配置不变 + target: 'http://127.0.0.1:8081' , + changeOrigin: true, // needed for virtual hosted sites + ws: true, // proxy websockets + pathRewrite: { + // '^/common': '' + } + } + ``` +4. 启动本地开发环境(自带热启动) + + ```bash + yarn serve + ``` + +5. 通过浏览器访问 + + ```http request + http://localhost:9527 + ``` + + diff --git a/docs/01.指南/03.打包部署.md b/docs/01.指南/03.打包部署.md new file mode 100644 index 0000000..94d4559 --- /dev/null +++ b/docs/01.指南/03.打包部署.md @@ -0,0 +1,75 @@ +--- +title: 打包部署 +date: 2021-02-03 09:26:32 +permalink: /pages/6fe261/ +--- +# 打包部署 + +::: tip +框架提供两种部署方式,第一种为前后端一起打包部署,第二种为前后端分离打包部署。 +::: + + +## 前后端一起部署 + +1. 修改生产环境Base url文件 `./.env.production` + + ``` properties + VUE_APP_BASE_API = './' + ``` + +2. 进入前端目录执行打包命令 + ``` bash + cd common-web + + yarn build:prod + ``` + +3. 执行完成后前端项目将生产dist文件夹 + + ![dist](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203155236.png) + +4. 打开dist目录,将除了 `index.html` 的文件全部考入后端项目 `src>main>resources>static` 目录下 +,将 `index.html` 文件考入到 `src>main>resources>templates` 目录下 + + ![static](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203155255.png) + +5. 进入后台代码目录,执行maven打包命令 + + ``` bash + cd common/ + + mvn clean package + ``` + +6. 将生成的war包上传到tomcat等服务器进行部署。 + +## 前后端分离部署 + +1. 修改生产环境Base url文件 `./.env.production` + + ``` properties + # Base api,此处填写后台请求的地址及端口及路径 + VUE_APP_BASE_API = 'http://192.168.1.123:8080/common' + ``` + +2. 进入前端目录执行打包命令 + ``` bash + cd common-web + + yarn build:prod + ``` + +3. 执行完成后前端项目将生产dist文件夹 + + ![dist](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203155236.png) + +4. 将生成的dist文件夹里面内容上传至Nginx或Apache静态服务器 + +5. 进入后台代码目录执行maven打包命令 + + ``` bash + mvn clean package + ``` + +6. 将生成的war包上传到tomcat等服务器进行部署。 diff --git a/docs/02.前端/01.说明/01.简介.md b/docs/02.前端/01.说明/01.简介.md new file mode 100644 index 0000000..f4eeb42 --- /dev/null +++ b/docs/02.前端/01.说明/01.简介.md @@ -0,0 +1,37 @@ +--- +title: 简介 +date: 2021-02-03 09:26:32 +permalink: /pages/introduce/ +categories: + - 前端 + - 说明 +tags: + - +--- +# 简介 +::: tip +前端项目来源于GitHub中Armour所写的开源项目[vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) + +项目地址:[https://github.com/Armour/vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) + +本框架前端基于 [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) ,并增加部分功能,部分说明文档来源于 [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) + +::: + + +## 前序准备 + +你需要在本地安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。本项目技术栈基于 [typescript](https://www.typescriptlang.org/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[vue-cli](https://github.com/vuejs/vue-cli) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element) ,提前了解和学习这些知识会对使用本项目有很大的帮助。 + + + +## 浏览器支持 + +Modern browsers and Internet Explorer 10+. + + + +| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| --------- | --------- | --------- | --------- | +| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions + diff --git a/docs/02.前端/01.说明/02.功能.md b/docs/02.前端/01.说明/02.功能.md new file mode 100644 index 0000000..0a5ebab --- /dev/null +++ b/docs/02.前端/01.说明/02.功能.md @@ -0,0 +1,88 @@ +--- +title: 功能 +date: 2021-02-03 09:26:32 +permalink: /pages/767f7b/ +categories: + - 前端 + - 说明 +tags: + - +--- +# 功能 + +```txt +- 登录 / 注销 + +- 权限验证 + - 页面权限 + - 指令权限 + - 权限配置 + - 二步登录 + +- 通用模板示例 + +- 后台配置可视化操作界面 + - 配置表增删改查 + - 配置字段增删改查 + - 配置基表及格式化字段增删改查 + +- 多环境发布 + - Dev / Stage / Prod + +- 全局功能 + - 国际化多语言 + - 动态换肤 + - 动态侧边栏(支持多级路由嵌套) + - 动态面包屑 + - 快捷导航(支持右键操作) + - 粘贴板 + - Svg 图标 + - 搜索 + - 全屏 + - 设置 + - Mock 数据 / Mock 服务器 + - 支持 PWA + +- 组件 + - 编辑器 + - 富文本编辑器 + - Markdown 编辑器 + - JSON 编辑器 + - 头像上传 + - 返回顶部 + - CountTo + - 拖放区 + - 拖拽弹窗 + - 拖拽看板 + - 拖拽列表 + - 拖拽选择 + - ECharts 图表 + - Mixin + - 拆分窗格 + - 黏性组件 + +- 表格 + - 动态表格 + - 拖拽表格 + - 内联编辑表格 + - 复杂表格 + +- Excel + - 导出excel + - 导入excel + - 前端可视化excel + +- Zip + - 导出zip + +- PDF + - 下载 pdf + +- 控制台 +- 引导页 +- 综合实例 +- 错误日志 +- 错误页面 + - 401 + - 404 +``` diff --git a/docs/02.前端/01.说明/03.如何设置以及启动项目.md b/docs/02.前端/01.说明/03.如何设置以及启动项目.md new file mode 100644 index 0000000..6847d1d --- /dev/null +++ b/docs/02.前端/01.说明/03.如何设置以及启动项目.md @@ -0,0 +1,63 @@ +--- +title: 如何设置以及启动项目 +date: 2021-02-03 09:26:32 +permalink: /pages/b7e102/ +categories: + - 前端 + - 说明 +tags: + - +--- +# 如何设置以及启动项目 + +## 安装依赖 + +```bash +yarn install +``` + +## 启动本地开发环境(自带热启动) + +```bash +yarn serve +``` + +## 构建生产环境 (自带压缩) + +```bash +yarn build:prod +``` + +## 构建阶段性生产环境 (自带压缩) + +```bash +yarn build:stage +``` + +## 代码格式检查以及自动修复 + +```bash +yarn lint +``` + +## 运行单元测试 + +```bash +yarn test:unit +``` + +## 运行端对端测试 + +```bash +yarn test:e2e +``` + +## 自动生成 svg 组件 + +```bash +yarn run svg +``` + +## 自定义 Vue 配置 + +请看 [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/docs/02.前端/01.说明/04.目录结构.md b/docs/02.前端/01.说明/04.目录结构.md new file mode 100644 index 0000000..5dd3beb --- /dev/null +++ b/docs/02.前端/01.说明/04.目录结构.md @@ -0,0 +1,58 @@ +--- +title: 目录结构 +date: 2021-02-03 09:26:32 +permalink: /pages/0ec1c8/ +categories: + - 前端 + - 说明 +tags: + - +--- +# 目录结构 + +本项目已经为你生成了一个完整的开发框架,提供了涵盖后台开发的各类功能和坑位,下面是整个项目的目录结构。 + +```bash +├── mock # mock 服务器 与 模拟数据 +├── public # 静态资源 (会被直接复制) +│ │── favicon.ico # favicon图标 +│ │── manifest.json # PWA 配置文件 +│   └── index.html # html模板 +├── src # 源代码 +│   ├── api # ts业务代码 +│  │  ├── api # 所有请求 +│  │  ├── mixins # mixins 代码 +│  │  └── views # 页面实例代码 +│   ├── assets # 主题 字体等静态资源 (由 webpack 处理加载) +│  ├── common # 通用工具类及公用方法 +│   ├── components # 全局组件 +│   ├── directive # 全局指令 +│   ├── filters # 全局过滤函数 +│   ├── icons # svg 图标 +│   ├── lang # 国际化 +│   ├── layout # 全局布局 +│ ├── pwa # PWA service worker 相关的文件 +│   ├── router # 路由 +│   ├── store # 全局 vuex store +│   ├── styles # 全局样式 +│   ├── utils # 全局方法 +│   ├── views # 所有页面 +│   ├── App.vue # 入口页面 +│   ├── main.js # 入口文件 加载组件 初始化等 +│ ├── permission.ts # 权限管理 +│ ├── settings.ts # 设置文件 +│ └── shims.d.ts # 模块注入 +├── tests # 测试 +├── .circleci/ # 自动化 CI 配置 +├── .browserslistrc # browserslistrc 配置文件 (用于支持 Autoprefixer) +├── .editorconfig # 编辑相关配置 +├── .env.xxx # 环境变量配置 +├── .eslintrc.js # eslint 配置 +├── babel.config.js # babel-loader 配置 +├── cypress.json # e2e 测试配置 +├── jest.config.js # jest 单元测试配置 +├── package.json # package.json 依赖 +├── postcss.config.js # postcss 配置 +├── tsconfig.json # typescript 配置 +└── vue.config.js # vue-cli 配置 +``` diff --git a/docs/02.前端/02.配置/01.BaseURL配置.md b/docs/02.前端/02.配置/01.BaseURL配置.md new file mode 100644 index 0000000..cc527d1 --- /dev/null +++ b/docs/02.前端/02.配置/01.BaseURL配置.md @@ -0,0 +1,70 @@ +--- +title: BaseURL配置 +date: 2021-02-03 09:26:32 +permalink: /pages/b8a7ad/ +--- +# Base url设置 + +::: tip +当我们配置好后台接口后,往往后台都会跟一个路径。如 `http://127.0.0.1:8080/common` 中的 `/common`,我们需要根据生产环境,开发环境,阶段性环境来进行不同的配置 +::: + +## 配置开发环境 + +开发环境需要配置文件 `./.env.development` + +``` properties +# Base api,此处配置端口后面的Base URL +VUE_APP_BASE_API = '/common' + +# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, +# to control whether the babel-plugin-dynamic-import-node plugin is enabled. +# It only does one thing by converting all import() to require(). +# This configuration can significantly increase the speed of hot updates, +# when you have a large number of pages. +# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js +# 此处默认开启,如需了解更多请访问上方URL +VUE_CLI_BABEL_TRANSPILE_MODULES = true +``` + +## 配置阶段性环境 + +阶段性环境配置文件 `./env.staging` + +- 前端后台部署在一起 + +``` properties +# 设置成部署环境 +NODE_ENV = production + +# Base api,配置 +VUE_APP_BASE_API = './' +``` + +- 前后端分离部署 + +``` properties +# 设置成部署环境 +NODE_ENV = production + +# Base api,分离部署需要添加http路径 +VUE_APP_BASE_API = 'http://192.168.1.123:8080/common' +``` + +## 配置生产环境 + +阶段性环境配置文件 `./env.production` + +- 前端后台部署在一起 + +``` properties +# Base api +VUE_APP_BASE_API = './' +``` + +- 前后端分离部署 + +``` properties +# Base api,分离部署需要添加http路径 +VUE_APP_BASE_API = 'http://192.168.1.123:8080/common' +``` diff --git a/docs/02.前端/02.配置/02.vueconfig配置.md b/docs/02.前端/02.配置/02.vueconfig配置.md new file mode 100644 index 0000000..ccc002b --- /dev/null +++ b/docs/02.前端/02.配置/02.vueconfig配置.md @@ -0,0 +1,126 @@ +--- +title: vue.config.js文件配置 +date: 2021-02-03 09:26:32 +permalink: /pages/f410bd/ +--- +# vue.config.js + +::: tip +本部分提供适当所需注释,如有其它配置更改需要,请查询[Vue-Cli](https://cli.vuejs.org/zh/config) +::: + +``` js +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path') + +// If your port is set to 80, +// use administrator privileges to execute the command line. +// For example, on Mac: sudo npm run / sudo yarn +const devServerPort = 9527 // TODO: get this variable from setting.ts +const mockServerPort = 9528 // TODO: get this variable from setting.ts +const name = 'Vue Typescript Admin' // TODO: get this variable from setting.ts + +module.exports = { + // 部署应用包时的基本 URL。用法和 webpack 本身的 output.publicPath 一致, + // 但是 Vue CLI 在一些其他地方也需要用到这个值,所以请始终使用 publicPath 而不要直接修改 webpack 的 output.publicPath + publicPath: './', + // 默认生成的打包文件夹路径 + outputDir: 'dist', + // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。 资源放的目录 + assetsDir: './static', + // 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径 index的路劲和名字 + indexPath: './index.html', + // 是否在开发环境下通过 eslint-loader 在每次保存时 lint 代码 + lintOnSave: process.env.NODE_ENV === 'development', + // 是否需要生成环境的 source map + productionSourceMap: false, + // 设置开发时的端口及后端目录 + devServer: { + // 开发环境的启动端口 + port: devServerPort, + open: true, + overlay: { + warnings: false, + errors: true + }, + // progress: false, + proxy: { + '/common': { + // 开发环境的后台请求接口 + target: 'http://127.0.0.1:8081' , + changeOrigin: true, // needed for virtual hosted sites + ws: true, // proxy websockets + pathRewrite: { + // '^/common': '' + } + } + } + }, + // 向 PWA 插件传递选项 + pwa: { + name: name, + workboxPluginMode: 'InjectManifest', + workboxOptions: { + swSrc: path.resolve(__dirname, 'src/pwa/service-worker.js') + } + }, + // 这是一个不进行任何 schema 验证的对象,因此它可以用来传递任何第三方插件选项 + pluginOptions: { + 'style-resources-loader': { + preProcessor: 'scss', + patterns: [ + // 全局样式配置文件,加上自己的路径,不能使用(如下:alias)中配置的别名路径 + path.resolve(__dirname, 'src/styles/_variables.scss'), + path.resolve(__dirname, 'src/styles/_mixins.scss') + ] + } + }, + // 将代码公共部分集中打包,减少打包所需时间,是打包后的文件体积更小 + chainWebpack(config) { + // provide the app's title in webpack's name field, so that + // it can be accessed in index.html to inject the correct title. + config.set('name', name) + + // https://webpack.js.org/configuration/devtool/#development + // Change development env source map if you want. + // The default in vue-cli is 'eval-cheap-module-source-map'. + // config + // .when(process.env.NODE_ENV === 'development', + // config => config.devtool('eval-cheap-source-map') + // ) + + config + .when(process.env.NODE_ENV !== 'development', + config => { + config + .optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app + test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm + }, + commons: { + name: 'chunk-commons', + test: path.resolve(__dirname, 'src/components'), + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single') + } + ) + } +} + +``` + diff --git a/docs/02.前端/03.代码示例/01.Api示例.md b/docs/02.前端/03.代码示例/01.Api示例.md new file mode 100644 index 0000000..8fba201 --- /dev/null +++ b/docs/02.前端/03.代码示例/01.Api示例.md @@ -0,0 +1,54 @@ +--- +title: Api示例 +date: 2021-02-03 09:26:32 +permalink: /pages/5c0128/ +--- +# Api示例 + +::: tip +此处提供一个如何从后台调用接口的Api示例 +::: + +::: warning 注意 +多个功能模块推荐创建多个api文件,方便代码维护 +::: + +1. 进入 `src>api>api` 文件夹中,创建一个文件,这里我们以UserApi.ts为例,并写入类的基本信息 + ``` ts + class UserApi{ + + } + + export default UserApi + ``` +2. 引入封装好的 `axios` 并继承 + + ``` ts {1,3} + import BaseAxios from '@/common/http' + + class UserApi extends BaseAxios{ + + } + + export default UserApi + ``` +3. 根据后台接口文档写入需要请求的方法,注意请求方法 + + ``` ts {5,6,7,8,10,11,12,13} + import BaseAxios from '@/common/http' + + class UserApi extends BaseAxios{ + + // 根据用户id获取角色id + public getRoleByUserId(param: any): Promise> { + return this.axios.get('/ftUser/getRoleByUserId', param) + } + + // 给用户添加角色 + public addRole(param: any): Promise> { + return this.axios.post('/ftUser/addRole', param) + } + } + + export default UserApi + ``` diff --git a/docs/02.前端/03.代码示例/02.新增页面示例.md b/docs/02.前端/03.代码示例/02.新增页面示例.md new file mode 100644 index 0000000..71a8538 --- /dev/null +++ b/docs/02.前端/03.代码示例/02.新增页面示例.md @@ -0,0 +1,263 @@ +--- +title: 新增页面示例 +date: 2021-02-03 09:26:32 +permalink: /pages/5003ef/ +--- +# 新增页面示例 + +::: tip +此处提供使用通用模板的页面使用方法,若要添加常规页面请参考 [Vue](https://cn.vuejs.org/) 官网提供的说明。 +使用此方法前需要已经在配置表配置好需要调用的单表信息。 +::: + +1. 首先在 `src>api>views` 中新建文件夹 `user-example` ,进入文件夹新建文件 `user-example.ts`,写入以下信息 + + ``` ts + import { Component, Prop, Vue } from 'vue-property-decorator' + import { mixins } from 'vue-class-component' + import Common from '@/api/mixins/Common' + + @Component({ + name: 'userExample' + }) + export default class extends mixins(Common) { + } + ``` + +2. 添加默认配置信息,需要使用者根据注释及页面需求更改配置 + + ``` ts {4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61} + import { Component, Prop, Vue } from 'vue-property-decorator' + import { mixins } from 'vue-class-component' + import Common from '@/api/mixins/Common' + import StringUtils from '@/common/StringUtils' + + @Component({ + name: 'userExample' + }) + export default class extends mixins(Common) { + // 表格中 '操作'列的宽度 + private operationWidth = 200 + + async created() { + // 设置默认排序 + this.sort = 'UPDATE_TIME' + this.order = 'DESC' + // 设置表别名 + this.tableAlias = 'user' + // 获取表头及表单信息 + await this.acquireTableInfo() + // 获取基表信息格式化下拉框及表格数据 + await this.getBaseTableInfo() + // 获取列表信息 + this.acquireTable() + // 设置默认校验构造 + this.formRules = { + PASSWORD: [{ required: true, message: '密码不能为空', trigger: 'blur' }], + USERNAME: [{ required: true, message: '用户名不能为空', trigger: 'blur' }] + } + // 设置默认禁用字段 + this.disableField = ['CREATE_TIME', 'UPDATE_TIME'] + } + + // 编辑表格新增行 + addRow() { + const obj: any = { + index: this.editList.length, + edit: true + } + this.editList.push(obj) + } + + // 可编辑表格select + editTableSelectionChange(val: any) { + this.editTableSelectList = val + } + + // 表格校验规则 + tableRules(val: any) { + const { rows, rules, callback } = val + let message = '' + Object.getOwnPropertyNames(rules).forEach(function(key) { + if (key !== '__ob__') { + if (StringUtils.isEmpty(rows[key])) { + message += rules[key][0].message + message += ',' + } + } + }) + callback(message) + } + } + ``` + +3. 在路径 `src>views` 新建文件夹 `user-example` , 进入 `user-example` 文件夹,新建文件 `user-example.vue`,并写入以下信息,scss请根据页面设计自行更改,或者复制通用页面示例中的scss,这里先不再显示。 + + ``` vue + + + + + + + ``` + +4. 在 `src>router>index.ts` 中增加 `user-example.vue` 的路由指向。 + + ``` ts {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18} + export const asyncRoutes: RouteConfig[] = [ + ...., + { + path: '/userExample', + component: Layout, + children: [ + { + path: 'userExample', + component: () => import(/* webpackChunkName: "icons" */ '@/views/user-example/userExample.vue'), + name: 'userExample', + meta: { + title: 'userExample', + icon: 'example', + noCache: true + } + } + ] + }, + .... + ] + ``` + + 此时可以看到目录上多了刚刚我们添加的路由 + + ![router](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203162310.png) + +5. 此时目录还未显示中文,由于框架使用i18n插件,此时我们需要进入语言目录下进行设置,进入 `src>lang>zh.ts` 中设置显示中文。 + + ::: warning 注意 + 此处设置需要添加到router对象下,且key值需要与路由中的title设置一致 + ::: + + ``` ts {3} + export default { + route: { + userExample:'用户示例', + ... + } + } + ``` + + 然后我们就可以看到显示目录变成中文。 + + ![router2](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203162850.png) + +6. 然后,我们添加通用组件到ts文件及页面中。 + + 首先引入组件到ts文件中 + + ``` ts {5,8,9,10} + import { Component, Prop, Vue } from 'vue-property-decorator' + import { mixins } from 'vue-class-component' + import Common from '@/api/mixins/Common' + import StringUtils from '@/common/StringUtils' + import tableHeadCo from '@/components/CommonCo/tableHeadCo.vue' + @Component({ + name: 'commonExample', + components: { + tableHeadCo, + } + }) + export default class extends mixins(Common) { + // 表格中 '操作'列的宽度 + private operationWidth = 200 + + async created() { + // 设置默认排序 + this.sort = 'UPDATE_TIME' + this.order = 'DESC' + // 设置表别名 + this.tableAlias = 'user' + // 获取表头及表单信息 + await this.acquireTableInfo() + // 获取基表信息格式化下拉框及表格数据 + await this.getBaseTableInfo() + // 获取列表信息 + this.acquireTable() + // 设置默认校验构造 + this.formRules = { + PASSWORD: [{ required: true, message: '密码不能为空', trigger: 'blur' }], + USERNAME: [{ required: true, message: '用户名不能为空', trigger: 'blur' }] + } + // 设置默认禁用字段 + this.disableField = ['CREATE_TIME', 'UPDATE_TIME'] + } + + // 编辑表格新增行 + addRow() { + const obj: any = { + index: this.editList.length, + edit: true + } + this.editList.push(obj) + } + + // 可编辑表格select + editTableSelectionChange(val: any) { + this.editTableSelectList = val + } + + // 表格校验规则 + tableRules(val: any) { + const { rows, rules, callback } = val + let message = '' + Object.getOwnPropertyNames(rules).forEach(function(key) { + if (key !== '__ob__') { + if (StringUtils.isEmpty(rows[key])) { + message += rules[key][0].message + message += ',' + } + } + }) + callback(message) + } + } + + ``` + + 然后再vue实例中引入组件 + + ``` vue {3,4,5,6,7,8,9,10,11,12,13,14} + + + + + + ``` + + 然后我们刷新页面,就可以看到刚刚引入的组件了。 + + ![table](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203162357.png) + +7. 页面实例讲解完毕,最后,请按照使用者按照需要进行页面相关的开发或引入其他组件进行开发。 + diff --git a/docs/02.前端/04.组件/01.Table组件.md b/docs/02.前端/04.组件/01.Table组件.md new file mode 100644 index 0000000..fd82646 --- /dev/null +++ b/docs/02.前端/04.组件/01.Table组件.md @@ -0,0 +1,457 @@ +--- +title: 通用表格组件 +date: 2021-02-03 09:26:32 +permalink: /pages/1ab4ce/ +--- +# 通用表格组件 + +## 普通表格 + +普通表格,仅传入数据显示 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + + + +``` + +***js版本*** + +``` vue + + + +``` + +::: + +## 带复选框的表格 + +带复选框的表格,提供复选框回调函数,点击复选框时可打开控制台查看打印内容。 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + + + +``` + +***js版本*** + +``` vue + + + + +``` + +::: + +## 带插槽的表格 + +表格组件提供插槽功能,可以将插槽设置成按钮等其他功能,点击编辑按钮可以查看控制台打印内容。 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + + +``` + +***js版本*** + +``` vue + + + +``` + +::: + +## Table Attributes + +  +  + +| 参数 | 说明 | 类型 | 可选值 | 默认值 +| :---| :---- | :---- |:---- |:---- | +| check-box-visible | 是否显示复选框 | Boolean |-|false| +| table-heads | 表头 | Array |-|-| +| form-list | 需要显示的数据 | Array |-|-| +| loading | 表格加载动画 | Boolean |-|false| +| formatter | 格式化表格数据 | Function(row, column, cellValue, index) |-|-| +| cellAlign | 表格对齐方式 | String | left/center/right | center | +| slotVisible | 是否显示操作列 | Boolean |-|false| +| width | 操作列的宽度 | Number |-|200| + +## Table Events + +  +  + +| 事件名 | 说明 | 参数 | +| :---| :---- | :---- | +| handle-selection-change | 当用户手动勾选数据行的 Checkbox 时触发的事件 | row | +| handle-sort-change | 当表格的排序条件发生变化的时候会触发该事件 | column | + +## Table Slot + +  +  +| name | 说明 | +| :---| :---- | +| - | 用于放置在操作列上的按钮等元素,需要从scope.item中获取当前行的值 | + +## Table Header style + +  +  +| name | 说明 | +| :---| :---- | +| header-cell-style | 用于设置表头样式 | diff --git a/docs/02.前端/04.组件/02.Form组件.md b/docs/02.前端/04.组件/02.Form组件.md new file mode 100644 index 0000000..dd92e5a --- /dev/null +++ b/docs/02.前端/04.组件/02.Form组件.md @@ -0,0 +1,672 @@ +--- +title: 表单组件 +date: 2021-02-03 09:26:32 +permalink: /pages/65ed1c/ +--- +# 表单组件 + +## 基本表单 + +表单组件以模态框的形式展示表单内容。 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + + +``` + +***js版本*** + +``` vue + + +``` + +::: + +## 查询表单 + +表单组件查询功能。 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + +``` + +***js版本*** + +``` vue + + + +``` + +::: + +## Form Attributes + +  +  + +| 参数 | 说明 | 类型 | 可选值 | 默认值 +| :---| :---- | :---- |:---- |:---- | +| form-title | 表单标题 | String |-|-| +| form-visible | 展示/关闭表单 | Boolean |-|-| +| form-sub-botton | 表单提交按钮显示文字 | String |-|-| +| form-key | 表单key值 | Array |-|-| +| form-data | 表单数据(可为空) | Object |-|-| +| form-rules | 表单校验规则 | Object | - | - | +| form-type | 表单提交类型 1为新增,2为编辑 | Number |-|-| +| disable-field | 表单不可编辑的部分 | Array |-|-| + +## Form Events + +  +  + +| 事件名 | 说明 | 参数 | +| :---| :---- | :---- | +| form-submit | 当用户手动点击提交按钮时触发的事件 | - | +| cancel-form | 当用户手动点击取消按钮时触发的事件 | - | +| search-submit | 表单提交触发的事件 | - | +| reset-search-form | 重置搜索表单触发的事件 | - | diff --git a/docs/02.前端/04.组件/03.流程图组件.md b/docs/02.前端/04.组件/03.流程图组件.md new file mode 100644 index 0000000..80634c7 --- /dev/null +++ b/docs/02.前端/04.组件/03.流程图组件.md @@ -0,0 +1,274 @@ +--- +title: 流程图组件 +date: 2021-02-03 09:26:32 +permalink: /pages/49aa37/ +--- +# 流程图组件 + +## 基本流程图 + +表格组件提供获取Object内容作为流程图的标识和名称,该组件也可以不传入任何参数直接引用,例如``即可调用组件。 + +注意:标识必须以字母和下划线开头,不支持中文。 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + + +``` + +***js版本*** + +``` vue + + + + + +``` + + +::: + +## 仅能查看的流程图 + +该组件仅提供查看功能。 + +*** + + + +::: details 代码块 + +***ts版本*** + +``` vue + + + + + +``` + +***js版本*** + +``` vue + + + + + +``` + +::: + +## Bpmn Attributes + +  +  + +| 参数 | 说明 | 类型 | 可选值 | 默认值 +| :---| :---- | :---- |:---- |:---- | +| updateBpmnFlag | 是否为编辑流程图 | Boolean |-|false| +| updateDate | 数据(编辑时传入String) | String/Object |-|-| + + +## Bpmn Components Events + +  +  + +| 事件名 | 说明 | 参数 | +| :---| :---- | :---- | +| openBpmn | 当用户手动点击文件icon时触发的事件,打开bpmn格式文件 | file | +| downloadBpmn | 当用户手动点击下载icon时触发的事件,下载bpmn格式文件 | - | +| downloadSvg | 当用户手动点击图片icon时触发的事件,下载svg格式文件 | - | +| handleUpdateData | 当用户手动点击上传icon时触发的事件,上传已有流程图 | - | + + diff --git a/docs/02.前端/04.组件/04.流程图组件之编辑.md b/docs/02.前端/04.组件/04.流程图组件之编辑.md new file mode 100644 index 0000000..8ba1380 --- /dev/null +++ b/docs/02.前端/04.组件/04.流程图组件之编辑.md @@ -0,0 +1,185 @@ +--- +title: 流程图组件之编辑 +date: 2021-02-03 09:26:32 +permalink: /pages/514c8f/ +--- +# 流程图组件之编辑 + +## 获取已有流程图内容,并编辑 + +表格组件提供获取已有流程图并编辑的功能。 + +*** + + + +::: details 代码块 + +***ts版本*** +``` vue + + + + +``` + +***js版本*** + +``` vue + + + + + +``` +::: + +## Bpmn Attributes + +  +  + +| 参数 | 说明 | 类型 | 可选值 | 默认值 +| :---| :---- | :---- |:---- |:---- | +| updateBpmnFlag | 是否为编辑流程图 | Boolean |-|false| +| updateDate | 数据(编辑时传入String) | String/Object |-|-| + + +## Bpmn Components Events + +  +  + +| 事件名 | 说明 | 参数 | +| :---| :---- | :---- | +| openBpmn | 当用户手动点击文件icon时触发的事件,打开bpmn格式文件 | file | +| downloadBpmn | 当用户手动点击下载icon时触发的事件,下载bpmn格式文件 | - | +| downloadSvg | 当用户手动点击图片icon时触发的事件,下载svg格式文件 | - | +| handleUpdateData | 当用户手动点击上传icon时触发的事件,上传已有流程图 | - | diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..67a696c --- /dev/null +++ b/docs/index.md @@ -0,0 +1,49 @@ +--- +home: true +heroImage: https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/logo/img.png +heroText: HCFrame +tagline: 通用框架组件 +actionText: 立刻进入 → +actionLink: /pages/a3c9a2/ +bannerBg: auto +features: # 可选的 + - title: 单表查询/联合查询 + details: 前后台操作代码封装,只需配置配置表即可生成页面、表单及增删改查,批量新增,批量修改等功能。 + imgUrl: /img/web.png # 可选 + - title: Vue + Typescript 驱动 + details: 通过Vue + Typescript进行开发,前端通过面向对象等方式,减小冗余代码,组件化重复标签,进行快速开发。 + imgUrl: /img/ui.png + - title: SSO单点登录 + details: 项目基于SSO单点登录,实现SESSION共享 + imgUrl: /img/other.png +postList: none +--- + +### 前端安装 + +``` bash +# 安装 +yarn install + +# 运行 +yarn serve + +# 打包 +yarn build:prod +``` + +### 后台安装 +``` bash +# 首先需要在编译器安装Lombok插件 + +# 后台为SpringBoot项目,下载后导入编译器,等待maven下载依赖 + +# 下载完成后运行启动类CommonApplication.java + +# maven 打包 +mvn clean package +``` + +::: warning 注意 +请确保你的 jdk使用1.8 版本 ,nodejs 请使用最新的LTS版本。 +::: diff --git a/package.json b/package.json new file mode 100644 index 0000000..d05418e --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "theme-vdoing-blog", + "version": "1.0.0", + "scripts": { + "dev": "vuepress dev docs", + "build": "vuepress build docs", + "deploy": "bash deploy.sh", + "editFm": "node utils/editFrontmatter.js", + "baiduPush": "node utils/baiduPush.js https://xugaoyi.com && bash baiduPush.sh", + "publish": "cd theme-vdoing && npm publish && cd ../ && npm run updateTheme", + "updateTheme": "npm uninstall vuepress-theme-vdoing && rm -rf node_modules && npm i && npm i vuepress-theme-vdoing -D" + }, + "license": "MIT", + "devDependencies": { + "dayjs": "^1.9.7", + "inquirer": "^7.1.0", + "json2yaml": "^1.1.0", + "vuepress": "1.8.0", + "vuepress-plugin-baidu-autopush": "^1.0.1", + "vuepress-plugin-baidu-tongji": "^1.0.1", + "vuepress-plugin-demo-block": "^0.7.2", + "vuepress-plugin-one-click-copy": "^1.0.2", + "vuepress-plugin-thirdparty-search": "^1.0.2", + "vuepress-plugin-zooming": "^1.1.7", + "vuepress-theme-vdoing": "^1.8.1", + "yamljs": "^0.3.0" + }, + "dependencies": { + "async-validator": "1.11.5", + "bpmn-js": "^7.3.0", + "bpmn-js-properties-panel": "^0.35.0", + "camunda-bpmn-moddle": "^4.4.0", + "element-ui": "^2.12.0", + "sass": "^1.22.10", + "sass-loader": "^7.3.1", + "vuepress-plugin-comment": "^0.7.3" + } +} diff --git a/theme-vdoing/LICENSE b/theme-vdoing/LICENSE new file mode 100644 index 0000000..16d6732 --- /dev/null +++ b/theme-vdoing/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-present gaoyi(Evan) Xu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/theme-vdoing/README.md b/theme-vdoing/README.md new file mode 100644 index 0000000..9d54bed --- /dev/null +++ b/theme-vdoing/README.md @@ -0,0 +1,7 @@ +# vuepress-theme-vdoing + +vuepress-theme-vdoing for vuepress + +一个基于VuePress的 知识管理兼博客 主题。 + +[More](https://github.com/xugaoyi/vuepress-theme-vdoing#readme). diff --git a/theme-vdoing/components/AlgoliaSearchBox.vue b/theme-vdoing/components/AlgoliaSearchBox.vue new file mode 100644 index 0000000..f0c3d3d --- /dev/null +++ b/theme-vdoing/components/AlgoliaSearchBox.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/theme-vdoing/components/ArchivesPage.vue b/theme-vdoing/components/ArchivesPage.vue new file mode 100644 index 0000000..3bf4bc8 --- /dev/null +++ b/theme-vdoing/components/ArchivesPage.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/theme-vdoing/components/ArticleInfo.vue b/theme-vdoing/components/ArticleInfo.vue new file mode 100644 index 0000000..185bc17 --- /dev/null +++ b/theme-vdoing/components/ArticleInfo.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/theme-vdoing/components/BloggerBar.vue b/theme-vdoing/components/BloggerBar.vue new file mode 100644 index 0000000..c99ac16 --- /dev/null +++ b/theme-vdoing/components/BloggerBar.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/theme-vdoing/components/BodyBgImg.vue b/theme-vdoing/components/BodyBgImg.vue new file mode 100644 index 0000000..15186e6 --- /dev/null +++ b/theme-vdoing/components/BodyBgImg.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/theme-vdoing/components/Buttons.vue b/theme-vdoing/components/Buttons.vue new file mode 100644 index 0000000..36dbdc0 --- /dev/null +++ b/theme-vdoing/components/Buttons.vue @@ -0,0 +1,262 @@ + + + + + diff --git a/theme-vdoing/components/Catalogue.vue b/theme-vdoing/components/Catalogue.vue new file mode 100644 index 0000000..29bb1fb --- /dev/null +++ b/theme-vdoing/components/Catalogue.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/theme-vdoing/components/CategoriesBar.vue b/theme-vdoing/components/CategoriesBar.vue new file mode 100644 index 0000000..285fc75 --- /dev/null +++ b/theme-vdoing/components/CategoriesBar.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/theme-vdoing/components/CategoriesPage.vue b/theme-vdoing/components/CategoriesPage.vue new file mode 100644 index 0000000..2947a74 --- /dev/null +++ b/theme-vdoing/components/CategoriesPage.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/theme-vdoing/components/DropdownLink.vue b/theme-vdoing/components/DropdownLink.vue new file mode 100644 index 0000000..63fa8bc --- /dev/null +++ b/theme-vdoing/components/DropdownLink.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/theme-vdoing/components/DropdownTransition.vue b/theme-vdoing/components/DropdownTransition.vue new file mode 100644 index 0000000..8b1b4b1 --- /dev/null +++ b/theme-vdoing/components/DropdownTransition.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/theme-vdoing/components/Footer.vue b/theme-vdoing/components/Footer.vue new file mode 100644 index 0000000..9935fc1 --- /dev/null +++ b/theme-vdoing/components/Footer.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/theme-vdoing/components/Home.vue b/theme-vdoing/components/Home.vue new file mode 100644 index 0000000..4a0acf5 --- /dev/null +++ b/theme-vdoing/components/Home.vue @@ -0,0 +1,540 @@ + + + + + diff --git a/theme-vdoing/components/MainLayout.vue b/theme-vdoing/components/MainLayout.vue new file mode 100644 index 0000000..7cc938d --- /dev/null +++ b/theme-vdoing/components/MainLayout.vue @@ -0,0 +1,59 @@ + + diff --git a/theme-vdoing/components/NavLink.vue b/theme-vdoing/components/NavLink.vue new file mode 100644 index 0000000..93feac7 --- /dev/null +++ b/theme-vdoing/components/NavLink.vue @@ -0,0 +1,54 @@ + + + diff --git a/theme-vdoing/components/NavLinks.vue b/theme-vdoing/components/NavLinks.vue new file mode 100644 index 0000000..7be2ca3 --- /dev/null +++ b/theme-vdoing/components/NavLinks.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/theme-vdoing/components/Navbar.vue b/theme-vdoing/components/Navbar.vue new file mode 100644 index 0000000..795b057 --- /dev/null +++ b/theme-vdoing/components/Navbar.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/theme-vdoing/components/Page.vue b/theme-vdoing/components/Page.vue new file mode 100644 index 0000000..88c1f6e --- /dev/null +++ b/theme-vdoing/components/Page.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/theme-vdoing/components/PageEdit.vue b/theme-vdoing/components/PageEdit.vue new file mode 100644 index 0000000..8ddafab --- /dev/null +++ b/theme-vdoing/components/PageEdit.vue @@ -0,0 +1,168 @@ + + + diff --git a/theme-vdoing/components/PageNav.vue b/theme-vdoing/components/PageNav.vue new file mode 100644 index 0000000..b6b2a86 --- /dev/null +++ b/theme-vdoing/components/PageNav.vue @@ -0,0 +1,237 @@ + + + diff --git a/theme-vdoing/components/Pagination.vue b/theme-vdoing/components/Pagination.vue new file mode 100644 index 0000000..19573ad --- /dev/null +++ b/theme-vdoing/components/Pagination.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/theme-vdoing/components/PostList.vue b/theme-vdoing/components/PostList.vue new file mode 100644 index 0000000..f4e128e --- /dev/null +++ b/theme-vdoing/components/PostList.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/theme-vdoing/components/RightMenu.vue b/theme-vdoing/components/RightMenu.vue new file mode 100644 index 0000000..bcf6fea --- /dev/null +++ b/theme-vdoing/components/RightMenu.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/theme-vdoing/components/Sidebar.vue b/theme-vdoing/components/Sidebar.vue new file mode 100644 index 0000000..4071dd2 --- /dev/null +++ b/theme-vdoing/components/Sidebar.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/theme-vdoing/components/SidebarButton.vue b/theme-vdoing/components/SidebarButton.vue new file mode 100644 index 0000000..706adf1 --- /dev/null +++ b/theme-vdoing/components/SidebarButton.vue @@ -0,0 +1,64 @@ + + + diff --git a/theme-vdoing/components/SidebarGroup.vue b/theme-vdoing/components/SidebarGroup.vue new file mode 100644 index 0000000..3ef66e2 --- /dev/null +++ b/theme-vdoing/components/SidebarGroup.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/theme-vdoing/components/SidebarLink.vue b/theme-vdoing/components/SidebarLink.vue new file mode 100644 index 0000000..adbdad7 --- /dev/null +++ b/theme-vdoing/components/SidebarLink.vue @@ -0,0 +1,124 @@ + + + diff --git a/theme-vdoing/components/SidebarLinks.vue b/theme-vdoing/components/SidebarLinks.vue new file mode 100644 index 0000000..678c94b --- /dev/null +++ b/theme-vdoing/components/SidebarLinks.vue @@ -0,0 +1,93 @@ + + + diff --git a/theme-vdoing/components/TagsBar.vue b/theme-vdoing/components/TagsBar.vue new file mode 100644 index 0000000..9f4072d --- /dev/null +++ b/theme-vdoing/components/TagsBar.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/theme-vdoing/components/TagsPage.vue b/theme-vdoing/components/TagsPage.vue new file mode 100644 index 0000000..f3187fd --- /dev/null +++ b/theme-vdoing/components/TagsPage.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/theme-vdoing/components/UpdateArticle.vue b/theme-vdoing/components/UpdateArticle.vue new file mode 100644 index 0000000..e02780d --- /dev/null +++ b/theme-vdoing/components/UpdateArticle.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/theme-vdoing/enhanceApp.js b/theme-vdoing/enhanceApp.js new file mode 100644 index 0000000..6c4d11c --- /dev/null +++ b/theme-vdoing/enhanceApp.js @@ -0,0 +1,47 @@ +// 解决代码选项卡无法加载的问题 +import Vue from 'vue' +import CodeBlock from "@theme/global-components/CodeBlock.vue" +import CodeGroup from "@theme/global-components/CodeGroup.vue" +// Register the Vue global component +Vue.component(CodeBlock) +Vue.component(CodeGroup) + +// 注:此文件在浏览器端运行 +import postsMixin from '@theme/mixins/posts' +export default ({ + Vue, // VuePress 正在使用的 Vue 构造函数 + options, // 附加到根实例的一些选项 + router, // 当前应用的路由实例 + siteData // 站点元数据 +}) => { + // 修复ISO8601时间格式为普通时间格式,以及添加作者信息 + siteData.pages.map(item => { + const { frontmatter: { date, author } } = item + if (typeof date === 'string' && date.charAt(date.length - 1) === 'Z') { + item.frontmatter.date = repairUTCDate(date) + } + if (author) { + item.author = author + } else { + if (siteData.themeConfig.author) { + item.author = siteData.themeConfig.author + } + } + }) + + // 将对文章数据的处理结果混入Vue实例 + Vue.mixin(postsMixin) +} + + +// 修复ISO8601时间格式为普通时间格式 +function repairUTCDate (date) { + if (!(date instanceof Date)) { + date = new Date(date) + } + return `${date.getUTCFullYear()}-${zero(date.getUTCMonth() + 1)}-${zero(date.getUTCDate())} ${zero(date.getUTCHours())}:${zero(date.getUTCMinutes())}:${zero(date.getUTCSeconds())}`; +} +// 小于10补0 +function zero (d) { + return d.toString().padStart(2, '0') +} diff --git a/theme-vdoing/global-components/Badge.vue b/theme-vdoing/global-components/Badge.vue new file mode 100644 index 0000000..c89600d --- /dev/null +++ b/theme-vdoing/global-components/Badge.vue @@ -0,0 +1,44 @@ + + + diff --git a/theme-vdoing/global-components/CodeBlock.vue b/theme-vdoing/global-components/CodeBlock.vue new file mode 100644 index 0000000..50cf503 --- /dev/null +++ b/theme-vdoing/global-components/CodeBlock.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/theme-vdoing/global-components/CodeGroup.vue b/theme-vdoing/global-components/CodeGroup.vue new file mode 100644 index 0000000..871c295 --- /dev/null +++ b/theme-vdoing/global-components/CodeGroup.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/theme-vdoing/index.js b/theme-vdoing/index.js new file mode 100644 index 0000000..1a051b6 --- /dev/null +++ b/theme-vdoing/index.js @@ -0,0 +1,282 @@ +const path = require('path') +const setFrontmatter = require('./node_utils/setFrontmatter') +const getSidebarData = require('./node_utils/getSidebarData') +const { createPage, deletePage } = require('./node_utils/handlePage') +const chalk = require('chalk') // 命令行打印美化 +const yaml = require('js-yaml') // yaml转js +const log = console.log + +// md容器名 +const CARD_LIST = 'cardList' +const CARD_IMG_LIST = 'cardImgList' + +// siteConfig base 配置 +let base = '' + + +// Theme API. +module.exports = (options, ctx) => { + const { sourceDir, themeConfig, siteConfig } = ctx + + // base路径 + base = siteConfig.base || '' + + // 自动设置front matter + setFrontmatter(sourceDir, themeConfig) + + // 自动生成结构化侧边栏 + const sidebar = themeConfig.sidebar + if (sidebar === 'structuring' || sidebar && sidebar.mode === 'structuring') { + const collapsable = themeConfig.sidebar.collapsable === false ? false : true + const sidebarData = getSidebarData(sourceDir, collapsable) + if (sidebarData) { + themeConfig.sidebar = sidebarData + log(chalk.blue('tip ') + chalk.green('add sidebar data. 侧边栏数据成功生成。')) + } else { + themeConfig.sidebar = 'auto' + log(chalk.yellow('warning: fail to add sidebar data, switch to "auto". 未能添加侧边栏数据,将切换为“auto”。')) + } + } + + // 分类页 + if (themeConfig.category !== false) { + createPage(sourceDir, 'categoriesPage') + } else { + deletePage(sourceDir, 'categoriesPage') + } + + // 标签页 + if (themeConfig.tag !== false) { + createPage(sourceDir, 'tagsPage') + } else { + deletePage(sourceDir, 'tagsPage') + } + + // 归档页 + if (themeConfig.archive !== false) { + createPage(sourceDir, 'archivesPage') + } else { + deletePage(sourceDir, 'archivesPage') + } + + // resolve algolia + const isAlgoliaSearch = ( + themeConfig.algolia + || Object + .keys(siteConfig.locales && themeConfig.locales || {}) + .some(base => themeConfig.locales[base].algolia) + ) + + const enableSmoothScroll = themeConfig.smoothScroll === true + + return { + alias () { + return { + '@AlgoliaSearchBox': isAlgoliaSearch + ? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue') + : path.resolve(__dirname, 'noopModule.js') + } + }, + + plugins: [ + ['@vuepress/active-header-links', options.activeHeaderLinks], + '@vuepress/search', + '@vuepress/plugin-nprogress', + ['smooth-scroll', enableSmoothScroll], + + ['container', { + type: 'note', + defaultTitle: { + '/': '笔记', + '/en/': 'NOTE' + } + }], + ['container', { + type: 'tip', + defaultTitle: { + '/': '提示', + '/en/': 'TIP' + } + }], + ['container', { + type: 'warning', + defaultTitle: { + '/': '注意', + '/en/': 'WARNING' + } + }], + ['container', { + type: 'danger', + defaultTitle: { + '/': '警告', + '/en/': 'WARNING' + } + }], + ['container', { + type: 'right', + defaultTitle: '' + }], + ['container', { + type: 'theorem', + before: info => `

${info}

`, + after: '
' + }], + ['container', { + type: 'details', + before: info => `
${info ? `${info}` : ''}\n`, + after: () => '
\n', + defaultTitle: { + '/': '点击查看', + '/en/': 'DETAILS' + } + }], + + // 内容居中容器 + ['container', { + type: 'center', + before: info => `
`, + after: () => '
' + }], + + // 卡片列表 + [ + 'container', + { + type: CARD_LIST, + render: (tokens, idx) => { + // tokens 是整个md文件的虚拟dom结构数组 + // idx 是tokens中':::' 所在的索引,而且是当前指定type的':::',分别有开始和结束两次的idx + // if (tokens[idx].nesting === 1) { // 开头的 ':::' 标记 + // } else { // 结束的 ':::' 标记 + // } + // 注意:修改这里面的代码后需要在md文件保存一下才会重新执行渲染 + return renderCardList(tokens, idx, CARD_LIST) + } + }, + ], + + // 图文卡片列表 + [ + 'container', + { + type: CARD_IMG_LIST, + render: (tokens, idx) => { + return renderCardList(tokens, idx, CARD_IMG_LIST) + } + }, + ], + + + ] + } +} + + +// 渲染md容器的卡片列表 +function renderCardList (tokens, idx, type) { + const END_TYPE = `container_${type}_close`, + _tokens$idx = tokens[idx], + nesting = _tokens$idx.nesting, + info = _tokens$idx.info; + + if (nesting === 1) { // 渲染开头的 ':::' 标记 + let yamlStr = ''; + + for (let i = idx; i < tokens.length; i++) { + let _tokens$i = tokens[i], + type = _tokens$i.type, + content = _tokens$i.content, + _info = _tokens$i.info; + if (type === END_TYPE) break; // 遇到结束的 ':::' 时 + if (!content) continue; + if (type === 'fence' && _info === 'yaml') { // 是代码块类型,并且是yaml代码 + yamlStr = content + } + } + + if (yamlStr) { // 正确解析出yaml字符串后 + const dataObj = yaml.safeLoad(yamlStr) // 将yaml字符串解析成js对象 + let dataList = [] + + if (dataObj) { // 正确解析出数据对象 + dataList = Array.isArray(dataObj) ? dataObj : dataObj.list + } + + if (dataList && dataList.length) { // 有列表数据 + + // 每行显示几个 + let row = Number(info.split(' ').pop()) + if (!row || row > 4 || row < 1) { + row = 3 // 默认 3 + } + + let listDOM = '' + if (type === CARD_LIST) { // 普通卡片列表 + listDOM = getCardListDOM(dataList, row) + } else if (type === CARD_IMG_LIST) { // 卡片图片列表 + listDOM = getCardImgListDOM(dataList, row) + } + + return `
${listDOM}
` + } + } + } else { // 渲染':::' 结尾 + return '
' + } +} + + +// 将数据解析成DOM结构 - 普通卡片列表 +function getCardListDOM (dataList, row) { + let listDOM = '' + dataList.forEach(item => { + listDOM += ` + <${item.link ? 'a href="' + item.link + '" target="_blank"' : 'span'} class="card-item ${row ? 'row-' + row : ''}" + style="${item.bgColor ? 'background-color:' + item.bgColor + ';--randomColor:' + item.bgColor + ';' : '--randomColor: var(--bodyBg);'}${item.textColor ? 'color:' + item.textColor + ';' : ''}" + > + ${item.avatar ? '' : ''} +
+

${item.name}

+

${item.desc}

+
+ + ` + }) + return listDOM +} + + +// 将数据解析成DOM结构 - 图文卡片列表 +function getCardImgListDOM (dataList, row) { + let listDOM = '' + dataList.forEach(item => { + listDOM += ` + + ` + }) + return listDOM +} + +// 添加base路径 +function withBase (path) { + if (base && path.charAt(0) === '/') { + return base + path.slice(1); + } else { + return path; + } +} diff --git a/theme-vdoing/layouts/404.vue b/theme-vdoing/layouts/404.vue new file mode 100644 index 0000000..c8b7672 --- /dev/null +++ b/theme-vdoing/layouts/404.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/theme-vdoing/layouts/Layout.vue b/theme-vdoing/layouts/Layout.vue new file mode 100644 index 0000000..e94e9c1 --- /dev/null +++ b/theme-vdoing/layouts/Layout.vue @@ -0,0 +1,382 @@ + + + + + diff --git a/theme-vdoing/mixins/posts.js b/theme-vdoing/mixins/posts.js new file mode 100644 index 0000000..2dec307 --- /dev/null +++ b/theme-vdoing/mixins/posts.js @@ -0,0 +1,21 @@ +import { filterPosts, sortPosts, sortPostsByDate, groupPosts, categoriesAndTags } from '../util/postData' + +export default { + computed: { + $filterPosts () { // 过滤非文章页和首页的文章数据 + return filterPosts(this.$site.pages) + }, + $sortPosts () { // 按置顶和时间排序的文章数据 + return sortPosts(this.$filterPosts) + }, + $sortPostsByDate () { // 仅按时间排序的文章数据 + return sortPostsByDate(this.$filterPosts) + }, + $groupPosts () { // 按分类和标签分组的文章数据 + return groupPosts(this.$sortPosts) + }, + $categoriesAndTags () { // 所有分类和标签数据 + return categoriesAndTags(this.$groupPosts) + } + } +} diff --git a/theme-vdoing/mixins/titleBadge.js b/theme-vdoing/mixins/titleBadge.js new file mode 100644 index 0000000..224f0c0 --- /dev/null +++ b/theme-vdoing/mixins/titleBadge.js @@ -0,0 +1,28 @@ +export default { + data () { + return { + badges: [ + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABGpJREFUSA3tVVtoXFUU3fvOI53UlmCaKIFmwEhsE7QK0ipFEdHEKpXaZGrp15SINsXUWvBDpBgQRKi0+KKoFeJHfZA+ED9KKoIU2gYD9UejTW4rVIzm0VSTziPzuNu1z507dibTTjL4U/DAzLn3nL3X2o91ziX6f9wMFdh6Jvbm9nNSV0msViVO6tN1Rm7NMu2OpeJ9lWBUTDxrJbYTS0hInuwciu9eLHlFxCLCZEk3MegsJmZ5K/JD6t7FkFdEvGUo1g7qJoG3MHImqRIn8/nzY1K9UPKKiJmtnUqHVE3Gbuay6vJE/N2FEmuxFjW2nUuE0yQXRRxLiTUAzs36zhZvOXJPdX850EVnnLZkB8prodQoM5JGj7Xk2mvC7JB8tG04Ef5PiXtG0UtxupRQSfTnBoCy554x18yJHI6I+G5Eru4LHmPJZEQsrvPUbMiA8G/WgMK7w7I+ez7++o2ANfbrjvaOl1tFMs+htG3IrZH9/hDX1Pr8Tc0UvH8tcX29KzAgIGcEkINyW5BF9x891hw6VYqgJHEk0huccS7vh3C6gTiODL+26huuBtbct8eZnqLML8PkxGYpuPZBqtqwkSjgc4mB5gbgig5i+y0UDK35LMxXisn9xQtK+nd26gTIHsHe/oblK/b29fUmN/8Y+9jAQrnBp56m1LcDlDp9irKTExSKduXJVWSqdBMA08pEJnEIOB3FPPMybu/oeV8zFeYN3xx576Q6RH+VmplE4ncQV5v+5rzSoyOU7PuEAg8g803PwBJ0CExno/jcMbN8tONYeOmHiuUNryvm3fRUy4tMPVLdAGkUhNWuggGrJcXPv+ouCjz0MKUHz1J2/E8IC9nqTabcxgaBYM0hPhD5Y65FsbxRQKxCQrDjDctW7PUM3HuZunFyifSAqEfuzCp48Il24luWUWZoyJCaPR82jE0+kFA643wRFVni4RYSq3ohJO2pZ7B5dO4xkDWbEpossJPLSrPjYID8rS2UHTlvyNxqIGsg674XJJ7vnh5L7PNwC4hh2sjCI96mzszOTpxLF0T7l88Yz7lAuK6OnL8gXLOnTvpzSb22YG8W7us3jSebFHeeqnXRG1vt+MoUM84LQIBmMsCTAcOauTh0T0l0neQK7m2bLMt2mGxU3HYssS0J2cdv5wljlPsrIuZLAG/2DOZIXgCYT8uMGZN+e2kSirfxZOPCsC0f24nTZzspnVn9VePS1Z5vubmAGGXG8ZFno9Hel0yfA5ZPhF7Dh972BQJ2qCpgH67lmWtBYbvk6sz02wjky2vXyz0XErP/kFB619js1BtwfOV4OPRqOQBjy3Qbk18vigUPPSD5ceHnwck7W9bhAqZdd7SuG7w4/P2F/GaJh8c7e9qgow+Q7cGBo+98WsLkuktFqiZabtXuQTu/Y5ETbR0v7tNSFnvrmu6pjdoan2KjMu8q/Hmj1EfCO2ZGfEIbIXKUlw8qaX9/b2oeSJmFksSeT/Fn0V3nSypChh4Gjh74ybO9aeZ/AN2dwciu2/MhAAAAAElFTkSuQmCC', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABH1JREFUSA3tVl1oHFUUPmdmd2ltklqbpJDiNnXFmgbFktho7YMPNiJSSZM0+CAYSkUELVhM6YuwIPpgoOKDqOBDC0XE2CQoNtQXBUFTTcCi+Wlh1V2TQExsUzcltd3M9Tt3ZjZzZ2fT+OJTL8yeM+eee757fmeJbq//KQL8X3DUSFOcfr7cRsRtxNQMWueeVzOkaITIGqQHNg5y8+jNW9ldM7A6nTpAjuolUikAwq7CE3WcM2RRDz+XGVgN3FptU/aUSlvq9Pa3iZ1+sgAqJyyAFqkipd9dqiwHF3P65YycLWc/6sqGrvoEoIp6DOFaX5h6+dnfjkWprwqsPk0dUGq5vySwDImC10KxFHgGL1SWoc92O3eVht09qdXNH11I2SsTsJYqMWzihqGMi+A+Garf3BAuuLI5oGlULyNfyB/HYNujwktOfRrMr5t77NmevqaUopx0grnKAyvVpmwUDB4x6FPXuGvYLTDwWsejwgtgkYKPqRJg8SV6xaiZ3ZTppGneS4yfH5/66fZSDHv+QZci/+h5c5UHtpy67JUqGppM0sh0Nc1dW6/N1W5Yoqat8/TU/VnadmdeW2PLLSyh0cvxBs3KbqTmwYPpxN4do/mzE8nEpvX/UMu2Wbp74zUAK5q6WkHns7V0eWkdPbPzd3rxkTGybadYySumVzhcaJFbs5UrEkQ/+CK8gF5dnh/6ciIZ73gwQ927L1IitoxKLXYP3SjYdOrHHfTZhRRlFyrorafPk20B3HPD1y2G3qKZME5Jcf3t/HUC13/8tSd++vqFveMUTwAUxSUFI1QekR1+bIze3D9MF2aq6cPvG72CgnldWCFqyRw3lwH8ZMerjTD9ElRO7Gv44wNpC90aASqGfVlz/Rx17srQ57/UU26hkhQqUB7dBR71WmzQhHUnblGmVOEw0jhbV1n9OlXUDCIRGaNV5Jp43N516fN7JmnTHdfp7Hgy0luO4aMhtkLL8Bi3bUWYvzh5Mn1dTxrL6QmGuRhGL/TiTTxRoEdTszSaq9GR0NGA3KdkOz3hqSV3MIDhQ5IVX/Ivx3umBti2es2h4eZby7x8br1rkf7Mo90AqC8aQ3sJeNzqFRu+vSANAQe3PL7l0HGOAdwDCeZYvNKeoZp1Qfs6Aipndh86HmFRi0LAnEO47wsqM6cdfjh3jBPUzhZy7nvlUfFsamED1VQt6aISHVymXZ/B2aCtIG8AI8xfobj2d3en1wWVhOeHELKmLQ1s211s88comkv4UCwWyF787mJdYXtNfhKAXVqnKTq8QZvGAGGOfaTo5pGZ/PwbUCr5+DPr/1J92JNHr9aOl/F3iI5+O1nfybsGxoimvZ3ViWSluDITw3P37mypheDIPY0tw7+O/5ApbkYw+zpfaUVu32Pi98+defdUhEpZkRFq0aqyNh9FuL9hpYbEm6iwi0z2REd09ZmyENEbuhjDWzKvZXTqKYaBIr3tt5kuPtQBZFvEUwHt60vfCNu41XsksH9Ij1BMMz1Y0OOunHNShFIP5868g5zeXmuLwL9T4b6Q2+KejgAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABKFJREFUSA3tVl1oFVcQnrMbrak3QUgkya1akpJYcrUtIqW1JvFBE9LiQ5v6JmJpolbMg32rVrhgoYK0QiMY6i9Y6EMaW5D+xFJaTYItIuK2Kr3+BJNwkxBj05sQY3b3nM6cs2dv9t7NT/vQJw/sndk5M/PNzJkzewGerP+pAmy+ON8lLzUJgA8ZYxYIYZmGYRnctDaWvJJAmTtfP1pvXsBCCPP8QFcCaRkZYACgDZFO4stNIcBCajEOlmmC9XpJ9bAGCaPaPmzPl32dvLSVu3BWCTQs0XQQ6g0DYgwLIoAZbBCdW/i+781o1VVlm/410mw4h06Y7bIPHNyWDyL4FHkX03Q8SrzNhZTZriieckWt7cL6MM85YcLpsi/7O9/iXFT6MswI0DmmpkSaJ0qLxFIm3+i1THHB3zmBH3PYx9CcykcLOeQVVa7QtdxTgQgEleX2AjHYfwA+2ddV77ruGoJUbhGDI09YSNXyMpUt5ylOzxgbUmtOp7NmbNt8v3arjTBfYELmLUV+M+nSawNNAUqpT3ClJWg5I3BLT+cGW/DXNGCa6tx1aakCGEigArTn4TDIPdrXXYKCZNrHLMCOEPvHBlLQ99s9eHB7EB6NTki73CVPQ2F5MSx/uRQixfmq7rK0wYD8w8E905bnPDfwoWs/rfv93NWN/ZfvwsLIU7A09gxECyISeGJkHAau98L97tuw7NXnoPyNF8FcYGLGKsOs0mN3OEyec9esGW/ZEl945dTP34wlR2FZVQWU1q0Cw8Tr7p+hgLLNL0FPxx/Q35mA8aEUrH6nCgwEl0tn7wUiZYJnNRh6DK4UH/k0lfyrsBKdPVv/AriGIQcEDQZ65LBAGe2Rzui9Ybjz7XUppz1/uKBbyVPGkN3ZAeC6hr0x7Nr38N5+EqkoOm17xpoqR9ohQF55ERSvr4Dkr3chNfC3DMzGJlNBElW8w9nsGQvhNGIzDkXzCg8cLK951xHsFBlTJspJNi3ZFIMF2AeDV3q8DNOB+YHi6QTrChDIWDBRi5U5f+ZMfJLu3ccrqxtdxk4SKH336LFxSmkqefwU5T8fhdSdQf9IVKD6aNiwI/hnmcAZ91isYMJIaCUCx9W098+LgruikeTqzqqxKPUwqJyCPJiyemVVZBOijDGjD38Os0jOiSPL1z3SPjXNANbiNPXAdzTfukjjuknNBbyz3nwgTd3AVFqUJ5hpHlq9MveLnWwttUfoygBmvVjuikxND3znrhsELnZk7k+OjIGxeNEkomyLVta0xxn+HZhjBc4YZ/AFjHjz9u3xRZl2BN4aq9nFwWh16IrQ1aHHEd3j1+4/dB9OtH4e29A2H1DyHQRmOSfQZ1Fy7MHBTGB6J/Djq6p3OxyO2cB+4Car7v/o3GXgfAkj23+x9ID1Teoamo/SXcbvSf2PX7Vc8DdCmE1vN9di+32P9/5YR3vLnhCVGUWBjEkr3yh4H8v9CzmsbdhzOKzsJKM90iFdaTMjRPhGVsakRvOaRidljo6H6G7j+ctrJpsP+4COhDIl0La2+FS4+5mlocBaXY5QnGZysIBYoeSsl5qQzrSj/cgNrfuEzlWBfwA+EjrZyWUvpAAAAABJRU5ErkJggg==' + ], + currentBadge: '' + } + }, + created () { + if (this.$themeConfig.titleBadgeIcons) { + this.badges = this.$themeConfig.titleBadgeIcons + } + this.currentBadge = this.getBadge() + }, + watch: { + '$route.path' () { + this.currentBadge = this.getBadge() + } + }, + methods: { + getBadge () { + return this.badges[Math.floor(Math.random() * this.badges.length)] + } + } +} diff --git a/theme-vdoing/node_utils/getSidebarData.js b/theme-vdoing/node_utils/getSidebarData.js new file mode 100644 index 0000000..c40690e --- /dev/null +++ b/theme-vdoing/node_utils/getSidebarData.js @@ -0,0 +1,158 @@ +const fs = require('fs'); // 文件模块 +const path = require('path'); // 路径模块 +const chalk = require('chalk') // 命令行打印美化 +const matter = require('gray-matter'); // front matter解析器 +const log = console.log + +let catalogueData = {}; // 目录页数据 + +/** + * 生成侧边栏数据 + * @param {String} sourceDir .md文件所在源目录(一般是docs目录) + * @param {Boolean} collapsable 是否可折叠 + */ +function createSidebarData (sourceDir, collapsable) { + const sidebarData = {}; + const tocs = readTocs(sourceDir); + tocs.forEach(toc => { // toc是每个目录的绝对路径 + + const tocArr = toc.split('\\') + if (tocArr[tocArr.length - 1] === '_posts') { // 碎片化文章 + + // 注释说明:碎片化文章不需要生成结构化侧边栏 2020.05.01 + // const sidebarArr = mapTocToPostSidebar(toc); + // sidebarData[`/${path.basename(toc)}/`] = sidebarArr + + } else { + const sidebarObj = mapTocToSidebar(toc, collapsable); + if (!sidebarObj.sidebar.length) { + log(chalk.yellow(`warning: 该目录 "${toc}" 内部没有任何文件或文件序号出错,将忽略生成对应侧边栏`)) + return; + } + sidebarData[`/${path.basename(toc)}/`] = sidebarObj.sidebar + sidebarData.catalogue = sidebarObj.catalogueData + } + }) + + return sidebarData +} + +module.exports = createSidebarData; + + +/** + * 读取指定目录下的文件绝对路径 + * @param {String} root 指定的目录 +*/ +function readTocs (root) { + const result = []; + const files = fs.readdirSync(root); // 读取目录,返回数组,成员是root底下所有的目录名 (包含文件夹和文件) + files.forEach(name => { + const file = path.resolve(root, name); // 将路径或路径片段的序列解析为绝对路径 + if (fs.statSync(file).isDirectory() && name !== '.vuepress' && name !== '@pages') { // 是否为文件夹目录,并排除.vuepress文件夹 + result.push(file); + } + }) + return result; +} + + +/** + * 将碎片化文章目录(_posts)映射为对应的侧边栏配置数据 + * @param {String} root + */ +function mapTocToPostSidebar (root) { + let postSidebar = [] // 碎片化文章数据 + const files = fs.readdirSync(root); // 读取目录(文件和文件夹),返回数组 + + files.forEach(filename => { + const file = path.resolve(root, filename); // 方法:将路径或路径片段的序列解析为绝对路径 + const stat = fs.statSync(file); // 文件信息 + + const fileNameArr = filename.split('.'); + if (fileNameArr.length > 2) { + log(chalk.yellow(`warning: 该文件 "${file}" 在_posts文件夹中,不应有序号,且文件名中间不应有'.'`)) + return + } + if (stat.isDirectory()) { // 是文件夹目录 + // log(chalk.yellow(`warning: 该目录 "${file}" 内文件无法生成侧边栏,_posts文件夹里面不能有二级目录。`)) + return + } + + let [title, type] = filename.split('.'); + if (type !== 'md') { + log(chalk.yellow(`warning: 该文件 "${file}" 非.md格式文件,不支持该文件类型`)) + return; + } + + const contentStr = fs.readFileSync(file, 'utf8') // 读取md文件内容,返回字符串 + const { data } = matter(contentStr) // 解析出front matter数据 + const permalink = data.permalink || '' + if (data.title) { + title = data.title + } + postSidebar.push([filename, title, permalink]); // [<路径>, <标题>, <永久链接>] + }) + + return postSidebar +} + + +/** + * 将目录映射为对应的侧边栏配置数据 + * @param {String} root + * @param {Boolean} collapsable + * @param {String} prefix + */ + +function mapTocToSidebar (root, collapsable, prefix = '') { + let sidebar = []; // 结构化文章侧边栏数据 + const files = fs.readdirSync(root); // 读取目录(文件和文件夹),返回数组 + + files.forEach(filename => { + const file = path.resolve(root, filename); // 方法:将路径或路径片段的序列解析为绝对路径 + const stat = fs.statSync(file); // 文件信息 + let [order, title, type] = filename.split('.'); + order = parseInt(order, 10); + if (isNaN(order) || order < 0) { + log(chalk.yellow(`warning: 该文件 "${file}" 序号出错,请填写正确的序号`)) + return; + } + if (sidebar[order]) { // 判断序号是否已经存在 + log(chalk.yellow(`warning: 该文件 "${file}" 的序号在同一级别中重复出现,将会被覆盖`)) + } + if (stat.isDirectory()) { // 是文件夹目录 + sidebar[order] = { + title, + collapsable, // 是否可折叠,默认true + children: mapTocToSidebar(file, collapsable, prefix + filename + '/').sidebar // 子栏路径添加前缀 + } + } else { // 是文件 + if (type !== 'md') { + log(chalk.yellow(`warning: 该文件 "${file}" 非.md格式文件,不支持该文件类型`)) + return; + } + const contentStr = fs.readFileSync(file, 'utf8') // 读取md文件内容,返回字符串 + const { data } = matter(contentStr) // 解析出front matter数据 + const permalink = data.permalink || '' + + // 目录页对应的永久链接,用于给面包屑提供链接 + const { pageComponent } = data + if (pageComponent && pageComponent.name === "Catalogue") { + catalogueData[title] = permalink + } + + if (data.title) { + title = data.title + } + sidebar[order] = [prefix + filename, title, permalink]; // [<路径>, <标题>, <永久链接>] + + } + }) + + sidebar = sidebar.filter(item => item !== null && item !== undefined); + return { + sidebar, + catalogueData + }; +} diff --git a/theme-vdoing/node_utils/handlePage.js b/theme-vdoing/node_utils/handlePage.js new file mode 100644 index 0000000..5268653 --- /dev/null +++ b/theme-vdoing/node_utils/handlePage.js @@ -0,0 +1,82 @@ +// 生成或删除页面(分类页、标签页、归档页...) + +const fs = require('fs'); // 文件模块 +const path = require('path'); // 路径模块 +const chalk = require('chalk') // 命令行打印美化 +const { type } = require('./modules/fn'); +const log = console.log + +function createPage (sourceDir, page) { + const dirPath = path.join(sourceDir, '@pages') // 生成的文件夹路径 + + // 文件夹不存在时 + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath) // 创建文件夹 + } + + const pagePath = path.join(dirPath, `${page}.md`) // 生成的文件路径 + + // 文件已经存在时跳出 + if (fs.existsSync(pagePath)) { + return + } + + // 注意:反引号字符串的格式会映射到文件 + let content = '' + if (page.indexOf('categories') > -1) { + content = `--- +categoriesPage: true +title: 分类 +permalink: /categories/ +article: false +---` + } else if (page.indexOf('tags') > -1) { + content = `--- +tagsPage: true +title: 标签 +permalink: /tags/ +article: false +---` + } else if (page.indexOf('archives') > -1) { + content = `--- +archivesPage: true +title: 归档 +permalink: /archives/ +article: false +---` + } + + if (content) { + fs.writeFileSync(pagePath, content) + log(chalk.blue('tip ') + chalk.green(`create page(生成页面): ${pagePath}`)) + } +} + +// 删除页面文件 +function deletePage (sourceDir, page) { + const dirPath = path.join(sourceDir, '@pages') // 文件夹路径 + const pagePath = path.join(dirPath, `${page}.md`) // 文件路径 + + // 文件是否存在 + if (fs.existsSync(pagePath)) { + fs.unlinkSync(pagePath) + log(chalk.blue('tip ') + chalk.green(`delete page(删除页面): ${pagePath}`)) + } + deleteDir(dirPath) +} + +// 删除文件夹 +function deleteDir (dirPath) { + if (fs.existsSync(dirPath)) { + const files = fs.readdirSync(dirPath) + if (type(files) === 'array' && files.length === 0) { + fs.rmdirSync(dirPath) + log(chalk.blue('tip ') + chalk.green(`delete dir(删除目录): ${dirPath}`)) + } + } +} + +module.exports = { + createPage, + deletePage +} diff --git a/theme-vdoing/node_utils/modules/fn.js b/theme-vdoing/node_utils/modules/fn.js new file mode 100644 index 0000000..1528498 --- /dev/null +++ b/theme-vdoing/node_utils/modules/fn.js @@ -0,0 +1,21 @@ +// 类型判断 +exports.type = function (o) { + var s = Object.prototype.toString.call(o) + return s.match(/\[object (.*?)\]/)[1].toLowerCase() +} + +// 修复date时区格式的问题 +exports.repairDate = function (date) { + date = new Date(date); + return `${date.getUTCFullYear()}-${zero(date.getUTCMonth() + 1)}-${zero(date.getUTCDate())} ${zero(date.getUTCHours())}:${zero(date.getUTCMinutes())}:${zero(date.getUTCSeconds())}`; +} + +// 日期的格式 +exports.dateFormat = function (date) { + return `${date.getFullYear()}-${zero(date.getMonth() + 1)}-${zero(date.getDate())} ${zero(date.getHours())}:${zero(date.getMinutes())}:${zero(date.getSeconds())}` +} + +// 小于10补0 +function zero (d) { + return d.toString().padStart(2, '0') +} diff --git a/theme-vdoing/node_utils/modules/readFileList.js b/theme-vdoing/node_utils/modules/readFileList.js new file mode 100644 index 0000000..7472296 --- /dev/null +++ b/theme-vdoing/node_utils/modules/readFileList.js @@ -0,0 +1,43 @@ +/** + * 读取所有md文件数据 + */ +const fs = require('fs'); // 文件模块 +const path = require('path'); // 路径模块 +const chalk = require('chalk') // 命令行打印美化 +const log = console.log + +function readFileList (dir, filesList = []) { + const files = fs.readdirSync(dir); + files.forEach((item, index) => { + let filePath = path.join(dir, item); + const stat = fs.statSync(filePath); + if (stat.isDirectory() && item !== '.vuepress' && item !== '@pages') { + readFileList(path.join(dir, item), filesList); //递归读取文件 + } else { + if (path.basename(dir) !== 'docs') { // 过滤docs目录级下的文件 + + const fileNameArr = path.basename(filePath).split('.') + let name = null, type = null; + if (fileNameArr.length === 2) { // 没有序号的文件 + name = fileNameArr[0] + type = fileNameArr[1] + } else if (fileNameArr.length === 3) { // 有序号的文件 + name = fileNameArr[1] + type = fileNameArr[2] + } else { // 超过两个‘.’的 + log(chalk.yellow(`warning: 该文件 "${filePath}" 没有按照约定命名,将忽略生成相应数据。`)) + return + } + if (type === 'md') { // 过滤非md文件 + filesList.push({ + name, + filePath + }); + } + } + } + }); + return filesList; +} + +module.exports = readFileList; diff --git a/theme-vdoing/node_utils/setFrontmatter.js b/theme-vdoing/node_utils/setFrontmatter.js new file mode 100644 index 0000000..36639bb --- /dev/null +++ b/theme-vdoing/node_utils/setFrontmatter.js @@ -0,0 +1,153 @@ +const fs = require('fs'); // 文件模块 +const matter = require('gray-matter'); // FrontMatter解析器 https://github.com/jonschlinkert/gray-matter +const jsonToYaml = require('json2yaml') +const chalk = require('chalk') // 命令行打印美化 +// const arg = process.argv.splice(2)[0]; // 获取命令行传入的参数 +const readFileList = require('./modules/readFileList'); +const { type, repairDate, dateFormat } = require('./modules/fn'); +const log = console.log +const path = require('path'); + +const PREFIX = '/pages/' + + +/** + * 给.md文件设置frontmatter(标题、日期、永久链接等数据) + */ +function setFrontmatter (sourceDir, themeConfig) { + + const isCategory = themeConfig.category + const isTag = themeConfig.tag + const categoryText = themeConfig.categoryText || '随笔' + + const files = readFileList(sourceDir); // 读取所有md文件数据 + + files.forEach(file => { + let dataStr = fs.readFileSync(file.filePath, 'utf8');// 读取每个md文件内容 + + // fileMatterObj => {content:'剔除frontmatter后的文件内容字符串', data:{}, ...} + const fileMatterObj = matter(dataStr); + + if (Object.keys(fileMatterObj.data).length === 0) { // 未定义FrontMatter数据 + const stat = fs.statSync(file.filePath); + const dateStr = dateFormat( + getBirthtime(stat) + ); // 文件的创建时间 + const categories = getCategories( + file, + categoryText + ); + + let cateLabelStr = ''; + categories.forEach(item => { + cateLabelStr += '\r\n - ' + item + }); + + let cateStr = ''; + if (!(isCategory === false)) { + cateStr = '\r\ncategories:' + cateLabelStr + }; + + // 注意下面这些反引号字符串的格式会映射到文件 + // const cateStr = isCategory === false ? '' : ` + // categories: + // - ${categories[0]}${categories[1] ? '\r\n - ' + categories[1] : ''}`; + + const tagsStr = isTag === false ? '' : ` +tags: + - `; + + const fmData = `--- +title: ${file.name} +date: ${dateStr} +permalink: ${getPermalink()}${file.filePath.indexOf('_posts') > -1 ? '\r\nsidebar: auto' : ''}${cateStr}${tagsStr} +---`; + + fs.writeFileSync(file.filePath, `${fmData}\r\n${fileMatterObj.content}`); // 写入 + log(chalk.blue('tip ') + chalk.green(`write frontmatter(写入frontmatter):${file.filePath} `)) + + } else { // 已有FrontMatter + const matterData = fileMatterObj.data; + let mark = false; + + // 已有FrontMatter,但是没有title、date、permalink、categories、tags数据的 + if (!matterData.hasOwnProperty('title')) { // 标题 + matterData.title = file.name; + mark = true; + } + + if (!matterData.hasOwnProperty('date')) { // 日期 + const stat = fs.statSync(file.filePath); + matterData.date = dateFormat(getBirthtime(stat)); + mark = true; + } + + if (!matterData.hasOwnProperty('permalink')) { // 永久链接 + matterData.permalink = getPermalink(); + mark = true; + } + + if (file.filePath.indexOf('_posts') > -1 && !matterData.hasOwnProperty('sidebar')) { // auto侧边栏,_posts文件夹特有 + matterData.sidebar = "auto"; + mark = true; + } + + if (!matterData.hasOwnProperty('pageComponent') && matterData.article !== false) { // 是文章页才添加分类和标签 + if (isCategory !== false && !matterData.hasOwnProperty('categories')) { // 分类 + matterData.categories = getCategories(file, categoryText) + mark = true; + } + + if (isTag !== false && !matterData.hasOwnProperty('tags')) { // 标签 + matterData.tags = ['']; + mark = true; + } + } + + if (mark) { + if (matterData.date && type(matterData.date) === 'date') { + matterData.date = repairDate(matterData.date) // 修复时间格式 + } + const newData = jsonToYaml.stringify(matterData).replace(/\n\s{2}/g, "\n").replace(/"/g, "") + '---\r\n' + fileMatterObj.content; + fs.writeFileSync(file.filePath, newData); // 写入 + log(chalk.blue('tip ') + chalk.green(`write frontmatter(写入frontmatter):${file.filePath} `)) + } + + } + }) +} + +// 获取分类数据 +function getCategories (file, categoryText) { + let categories = [] + + if (file.filePath.indexOf('_posts') === -1) { + // 不在_posts文件夹 + let filePathArr = file.filePath.split(path.sep) // path.sep用于兼容不同系统下的路径斜杠 + filePathArr.pop() + + let ind = filePathArr.indexOf('docs') + if (ind !== -1) { + while (filePathArr[++ind] !== undefined) { + categories.push(filePathArr[ind].split('.').pop()) // 获取分类 + } + } + } else { + categories.push(categoryText) + } + return categories +} + +// 获取文件创建时间 +function getBirthtime (stat) { + // 在一些系统下无法获取birthtime属性的正确时间,使用atime代替 + return stat.birthtime.getFullYear() != 1970 ? stat.birthtime : stat.atime +} + +// 定义永久链接数据 +function getPermalink () { + return `${PREFIX + (Math.random() + Math.random()).toString(16).slice(2, 8)}/` +} + + +module.exports = setFrontmatter; diff --git a/theme-vdoing/noopModule.js b/theme-vdoing/noopModule.js new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/theme-vdoing/noopModule.js @@ -0,0 +1 @@ +export default {} diff --git a/theme-vdoing/package.json b/theme-vdoing/package.json new file mode 100644 index 0000000..a1a77a3 --- /dev/null +++ b/theme-vdoing/package.json @@ -0,0 +1,47 @@ +{ + "name": "vuepress-theme-vdoing", + "version": "1.8.1", + "description": "Vdoing theme for VuePress. 一个基于VuePress的知识管理兼博客主题。", + "author": { + "name": "gaoyi(Evan) Xu" + }, + "homepage": "https://github.com/xugaoyi/vuepress-theme-vdoing#readme", + "bugs": { + "url": "https://github.com/xugaoyi/vuepress-theme-vdoing/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/xugaoyi/vuepress-theme-vdoing.git" + }, + "dependencies": { + "@better-scroll/core": "^2.0.0-beta.6", + "@better-scroll/slide": "^2.0.0-beta.6", + "@vuepress/plugin-active-header-links": "^1.2.0", + "@vuepress/plugin-nprogress": "^1.2.0", + "@vuepress/plugin-search": "^1.2.0", + "chalk": "^4.0.0", + "json2yaml": "^1.1.0", + "js-yaml": "^3.13.1", + "docsearch.js": "^2.5.2", + "good-storage": "^1.1.1", + "lodash": "^4.17.15", + "stylus": "^0.54.5", + "stylus-loader": "^3.0.2", + "vuepress-plugin-container": "^2.0.2", + "vuepress-plugin-smooth-scroll": "^0.0.3" + }, + "keywords": [ + "documentation", + "vue", + "vuepress", + "generator", + "theme", + "blog" + ], + "license": "MIT", + "main": "index.js", + "maintainers": [{ + "name": "Evan xu", + "email": "894072666@qq.com" + }] +} diff --git a/theme-vdoing/styles/arrow.styl b/theme-vdoing/styles/arrow.styl new file mode 100644 index 0000000..20bffc0 --- /dev/null +++ b/theme-vdoing/styles/arrow.styl @@ -0,0 +1,22 @@ +@require './config' + +.arrow + display inline-block + width 0 + height 0 + &.up + border-left 4px solid transparent + border-right 4px solid transparent + border-bottom 6px solid $arrowBgColor + &.down + border-left 4px solid transparent + border-right 4px solid transparent + border-top 6px solid $arrowBgColor + &.right + border-top 4px solid transparent + border-bottom 4px solid transparent + border-left 6px solid $arrowBgColor + &.left + border-top 4px solid transparent + border-bottom 4px solid transparent + border-right 6px solid $arrowBgColor diff --git a/theme-vdoing/styles/code-theme.styl b/theme-vdoing/styles/code-theme.styl new file mode 100644 index 0000000..922a67c --- /dev/null +++ b/theme-vdoing/styles/code-theme.styl @@ -0,0 +1,274 @@ +// 适合浅色背景 //@import '~prismjs/themes/prism.css' +codeThemeLight() + code[class*="language-"], + pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + } + + pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, + code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; + } + + pre[class*="language-"]::selection, pre[class*="language-"] ::selection, + code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; + } + + @media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } + } + + /* Code blocks */ + pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; + } + + :not(pre) > code[class*="language-"], + pre[class*="language-"] { + background: #f5f2f0; + } + + /* Inline code */ + :not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; + } + + .token.comment, + .token.prolog, + .token.doctype, + .token.cdata { + color: slategray; + } + + .token.punctuation { + color: #999; + } + + .namespace { + opacity: .7; + } + + .token.property, + .token.tag, + .token.boolean, + .token.number, + .token.constant, + .token.symbol, + .token.deleted { + color: #905; + } + + .token.selector, + .token.attr-name, + .token.string, + .token.char, + .token.builtin, + .token.inserted { + color: #690; + } + + .token.operator, + .token.entity, + .token.url, + .language-css .token.string, + .style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); + } + + .token.atrule, + .token.attr-value, + .token.keyword { + color: #07a; + } + + .token.function, + .token.class-name { + color: #DD4A68; + } + + .token.regex, + .token.important, + .token.variable { + color: #e90; + } + + .token.important, + .token.bold { + font-weight: bold; + } + .token.italic { + font-style: italic; + } + + .token.entity { + cursor: help; + } + + // 行高亮颜色 + div[class*="language-"] + .highlight-lines + .highlighted + background-color rgba(200, 200, 200, 40%) + &.line-numbers-mode + .highlight-lines .highlighted + &:before + background-color rgba(200, 200, 200, 40%) + + +// 适合深色背景 // @import '~prismjs/themes/prism-tomorrow.css' +codeThemeDark() + code[class*="language-"], + pre[class*="language-"] { + color: #ccc; + background: none; + text-shadow: none; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + + } + + /* Code blocks */ + pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; + } + + :not(pre) > code[class*="language-"], + pre[class*="language-"] { + background: #2d2d2d; + } + + /* Inline code */ + :not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; + } + + .token.comment, + .token.block-comment, + .token.prolog, + .token.doctype, + .token.cdata { + color: #999; + } + + .token.punctuation { + color: #ccc; + } + + .token.tag, + .token.attr-name, + .token.namespace, + .token.deleted { + color: #e2777a; + } + + .token.function-name { + color: #6196cc; + } + + .token.boolean, + .token.number, + .token.function { + color: #f08d49; + } + + .token.property, + .token.class-name, + .token.constant, + .token.symbol { + color: #f8c555; + } + + .token.selector, + .token.important, + .token.atrule, + .token.keyword, + .token.builtin { + color: #cc99cd; + } + + .token.string, + .token.char, + .token.attr-value, + .token.regex, + .token.variable { + color: #7ec699; + } + + .token.operator, + .token.entity, + .token.url { + color: #67cdcc; + } + + .token.operator, + .token.entity, + .token.url, + .language-css .token.string, + .style .token.string { + background: none; + } + + .token.important, + .token.bold { + font-weight: bold; + } + .token.italic { + font-style: italic; + } + + .token.entity { + cursor: help; + } + + .token.inserted { + color: green; + } diff --git a/theme-vdoing/styles/code.styl b/theme-vdoing/styles/code.styl new file mode 100644 index 0000000..36206af --- /dev/null +++ b/theme-vdoing/styles/code.styl @@ -0,0 +1,142 @@ +body {$contentClass} + code + color var(--textLightenColor) + padding 0.25rem 0.5rem + margin 0 + font-size 0.9em + // background-color rgba(27,31,35,0.05) + background-color rgba(100,100,100,0.08) + border-radius 3px + .token + &.deleted + color #EC5975 + &.inserted + color $accentColor + +body {$contentClass} + pre, pre[class*="language-"] + line-height 1.4 + padding 1.25rem 1.5rem + margin 0.85rem 0 + background-color $codeBgColor + border-radius 6px + overflow auto + code + // color #fff + color var(--codeColor) + padding 0 + background-color transparent + border-radius 0 + +div[class*="language-"] + position relative + // background-color $codeBgColor + background-color var(--codeBg) + border-radius 6px + .highlight-lines + user-select none + padding-top 1.3rem + position absolute + top 0 + left 0 + width 100% + line-height 1.4 + .highlighted + background-color rgba(0, 0, 0, 30%) + pre, pre[class*="language-"] + background transparent + position relative !important + z-index 1 + &::before + position absolute + z-index 3 + top 0.8em + right 1em + font-size 0.8rem + color rgba(150,150,150,.7) + // color rgba(255, 255, 255, 0.4) + &:not(.line-numbers-mode) + .line-numbers-wrapper + display none + &.line-numbers-mode + .highlight-lines .highlighted + position relative + &:before + content ' ' + position absolute + z-index 3 + left 0 + top 0 + display block + width $lineNumbersWrapperWidth + height 100% + background-color rgba(0, 0, 0, 30%) + pre + padding-left $lineNumbersWrapperWidth + 1 rem + vertical-align middle + .line-numbers-wrapper + position absolute + top 0 + width $lineNumbersWrapperWidth + text-align center + // color rgba(255, 255, 255, 0.3) + color rgba(127, 127, 127, .5) + padding 1.25rem 0 + line-height 1.4 + br + user-select none + .line-number + position relative + z-index 4 + user-select none + font-size 0.85em + &::after + content '' + position absolute + z-index 2 + top 0 + left 0 + width $lineNumbersWrapperWidth + height 100% + border-radius 6px 0 0 6px + // border-right 1px solid rgba(0, 0, 0, 66%) + // background-color $codeBgColor + border-right 1px solid var(--borderColor) + background-color var(--codeBg) + + +for lang in $codeLang + div{'[class~="language-' + lang + '"]'} + &:before + content ('' + lang) + +div[class~="language-javascript"] + &:before + content "js" + +div[class~="language-typescript"] + &:before + content "ts" + +div[class~="language-markup"] + &:before + content "html" + +div[class~="language-markdown"] + &:before + content "md" + +div[class~="language-json"]:before + content "json" + +div[class~="language-ruby"]:before + content "rb" + +div[class~="language-python"]:before + content "py" + +div[class~="language-bash"]:before + content "sh" + +div[class~="language-php"]:before + content "php" diff --git a/theme-vdoing/styles/config.styl b/theme-vdoing/styles/config.styl new file mode 100644 index 0000000..fa973ff --- /dev/null +++ b/theme-vdoing/styles/config.styl @@ -0,0 +1 @@ +$contentClass = '.theme-vdoing-content' diff --git a/theme-vdoing/styles/custom-blocks.styl b/theme-vdoing/styles/custom-blocks.styl new file mode 100644 index 0000000..e212779 --- /dev/null +++ b/theme-vdoing/styles/custom-blocks.styl @@ -0,0 +1,87 @@ +.custom-block + .custom-block-title + font-weight 600 + margin-bottom .2rem + p + margin 0 + &.tip, &.warning, &.danger, &.note + padding .5rem 1.5rem + border-left-width .5rem + border-left-style solid + margin 1rem 0 + &.tip + background-color #f3f5f7 + border-color #42b983 + color darken(#42b983, 50%) + &.warning + background-color #FFF7D0 + border-color darken(#ffe564, 35%) + color darken(#ffe564, 70%) + .custom-block-title + color darken(#ffe564, 50%) + a + color var(--textColor) + &.danger + background-color #ffe6e6 + border-color darken(red, 20%) + color darken(red, 70%) + .custom-block-title + color darken(red, 40%) + a + color var(--textColor) + &.note + background-color #E8F5FA + border-color #157BAE + color darken(#157BAE, 40%) + &.right + color var(--textColor) + font-size 0.9rem + text-align right + &.theorem + margin 1rem 0 + padding .8rem 1.5rem + border-radius 2px + background-color var(--customBlockBg) + .title + font-weight bold + margin .5rem 0 + &.details + display block + position relative + border-radius 2px + margin 1em 0 + padding 1.6em + background-color var(--customBlockBg) + p + margin .8rem 0 + h4 + margin-top 0 + figure, p + &:last-child + margin-bottom 0 + padding-bottom 0 + summary + outline none + cursor pointer + &:hover + color $accentColor + +// 深色模式适配 +.theme-mode-dark + .custom-block + &.warning + background-color rgba(255, 247, 208, .2) + color darken(#ffe564, 35%) + .custom-block-title + color darken(#ffe564, 15%) + &.tip + background-color rgba(243, 245, 247, .2) + color darken(#42b983, 0%) + &.danger + background-color rgba(255, 230, 230, .4) + color darken(red, 50%) + a + color $accentColor + &.note + background-color rgba(243, 245, 247, .2) + color darken(#157BAE, 0%) diff --git a/theme-vdoing/styles/index.styl b/theme-vdoing/styles/index.styl new file mode 100644 index 0000000..28c2aa4 --- /dev/null +++ b/theme-vdoing/styles/index.styl @@ -0,0 +1,286 @@ +// 引入字体图标 +@import '//at.alicdn.com/t/font_1678482_kdcbbwxa6v.css' + +@require './config' +@require './code' +@require './custom-blocks' +@require './arrow' +@require './wrapper' +@require './toc' +@require './markdown-container' + + +html, body + padding 0 + margin 0 + +body + font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif + -webkit-font-smoothing antialiased + -moz-osx-font-smoothing grayscale + -webkit-tap-highlight-color transparent + font-size 16px + color $textColor + background var(--bodyBg) + +// 去掉黄色边框 +a,input,button + outline: none; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); -webkit-focus-ring-color: rgba(0, 0, 0, 0); +// 滚动条样式 +@media (min-width: $MQMobile) + ::-webkit-scrollbar + width:6px; + height:5px; + ::-webkit-scrollbar-track-piece + background-color:rgba(0,0,0,.15) + -webkit-border-radius:3px + ::-webkit-scrollbar-thumb:vertical + height:5px; + background-color:rgba(0,0,0,.28) + -webkit-border-radius:3px + ::-webkit-scrollbar-thumb:horizontal + width:5px; + background-color:rgba(0,0,0,.28) + -webkit-border-radius:3px + + +.card-box // 卡片 + border-radius 5px + background var(--mainBg) + box-shadow 0 1px 2px 0 rgba(0,0,0,.1) + transition box-shadow .5s + &:hover + box-shadow 0 1px 15px 0 rgba(0,0,0,.1) + +.blur // 模糊滤镜 + backdrop-filter saturate(200%) blur(20px) + +.custom-page // 自定义页面 + min-height calc(100vh - 3.6rem) + padding-top $navbarHeight + padding-bottom .9rem + .theme-vdoing-wrapper + margin 0 auto + +// 默认的搜索框样式重置 +body .search-box + input + background-color transparent + color var(--textColor) + border 1px solid var(--borderColor, #ccc) + @media (max-width: $MQNarrow) + border-color transparent + + + +.page + transition padding .2s ease + padding-left .8rem + +.navbar + position fixed + z-index 20 + top 0 + left 0 + right 0 + height $navbarHeight + background-color var(--blurBg) + box-sizing border-box + box-shadow 0 2px 5px rgba(0,0,0,.06) + +.sidebar-mask + position fixed + z-index 12 + top 0 + left 0 + width 100vw + height 100vh + display none + +.sidebar + font-size 16px + background-color var(--sidebarBg) + width $sidebarWidth + position fixed + z-index 13 + margin 0 + top $navbarHeight + left 0 + bottom 0 + box-sizing border-box + border-right 1px solid var(--borderColor) + overflow-y auto + transform translateX(-100%) + transition transform .2s + @media (max-width: $MQMobile) + background-color var(--mainBg) + + +{$contentClass}:not(.custom) + word-wrap break-word + @extend $wrapper + > *:first-child + // margin-top $navbarHeight // 内容第一个元素的top距离 + + a:hover + text-decoration underline + + p.demo + padding 1rem 1.5rem + border 1px solid #ddd + border-radius 4px + + img + max-width 100% + +{$contentClass}.custom + padding 0 + margin 0 + + img + max-width 100% + +a + font-weight 500 + color $accentColor + text-decoration none + +p a code + font-weight 400 + color $accentColor + +kbd + background #eee + border solid 0.15rem #ddd + border-bottom solid 0.25rem #ddd + border-radius 0.15rem + padding 0 0.15em + +blockquote + font-size 1rem + opacity .75 + border-left .2rem solid rgba(100,100,100,.3) + margin 1rem 0 + padding .25rem 0 .25rem 1rem + + & > p + margin 0 + +ul, ol + padding-left 1.2em + +strong + font-weight 600 + +h1, h2, h3, h4, h5, h6 + font-weight 600 + line-height 1.25 + + {$contentClass}:not(.custom) > & + margin-top (0.5rem - $navbarHeight) + padding-top ($navbarHeight + 1rem) + margin-bottom 0 + + &:first-child + // margin-top -1.5rem + margin-bottom 1rem + + + p, + pre, + .custom-block + margin-top 2rem + + &:focus .header-anchor, + &:hover .header-anchor + opacity: 1 + + +// 没有h1标签时 +p,pre,.custom-block + {$contentClass}:not(.custom) > & + &:first-child + margin-top 2rem + + +h1 + font-size 1.8rem + {$contentClass}:not(.custom) > & // 页面内容的首个h1标签隐藏 + &:first-child + display none + +h2 + font-size 1.5rem + padding-bottom .3rem + border-bottom 1px solid var(--borderColor) + +h3 + font-size 1.35rem + +a.header-anchor + font-size 0.85em + float left + margin-left -0.87em + padding-right 0.23em + margin-top 0.125em + opacity 0 + + &:focus, + &:hover + text-decoration none + +code, kbd, .line-number + font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace + +p, ul, ol + line-height 1.7 + +hr + border 0 + border-top 1px solid var(--borderColor) + +table + border-collapse collapse + margin 1rem 0 + overflow-x: auto + width 100% + display inline-table + @media (max-width: $MQMobile) + display block +tr + border-top 1px solid var(--borderColor) + + &:nth-child(2n) + background-color rgba(150,150,150,0.1) + +th, td + border 1px solid var(--borderColor) + padding .6em 1em + @media (max-width: $MQMobile) + padding .3em .5em + a + word-break break-all + +.theme-container + // background var(--mainBg) + color var(--textColor) + min-height 100vh + &.sidebar-open + .sidebar-mask + display: block + + &.no-navbar + {$contentClass}:not(.custom) > h1, h2, h3, h4, h5, h6 + margin-top 1.5rem + padding-top 0 + + .sidebar + top 0 + + +@media (min-width: ($MQMobile + 1px)) + .theme-container.no-sidebar + .sidebar + display none + + .page + padding-left 0 + +@require 'mobile.styl' diff --git a/theme-vdoing/styles/markdown-container.styl b/theme-vdoing/styles/markdown-container.styl new file mode 100644 index 0000000..7255c97 --- /dev/null +++ b/theme-vdoing/styles/markdown-container.styl @@ -0,0 +1,206 @@ +// markdown容器样式 + +// 居中容器 +.center-container + text-align: center + +h1, h2, h3, h4, h5, h6 + .center-container > & + margin-top (0.5rem - $navbarHeight) + padding-top ($navbarHeight + 1rem) + margin-bottom 0 + a.header-anchor + float none + padding-right: 0 + margin-left: -.9rem + + +// 普通卡片列表 +.cardListContainer + margin .7rem 0 + &>:not(.card-list) + display none + .card-list + margin -0.35rem + display: flex; + flex-wrap: wrap; + align-items: flex-start; + .card-item + width calc(100%/3 - .7rem) + margin .35rem + background var(--bodyBg) + border-radius 3px + color var(--textColor) + display flex + box-shadow 1px 1px 2px 0 rgba(0,0,0,.06) + transition all .4s + &:hover + text-decoration none + box-shadow: 0 10px 20px -10px var(--randomColor, rgba(0,0,0,0.15)); + transform: translateY(-3px) scale(1.01, 1.01) + img + // transform rotate(8deg) scale(1.1, 1.1) + box-shadow 3px 2px 7px rgba(0, 0, 0, 0.15) + div p + text-shadow 3px 2px 5px rgba(0, 0, 0, 0.15) + img + width 60px + height 60px + border-radius 50% + border 2px solid #fff + margin 1rem + margin-right 0 + box-shadow 3px 2px 5px rgba(0, 0, 0, 0.08) + transition all .4s + div + flex 1 + display inline-block + float right + padding 1rem 0 + p + margin 0 + padding 0 1rem + transition text-shadow .4s + text-align center + .name + margin .2rem 0 .3rem 0 + .desc + font-size .8rem + line-height 1.1rem + opacity .8 + margin-bottom .2rem + .card-item.row-1 + width calc(100% - .7rem) + img + margin-left 2rem + .card-item.row-2 + width calc(100%/2 - .7rem) + img + margin-left 1.5rem + .card-item.row-3 + width calc(100%/3 - .7rem) + .card-item.row-4 + width calc(100%/4 - .7rem) + +// 图文卡片列表 +.cardImgListContainer + margin 1rem 0 + &>:not(.card-list) + display none + .card-list + margin -0.5rem + display: flex; + flex-wrap: wrap; + align-items: flex-start; + .card-item + width calc(100%/3 - 1rem) + margin .5rem + background var(--mainBg) + border 1px solid rgba(0,0,0,0.1) + box-sizing: border-box + border-radius 3px + overflow hidden + color var(--textColor) + box-shadow 2px 2px 10px rgba(0,0,0,.04) + display flex + flex-direction: column; + justify-content: flex-start; + align-items: stretch; + align-content: stretch; + transition: all .4s + &:hover + box-shadow 1px 1px 20px rgba(0,0,0,.1) + transform: translateY(-3px) + .box-img + overflow hidden + position relative + background #000 + img + display block + width 100% + height auto + transition: all .3s + // &:hover + // img + // transform: scale(1.1, 1.1) + // opacity .75 + a + color var(--textColor) + transition: color .3s + &:hover + // color $accentColor + text-decoration none + .box-info + padding: .8rem 1rem + p + margin 0 + .desc + margin-top: .3rem + opacity .8 + font-size: .9rem + line-height: 1.1rem + .box-footer + overflow hidden + padding: .8rem 1rem + border-top: 1px solid rgba(0,0,0,0.1) + img + width 1.8rem + height 1.8rem + border-radius 50% + float left + span + line-height 1.8rem + float left + margin-left: .6rem + font-size: .8rem + .card-item.row-1 + width calc(100% - 1rem) + .card-item.row-2 + width calc(100%/2 - 1rem) + .card-item.row-3 + width calc(100%/3 - 1rem) + .card-item.row-4 + width calc(100%/4 - 1rem) + +.theme-mode-dark + .cardImgListContainer + .card-list + .card-item + border-color: var(--borderColor) + .box-footer + border-color: var(--borderColor) + +// 卡片列表的响应 +@media (max-width: 900px) + .cardListContainer + .card-list + .card-item.row-4 + width calc(100%/3 - .7rem) + .cardImgListContainer + .card-list + .card-item.row-4 + width calc(100%/3 - 1rem) + +@media (max-width: 720px) + .cardListContainer + .card-list + .card-item.row-3, .card-item.row-4 + width calc(100%/2 - .7rem) + img + margin-left 1.5rem + .cardImgListContainer + .card-list + .card-item.row-3, .card-item.row-4 + width calc(100%/2 - 1rem) + +@media (max-width: 500px) + .cardListContainer + .card-list + .card-item.row-1, .card-item.row-2, .card-item.row-3, .card-item.row-4 + width calc(100% - .7rem) + img + margin-left 1.5rem + .cardImgListContainer + .card-list + .card-item.row-1, .card-item.row-2, .card-item.row-3, .card-item.row-4 + width calc(100% - 1rem) diff --git a/theme-vdoing/styles/mobile.styl b/theme-vdoing/styles/mobile.styl new file mode 100644 index 0000000..3adc63d --- /dev/null +++ b/theme-vdoing/styles/mobile.styl @@ -0,0 +1,78 @@ +@require './config' + +$mobileSidebarWidth = $sidebarWidth * 0.9 + +// narrow desktop / iPad +@media (max-width: $MQNarrow) + .sidebar + font-size 15px + +@media (max-width: $MQMobile) + .sidebar + width $sidebarWidth * 0.95 + +@media (min-width: ($MQMobile + 1px)) and (max-width: $MQNarrow) + .sidebar + width $mobileSidebarWidth + .theme-container + &.sidebar-open + .page + padding-left ($mobileSidebarWidth + .8rem)!important + +// wide mobile +@media (max-width: $MQMobile) + .sidebar + top 0 + height 100vh + padding-top $navbarHeight + transform translateX(-100%) + transition transform .2s ease + .page + padding-left 0 + .theme-container + &.sidebar-open + .sidebar + transform translateX(0) + .sidebar-mask // 蒙版在小屏中才能显示 + display block + &.no-navbar + .sidebar + padding-top: 0 + +// narrow mobile +@media (max-width: $MQMobileNarrow) + h1 + font-size 1.9rem + {$contentClass} + div[class*="language-"] + margin 0.85rem -1.5rem + border-radius 0 + +// 侧边栏显示隐藏的适配 +@media (min-width: ($MQMobile + 1px)) // 720px + .theme-container + &.sidebar-open + .sidebar-mask + display: none + .sidebar + transform translateX(0) + .sidebar-button + left $sidebarWidth + .page + padding-left ($sidebarWidth + .8rem) + padding-right .8rem + &.have-rightmenu + .page + padding-right ($rightMenuWidth + .8rem) + &.no-sidebar + .page + padding-left 0!important + @media (max-width: $MQNarrow) + .theme-container + &.sidebar-open:not(.on-sidebar) + .sidebar-button + $mobileSidebarWidth = $sidebarWidth * 0.7 + left $mobileSidebarWidth + .theme-container.no-sidebar + .sidebar-button + display none diff --git a/theme-vdoing/styles/palette.styl b/theme-vdoing/styles/palette.styl new file mode 100644 index 0000000..b0a0155 --- /dev/null +++ b/theme-vdoing/styles/palette.styl @@ -0,0 +1,86 @@ +// 主题调色板 + +// 原主题预设变量 +// 颜色 +// $accentColor = #3eaf7c +// $textColor = #2c3e50 +// $borderColor = #eaecef +// $codeBgColor = #282c34 +// $arrowBgColor = #ccc +// $badgeTipColor = #42b983 +// $badgeWarningColor = darken(#ffe564, 35%) +// $badgeErrorColor = #DA5961 +// 布局 +// $navbarHeight = 3.6rem +// $sidebarWidth = 20rem +// $contentWidth = 740px +// $homePageWidth = 960px +// 代码块 +// $lineNumbersWrapperWidth = 3.5rem + + +@require './code-theme' + +//***vdoing主题-变量***// + +// 颜色 + +$bannerTextColor = #fff // 首页banner区(博客标题)文本颜色 +$accentColor = #11A8CD +$activeColor = #ff5722 +$arrowBgColor = #ccc +$badgeTipColor = #42b983 +$badgeWarningColor = darken(#ffe564, 35%) +$badgeErrorColor = #DA5961 + +// 布局 +$navbarHeight = 3.6rem +$sidebarWidth = 18rem +$contentWidth = 860px +$homePageWidth = 1100px +$rightMenuWidth = 230px // 右侧菜单 + +// 代码块 +$lineNumbersWrapperWidth = 2.5rem + +// 浅色模式 +.theme-mode-light + --bodyBg: #f4f4f4 + --mainBg: rgba(255,255,255,1) + --sidebarBg: rgba(255,255,255,.8) + --blurBg: rgba(255,255,255,.9) + --customBlockBg: #f1f1f1 + --textColor: #00323c + --textLightenColor: #0085AD + --borderColor: rgba(0,0,0,.15) + --codeBg: #f6f6f6 + --codeColor: #525252 + codeThemeLight() + +// 深色模式 +.theme-mode-dark + --bodyBg: rgb(39,39,43) + --mainBg: rgba(30,30,34,1) + --sidebarBg: rgba(30,30,34,.8) + --blurBg: rgba(30,30,34,.8) + --customBlockBg: rgb(39,39,43) + --textColor: rgb(155,155,170) + --textLightenColor: #0085AD + --borderColor: #2C2C3A + --codeBg: #252526 + --codeColor: #fff + codeThemeDark() + +// 阅读模式 +.theme-mode-read + --bodyBg: rgb(236,236,204) + --mainBg: rgba(245,245,213,1) + --sidebarBg: rgba(245,245,213,.8) + --blurBg: rgba(245,245,213,.9) + --customBlockBg: rgb(236,236,204) + --textColor: #704214 + --textLightenColor: #996633 + --borderColor: rgba(0,0,0,.15) + --codeBg: #282c34 + --codeColor: #fff + codeThemeDark() diff --git a/theme-vdoing/styles/toc.styl b/theme-vdoing/styles/toc.styl new file mode 100644 index 0000000..d3e7106 --- /dev/null +++ b/theme-vdoing/styles/toc.styl @@ -0,0 +1,3 @@ +.table-of-contents + .badge + vertical-align middle diff --git a/theme-vdoing/styles/wrapper.styl b/theme-vdoing/styles/wrapper.styl new file mode 100644 index 0000000..230e230 --- /dev/null +++ b/theme-vdoing/styles/wrapper.styl @@ -0,0 +1,16 @@ +$vdoing-wrapper + max-width $contentWidth + margin 0 auto + padding 1rem 2.5rem 2rem 2.5rem + &:not(.footer) + background var(--mainBg) + box-shadow 0 1px 2px 0 rgba(0,0,0,.1) + margin-bottom 1rem + @media (min-width $contentWidth + 80) + border-radius 2px + @media (max-width: $MQNarrow) + padding 1rem 2rem + @media (max-width: $MQMobileNarrow) + padding 1rem 1.5rem +$wrapper + max-width $contentWidth diff --git a/theme-vdoing/templates/dev.html b/theme-vdoing/templates/dev.html new file mode 100644 index 0000000..2c53351 --- /dev/null +++ b/theme-vdoing/templates/dev.html @@ -0,0 +1,11 @@ + + + + + + + + +
+ + \ No newline at end of file diff --git a/theme-vdoing/templates/ssr.html b/theme-vdoing/templates/ssr.html new file mode 100644 index 0000000..2cc6549 --- /dev/null +++ b/theme-vdoing/templates/ssr.html @@ -0,0 +1,17 @@ + + + + + + {{ title }} + + {{{ userHeadTags }}} + {{{ pageMeta }}} + {{{ renderResourceHints() }}} + {{{ renderStyles() }}} + + + + {{{ renderScripts() }}} + + \ No newline at end of file diff --git a/theme-vdoing/util/index.js b/theme-vdoing/util/index.js new file mode 100644 index 0000000..7cfc197 --- /dev/null +++ b/theme-vdoing/util/index.js @@ -0,0 +1,291 @@ +export const hashRE = /#.*$/ +export const extRE = /\.(md|html)$/ +export const endingSlashRE = /\/$/ +export const outboundRE = /^[a-z]+:/i + +export function normalize (path) { + return decodeURI(path) + .replace(hashRE, '') + .replace(extRE, '') +} + +export function getHash (path) { + const match = path.match(hashRE) + if (match) { + return match[0] + } +} + +export function isExternal (path) { + return outboundRE.test(path) +} + +export function isMailto (path) { + return /^mailto:/.test(path) +} + +export function isTel (path) { + return /^tel:/.test(path) +} + +export function ensureExt (path) { + if (isExternal(path)) { + return path + } + const hashMatch = path.match(hashRE) + const hash = hashMatch ? hashMatch[0] : '' + const normalized = normalize(path) + + if (endingSlashRE.test(normalized)) { + return path + } + return normalized + '.html' + hash +} + +export function isActive (route, path) { + const routeHash = route.hash + const linkHash = getHash(path) + if (linkHash && routeHash !== linkHash) { + return false + } + const routePath = normalize(route.path) + const pagePath = normalize(path) + return routePath === pagePath +} + +export function resolvePage (pages, rawPath, base) { + if (isExternal(rawPath)) { + return { + type: 'external', + path: rawPath + } + } + if (base) { + rawPath = resolvePath(rawPath, base) + } + const path = normalize(rawPath) + for (let i = 0; i < pages.length; i++) { + if (normalize(pages[i].regularPath) === path) { + return Object.assign({}, pages[i], { + type: 'page', + path: ensureExt(pages[i].path) + }) + } + } + console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`) + return {} +} + +function resolvePath (relative, base, append) { + const firstChar = relative.charAt(0) + if (firstChar === '/') { + return relative + } + + if (firstChar === '?' || firstChar === '#') { + return base + relative + } + + const stack = base.split('/') + + // remove trailing segment if: + // - not appending + // - appending to trailing slash (last segment is empty) + if (!append || !stack[stack.length - 1]) { + stack.pop() + } + + // resolve relative path + const segments = relative.replace(/^\//, '').split('/') + for (let i = 0; i < segments.length; i++) { + const segment = segments[i] + if (segment === '..') { + stack.pop() + } else if (segment !== '.') { + stack.push(segment) + } + } + + // ensure leading slash + if (stack[0] !== '') { + stack.unshift('') + } + + return stack.join('/') +} + +/** + * @param { Page } page + * @param { string } regularPath + * @param { SiteData } site + * @param { string } localePath + * @returns { SidebarGroup } + */ +export function resolveSidebarItems (page, regularPath, site, localePath) { + const { pages, themeConfig } = site + + const localeConfig = localePath && themeConfig.locales + ? themeConfig.locales[localePath] || themeConfig + : themeConfig + + const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar + if (pageSidebarConfig === 'auto') { + return resolveHeaders(page) + } + + const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar + if (!sidebarConfig) { + return [] + } else { + const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig) + if (config === 'auto') { + return resolveHeaders(page) + } + return config + ? config.map(item => resolveItem(item, pages, base)) + : [] + } +} + +/** + * @param { Page } page + * @returns { SidebarGroup } + */ +function resolveHeaders (page) { + const headers = groupHeaders(page.headers || []) + return [{ + type: 'group', + collapsable: false, + title: page.title, + path: null, + children: headers.map(h => ({ + type: 'auto', + title: h.title, + basePath: page.path, + path: page.path + '#' + h.slug, + children: h.children || [] + })) + }] +} + +export function groupHeaders (headers) { + // group h3s under h2 + headers = headers.map(h => Object.assign({}, h)) + let lastH2 + headers.forEach(h => { + if (h.level === 2) { + lastH2 = h + } else if (lastH2) { + (lastH2.children || (lastH2.children = [])).push(h) + } + }) + return headers.filter(h => h.level === 2) +} + +export function resolveNavLinkItem (linkItem) { + return Object.assign(linkItem, { + type: linkItem.items && linkItem.items.length ? 'links' : 'link' + }) +} + +/** + * @param { Route } route + * @param { Array | Array | [link: string]: SidebarConfig } config + * @returns { base: string, config: SidebarConfig } + */ +export function resolveMatchingConfig (regularPath, config) { + if (Array.isArray(config)) { + return { + base: '/', + config: config + } + } + for (const base in config) { + if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) { + return { + base, + config: config[base] + } + } + } + return {} +} + +function ensureEndingSlash (path) { + return /(\.html|\/)$/.test(path) + ? path + : path + '/' +} + +function resolveItem (item, pages, base, groupDepth = 1) { + if (typeof item === 'string') { + return resolvePage(pages, item, base) + } else if (Array.isArray(item)) { + return Object.assign(resolvePage(pages, item[0], base), { + title: item[1] + }) + } else { + if (groupDepth > 3) { + console.error( + '[vuepress] detected a too deep nested sidebar group.' + ) + } + const children = item.children || [] + if (children.length === 0 && item.path) { + return Object.assign(resolvePage(pages, item.path, base), { + title: item.title + }) + } + return { + type: 'group', + path: item.path, + title: item.title, + sidebarDepth: item.sidebarDepth, + initialOpenGroupIndex: item.initialOpenGroupIndex, + children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)), + collapsable: item.collapsable !== false + } + } +} + + +// 类型判断 +export function type (o) { + const s = Object.prototype.toString.call(o) + return s.match(/\[object (.*?)\]/)[1].toLowerCase() +} + +// 日期格式化(只获取年月日) +export function dateFormat (date) { + if (!(date instanceof Date)) { + date = new Date(date) + } + return `${date.getUTCFullYear()}-${zero(date.getUTCMonth() + 1)}-${zero(date.getUTCDate())}` +} + +// 小于10补0 +export function zero (d) { + return d.toString().padStart(2, '0') +} + +// 获取时间的时间戳 +export function getTimeNum (post) { + let dateStr = post.frontmatter.date || post.lastUpdated + let date = new Date(dateStr) + if (date == "Invalid Date") { // 修复new Date()在Safari下出现Invalid Date的问题 + date = new Date(dateStr.replace(/-/g, '/')) + } + return date.getTime() +} + +// 比对时间 +export function compareDate (a, b) { + return getTimeNum(b) - getTimeNum(a) +} + +// 将特殊符号编码(应用于url) +export function encodeUrl (str) { + str = str + '' + str = str.replace(/ |((?=[\x21-\x7e]+)[^A-Za-z0-9])/g, '-') + return str +} diff --git a/theme-vdoing/util/postData.js b/theme-vdoing/util/postData.js new file mode 100644 index 0000000..91eba96 --- /dev/null +++ b/theme-vdoing/util/postData.js @@ -0,0 +1,108 @@ +import { type, compareDate } from './index' + +/** + * 过滤非文章页 + * @param {Array} posts 所有文章数据 + */ +export function filterPosts (posts) { + posts = posts.filter(item => { + const { frontmatter: { pageComponent, article, home } } = item + return !(pageComponent || article === false || home === true) // 存在页面组件、article字段为false,以及首页 + }) + return posts +} + +/** + * 按置顶和时间排序 + * @param {Array} posts 过滤非文章页之后的文章数据 + */ +export function sortPosts (posts) { + posts.sort((prev, next) => { + const prevSticky = prev.frontmatter.sticky + const nextSticky = next.frontmatter.sticky + if (prevSticky && nextSticky) { + return prevSticky == nextSticky ? compareDate(prev, next) : (prevSticky - nextSticky) + } else if (prevSticky && !nextSticky) { + return -1 + } else if (!prevSticky && nextSticky) { + return 1 + } + return compareDate(prev, next) + }) + return posts +} + +/** + * 按时间排序 + * @param {Array} posts 过滤非文章页之后的文章数据 + */ +export function sortPostsByDate (posts) { + posts.sort((prev, next) => { + return compareDate(prev, next) + }) + return posts +} + +/** + * 按分类和标签分组 + * @param {Array} posts 按时间排序之后的文章数据 + */ +export function groupPosts (posts) { + const categoriesObj = {} + const tagsObj = {} + + for (let i = 0, postsL = posts.length; i < postsL; i++) { + const { frontmatter: { categories, tags } } = posts[i] + if (type(categories) === 'array') { + categories.forEach(item => { + if (item) { // 分类值是有效的 + if (!categoriesObj[item]) { + categoriesObj[item] = [] + } + categoriesObj[item].push(posts[i]) + } + }) + } + if (type(tags) === 'array') { + tags.forEach(item => { + if (item) { // 标签值是有效的 + if (!tagsObj[item]) { + tagsObj[item] = [] + } + tagsObj[item].push(posts[i]) + } + }) + } + } + return { + categories: categoriesObj, + tags: tagsObj + } +} + +/** + * 获取所有分类和标签 + * @param {Object} groupPosts 按分类和标签分组之后的文章数据 + */ +export function categoriesAndTags (groupPosts) { + const categoriesArr = [] + const tagsArr = [] + + for (let key in groupPosts.categories) { + categoriesArr.push({ + key, + length: groupPosts.categories[key].length + }) + } + + for (let key in groupPosts.tags) { + tagsArr.push({ + key, + length: groupPosts.tags[key].length + }) + } + return { + categories: categoriesArr, + tags: tagsArr + } +} diff --git a/utils/baiduPush.js b/utils/baiduPush.js new file mode 100644 index 0000000..7ed2df0 --- /dev/null +++ b/utils/baiduPush.js @@ -0,0 +1,35 @@ +/** + * 生成百度链接推送文件 + */ +const fs = require('fs'); +const path = require('path'); +const chalk = require('chalk') +const matter = require('gray-matter'); // FrontMatter解析器 https://github.com/jonschlinkert/gray-matter +const readFileList = require('./modules/readFileList'); +const urlsRoot = path.join(__dirname, '..', 'urls.txt'); // 百度链接推送文件 +const DOMAIN = process.argv.splice(2)[0]; // 获取命令行传入的参数 + +if (!DOMAIN) { + console.log(chalk.red('请在运行此文件时指定一个你要进行百度推送的域名参数,例:node utils/baiduPush.js https://xugaoyi.com')) + return +} + +main(); + +/** + * 主体函数 + */ +function main() { + fs.writeFileSync(urlsRoot, DOMAIN) + const files = readFileList(); // 读取所有md文件数据 + + files.forEach( file => { + const { data } = matter(fs.readFileSync(file.filePath, 'utf8')); + + if (data.permalink) { + const link = `\r\n${DOMAIN}${data.permalink}`; + console.log(link) + fs.appendFileSync(urlsRoot, link); + } + }) +} diff --git a/utils/config.yml b/utils/config.yml new file mode 100644 index 0000000..268f8c3 --- /dev/null +++ b/utils/config.yml @@ -0,0 +1,16 @@ +#批量添加和修改、删除front matter配置文件 + +# 需要批量处理的路径,docs文件夹内的文件夹 (数组,映射路径:path[0]/path[1]/path[2] ... ) +path: + - docs # 第一个成员必须是docs + - 04.更多 + +# 要删除的字段 (数组) +delete: + # - tags + + # 要添加、修改front matter的数据 (front matter中没有的数据则添加,已有的数据则覆盖) +data: + # author: + # name: xugaoyi + # link: https://github.com/xugaoyi diff --git a/utils/editFrontmatter.js b/utils/editFrontmatter.js new file mode 100644 index 0000000..8c223f4 --- /dev/null +++ b/utils/editFrontmatter.js @@ -0,0 +1,92 @@ +/** + * 批量添加和修改front matter ,需要配置 ./config.yml 文件。 + */ +const fs = require('fs'); // 文件模块 +const path = require('path'); // 路径模块 +const matter = require('gray-matter'); // front matter解析器 https://github.com/jonschlinkert/gray-matter +const jsonToYaml = require('json2yaml') +const yamlToJs = require('yamljs') +const inquirer = require('inquirer') // 命令行操作 +const chalk = require('chalk') // 命令行打印美化 +const readFileList = require('./modules/readFileList'); +const { type, repairDate} = require('./modules/fn'); +const log = console.log + +const configPath = path.join(__dirname, 'config.yml') // 配置文件的路径 + +main(); + +/** + * 主体函数 + */ +async function main() { + + const promptList = [{ + type: "confirm", + message: chalk.yellow('批量操作frontmatter有修改数据的风险,确定要继续吗?'), + name: "edit", + }]; + let edit = true; + + await inquirer.prompt(promptList).then(answers => { + edit = answers.edit + }) + + if(!edit) { // 退出操作 + return + } + + const config = yamlToJs.load(configPath) // 解析配置文件的数据转为js对象 + + if (type(config.path) !== 'array') { + log(chalk.red('路径配置有误,path字段应该是一个数组')) + return + } + + if (config.path[0] !== 'docs') { + log(chalk.red("路径配置有误,path数组的第一个成员必须是'docs'")) + return + } + + const filePath = path.join(__dirname, '..', ...config.path); // 要批量修改的文件路径 + const files = readFileList(filePath); // 读取所有md文件数据 + + files.forEach(file => { + let dataStr = fs.readFileSync(file.filePath, 'utf8');// 读取每个md文件的内容 + const fileMatterObj = matter(dataStr) // 解析md文件的front Matter。 fileMatterObj => {content:'剔除frontmatter后的文件内容字符串', data:{}, ...} + let matterData = fileMatterObj.data; // 得到md文件的front Matter + + let mark = false + // 删除操作 + if (config.delete) { + if( type(config.delete) !== 'array' ) { + log(chalk.yellow('未能完成删除操作,delete字段的值应该是一个数组!')) + } else { + config.delete.forEach(item => { + if (matterData[item]) { + delete matterData[item] + mark = true + } + }) + + } + } + + // 添加、修改操作 + if (type(config.data) === 'object') { + Object.assign(matterData, config.data) // 将配置数据合并到front Matter对象 + mark = true + } + + // 有操作时才继续 + if (mark) { + if(matterData.date && type(matterData.date) === 'date') { + matterData.date = repairDate(matterData.date) // 修复时间格式 + } + const newData = jsonToYaml.stringify(matterData).replace(/\n\s{2}/g,"\n").replace(/"/g,"") + '---\r\n' + fileMatterObj.content; + fs.writeFileSync(file.filePath, newData); // 写入 + log(chalk.green(`update frontmatter:${file.filePath} `)) + } + + }) +} diff --git a/utils/modules/fn.js b/utils/modules/fn.js new file mode 100644 index 0000000..48cbbd1 --- /dev/null +++ b/utils/modules/fn.js @@ -0,0 +1,21 @@ +// 类型判断 +exports.type = function (o){ + var s = Object.prototype.toString.call(o) + return s.match(/\[object (.*?)\]/)[1].toLowerCase() +} + + // 修复date时区格式的问题 + exports.repairDate = function (date) { + date = new Date(date); + return `${date.getUTCFullYear()}-${zero(date.getUTCMonth()+1)}-${zero(date.getUTCDate())} ${zero(date.getUTCHours())}:${zero(date.getUTCMinutes())}:${zero(date.getUTCSeconds())}`; +} + +// 日期的格式 +exports.dateFormat = function (date) { + return `${date.getFullYear()}-${zero(date.getMonth()+1)}-${zero(date.getDate())} ${zero(date.getHours())}:${zero(date.getMinutes())}:${zero(date.getSeconds())}` +} + +// 小于10补0 +function zero(d){ + return d.toString().padStart(2,'0') +} \ No newline at end of file diff --git a/utils/modules/readFileList.js b/utils/modules/readFileList.js new file mode 100644 index 0000000..8eb97c6 --- /dev/null +++ b/utils/modules/readFileList.js @@ -0,0 +1,43 @@ +/** + * 读取所有md文件数据 + */ +const fs = require('fs'); // 文件模块 +const path = require('path'); // 路径模块 +const docsRoot = path.join(__dirname, '..', '..', 'docs'); // docs文件路径 + +function readFileList(dir = docsRoot, filesList = []) { + const files = fs.readdirSync(dir); + files.forEach( (item, index) => { + let filePath = path.join(dir, item); + const stat = fs.statSync(filePath); + if (stat.isDirectory() && item !== '.vuepress') { + readFileList(path.join(dir, item), filesList); //递归读取文件 + } else { + if(path.basename(dir) !== 'docs'){ // 过滤docs目录级下的文件 + + const fileNameArr = path.basename(filePath).split('.') + let name = null, type = null; + if (fileNameArr.length === 2) { // 没有序号的文件 + name = fileNameArr[0] + type = fileNameArr[1] + } else if (fileNameArr.length === 3) { // 有序号的文件 + name = fileNameArr[1] + type = fileNameArr[2] + } else { // 超过两个‘.’的 + log(chalk.yellow(`warning: 该文件 "${filePath}" 没有按照约定命名,将忽略生成相应数据。`)) + return + } + if(type === 'md'){ // 过滤非md文件 + filesList.push({ + name, + filePath + }); + } + + } + } + }); + return filesList; +} + +module.exports = readFileList; \ No newline at end of file