创建文档

This commit is contained in:
lhc
2021-02-03 17:01:33 +08:00
commit 6f2985e47a
139 changed files with 13727 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
# npm
package-lock.json
node_modules
# yarn
yarn.lock
# vscode
.vscode
# vuepress
docs/.vuepress/dist
# 百度链接推送
urls.txt
# mac
.DS_Store

21
LICENSE Normal file
View File

@@ -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.

100
README.MD Normal file
View File

@@ -0,0 +1,100 @@
<p align="center"><a href="https://xugaoyi.com/" target="_blank" rel="noopener noreferrer"><img width="180" src="https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200409124835.png" alt="logo"></a></p>
<p align="center">
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing/actions?query=workflow%3ACI"><img src="https://github.com/xugaoyi/vuepress-theme-vdoing/workflows/CI/badge.svg" alt="CI"></a>
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing/actions?query=workflow%3AbaiduPush"><img src="https://github.com/xugaoyi/vuepress-theme-vdoing/workflows/baiduPush/badge.svg" alt="baiduPush"></a>
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing/blob/master/LICENSE"><img src="https://img.shields.io/github/license/xugaoyi/vuepress-theme-vdoing
" alt="License"></a>
<a href="https://www.npmjs.com/package/vuepress-theme-vdoing"><img alt="npm" src="https://img.shields.io/npm/v/vuepress-theme-vdoing"></a>
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing/stargazers"><img src="https://img.shields.io/github/stars/xugaoyi/vuepress-theme-vdoing?logo=ReverbNation&logoColor=rgba(255,255,255,.6)" alt="GitHub stars"></a>
</p>
<h2 align="center">vuepress-theme-vdoing</h2>
* [文档 (国内源)](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:
| 微信赞赏 | 微信 | 支付宝 |
| :---: | :---: | :---: |
| <img src="https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200523131533.jpg" alt="赞赏码" width=150> | <img src="https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200410113708.jpg" alt="Wechat QRcode" width=150>| <img src="https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200410113707.jpg" alt="Alipay QRcode" width=150> |
二维码没有正常显示?点 [这里😎](https://doc.xugaoyi.com/pages/1b12ed/)
## 致谢
:heart:感谢支持这个项目的朋友
:heart:感谢为这个项目贡献代码的朋友 → [Contributors](https://github.com/xugaoyi/vuepress-theme-vdoing/graphs/contributors)
## Vdoing官方交流群
群号694387113
<img src="https://cdn.jsdelivr.net/gh/xugaoyi/image_store/blog/20200712122307.jpg" alt="群号694387113" width="200">
## 许可证
[MIT](https://github.com/xugaoyi/vuepress-theme-vdoing/blob/master/LICENSE)
Copyright (c) 2019-present Evan Xu

9
baiduPush.sh Normal file
View File

@@ -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 # 删除文件

42
deploy.sh Normal file
View File

@@ -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

View File

@@ -0,0 +1,33 @@
<template>
<p class="bit-sponsor">
<a
href="https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue"
target="_blank"
rel="noopener noreferrer"
>
<span>This project is sponsored by</span>
<img
alt="bit"
src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bit.png"
>
</a>
<el-button>默认按钮</el-button>
</p>
</template>
<style lang="stylus">
.bit-sponsor
font-weight 600
background-color #f3f6f8
padding 0.6em 1.2em
border-radius 8px
display inline-block
margin 1em 0 !important
a
color #999
img
height 40px
margin-left 15px
img, span
vertical-align middle
</style>

View File

@@ -0,0 +1,306 @@
<template>
<div class="container">
<div class="bpmn-container">
<div ref="canvas" class="bpmn-canvas" />
<div id="js-properties-panel" class="bpmn-js-properties-panel" />
</div>
<div class="action">
<el-upload action class="upload-demo" :before-upload="openBpmn">
<el-button icon="el-icon-folder-opened" />
</el-upload>
<el-button class="new" icon="el-icon-circle-plus" @click="newDiagram" />
<el-button icon="el-icon-download" @click="downloadBpmn" />
<el-button icon="el-icon-picture" @click="downloadSvg" />
<el-button v-if="updateDate!=undefined" icon="el-icon-upload" @click="handleUpdateData" />
<a ref="downloadLink" hidden />
</div>
</div>
</template>
<script>
import BpmnModeler from "bpmn-js/lib/Modeler";
import propertiesProviderModule from "bpmn-js-properties-panel/lib/provider/camunda";
import propertiesPanelModule from "bpmn-js-properties-panel";
import camundaModdleDescriptor from "camunda-bpmn-moddle/resources/camunda";
import customTranslate from "./customTranslate/customTranslate";
export default {
name: "Bpmn",
props: {
updateBpmnFlag: {
type: Boolean,
default: false,
},
updateDate: {
default: null,
},
},
data() {
return {
bpmnModeler: null,
canvas: null,
bpmnTemplate: `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn"
id="m1577635100724"
name=""
targetNamespace="http://www.activiti.org/testm1577635100724"
>
<process id="process" name="" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties>
</camunda:properties>
</extensionElements>
<startEvent id="_2" name="start" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="leave">
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`,
bpmnNameFrom: null,
editBpmnFile: null,
};
},
mounted() {
this.$nextTick(() => {
if (this.updateDate != undefined) {
this.initUpdateDate();
}
this.initBpmn();
});
},
methods: {
initUpdateDate() {
if (this.updateBpmnFlag) {
const updateDate = this.updateDate.replace(new RegExp("activiti:", "gm"), "camunda:");
this.editBpmnFile = this.updateDate;
} else {
this.bpmnNameFrom = this.updateDate;
}
},
async initBpmn() {
this.canvas = this.$refs.canvas;
const customTranslateModule = {
translate: ["value", customTranslate],
};
this.bpmnModeler = new BpmnModeler({
container: this.canvas,
propertiesPanel: {
parent: "#js-properties-panel"
},
additionalModules: [
propertiesProviderModule,
propertiesPanelModule,
customTranslateModule,
],
moddleExtensions: {
camunda: camundaModdleDescriptor,
},
});
if (this.updateBpmnFlag) {
await this.createNewDiagram(this.editBpmnFile);
} else {
await this.createNewDiagram(this.bpmnTemplate);
}
},
replaceString(xml, nodeName, property, propertyLength, replaceChar) {
const node = xml.indexOf(nodeName);
let xmlName = xml.substr(node, xml.indexOf(property));
const begin = node + xmlName.indexOf(property) + propertyLength;
xmlName = xmlName.substr(xmlName.indexOf(property) + propertyLength);
const end = begin + xmlName.indexOf('"');
const fstStr = xml.substring(0, begin);
const lstStr = xml.substring(end, xml.length);
const newXml = fstStr + replaceChar + lstStr;
return newXml;
},
createNewDiagram(bpmn) {
if (!this.updateBpmnFlag && this.updateDate != undefined) {
bpmn = this.replaceString(
bpmn,
"process",
"name",
6,
this.bpmnNameFrom.name
);
bpmn = this.replaceString(
bpmn,
"process",
"id",
4,
this.bpmnNameFrom.key
);
}
this.bpmnModeler.importXML(bpmn, (err) => {
if (err) {
this.$message({
message: "打开模型出错,请确认该模型符合Bpmn2.0规范",
type: "error",
});
}
});
},
newDiagram() {
this.createNewDiagram(this.bpmnTemplate);
},
downloadBpmn() {
this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
if (!err) {
const name = `${this.getFilename(xml)}.bpmn`;
this.download({ name: name, data: xml });
}
});
},
downloadSvg() {
this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
if (!err) {
const name = `${this.getFilename(xml)}.svg`;
let context = "";
const djsGroupAll = this.$refs.canvas.querySelectorAll(".djs-group");
for (const item of djsGroupAll) {
context += item.innerHTML;
}
const viewport = this.$refs.canvas
.querySelector(".viewport")
.getBBox();
const svg = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="${viewport.width}"
height="${viewport.height}"
viewBox="${viewport.x} ${viewport.y} ${viewport.width} ${viewport.height}"
version="1.1"
>
${context}
</svg>
`;
this.download({ name: name, data: svg });
}
});
},
openBpmn(file) {
const reader = new FileReader();
reader.readAsText(file, "utf-8");
reader.onload = () => {
this.createNewDiagram(reader.result);
};
return false;
},
getFilename(xml) {
const start = xml.indexOf("process");
let filename = xml.substr(start, xml.indexOf(">"));
filename = filename.substr(filename.indexOf("id") + 4);
filename = filename.substr(0, filename.indexOf('"'));
return filename;
},
getFileInfo(xml, nodeName, property, propertyLength) {
const start = xml.indexOf(nodeName);
let fileInfo = xml.substr(start, xml.indexOf(">"));
fileInfo = fileInfo.substr(fileInfo.indexOf(property) + propertyLength);
fileInfo = fileInfo.substr(0, fileInfo.indexOf('"'));
return;
},
download({ name = "diagram.bpmn", data }) {
const downloadLink = this.$refs.downloadLink;
data = data.replace(new RegExp("camunda:", "gm"), "activiti:");
const encodedData = encodeURIComponent(data);
if (data) {
downloadLink.href =
"data:application/bpmn20-xml;charset=UTF-8," + encodedData;
downloadLink.download = name;
downloadLink.click();
}
},
handleUpdateData() {
if (!this.updateBpmnFlag) {
this.handleCreateData();
} else {
this.handleEditData();
}
},
handleCreateData() {
this.bpmnModeler.saveXML({ format: true }, async (err, xml) => {
if (!err) {
const fileName = `${this.getFileInfo(xml, "process", "name", 6)}`;
const filekey = `${this.getFileInfo(xml, "process", "id", 4)}.bpmn`;
xml = xml.replace(new RegExp("camunda:", "gm"), "activiti:");
this.$emit("createData", xml, filekey, fileName);
}
});
},
handleEditData() {
this.bpmnModeler.saveXML({ format: true }, async (err, xml) => {
if (!err) {
xml = xml.replace(new RegExp("camunda:", "gm"), "activiti:");
this.$emit("editData", xml);
}
});
},
},
};
</script>
<style>
.bpmn-container {
position: relative;
width: 100%;
height: 100vh;
display: flex;
}
.bpmn-canvas {
width: calc(100% - 300px);
height: 100vh;
}
.bpmn-js-properties-panel {
width: 320px;
height: inherit;
overflow-y: auto;
}
.action {
position: absolute;
right: 30rem;
display: flex;
margin-top: -40px;
}
.upload-demo {
margin-right: 10px;
}
.bjs-powered-by {
display: none;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<div ref="showCanvas" />
</template>
<script>
import BpmnViewer from 'bpmn-js/lib/Viewer'
export default {
props: {
showData: {
default: null
}
},
data () {
return {}
},
mounted () {
if (this.showData) {
this.showBpmn(this.showData)
}
},
methods: {
showBpmn (showData) {
this.bpmnViewer && this.bpmnViewer.destroy()
this.bpmnViewer = new BpmnViewer({ container: this.$refs.showCanvas })
this.bpmnViewer.importXML(showData, (err) => {
if (err) {
this.$message({
message: '打开模型出错,请确认该模型符合Bpmn2.0规范',
type: 'error'
})
} else {
this.bpmnViewer.get('canvas').zoom('fit-viewport', 'auto') // 居中显示
}
})
}
}
}
</script>

View File

@@ -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 + '}'
})
}

View File

@@ -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': '变量'
}

View File

@@ -0,0 +1,205 @@
<template>
<el-dialog
ref="form"
:title="formTitle"
:visible.sync="formVisible"
:close-on-click-modal="false"
:before-close="beforeCancelForm"
>
<el-form
ref="formCommon"
:model="formData"
label-width="130px"
:rules="formRules"
>
<div
v-for="col in formKey"
:key="col.fieldName"
>
<component
:is="'el-form-item'"
v-if="col.isForm===1"
:label="col.nameCn"
:prop="col.fieldName"
>
<div>
<component
:is="col.webType"
v-if="
(col.javaType !== 'date' || isAdd) &&
col.webType !== 'textArea' && col.webType!=='password'
"
v-model="formData[col.fieldName]"
filterable
:type="col.javaType"
style="width:80%"
:value-format="timeFormat"
:disabled="disFunction(col.isChange, col.fieldName)"
>
<div v-if="col.selectList">
<component
:is="'el-option'"
v-for="(sel,val) in col.selectList"
:key="val"
:label="sel.selectKey"
:value="formatSelectVal(sel.selectValue,col.javaType)"
/>
</div>
</component>
<el-input
v-if="col.webType === 'textArea'"
v-model="formData[col.fieldName]"
style="width:80%"
type="textarea"
:disabled="disFunction(col.isChange, col.fieldName)"
/>
<el-input
v-if="col.webType === 'password'"
v-model="formData[col.fieldName]"
style="width:80%"
type="password"
:disabled="disFunction(col.isChange, col.fieldName)"
/>
<el-col
v-if="!isAdd && col.javaType === 'date'"
:span="8"
>
<el-form-item prop="date1">
<el-date-picker
v-model="formData[col.fieldName + '_start']"
type="datetime"
placeholder="选择时间"
style="width: 95%;"
:value-format="timeFormat"
/>
</el-form-item>
</el-col>
<el-col
v-if="!isAdd && col.javaType === 'date'"
class="line"
:span="1"
>
-
</el-col>
<el-col
v-if="!isAdd && col.javaType === 'date'"
:span="10"
>
<el-form-item prop="date2">
<el-date-picker
v-model="formData[col.fieldName + '_end']"
:type="col.javaType"
placeholder="选择时间"
:value-format="timeFormat"
style="width: 80%;"
/>
</el-form-item>
</el-col>
</div>
</component>
</div>
</el-form>
<div
slot="footer"
class="dialog-footer"
>
<el-button @click="cancelForm">
取消
</el-button>
<el-button
type="primary"
@click="formSubmit('formCommon', formType)"
>
{{ formSubBotton }}
</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'formCo',
props: {
timeFormat: {
type: String,
default: 'timestamp'
},
formTitle: {
type: String
},
formData: {
type: Object
},
formVisible: {
type: Boolean
},
formKey: {
type: Array
},
formSubBotton: {
type: String
},
formRules: {
type: Object
},
isAdd: {
type: Boolean,
default: true
},
formType: {
type: Number
},
disableField: {
type: Array
}
},
methods: {
disFunction (change, fieldName) {
let flag = false
this.disableField.forEach((value) => {
if (value === fieldName) {
flag = true
}
})
if (this.formType === 1) {
let flag = false
this.disableField.forEach((value) => {
if (value === fieldName) {
flag = true
}
})
return flag
} else {
if (change === 0) {
return true
} else {
let flag = false
this.disableField.forEach((value) => {
if (value === fieldName) {
flag = true
}
})
return flag
}
}
},
formSubmit (val, type) {
this.$emit('form-submit', val, type)
},
cancelForm () {
this.$emit('cancel-form')
},
beforeCancelForm () {
this.$emit('before-cancel-form')
},
formatSelectVal (val, type) {
if (type === 'Integer') {
return +val
} else {
return val
}
}
}
}
</script>

View File

@@ -0,0 +1,104 @@
<template>
<el-form :inline="true" :model="searchData">
<el-form-item v-for="col in formKey" :key="col.fieldName" :prop="col.fieldName">
<el-row
v-if="col.isSearch===1 && col.webType !== 'el-date-picker' && col.webType!=='password'"
>
<el-col :span="8">{{ col.nameCn }}:</el-col>
<el-col :span="16">
<component
:is="col.webType"
v-if="col.webType !== 'el-date-picker'&&col.webType!=='password'"
v-model="searchData[col.fieldName]"
:type="col.javaType"
:value-format="timeFormat"
:placeholder="'请输入'+col.nameCn"
size="small"
>
<div v-if="col.selectList">
<component
:is="'el-option'"
v-for="(sel,val) in col.selectList"
:key="val"
:label="sel.selectKey"
:value="formatSelectVal(sel.selectValue,col.javaType)"
/>
</div>
</component>
</el-col>
</el-row>
<el-row v-if="col.webType === 'el-date-picker'">
<el-col :span="4">{{ col.nameCn }}:</el-col>
<el-col :span="9">
<el-form-item prop="date1">
<el-date-picker
v-model="searchData[col.fieldName + '_start']"
type="datetime"
:placeholder="col.nameCn+'开始'"
style="width: 95%;"
:value-format="timeFormat"
size="small"
/>
</el-form-item>
</el-col>
<el-col v-if=" col.webType === 'el-date-picker'" class="line" :span="1">-</el-col>
<el-col v-if="col.webType === 'el-date-picker'" :span="10">
<el-form-item prop="date2">
<el-date-picker
v-model="searchData[col.fieldName + '_end']"
:type="col.javaType"
:placeholder="col.nameCn+'结束'"
:value-format="timeFormat"
style="width: 80%;"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-row style="text-align:center">
<el-form-item>
<el-button type="primary" size="small" @click="searchSubmit">查询</el-button>
</el-form-item>
<el-form-item>
<el-button size="small" @click="resetSearchForm">重置</el-button>
</el-form-item>
</el-row>
</el-form>
</template>
<script>
export default {
name: "searchCo",
props: {
timeFormat: {
type: String,
default: "timestamp",
},
searchData: {
type: Object,
},
formVisible: {
type: Boolean,
},
formKey: {
type: Array,
},
},
methods: {
searchSubmit(val, type) {
this.$emit("search-submit");
},
resetSearchForm() {
this.$emit("reset-search-form");
},
formatSelectVal(val, type) {
if (type === "Integer") {
return +val;
} else {
return val;
}
},
},
};
</script>

View File

@@ -0,0 +1,127 @@
ya<template>
<div>
<el-table
id="commonTable"
ref="commonTable"
v-loading="loading"
:data="formList"
border
highlight-current-row
style="width: 100%;"
:header-cell-style="{background: '#3e7eaf',color:'#fff',textAlign:'center',fontWeight:'bold'}"
:cell-style="{textAlign:'center'}"
@sort-change="handleSortChange"
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="checkBoxVisible"
type="selection"
/>
<el-table-column
type="index"
width="60"
:label="'#'"
/>
<template v-for="col in tableHeads">
<el-table-column
v-if="col.isHead ===1"
:key="col.fieldName"
:prop="col.fieldName"
:label="col.nameCn"
:formatter="formatter"
:width="col.commonWidth"
sortable
/>
</template>
<el-table-column
v-if="slotVisible"
label="操作"
:width="width"
>
<template
v-slot="scope"
>
<slot :item="scope" />
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'TableHeadCo',
props: {
formList: {
type: Array,
default: null
},
checkBoxVisible: {
type: Boolean,
default: false
},
tableHeads: {
type: Array
},
loading: {
type: Boolean,
default: false
},
formatter: {
type: Function
},
slotVisible: {
type: Boolean,
default: false
},
number: {
type: Boolean,
default: false
},
width: {
type: Number,
default: 200
}
},
methods: {
handleSortChange (val) {
this.$emit('handle-sort-change', val)
},
handleSelectionChange (val) {
this.$emit('handle-selection-change', val)
}
}
}
</script>
<style lang="scss">
.el-table th {
background-color: #eeeeee;
font-weight: 200;
}
.form-expend .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 25%;
}
.form-expend {
font-size: 0;
margin-left: 20px;
.el-form-item__label {
font-size: 12px;
// color: $middleGray;
}
}
.form-expend label {
width: 90px;
color: #99a9bf;
}
.table-dropdown{
.el-table--medium th, .el-table--medium td {
padding: 5px 0;
}
/*.el-table-column--selection{*/
/* display:none;*/
/*}*/
}
</style>

View File

@@ -0,0 +1,15 @@
<template>
<p class="demo">
{{ msg }}
</p>
</template>
<script>
export default {
data () {
return {
msg: 'Hello this is <Foo-Bar>'
}
}
}
</script>

View File

@@ -0,0 +1,5 @@
<template>
<p class="demo">
This is another component
</p>
</template>

View File

@@ -0,0 +1,35 @@
<script>
export default {
functional: true,
props: {
title: {
type: String,
required: true
}
},
render (h, { props, slots }) {
return h('div',
{
class: ['upgrade-path']
},
[
h('h4', props.title || 'Upgrade Path'),
slots().default
]
)
}
}
</script>
<style lang="stylus">
.upgrade-path
margin-top: 2em
padding: 2em
background: rgba(73, 195, 140, .1)
border-radius: 2px
> h4
margin-top: 0
> p:last-child
margin-bottom: 0
padding-bottom: 0
</style>

View File

@@ -0,0 +1,17 @@
<template>
<div>
<bpmn />
</div>
</template>
<script>
import bpmn from '../Bpmn/bpmn'
export default {
components: {
bpmn
},
data () {
return {}
}
}
</script>

View File

@@ -0,0 +1,20 @@
<template>
<div>
<bpmn :updateDate="updateDate" :updateBpmnFlag="updateBpmnFlag" />
</div>
</template>
<script>
import bpmn from "../Bpmn/bpmn";
export default {
components: {
bpmn,
},
data() {
return {
updateDate: { key: "process", name: "流程图" },
updateBpmnFlag: false
};
},
};
</script>

View File

@@ -0,0 +1,65 @@
<template>
<div>
<bpmn :updateDate="updateDate" :updateBpmnFlag="updateBpmnFlag" />
</div>
</template>
<script>
import bpmn from "../Bpmn/bpmn";
export default {
components: {
bpmn,
},
data() {
return {
updateBpmnFlag: true,
updateDate: `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" id="m1577635100724" name="" targetNamespace="http://www.activiti.org/testm1577635100724">
<process id="process1" name="流程图1" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties />
</extensionElements>
<startEvent id="_2" name="start">
<outgoing>Flow_1mn3ncy</outgoing>
</startEvent>
<sequenceFlow id="Flow_1mn3ncy" sourceRef="_2" targetRef="Activity_03u7gdx" />
<endEvent id="Event_1jrsaua">
<incoming>Flow_0wm6kz4</incoming>
</endEvent>
<sequenceFlow id="Flow_0wm6kz4" sourceRef="Activity_03u7gdx" targetRef="Event_1jrsaua" />
<userTask id="Activity_03u7gdx">
<incoming>Flow_1mn3ncy</incoming>
<outgoing>Flow_0wm6kz4</outgoing>
</userTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="process1">
<bpmndi:BPMNEdge id="Flow_1mn3ncy_di" bpmnElement="Flow_1mn3ncy">
<di:waypoint x="176" y="384" />
<di:waypoint x="230" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wm6kz4_di" bpmnElement="Flow_0wm6kz4">
<di:waypoint x="330" y="384" />
<di:waypoint x="392" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1jrsaua_di" bpmnElement="Event_1jrsaua">
<omgdc:Bounds x="392" y="366" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f4773x_di" bpmnElement="Activity_03u7gdx">
<omgdc:Bounds x="230" y="344" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`
};
},
};
</script>

View File

@@ -0,0 +1,168 @@
<template>
<div>
<el-button
type="primary"
size="medium"
@click="showAdd"
>
新增表单
</el-button>
<form-co
ref="formCo"
:form-title="dialogTitle"
:form-visible="dialogCreateFormVisible"
:form-sub-botton="'提交'"
:form-key="tableHead"
:form-data="formData"
:form-rules="formRules"
:form-type="formType"
:disable-field="disableField"
@form-submit="formSubmit"
@cancel-form="cancelForm"
@before-cancel-form="cancelForm"
/>
</div>
</template>
<script>
import formCo from '../Co/formCo'
export default {
components: {
formCo
},
data () {
return {
dialogTitle: '新增记录',
dialogCreateFormVisible: false,
tableHead: [
{
fieldId: 1,
fieldName: 'user_id',
isChange: 0,
isForm: 0,
javaType: 'Integer',
nameCn: '用户ID',
webType: 'el-input'
},
{
fieldId: 2,
fieldName: 'username',
isChange: 1,
isForm: 1,
javaType: 'String',
nameCn: '用户名',
webType: 'el-input'
},
{
fieldId: 3,
fieldName: 'password',
isChange: 1,
isForm: 1,
javaType: 'String',
nameCn: '密码',
webType: 'password'
},
{
fieldId: 4,
fieldName: 'enabled',
isChange: 1,
isForm: 1,
javaType: 'Integer',
nameCn: '用户状态',
selectList: [
{
fieldId: 4,
selectId: 1,
selectKey: '禁用',
selectType: 1,
selectValue: '1'
},
{
fieldId: 4,
selectId: 2,
selectKey: '启用',
selectType: 1,
selectValue: '2'
}
],
webType: 'el-select'
},
{
fieldId: 5,
fieldName: 'user_type',
isChange: 1,
isForm: 1,
javaType: 'Integer',
nameCn: '用户类型',
selectList: [
{
fieldId: 5,
selectId: 3,
selectKey: '外部用户',
selectType: 1,
selectValue: '1'
},
{
fieldId: 5,
selectId: 4,
selectKey: '内部用户',
selectType: 1,
selectValue: '2'
}
],
webType: 'el-select'
},
{
fieldId: 6,
fieldName: 'create_time',
isChange: 1,
isForm: 1,
javaType: 'datetime',
nameCn: '创建时间',
webType: 'el-date-picker'
},
{
fieldId: 7,
fieldName: 'update_time',
isChange: 1,
isForm: 1,
javaType: 'datetime',
nameCn: '修改时间',
webType: 'el-date-picker'
}
],
formData: {},
formType: 1,
disableField: ['create_time', 'update_time'],
formRules: {
password: [
{ required: true, message: '密码不能为空', trigger: 'blur' }
],
username: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }
]
}
}
},
methods: {
// 提交表单
formSubmit (value) {
this.dialogCreateFormVisible = false
},
// 关闭表单
cancelForm () {
this.dialogCreateFormVisible = false
this.formData = {}
this.$nextTick(() => {
const ref = this.$refs.formCo
ref.$refs.formCommon.resetFields()
})
},
showAdd () {
this.dialogTitle = '新增记录'
this.formType = 1
this.formData = {}
this.dialogCreateFormVisible = true
}
}
}
</script>

View File

@@ -0,0 +1,81 @@
<template>
<div>
<el-button
type="primary"
@click="showBpmn()"
>
查看流程图
</el-button>
<el-dialog
title="流程图"
:visible.sync="dialogShowBpmnVisible"
>
<bpmnView :show-data="showBpmnData" />
</el-dialog>
</div>
</template>
<script>
import bpmnView from '../Bpmn/bpmnView'
export default {
components: {
bpmnView
},
data () {
return {
dialogShowBpmnVisible: false,
showBpmnData: `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" id="m1577635100724" name="" targetNamespace="http://www.activiti.org/testm1577635100724">
<process id="process1" name="流程图1" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties />
</extensionElements>
<startEvent id="_2" name="start">
<outgoing>Flow_1mn3ncy</outgoing>
</startEvent>
<sequenceFlow id="Flow_1mn3ncy" sourceRef="_2" targetRef="Activity_03u7gdx" />
<endEvent id="Event_1jrsaua">
<incoming>Flow_0wm6kz4</incoming>
</endEvent>
<sequenceFlow id="Flow_0wm6kz4" sourceRef="Activity_03u7gdx" targetRef="Event_1jrsaua" />
<userTask id="Activity_03u7gdx">
<incoming>Flow_1mn3ncy</incoming>
<outgoing>Flow_0wm6kz4</outgoing>
</userTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="process1">
<bpmndi:BPMNEdge id="Flow_1mn3ncy_di" bpmnElement="Flow_1mn3ncy">
<di:waypoint x="176" y="384" />
<di:waypoint x="230" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wm6kz4_di" bpmnElement="Flow_0wm6kz4">
<di:waypoint x="330" y="384" />
<di:waypoint x="392" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1jrsaua_di" bpmnElement="Event_1jrsaua">
<omgdc:Bounds x="392" y="366" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f4773x_di" bpmnElement="Activity_03u7gdx">
<omgdc:Bounds x="230" y="344" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`
}
},
methods: {
showBpmn () {
this.dialogShowBpmnVisible = true
}
}
}
</script>

View File

@@ -0,0 +1,134 @@
<template>
<el-row>
<el-col :span="24">
<el-card shadow="always" class="select-card">
<search-co
:form-key="tableHead"
:search-data="searchForm"
:time-format="'yyyy-MM-dd HH:mm:ss'"
@search-submit="query"
@reset-search-form="resetSearch"
/>
</el-card>
</el-col>
</el-row>
</template>
<script>
import searchCo from "../Co/searchCo";
export default {
components: {
searchCo,
},
data() {
return {
tableHead: [
{
fieldId: 1,
fieldName: "user_id",
isChange: 0,
isForm: 0,
javaType: "Integer",
nameCn: "用户ID",
webType: "el-input",
},
{
fieldId: 2,
fieldName: "username",
isChange: 1,
isSearch: 1,
javaType: "String",
nameCn: "用户名",
webType: "el-input",
},
{
fieldId: 3,
fieldName: "password",
isChange: 1,
isSearch: 0,
javaType: "String",
nameCn: "密码",
webType: "password",
},
{
fieldId: 4,
fieldName: "enabled",
isChange: 1,
isSearch: 1,
javaType: "Integer",
nameCn: "用户状态",
selectList: [
{
fieldId: 4,
selectId: 1,
selectKey: "禁用",
selectType: 1,
selectValue: "1",
},
{
fieldId: 4,
selectId: 2,
selectKey: "启用",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 5,
fieldName: "user_type",
isChange: 1,
isSearch: 1,
javaType: "Integer",
nameCn: "用户类型",
selectList: [
{
fieldId: 5,
selectId: 3,
selectKey: "外部用户",
selectType: 1,
selectValue: "1",
},
{
fieldId: 5,
selectId: 4,
selectKey: "内部用户",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 6,
fieldName: "create_time",
isChange: 1,
isSearch: 1,
javaType: "datetime",
nameCn: "创建时间",
webType: "el-date-picker",
},
{
fieldId: 7,
fieldName: "update_time",
isChange: 1,
isSearch: 1,
javaType: "datetime",
nameCn: "修改时间",
webType: "el-date-picker",
},
],
searchForm: {},
};
},
methods: {
// 查询表格
query() {},
// 重置search表单
resetSearch() {
this.searchForm = {};
},
},
};
</script>

View File

@@ -0,0 +1,48 @@
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
/>
</div>
</template>
<script>
import tableHeadCo from '../Co/tableHeadCo'
export default {
components: {
tableHeadCo
},
data () {
return {
tableHead: [
{ fieldName: 'date', nameCn: '日期', isHead: 1 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 }
],
tableList: [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,56 @@
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:check-box-visible="true"
@handle-selection-change="handleSelectionChange"
/>
</div>
</template>
<script>
import tableHeadCo from '../Co/tableHeadCo'
export default {
components: {
tableHeadCo
},
data(){
return {
tableHead: [
{ fieldName: 'date', nameCn: '日期', isHead: 1 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
],
tableList: [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
},
methods:{
// 复选框改变监听事件
handleSelectionChange(val){
console.log("这里是checkBox获取的内容--->",val)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,66 @@
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:slot-visible="true"
>
<template
v-slot="scope"
style="text-align: center"
>
<el-button
size="mini"
@click="showEdit(scope.item.row)"
>
编辑
</el-button>
</template>
</table-head-co>
</div>
</template>
<script>
import tableHeadCo from '../Co/tableHeadCo'
export default {
components: {
tableHeadCo
},
data () {
return {
tableHead: [
{ fieldName: 'date', nameCn: '日期', isHead: 1, commonWidth: 120 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1, commonWidth: 100 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 }
],
tableList: [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
},
methods: {
showEdit (val) {
console.log('这里是插槽获取的内容--->', val)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,15 @@
<template>
<p class="demo">
{{ msg }}
</p>
</template>
<script>
export default {
data () {
return {
msg: 'Hello this is <demo-1>'
}
}
}
</script>

View File

@@ -0,0 +1,204 @@
<template>
<svg-container>
<svg
width="400px"
viewBox="0 0 688 403"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<rect
id="path-1"
x="140"
y="0"
width="549"
height="176"
rx="20"
/>
<mask
id="mask-2"
maskContentUnits="userSpaceOnUse"
maskUnits="objectBoundingBox"
x="0"
y="0"
width="549"
height="176"
fill="white"
>
<use xlink:href="#path-1" />
</mask>
</defs>
<g
id="Page-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
>
<g
id="diagram-markdown-slot-relationship"
transform="translate(-1.000000, 0.000000)"
>
<circle
id="Oval"
fill="#00BC7D"
fill-rule="nonzero"
cx="235"
cy="88"
r="59"
/>
<text
id="Markdown-File-1"
font-family="PingFangSC-Regular, PingFang SC"
font-size="20"
font-weight="normal"
fill="#FFFFFF"
>
<tspan
x="186.79"
y="85"
>Markdown </tspan>
<tspan
x="211.43"
y="113"
>File 1</tspan>
</text>
<circle
id="Oval"
fill="#00BC7D"
fill-rule="nonzero"
cx="415"
cy="88"
r="59"
/>
<text
id="Markdown-File-2"
font-family="PingFangSC-Regular, PingFang SC"
font-size="20"
font-weight="normal"
fill="#FFFFFF"
>
<tspan
x="366.79"
y="85"
>Markdown </tspan>
<tspan
x="389.44"
y="113"
>File 2</tspan>
</text>
<circle
id="Oval"
fill="#EB4D5D"
fill-rule="nonzero"
transform="translate(419.000000, 320.000000) rotate(-360.000000) translate(-419.000000, -320.000000) "
cx="419"
cy="320"
r="83"
/>
<text
id="Layout-Component"
transform="translate(419.000000, 320.000000) rotate(-360.000000) translate(-419.000000, -320.000000) "
font-family="PingFangSC-Regular, PingFang SC"
font-size="24"
font-weight="normal"
fill="#FFFFFF"
>
<tspan
x="381.272"
y="312"
>Layout</tspan>
<tspan
x="354.572"
y="345"
>Component</tspan>
</text>
<circle
id="Oval"
fill="#00BC7D"
fill-rule="nonzero"
cx="595"
cy="88"
r="59"
/>
<text
id="Markdown-File-3"
font-family="PingFangSC-Regular, PingFang SC"
font-size="20"
font-weight="normal"
fill="#FFFFFF"
>
<tspan
x="546.79"
y="85"
>Markdown </tspan>
<tspan
x="569.44"
y="113"
>File 3</tspan>
</text>
<use
id="Rectangle"
stroke="#DC585F"
mask="url(#mask-2)"
stroke-width="8"
stroke-dasharray="5"
fill-rule="nonzero"
xlink:href="#path-1"
/>
<path
id="Path"
d="M317.737594,275.914863 C316.399837,275.439513 315.071319,274.934993 313.752054,274.40131 L314.877079,271.620246 C316.062011,272.099587 317.254567,272.554642 318.454758,272.985416 L320.320183,265.365584 L336.516196,279.111098 L315.802177,283.8206 L317.737594,275.914863 Z M237.053972,153.839171 L240.036678,154.160829 C239.860222,155.797097 239.727284,157.431576 239.637858,159.064293 L236.642347,158.900223 C236.734662,157.214791 236.871873,155.527765 237.053972,153.839171 Z M236.500062,163.984683 L239.50006,163.988115 C239.498183,165.6288 239.540423,167.267783 239.626781,168.905092 L236.630945,169.063103 C236.541751,167.372018 236.498123,165.679201 236.500062,163.984683 Z M237.034923,174.12831 L240.017951,173.809652 C240.191774,175.436839 240.409353,177.062453 240.670697,178.686519 L237.708802,179.163147 C237.439026,177.486687 237.214397,175.808399 237.034923,174.12831 Z M238.651506,184.177472 L241.584397,183.54648 C241.927872,185.142975 242.313817,186.738038 242.742248,188.33169 L239.845114,189.110544 C239.40355,187.46804 239.005676,185.823676 238.651506,184.177472 Z M241.277699,193.960604 L244.133333,193.041173 C244.633282,194.593949 245.173728,196.145426 245.754688,197.695614 L242.945486,198.748412 C242.348064,197.154297 241.792129,195.558357 241.277699,193.960604 Z M244.837653,203.467846 L247.596505,202.289399 C248.237243,203.789426 248.91606,205.288264 249.632972,206.785917 L246.927022,208.081229 C246.191725,206.545169 245.495264,205.007373 244.837653,203.467846 Z M249.214331,212.61803 L251.865379,211.213771 C252.629062,212.6555 253.428173,214.096132 254.262722,215.535667 L251.667331,217.040308 C250.813682,215.567829 249.996012,214.093736 249.214331,212.61803 Z M254.297283,221.391099 L256.836394,219.793315 C257.706019,221.175275 258.60841,222.556214 259.543577,223.936131 L257.060143,225.61915 C256.106032,224.21128 255.185076,222.801931 254.297283,221.391099 Z M259.974465,229.772936 L262.402649,228.01114 C263.362565,229.334138 264.3527,230.656183 265.373059,231.977271 L262.998786,233.811072 C261.960192,232.466375 260.952083,231.120331 259.974465,229.772936 Z M266.091044,237.698109 L268.411267,235.796382 C269.468483,237.08625 270.534696,238.346975 271.6099,239.578562 L269.349958,241.551545 C268.25419,240.296403 267.167887,239.011923 266.091044,237.698109 Z M272.734865,245.303177 L274.928459,243.25668 C276.064358,244.474225 277.209951,245.660334 278.365233,246.815016 L276.244465,248.936888 C275.064352,247.757389 273.894487,246.546149 272.734865,245.303177 Z M279.887015,252.446537 L281.927895,250.247718 C283.145938,251.378269 284.374389,252.475061 285.613248,253.538106 L283.65964,255.814817 C282.391056,254.726266 281.133515,253.603502 279.887015,252.446537 Z M287.576785,259.039625 L289.435074,256.684468 C290.736664,257.711462 292.049366,258.702468 293.373181,259.657502 L291.617984,262.09046 C290.259116,261.110138 288.912049,260.093188 287.576785,259.039625 Z M295.785278,264.957777 L297.4295,262.448487 C298.812943,263.354993 300.208145,264.223547 301.615114,265.054165 L300.089978,267.637566 C298.64262,266.783104 297.207718,265.889835 295.785278,264.957777 Z M304.529995,270.116457 L305.928241,267.462232 C307.386943,268.230677 308.857919,268.959729 310.341185,269.649406 L309.076317,272.36972 C307.547965,271.659079 306.032519,270.907986 304.529995,270.116457 Z"
fill="#8599A4"
fill-rule="nonzero"
/>
<path
id="Path"
d="M505.524249,279.462157 L520.680172,264.577668 L526.525394,282.656202 L505.524249,279.462157 Z M607.459883,153.645338 L610.450216,153.404698 C610.586546,155.098816 610.691833,156.77921 610.766069,158.445876 L607.76904,158.579368 C607.696405,156.948625 607.593356,155.303949 607.459883,153.645338 Z M607.896371,163.523725 L610.896305,163.503827 C610.907594,165.205698 610.885378,166.892755 610.829647,168.56499 L607.831312,168.465065 C607.885706,166.832931 607.907396,165.18582 607.896371,163.523725 Z M607.562992,173.41012 L610.554458,173.636245 C610.42622,175.332733 610.262196,177.013376 610.062375,178.678161 L607.083754,178.320643 C607.278311,176.69972 607.43806,175.062883 607.562992,173.41012 Z M606.384557,183.20243 L609.343631,183.696275 C609.063719,185.373481 608.746022,187.033908 608.390534,188.677536 L605.458331,188.043351 C605.803721,186.446416 606.112465,184.832782 606.384557,183.20243 Z M604.291922,192.859811 L607.189213,193.63808 C606.748727,195.277893 606.269015,196.900206 605.750076,198.504997 L602.895608,197.581952 C603.399033,196.025135 603.864471,194.451096 604.291922,192.859811 Z M601.271525,202.20035 L604.075335,203.267423 C603.472603,204.851146 602.829959,206.41696 602.147412,207.96484 L599.402435,206.754426 C600.064131,205.253832 600.687158,203.735815 601.271525,202.20035 Z M597.290807,211.224832 L599.969243,212.576121 C599.208118,214.084773 598.407288,215.575496 597.566769,217.048269 L594.961228,215.561273 C595.776314,214.133064 596.552836,212.687591 597.290807,211.224832 Z M592.397896,219.797817 L594.924421,221.41543 C594.015938,222.834374 593.068787,224.235757 592.082984,225.619564 L589.639589,223.878927 C590.596732,222.53535 591.516161,221.174984 592.397896,219.797817 Z M586.660862,227.851867 L589.017105,229.708777 C587.977352,231.028126 586.900649,232.33062 585.787016,233.616254 L583.51944,231.652049 C584.602788,230.401378 585.649923,229.134653 586.660862,227.851867 Z M580.265616,235.264939 L582.455796,237.315088 C581.30524,238.54423 580.129172,239.752667 578.927599,240.940392 L576.818608,238.806814 C577.992605,237.646347 579.141605,236.465724 580.265616,235.264939 Z M573.232259,242.218017 L575.257873,244.430909 C574.018262,245.565611 572.753689,246.680004 571.464163,247.774085 L569.523296,245.486502 C570.784152,244.416746 572.02047,243.327252 573.232259,242.218017 Z M565.69619,248.613677 L567.551459,250.971213 C566.232998,252.008779 564.890366,253.026639 563.523572,254.024792 L561.754283,251.602063 C563.092093,250.625076 564.406059,249.628948 565.69619,248.613677 Z M557.696251,254.45734 L559.379522,256.940603 C557.993427,257.880163 556.584154,258.800801 555.151714,259.702516 L553.553517,257.163665 C554.957366,256.279948 556.338275,255.377839 557.696251,254.45734 Z M549.296271,259.746128 L550.810006,262.336225 C549.366211,263.180025 547.900334,264.005779 546.412381,264.81349 L544.981154,262.176903 C546.441447,261.384207 547.879818,260.573948 549.296271,259.746128 Z M540.616615,264.460914 L541.967561,267.139522 C540.476204,267.891683 538.963914,268.626729 537.430695,269.344665 L536.158499,266.62777 C537.665372,265.922171 539.15141,265.199884 540.616615,264.460914 Z M531.631899,268.669952 L532.82743,271.421444 C531.297326,272.086278 529.747462,272.734956 528.17784,273.367482 L527.056524,270.58492 C528.601359,269.962383 530.126483,269.324059 531.631899,268.669952 Z"
fill="#8599A4"
fill-rule="nonzero"
/>
<path
id="Line"
d="M415,229 L405.5,210 L424.5,210 L415,229 Z M413.5,154 L416.5,154 L416.5,159 L413.5,159 L413.5,154 Z M413.5,164 L416.5,164 L416.5,169 L413.5,169 L413.5,164 Z M413.5,174 L416.5,174 L416.5,179 L413.5,179 L413.5,174 Z M413.5,184 L416.5,184 L416.5,189 L413.5,189 L413.5,184 Z M413.5,194 L416.5,194 L416.5,199 L413.5,199 L413.5,194 Z M413.5,204 L416.5,204 L416.5,209 L413.5,209 L413.5,204 Z"
fill="#8599A4"
fill-rule="nonzero"
/>
<text
id="Provider"
fill="#8599A4"
font-family="Arial-Black, Arial Black"
font-size="25"
font-weight="700"
>
<tspan
x="0.06640625"
y="28"
>Provider</tspan>
</text>
<text
id="Consumer"
fill="#8599A4"
font-family="Arial-Black, Arial Black"
font-size="25"
font-weight="700"
>
<tspan
x="0.243896484"
y="327"
>Consumer</tspan>
</text>
<path
id="Line-2"
d="M161,320 L320.5,320"
stroke="#DC585F"
stroke-width="4"
stroke-dasharray="5"
fill-rule="nonzero"
/>
</g>
</g>
</svg>
</svg-container>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<div class="svg-container">
<slot />
</div>
</template>
<style lang="stylus">
.svg-container
margin 30px auto
text-align center
& > svg
max-width 100%
</style>

18
docs/.vuepress/config.js Normal file
View File

@@ -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,
}

View File

@@ -0,0 +1,22 @@
// head
module.exports = [
// 注入到页面<head> 中的标签,格式[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格式广告支持
]

View File

@@ -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')
},
},
],
]

View File

@@ -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>', // 博客版权信息支持a标签
},
// htmlModules // 插入hmtl(广告)模块
}

View File

@@ -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 和 windowRB1.展示区块宽高最大是200*200px。2.请给自定义元素定一个不超过200px的固定宽高。3.在屏宽小于960px时无论如何都不会显示。
*/
module.exports = {
homeSidebarB:
`<!-- 纵向自适应 -->
<ins class="adsbygoogle"
style="display:block;padding: 0.95rem;"
data-ad-client="ca-pub-7828333725993554"
data-ad-slot="7802654582"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>`,
// sidebarT:
// `<!-- 固定100% * 150px可显示max-height:150px 未见显示-->
// <ins class="adsbygoogle"
// style="display:inline-block;width:100%;max-height:150px"
// data-ad-client="ca-pub-7828333725993554"
// data-ad-slot="6625304284"></ins>
// <script>
// (adsbygoogle = window.adsbygoogle || []).push({});
// </script>`,
sidebarB:
`<!-- 正方形 -->
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-7828333725993554"
data-ad-slot="3508773082"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>`,
pageT:
`<!-- 固定100% * 90px可显示max-height:90px未见显示-->
<ins class="adsbygoogle"
style="display:inline-block;width:100%;max-height:90px"
data-ad-client="ca-pub-7828333725993554"
data-ad-slot="6625304284"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>`,
// pageTshowMode: 'article',
pageB:
`<!-- 横向自适应 -->
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-7828333725993554"
data-ad-slot="6620245489"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>`,
// pageBshowMode: 'article',
// windowLB: // 会遮挡部分侧边栏
// `<!-- 固定200*200px -->
// <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
// <ins class="adsbygoogle"
// style="display:inline-block;width:200px;height:200px"
// data-ad-client="ca-pub-7828333725993554"
// data-ad-slot="6625304284"></ins>
// <script>
// (adsbygoogle = window.adsbygoogle || []).push({});
// </script>`,
windowRB:
`<!-- 固定160*160px -->
<ins class="adsbygoogle"
style="display:inline-block;max-width:160px;max-height:160px"
data-ad-client="ca-pub-7828333725993554"
data-ad-slot="8377369658"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
`,
}
// module.exports = {
// homeSidebarB: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// sidebarT: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// sidebarB: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// pageT: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// pageB: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// windowLB: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// windowRB: `<div style="width:100%;height:100px;color:#fff;background: #eee;">自定义模块测试</div>`,
// }

View File

@@ -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/',
},
],
},
]

View File

@@ -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',
// // ]
// // }
// ],
}

View File

@@ -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生效
}

View File

@@ -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;

View File

@@ -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)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -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;

View File

@@ -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()

View File

@@ -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
---

View File

@@ -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
---

113
docs/01.指南/01.介绍.md Normal file
View File

@@ -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月微软发布了首个公开版本的TypeScript2013年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 POJOPlain 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<String, Object> 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<String, Object> map) {
return tableService.saveWithDate(tableNameUtil.getTableName(typeName), map);
}
```
在前端中页面实例再创建后首先需要通过getTableInfo接口获取当前页面需要渲染的表的表信息。
然后前端组件通过读取表信息后,自动生成表格,搜索表单,新增及编辑表单,按钮等页面内容。
最后通过读取单表通用接口数据,将数据加载入表格当中完成整个页面渲染。
**前端通用页面示例:**
![web](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203144128.png)

View File

@@ -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
```

View File

@@ -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等服务器进行部署。

View File

@@ -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+.
| [<img src="https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203122300.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203122347.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203122223.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203122410.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions

View File

@@ -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
```

View File

@@ -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/).

View File

@@ -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 配置
```

View File

@@ -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'
```

View File

@@ -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')
}
)
}
}
```

View File

@@ -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<any, any>{
}
export default UserApi
```
3. 根据后台接口文档写入需要请求的方法,注意请求方法
``` ts {5,6,7,8,10,11,12,13}
import BaseAxios from '@/common/http'
class UserApi extends BaseAxios<any, any>{
// 根据用户id获取角色id
public getRoleByUserId(param: any): Promise<Base.AxiosResponse<any>> {
return this.axios.get('/ftUser/getRoleByUserId', param)
}
// 给用户添加角色
public addRole(param: any): Promise<Base.AxiosResponse<any>> {
return this.axios.post('/ftUser/addRole', param)
}
}
export default UserApi
```

View File

@@ -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
<template>
<div class="dashboard-editor-container">
</div>
</template>
<script lang="ts" src="../../api/views/user-example/user-example.ts"></script>
<style lang="scss">
</style>
```
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}
<template>
<div class="dashboard-editor-container">
<table-head-co
ref="tableCo"
:check-box-visible="true"
:formatter="tableFomatter"
:form-list="tableList"
:table-heads="tableHead"
:loading="loading"
:slot-visible="true"
:width="operationWidth"
@handle-sort-change="handleSortChange"
@handle-selection-change="handleSelectionChange"
/>
</div>
</template>
<script lang="ts" src="../../api/views/user-example/user-example.ts"></script>
<style lang="scss">
</style>
```
然后我们刷新页面,就可以看到刚刚引入的组件了。
![table](https://cdn.jsdelivr.net/gh/taixingyiji/image_store@main/blog/20210203162357.png)
7. 页面实例讲解完毕,最后,请按照使用者按照需要进行页面相关的开发或引入其他组件进行开发。

View File

@@ -0,0 +1,457 @@
---
title: 通用表格组件
date: 2021-02-03 09:26:32
permalink: /pages/1ab4ce/
---
# 通用表格组件
## 普通表格
普通表格,仅传入数据显示
***
<ClientOnly><Web-Table></Web-Table></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:cellAlign="center"
/>
</div>
</template>
<script>
import tableHeadCo from '@/components/CommonCo/tableHeadCo.vue'
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component({
name: 'commonExample',
components: {
tableHeadCo,
}
})
export default class extends Vue {
tableHead:any[] = [
{ fieldName: 'date', nameCn: '日期', isHead: 1 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
]
tableList:any[] = [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
</script>
<style scoped>
</style>
```
***js版本***
``` vue
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:cellAlign="center"
/>
</div>
</template>
<script>
import tableHeadCo from '@/components/CommonCo/tableHeadCo.vue'
export default {
components: {
tableHeadCo
},
data(){
return {
tableHead: [
{ fieldName: 'date', nameCn: '日期', isHead: 1 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
],
tableList: [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
}
}
</script>
<style scoped>
</style>
```
:::
## 带复选框的表格
带复选框的表格,提供复选框回调函数,点击复选框时可打开控制台查看打印内容。
***
<ClientOnly><Web-TableCheckBox></Web-TableCheckBox></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:check-box-visible="true"
@handle-selection-change="handleSelectionChange"
/>
</div>
</template>
<script>
import tableHeadCo from '@/components/CommonCo/tableHeadCo.vue'
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component({
name: 'commonExample',
components: {
tableHeadCo,
}
})
export default class extends Vue {
tableHead:any[] = [
{ fieldName: 'date', nameCn: '日期', isHead: 1 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
]
tableList:any[] = [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
// 复选框监听事件
handleSelectionChange(val){
console.log(val)
}
}
</script>
<style scoped>
</style>
```
***js版本***
``` vue
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:check-box-visible="true"
@handle-selection-change="handleSelectionChange"
/>
</div>
</template>
<script>
import tableHeadCo from '@/components/CommonCo/tableHeadCo.vue'
export default {
components: {
tableHeadCo
},
data(){
return {
tableHead: [
{ fieldName: 'date', nameCn: '日期', isHead: 1 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
],
tableList: [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
},
methods:{
// 复选框改变监听事件
handleSelectionChange(val){
console.log(val)
}
}
}
</script>
<style scoped>
</style>
```
:::
## 带插槽的表格
表格组件提供插槽功能,可以将插槽设置成按钮等其他功能,点击编辑按钮可以查看控制台打印内容。
***
<ClientOnly><Web-TableSlot></Web-TableSlot></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:slot-visible="true"
>
<template
v-slot="scope"
>
<el-button
size="mini"
@click="showEdit(scope.item.row)"
>
编辑
</el-button>
</template>
</table-head-co>
</div>
</template>
<script>
import tableHeadCo from '../Co/tableHeadCo'
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component({
name: 'commonExample',
components: {
tableHeadCo,
}
})
export default class extends Vue {
tableHead:any[] = [
{ fieldName: 'date', nameCn: '日期', isHead: 1, commonWidth: 120 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1, commonWidth: 100 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
]
tableList:any[] = [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
// 点击事件
showEdit(val){
console.log(val)
}
}
</script>
<style scoped>
</style>
```
***js版本***
``` vue
<template>
<div>
<table-head-co
ref="tableCo"
:form-list="tableList"
:table-heads="tableHead"
:width="200"
:slot-visible="true"
>
<template
v-slot="scope"
>
<el-button
size="mini"
@click="showEdit(scope.item.row)"
>
编辑
</el-button>
</template>
</table-head-co>
</div>
</template>
<script>
import tableHeadCo from '@/components/CommonCo/tableHeadCo.vue'
export default {
components: {
tableHeadCo
},
data(){
return {
tableHead: [
{ fieldName: 'date', nameCn: '日期', isHead: 1, commonWidth: 120 },
{ fieldName: 'name', nameCn: '姓名', isHead: 1, commonWidth: 100 },
{ fieldName: 'address', nameCn: '地址', isHead: 1 },
],
tableList: [{
date: '2016-05-02',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-04',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-01',
name: '小明',
address: '北京市海淀区中关村南四街'
}, {
date: '2016-05-03',
name: '小明',
address: '北京市海淀区中关村南四街'
}]
}
},
methods:{
showEdit(val){
console.log(val)
}
}
}
</script>
<style scoped>
</style>
```
:::
## Table Attributes
&nbsp;
&nbsp;
| 参数 | 说明 | 类型 | 可选值 | 默认值
| :---| :---- | :---- |:---- |:---- |
| 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
&nbsp;
&nbsp;
| 事件名 | 说明 | 参数 |
| :---| :---- | :---- |
| handle-selection-change | 当用户手动勾选数据行的 Checkbox 时触发的事件 | row |
| handle-sort-change | 当表格的排序条件发生变化的时候会触发该事件 | column |
## Table Slot
&nbsp;
&nbsp;
| name | 说明 |
| :---| :---- |
| - | 用于放置在操作列上的按钮等元素需要从scope.item中获取当前行的值 |
## Table Header style
&nbsp;
&nbsp;
| name | 说明 |
| :---| :---- |
| header-cell-style | 用于设置表头样式 |

View File

@@ -0,0 +1,672 @@
---
title: 表单组件
date: 2021-02-03 09:26:32
permalink: /pages/65ed1c/
---
# 表单组件
## 基本表单
表单组件以模态框的形式展示表单内容。
***
<ClientOnly><Web-Form></Web-Form></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<el-button type="primary" size="medium" @click="showAdd">新增表单</el-button>
<form-co
ref="formCo"
:form-title="dialogTitle"
:form-visible="dialogCreateFormVisible"
:form-sub-botton="'提交'"
:form-key="tableHead"
:form-data="formData"
:form-rules="formRules"
:form-type="formType"
:disable-field="disableField"
@form-submit="formSubmit"
@cancel-form="cancelForm"
@before-cancel-form="cancelForm"
/>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import formCo from "../Co/formCo";
@Component({
name: "commonForm",
components: {
formCo
}
})
export default class extends Vue {
public dialogTitle = '新增记录';
public formType = 1;
public formData: any = {};
public dialogCreateFormVisible = false;
public tableHead: any = [
{
fieldId: 1,
fieldName: "user_id",
isChange: 0,
isForm: 0,
javaType: "Integer",
nameCn: "用户ID",
webType: "el-input",
},
{
fieldId: 2,
fieldName: "username",
isChange: 1,
isForm: 1,
javaType: "String",
nameCn: "用户名",
webType: "el-input",
},
{
fieldId: 3,
fieldName: "password",
isChange: 1,
isForm: 1,
javaType: "String",
nameCn: "密码",
webType: "password",
},
{
fieldId: 4,
fieldName: "enabled",
isChange: 1,
isForm: 1,
javaType: "Integer",
nameCn: "用户状态",
selectList: [
{
fieldId: 4,
selectId: 1,
selectKey: "禁用",
selectType: 1,
selectValue: "1",
},
{
fieldId: 4,
selectId: 2,
selectKey: "启用",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 5,
fieldName: "user_type",
isChange: 1,
isForm: 1,
javaType: "Integer",
nameCn: "用户类型",
selectList: [
{
fieldId: 5,
selectId: 3,
selectKey: "外部用户",
selectType: 1,
selectValue: "1",
},
{
fieldId: 5,
selectId: 4,
selectKey: "内部用户",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 6,
fieldName: "create_time",
isChange: 1,
isForm: 1,
javaType: "datetime",
nameCn: "创建时间",
webType: "el-date-picker",
},
{
fieldId: 7,
fieldName: "update_time",
isChange: 1,
isForm: 1,
javaType: "datetime",
nameCn: "修改时间",
webType: "el-date-picker",
}
];
public disableField: Array<any> = ["create_time", "update_time"];
public formRules: any = {
assword: [
{ required: true, message: "密码不能为空", trigger: "blur" },
],
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" },
],
};
//提交表单
formSubmit(value) {
this.dialogCreateFormVisible = false;
},
//关闭表单
cancelForm() {
this.dialogCreateFormVisible = false;
this.formData = {};
this.$nextTick(() => {
const ref = this.$refs.formCo;
ref.$refs.formCommon.resetFields();
});
},
showAdd() {
this.dialogTitle = "新增记录";
this.formType = 1;
this.formData = {};
this.dialogCreateFormVisible = true;
},
}
</script>
```
***js版本***
``` vue
<template>
<div>
<el-button type="primary" size="medium" @click="showAdd">新增表单</el-button>
<form-co
ref="formCo"
:form-title="dialogTitle"
:form-visible="dialogCreateFormVisible"
:form-sub-botton="'提交'"
:form-key="tableHead"
:form-data="formData"
:form-rules="formRules"
:form-type="formType"
:disable-field="disableField"
@form-submit="formSubmit"
@cancel-form="cancelForm"
@before-cancel-form="cancelForm"
/>
</div>
</template>
<script>
import formCo from "../Co/formCo";
export default {
components: {
formCo,
},
data() {
return {
dialogTitle: "新增记录",
formType: 1,
formData: {},
dialogCreateFormVisible: false,
tableHead: [
{
fieldId: 1,
fieldName: "user_id",
isChange: 0,
isForm: 0,
javaType: "Integer",
nameCn: "用户ID",
webType: "el-input",
},
{
fieldId: 2,
fieldName: "username",
isChange: 1,
isForm: 1,
javaType: "String",
nameCn: "用户名",
webType: "el-input",
},
{
fieldId: 3,
fieldName: "password",
isChange: 1,
isForm: 1,
javaType: "String",
nameCn: "密码",
webType: "password",
},
{
fieldId: 4,
fieldName: "enabled",
isChange: 1,
isForm: 1,
javaType: "Integer",
nameCn: "用户状态",
selectList: [
{
fieldId: 4,
selectId: 1,
selectKey: "禁用",
selectType: 1,
selectValue: "1",
},
{
fieldId: 4,
selectId: 2,
selectKey: "启用",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 5,
fieldName: "user_type",
isChange: 1,
isForm: 1,
javaType: "Integer",
nameCn: "用户类型",
selectList: [
{
fieldId: 5,
selectId: 3,
selectKey: "外部用户",
selectType: 1,
selectValue: "1",
},
{
fieldId: 5,
selectId: 4,
selectKey: "内部用户",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 6,
fieldName: "create_time",
isChange: 1,
isForm: 1,
javaType: "datetime",
nameCn: "创建时间",
webType: "el-date-picker",
},
{
fieldId: 7,
fieldName: "update_time",
isChange: 1,
isForm: 1,
javaType: "datetime",
nameCn: "修改时间",
webType: "el-date-picker",
},
],
disableField: ["create_time", "update_time"],
formRules: {
password: [
{ required: true, message: "密码不能为空", trigger: "blur" },
],
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" },
],
},
};
},
methods: {
//提交表单
formSubmit(value) {
this.dialogCreateFormVisible = false;
},
//关闭表单
cancelForm() {
this.dialogCreateFormVisible = false;
this.formData = {};
this.$nextTick(() => {
const ref = this.$refs.formCo;
ref.$refs.formCommon.resetFields();
});
},
showAdd() {
this.dialogTitle = "新增记录";
this.formType = 1;
this.formData = {};
this.dialogCreateFormVisible = true;
},
},
};
</script>
```
:::
## 查询表单
表单组件查询功能。
***
<ClientOnly><Web-SearchForm></Web-SearchForm></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<el-row>
<el-col :span="24">
<el-card shadow="always" class="select-card">
<search-co
:form-key="tableHead"
:search-data="searchForm"
:time-format="'yyyy-MM-dd HH:mm:ss'"
@search-submit="query"
@reset-search-form="resetSearch"
/>
</el-card>
</el-col>
</el-row>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import searchCo from "../Co/searchCo";
@Component({
name: "commonForm",
components: {
searchCo
}
})
export default class extends Vue {
public searchForm: any = {};
public tableHead: any = [
{
fieldId: 1,
fieldName: "user_id",
isChange: 0,
isForm: 0,
javaType: "Integer",
nameCn: "用户ID",
webType: "el-input",
},
{
fieldId: 2,
fieldName: "username",
isChange: 1,
isSearch: 1,
javaType: "String",
nameCn: "用户名",
webType: "el-input",
},
{
fieldId: 3,
fieldName: "password",
isChange: 1,
isSearch: 0,
javaType: "String",
nameCn: "密码",
webType: "password",
},
{
fieldId: 4,
fieldName: "enabled",
isChange: 1,
isSearch: 1,
javaType: "Integer",
nameCn: "用户状态",
selectList: [
{
fieldId: 4,
selectId: 1,
selectKey: "禁用",
selectType: 1,
selectValue: "1",
},
{
fieldId: 4,
selectId: 2,
selectKey: "启用",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 5,
fieldName: "user_type",
isChange: 1,
isSearch: 1,
javaType: "Integer",
nameCn: "用户类型",
selectList: [
{
fieldId: 5,
selectId: 3,
selectKey: "外部用户",
selectType: 1,
selectValue: "1",
},
{
fieldId: 5,
selectId: 4,
selectKey: "内部用户",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 6,
fieldName: "create_time",
isChange: 1,
isSearch: 1,
javaType: "datetime",
nameCn: "创建时间",
webType: "el-date-picker",
},
{
fieldId: 7,
fieldName: "update_time",
isChange: 1,
isSearch: 1,
javaType: "datetime",
nameCn: "修改时间",
webType: "el-date-picker",
},
];
// 查询表格
query() {};
// 重置search表单
resetSearch() {
this.searchForm = {};
};
}
</script>
```
***js版本***
``` vue
<template>
<el-row>
<el-col :span="24">
<el-card shadow="always" class="select-card">
<search-co
:form-key="tableHead"
:search-data="searchForm"
:time-format="'yyyy-MM-dd HH:mm:ss'"
@search-submit="query"
@reset-search-form="resetSearch"
/>
</el-card>
</el-col>
</el-row>
</template>
<script>
import searchCo from "../Co/searchCo";
export default {
components: {
searchCo,
},
data() {
return {
searchForm: {},
tableHead: [
{
fieldId: 1,
fieldName: "user_id",
isChange: 0,
isForm: 0,
javaType: "Integer",
nameCn: "用户ID",
webType: "el-input",
},
{
fieldId: 2,
fieldName: "username",
isChange: 1,
isSearch: 1,
javaType: "String",
nameCn: "用户名",
webType: "el-input",
},
{
fieldId: 3,
fieldName: "password",
isChange: 1,
isSearch: 0,
javaType: "String",
nameCn: "密码",
webType: "password",
},
{
fieldId: 4,
fieldName: "enabled",
isChange: 1,
isSearch: 1,
javaType: "Integer",
nameCn: "用户状态",
selectList: [
{
fieldId: 4,
selectId: 1,
selectKey: "禁用",
selectType: 1,
selectValue: "1",
},
{
fieldId: 4,
selectId: 2,
selectKey: "启用",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 5,
fieldName: "user_type",
isChange: 1,
isSearch: 1,
javaType: "Integer",
nameCn: "用户类型",
selectList: [
{
fieldId: 5,
selectId: 3,
selectKey: "外部用户",
selectType: 1,
selectValue: "1",
},
{
fieldId: 5,
selectId: 4,
selectKey: "内部用户",
selectType: 1,
selectValue: "2",
},
],
webType: "el-select",
},
{
fieldId: 6,
fieldName: "create_time",
isChange: 1,
isSearch: 1,
javaType: "datetime",
nameCn: "创建时间",
webType: "el-date-picker",
},
{
fieldId: 7,
fieldName: "update_time",
isChange: 1,
isSearch: 1,
javaType: "datetime",
nameCn: "修改时间",
webType: "el-date-picker",
},
],
};
},
methods: {
// 查询表格
query() {},
// 重置search表单
resetSearch() {
this.searchForm = {};
},
},
};
</script>
```
:::
## Form Attributes
&nbsp;
&nbsp;
| 参数 | 说明 | 类型 | 可选值 | 默认值
| :---| :---- | :---- |:---- |:---- |
| 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
&nbsp;
&nbsp;
| 事件名 | 说明 | 参数 |
| :---| :---- | :---- |
| form-submit | 当用户手动点击提交按钮时触发的事件 | - |
| cancel-form | 当用户手动点击取消按钮时触发的事件 | - |
| search-submit | 表单提交触发的事件 | - |
| reset-search-form | 重置搜索表单触发的事件 | - |

View File

@@ -0,0 +1,274 @@
---
title: 流程图组件
date: 2021-02-03 09:26:32
permalink: /pages/49aa37/
---
# 流程图组件
## 基本流程图
表格组件提供获取Object内容作为流程图的标识和名称该组件也可以不传入任何参数直接引用例如`<bpmn/>`即可调用组件。
注意:标识必须以字母和下划线开头,不支持中文。
***
<ClientOnly><Web-BpmnNameForm></Web-BpmnNameForm></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<bpmn :updateDate="updateDate" :updateBpmnFlag="updateBpmnFlag" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import bpmn from "../Bpmn/bpmn";
@Component({
name: "bpmnNameForm",
components: {
bpmn
}
})
export default class extends Vue {
private updateDate: { [key: string]: any } = { key: "process", name: "流程图" }
private updateBpmnFlag: Boolean = false
}
</script>
```
***js版本***
``` vue
<template>
<div>
<bpmn :updateDate="updateDate" :updateBpmnFlag="updateBpmnFlag" />
</div>
</template>
<script>
import bpmn from "../Bpmn/bpmn";
export default {
components: {
bpmn,
},
data() {
return {
updateDate: { key: "process", name: "流程图" },
updateBpmnFlag: false
};
},
};
</script>
```
:::
## 仅能查看的流程图
该组件仅提供查看功能。
***
<ClientOnly><Web-OnlyShowBpmn></Web-OnlyShowBpmn></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<el-button type="primary" @click="showBpmn()">查看流程图</el-button>
<el-dialog title="流程图" :visible.sync="dialogShowBpmnVisible">
<bpmnView :showData='showBpmnData'></bpmnView>
</el-dialog>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import bpmnView from "../Bpmn/bpmnView";
@Component({
name: "onlyShowBpmn",
components: {
bpmnView
}
})
export default class extends Vue {
private dialogShowBpmnVisible: boolean = false;
private showBpmnData:any =
`
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" id="m1577635100724" name="" targetNamespace="http://www.activiti.org/testm1577635100724">
<process id="process1" name="流程图1" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties />
</extensionElements>
<startEvent id="_2" name="start">
<outgoing>Flow_1mn3ncy</outgoing>
</startEvent>
<sequenceFlow id="Flow_1mn3ncy" sourceRef="_2" targetRef="Activity_03u7gdx" />
<endEvent id="Event_1jrsaua">
<incoming>Flow_0wm6kz4</incoming>
</endEvent>
<sequenceFlow id="Flow_0wm6kz4" sourceRef="Activity_03u7gdx" targetRef="Event_1jrsaua" />
<userTask id="Activity_03u7gdx">
<incoming>Flow_1mn3ncy</incoming>
<outgoing>Flow_0wm6kz4</outgoing>
</userTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="process1">
<bpmndi:BPMNEdge id="Flow_1mn3ncy_di" bpmnElement="Flow_1mn3ncy">
<di:waypoint x="176" y="384" />
<di:waypoint x="230" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wm6kz4_di" bpmnElement="Flow_0wm6kz4">
<di:waypoint x="330" y="384" />
<di:waypoint x="392" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1jrsaua_di" bpmnElement="Event_1jrsaua">
<omgdc:Bounds x="392" y="366" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f4773x_di" bpmnElement="Activity_03u7gdx">
<omgdc:Bounds x="230" y="344" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`,
private async showBpmn() {
this.dialogShowBpmnVisible = true;
}
}
</script>
```
***js版本***
``` vue
<template>
<div>
<el-button type="primary" @click="showBpmn()">查看流程图</el-button>
<el-dialog title="流程图" :visible.sync="dialogShowBpmnVisible">
<bpmn-view :showData='showBpmnData'></bpmn-view>
</el-dialog>
</div>
</template>
<script>
import bpmnView from "../Bpmn/bpmnView";
export default {
components: {
bpmnView,
},
data() {
return {
dialogShowBpmnVisible: false,
showBpmnData: `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" id="m1577635100724" name="" targetNamespace="http://www.activiti.org/testm1577635100724">
<process id="process1" name="流程图1" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties />
</extensionElements>
<startEvent id="_2" name="start">
<outgoing>Flow_1mn3ncy</outgoing>
</startEvent>
<sequenceFlow id="Flow_1mn3ncy" sourceRef="_2" targetRef="Activity_03u7gdx" />
<endEvent id="Event_1jrsaua">
<incoming>Flow_0wm6kz4</incoming>
</endEvent>
<sequenceFlow id="Flow_0wm6kz4" sourceRef="Activity_03u7gdx" targetRef="Event_1jrsaua" />
<userTask id="Activity_03u7gdx">
<incoming>Flow_1mn3ncy</incoming>
<outgoing>Flow_0wm6kz4</outgoing>
</userTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="process1">
<bpmndi:BPMNEdge id="Flow_1mn3ncy_di" bpmnElement="Flow_1mn3ncy">
<di:waypoint x="176" y="384" />
<di:waypoint x="230" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wm6kz4_di" bpmnElement="Flow_0wm6kz4">
<di:waypoint x="330" y="384" />
<di:waypoint x="392" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1jrsaua_di" bpmnElement="Event_1jrsaua">
<omgdc:Bounds x="392" y="366" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f4773x_di" bpmnElement="Activity_03u7gdx">
<omgdc:Bounds x="230" y="344" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`,
};
},
methods: {
showBpmn() {
this.dialogShowBpmnVisible = true;
},
},
};
</script>
```
:::
## Bpmn Attributes
&nbsp;
&nbsp;
| 参数 | 说明 | 类型 | 可选值 | 默认值
| :---| :---- | :---- |:---- |:---- |
| updateBpmnFlag | 是否为编辑流程图 | Boolean |-|false|
| updateDate | 数据(编辑时传入String) | String/Object |-|-|
## Bpmn Components Events
&nbsp;
&nbsp;
| 事件名 | 说明 | 参数 |
| :---| :---- | :---- |
| openBpmn | 当用户手动点击文件icon时触发的事件打开bpmn格式文件 | file |
| downloadBpmn | 当用户手动点击下载icon时触发的事件下载bpmn格式文件 | - |
| downloadSvg | 当用户手动点击图片icon时触发的事件下载svg格式文件 | - |
| handleUpdateData | 当用户手动点击上传icon时触发的事件上传已有流程图 | - |

View File

@@ -0,0 +1,185 @@
---
title: 流程图组件之编辑
date: 2021-02-03 09:26:32
permalink: /pages/514c8f/
---
# 流程图组件之编辑
## 获取已有流程图内容,并编辑
表格组件提供获取已有流程图并编辑的功能。
***
<ClientOnly><Web-EditBpmn></Web-EditBpmn></ClientOnly>
::: details 代码块
***ts版本***
``` vue
<template>
<div>
<bpmn :updateDate="updateDate" :updateBpmnFlag="updateBpmnFlag" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import bpmn from "../Bpmn/bpmn";
@Component({
name: "editBpmn",
components: {
bpmn
}
})
export default class extends Vue {
private updateBpmnFlag: Boolean = false
private updateDate: any = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" id="m1577635100724" name="" targetNamespace="http://www.activiti.org/testm1577635100724">
<process id="process1" name="流程图1" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties />
</extensionElements>
<startEvent id="_2" name="start">
<outgoing>Flow_1mn3ncy</outgoing>
</startEvent>
<sequenceFlow id="Flow_1mn3ncy" sourceRef="_2" targetRef="Activity_03u7gdx" />
<endEvent id="Event_1jrsaua">
<incoming>Flow_0wm6kz4</incoming>
</endEvent>
<sequenceFlow id="Flow_0wm6kz4" sourceRef="Activity_03u7gdx" targetRef="Event_1jrsaua" />
<userTask id="Activity_03u7gdx">
<incoming>Flow_1mn3ncy</incoming>
<outgoing>Flow_0wm6kz4</outgoing>
</userTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="process1">
<bpmndi:BPMNEdge id="Flow_1mn3ncy_di" bpmnElement="Flow_1mn3ncy">
<di:waypoint x="176" y="384" />
<di:waypoint x="230" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wm6kz4_di" bpmnElement="Flow_0wm6kz4">
<di:waypoint x="330" y="384" />
<di:waypoint x="392" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1jrsaua_di" bpmnElement="Event_1jrsaua">
<omgdc:Bounds x="392" y="366" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f4773x_di" bpmnElement="Activity_03u7gdx">
<omgdc:Bounds x="230" y="344" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`
}
</script>
```
***js版本***
``` vue
<template>
<div>
<bpmn :updateDate="updateDate" :updateBpmnFlag="updateBpmnFlag" />
</div>
</template>
<script>
import bpmn from "../Bpmn/bpmn";
export default {
components: {
bpmn,
},
data() {
return {
updateBpmnFlag: true,
updateDate: `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" id="m1577635100724" name="" targetNamespace="http://www.activiti.org/testm1577635100724">
<process id="process1" name="流程图1" processType="None" isClosed="false" isExecutable="true">
<extensionElements>
<camunda:properties />
</extensionElements>
<startEvent id="_2" name="start">
<outgoing>Flow_1mn3ncy</outgoing>
</startEvent>
<sequenceFlow id="Flow_1mn3ncy" sourceRef="_2" targetRef="Activity_03u7gdx" />
<endEvent id="Event_1jrsaua">
<incoming>Flow_0wm6kz4</incoming>
</endEvent>
<sequenceFlow id="Flow_0wm6kz4" sourceRef="Activity_03u7gdx" targetRef="Event_1jrsaua" />
<userTask id="Activity_03u7gdx">
<incoming>Flow_1mn3ncy</incoming>
<outgoing>Flow_0wm6kz4</outgoing>
</userTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="process1">
<bpmndi:BPMNEdge id="Flow_1mn3ncy_di" bpmnElement="Flow_1mn3ncy">
<di:waypoint x="176" y="384" />
<di:waypoint x="230" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wm6kz4_di" bpmnElement="Flow_0wm6kz4">
<di:waypoint x="330" y="384" />
<di:waypoint x="392" y="384" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape__2" bpmnElement="_2">
<omgdc:Bounds x="144" y="368" width="32" height="32" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="149" y="400" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1jrsaua_di" bpmnElement="Event_1jrsaua">
<omgdc:Bounds x="392" y="366" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f4773x_di" bpmnElement="Activity_03u7gdx">
<omgdc:Bounds x="230" y="344" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`
};
},
};
</script>
```
:::
## Bpmn Attributes
&nbsp;
&nbsp;
| 参数 | 说明 | 类型 | 可选值 | 默认值
| :---| :---- | :---- |:---- |:---- |
| updateBpmnFlag | 是否为编辑流程图 | Boolean |-|false|
| updateDate | 数据(编辑时传入String) | String/Object |-|-|
## Bpmn Components Events
&nbsp;
&nbsp;
| 事件名 | 说明 | 参数 |
| :---| :---- | :---- |
| openBpmn | 当用户手动点击文件icon时触发的事件打开bpmn格式文件 | file |
| downloadBpmn | 当用户手动点击下载icon时触发的事件下载bpmn格式文件 | - |
| downloadSvg | 当用户手动点击图片icon时触发的事件下载svg格式文件 | - |
| handleUpdateData | 当用户手动点击上传icon时触发的事件上传已有流程图 | - |

49
docs/index.md Normal file
View File

@@ -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版本。
:::

38
package.json Normal file
View File

@@ -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"
}
}

21
theme-vdoing/LICENSE Normal file
View File

@@ -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.

7
theme-vdoing/README.md Normal file
View File

@@ -0,0 +1,7 @@
# vuepress-theme-vdoing
vuepress-theme-vdoing for vuepress
一个基于VuePress的 知识管理兼博客 主题。
[More](https://github.com/xugaoyi/vuepress-theme-vdoing#readme).

View File

@@ -0,0 +1,165 @@
<template>
<form
id="search-form"
class="algolia-search-wrapper search-box"
role="search"
>
<input
id="algolia-search-input"
class="search-query"
:placeholder="placeholder"
/>
</form>
</template>
<script>
export default {
props: ['options'],
data () {
return {
placeholder: undefined
}
},
mounted () {
this.initialize(this.options, this.$lang)
this.placeholder = this.$site.themeConfig.searchPlaceholder || ''
},
methods: {
initialize (userOptions, lang) {
Promise.all([
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
]).then(([docsearch]) => {
docsearch = docsearch.default
const { algoliaOptions = {} } = userOptions
docsearch(Object.assign(
{},
userOptions,
{
inputSelector: '#algolia-search-input',
// #697 Make docsearch work well at i18n mode.
algoliaOptions: Object.assign({
'facetFilters': [`lang:${lang}`].concat(algoliaOptions.facetFilters || [])
}, algoliaOptions),
handleSelected: (input, event, suggestion) => {
const { pathname, hash } = new URL(suggestion.url)
const _hash = decodeURIComponent(hash)
this.$router.push(`${routepath}${_hash}`)
}
}
))
})
},
update (options, lang) {
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
this.initialize(options, lang)
}
},
watch: {
$lang (newValue) {
this.update(this.options, newValue)
},
options (newValue) {
this.update(newValue, this.$lang)
}
}
}
</script>
<style lang="stylus">
.algolia-search-wrapper
& > span
vertical-align middle
.algolia-autocomplete
line-height normal
.ds-dropdown-menu
background-color #fff
border 1px solid #999
border-radius 4px
font-size 16px
margin 6px 0 0
padding 4px
text-align left
&:before
border-color #999
[class*=ds-dataset-]
border none
padding 0
.ds-suggestions
margin-top 0
.ds-suggestion
border-bottom 1px solid var(--borderColor)
.algolia-docsearch-suggestion--highlight
color #2c815b
.algolia-docsearch-suggestion
border-color var(--borderColor)
padding 0
.algolia-docsearch-suggestion--category-header
padding 5px 10px
margin-top 0
background $accentColor
color #fff
font-weight 600
.algolia-docsearch-suggestion--highlight
background rgba(255, 255, 255, 0.6)
.algolia-docsearch-suggestion--wrapper
padding 0
.algolia-docsearch-suggestion--title
font-weight 600
margin-bottom 0
color var(--textColor)
.algolia-docsearch-suggestion--subcategory-column
vertical-align top
padding 5px 7px 5px 5px
border-color var(--borderColor)
background #f1f3f5
&:after
display none
.algolia-docsearch-suggestion--subcategory-column-text
color #555
.algolia-docsearch-footer
border-color var(--borderColor)
.ds-cursor .algolia-docsearch-suggestion--content
background-color #e7edf3 !important
color var(--textColor)
@media (min-width $MQMobile)
.algolia-search-wrapper
.algolia-autocomplete
.algolia-docsearch-suggestion
.algolia-docsearch-suggestion--subcategory-column
float none
width 150px
min-width 150px
display table-cell
.algolia-docsearch-suggestion--content
float none
display table-cell
width 100%
vertical-align top
.ds-dropdown-menu
min-width 515px !important
@media (max-width $MQMobile)
.algolia-search-wrapper
.ds-dropdown-menu
min-width calc(100vw - 4rem) !important
max-width calc(100vw - 4rem) !important
.algolia-docsearch-suggestion--wrapper
padding 5px 7px 5px 5px !important
.algolia-docsearch-suggestion--subcategory-column
padding 0 !important
background white !important
.algolia-docsearch-suggestion--subcategory-column-text:after
content ' > '
font-size 10px
line-height 14.4px
display inline-block
width 5px
margin -3px 3px 0
vertical-align middle
</style>

View File

@@ -0,0 +1,153 @@
<template>
<div class="custom-page archives-page">
<div class="theme-vdoing-wrapper">
<h1>
<img
:src="currentBadge"
v-if="$themeConfig.titleBadge === false ? false : true"
/>
{{this.$page.title}}
</h1>
<ul>
<template v-for="(item, index) in postsList">
<li
class="year"
v-if="(year = getYear(index)) !== getYear(index-1)"
:key="index+$sortPostsByDate.length"
>
<h2>{{year}}</h2>
</li>
<li :key="index">
<router-link :to="item.path">
<span>{{ getDate(item) }}</span>
{{item.title}}
</router-link>
</li>
</template>
</ul>
</div>
</div>
</template>
<script>
import debounce from 'lodash.debounce'
import { type } from '../util'
import TitleBadgeMixin from '../mixins/titleBadge'
export default {
mixins: [TitleBadgeMixin],
data () {
return {
postsList: [],
perPage: 80, // 每页长
currentPage: 1// 当前页
}
},
created () {
this.getPageData()
},
mounted () {
window.addEventListener('scroll', debounce(() => {
if (this.postsList.length < this.$sortPostsByDate.length) {
const docEl = document.documentElement
const docBody = document.body
const scrollTop = docEl.scrollTop || docBody.scrollTop;
const clientHeight = docEl.clientHeight || docBody.clientHeight;
const scrollHeight = docEl.scrollHeight || docBody.scrollHeight;
if (scrollHeight > clientHeight && scrollTop + clientHeight >= scrollHeight - 250) {
this.loadmore()
}
}
}, 200))
},
methods: {
getPageData () {
const currentPage = this.currentPage
const perPage = this.perPage
this.postsList = this.postsList.concat(this.$sortPostsByDate.slice((currentPage - 1) * perPage, currentPage * perPage))
},
loadmore () {
this.currentPage = this.currentPage + 1
this.getPageData()
},
getYear (index) {
const item = this.postsList[index]
if (!item) {
return
}
const { frontmatter: { date } } = item
if (date && type(date) === 'string') {
return date.split(" ")[0].slice(0, 4)
}
},
getDate (item) {
const { frontmatter: { date } } = item
if (date && type(date) === 'string') {
return date.split(" ")[0].slice(5, 10)
}
}
}
}
</script>
<style lang='stylus'>
@require '../styles/wrapper.styl'
.archives-page
.theme-vdoing-wrapper
@extend $vdoing-wrapper
position relative
@media (min-width $contentWidth + 80)
margin-top 1.5rem !important
ul, li
margin 0
padding 0
li
list-style none
&.year
position sticky
top $navbarHeight
background var(--mainBg)
z-index 1
&.year:not(:first-child)
margin-top 3.5rem
h2
margin-bottom 0.8rem
font-weight 400
padding 0.5rem 0
a
display block
color var(--textColor)
transition padding 0.3s
padding 0.5rem 2rem
line-height 1.2rem
&:hover
padding-left 2.5rem
color $accentColor
background #f9f9f9
@media (max-width $contentWidth + 80)
padding 0.5rem 1rem
font-weight normal
&:hover
padding-left 1.5rem
span
opacity 0.6
font-size 0.85rem
font-weight 400
margin-right 0.3rem
.loadmore
text-align center
margin-top 1rem
opacity 0.5
.theme-mode-dark .archives-page .theme-vdoing-wrapper li a:hover, .theme-mode-read .archives-page .theme-vdoing-wrapper li a:hover
background var(--customBlockBg)
.hide-navbar
.archives-page
.theme-vdoing-wrapper
li.year
top 0
</style>

View File

@@ -0,0 +1,206 @@
<template>
<div class="articleInfo-wrap">
<div class="articleInfo">
<ul
class="breadcrumbs"
v-if="articleInfo.classify1 && articleInfo.classify1 !== '_posts'"
>
<li>
<router-link
to="/"
class="iconfont icon-home"
title="首页"
/>
</li>
<li>
<router-link
v-if="articleInfo.cataloguePermalink"
:to="articleInfo.cataloguePermalink"
:title="articleInfo.classify1+'-目录页'"
>{{articleInfo.classify1}}</router-link>
<router-link
v-else-if="$themeConfig.category !== false"
:to="`/categories/?category=${encodeURIComponent(articleInfo.classify1)}`"
title="分类"
>{{articleInfo.classify1}}</router-link>
<span v-else>{{ articleInfo.classify1 }}</span>
</li>
<li v-if="articleInfo.classify2">
<router-link
v-if="articleInfo.cataloguePermalink"
:to="articleInfo.cataloguePermalink + '/#' + articleInfo.classify2"
:title="articleInfo.classify1+'#'+articleInfo.classify2"
>{{articleInfo.classify2}}</router-link>
<router-link
v-else-if="$themeConfig.category !== false"
:to="`/categories/?category=${encodeURIComponent(articleInfo.classify2)}`"
title="分类"
>{{articleInfo.classify2}}</router-link>
<span v-else>{{articleInfo.classify2}}</span>
</li>
<li v-if="articleInfo.classify3">
<router-link
v-if="articleInfo.cataloguePermalink"
:to="articleInfo.cataloguePermalink + '/#' + articleInfo.classify3"
:title="articleInfo.classify1+'#'+articleInfo.classify3"
>{{articleInfo.classify3}}</router-link>
<router-link
v-else-if="$themeConfig.category !== false"
:to="`/categories/?category=${encodeURIComponent(articleInfo.classify3)}`"
title="分类"
>{{articleInfo.classify3}}</router-link>
<span v-else>{{articleInfo.classify3}}</span>
</li>
</ul>
<div class="info">
<div
class="author iconfont icon-touxiang"
title="作者"
v-if="articleInfo.author"
>
<a
:href="articleInfo.author.href || articleInfo.author.link"
v-if="articleInfo.author.href || articleInfo.author.link && typeof(articleInfo.author.link) === 'string'"
target="_blank"
class="beLink"
title="作者"
>{{articleInfo.author.name}}</a>
<a
v-else
href="javascript:;"
>{{articleInfo.author.name || articleInfo.author}}</a>
</div>
<div
class="date iconfont icon-riqi"
title="创建时间"
v-if="articleInfo.date"
>
<a href="javascript:;">{{articleInfo.date}}</a>
</div>
<div
class="date iconfont icon-wenjian"
title="分类"
v-if="$themeConfig.category !== false && !(articleInfo.classify1 && articleInfo.classify1 !== '_posts') && articleInfo.categories"
>
<router-link
:to="`/categories/?category=${encodeURIComponent(item)}`"
v-for="(item, index) in articleInfo.categories"
:key="index"
>{{item + ' '}}</router-link>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
articleInfo: {}
}
},
created () {
this.articleInfo = this.getPageInfo()
},
watch: {
'$route.path' () {
this.articleInfo = this.getPageInfo()
}
},
methods: {
getPageInfo () {
const pageInfo = this.$page
const { relativePath } = pageInfo
const { sidebar } = this.$themeConfig
// 分类采用解析文件夹地址名称的方式
const relativePathArr = relativePath.split('/')
const classifyArr = relativePathArr[0].split('.')
const classify1 = classifyArr.length > 1 ? classifyArr[1] : classifyArr[0] // 文章一级分类名称
const classify2 = relativePathArr.length > 2 ? relativePathArr[1].split('.')[1] : undefined// 文章二级分类名称
const classify3 = relativePathArr.length > 3 ? relativePathArr[2].split('.')[1] : undefined// 文章三级分类名称
const cataloguePermalink = sidebar && sidebar.catalogue ? sidebar.catalogue[classify1] : undefined// 目录页永久链接
const author = this.$frontmatter.author || this.$themeConfig.author // 作者
let date = (pageInfo.frontmatter.date || '').split(' ')[0] // 文章创建时间
// 获取页面frontmatter的分类碎片化文章使用
const { categories } = this.$frontmatter
return {
date,
classify1,
classify2,
classify3,
cataloguePermalink,
author,
categories
}
}
}
}
</script>
<style lang='stylus' scoped>
@require '../styles/wrapper.styl'
.articleInfo-wrap
@extend $wrapper
position relative
z-index 1
color #888
.articleInfo
overflow hidden
font-size 0.92rem
.breadcrumbs
margin 0
padding 0
overflow hidden
display inline-block
line-height 2rem
@media (max-width 960px)
width 100%
li
list-style-type none
float left
padding-right 5px
&:after
content '/'
margin-left 5px
color #999
&:last-child
&:after
content ''
a
color #888
&:before
font-size 0.92rem
&:hover
color $accentColor
.icon-home
text-decoration none
.info
float right
line-height 32px
@media (max-width 960px)
float left
div
float left
margin-left 20px
font-size 0.8rem
@media (max-width 960px)
margin 0 20px 0 0
&:before
margin-right 3px
a
color #888
&:hover
text-decoration none
a.beLink
&:hover
color $accentColor
text-decoration underline
</style>

View File

@@ -0,0 +1,83 @@
<template>
<aside class="blogger-wrapper card-box">
<div class="avatar">
<img
:src="blogger.avatar"
alt="头像"
title="我好看吗"
/>
</div>
<div
class="icons"
v-if="social && social.icons && social.icons.length"
>
<a
v-for="(item, index) in social.icons"
:key="index"
:href="item.link"
:title="item.title"
:class="['iconfont', item.iconClass]"
:style="{width: 100/social.icons.length + '%'}"
target="_blank"
/>
</div>
<div class="blogger">
<span class="name">{{blogger.name}}</span>
<span class="slogan">{{blogger.slogan}}</span>
</div>
</aside>
</template>
<script>
export default {
computed: {
blogger () {
return this.$themeConfig.blogger
},
social () {
return this.$themeConfig.social
}
}
}
</script>
<style lang='stylus'>
.blogger-wrapper
height auto
display inline-table
padding-top 0!important
overflow hidden
.avatar
width 100%
// height 235px
overflow hidden
@media (max-width 900px)
// width 205px
// height 205px
img
width 100%
height 100%
.icons
// border 1px solid var(--borderColor)
border-top none
height 35px
line-height 35px
a
font-size 20px
width 33%
color var(--textColor)
display block
float left
text-align center
opacity 0.8
&:hover
color $accentColor
.blogger
padding 0.3rem 0.95rem 0 0.95rem
.name
font-size 1.3rem
display block
margin-bottom 6px
.slogan
color var(--textColor)
</style>

View File

@@ -0,0 +1,53 @@
<template>
<div
class="body-bg"
:style="`background: url(${bgImg}) center center / cover no-repeat;opacity:${opacity}`"
></div>
</template>
<script>
import { type } from '../util'
export default {
data () {
return {
bgImg: '',
opacity: 0.5
}
},
mounted () {
let { bodyBgImg, bodyBgImgOpacity } = this.$themeConfig
if (type(bodyBgImg) === 'string') {
this.bgImg = bodyBgImg
} else if (type(bodyBgImg) === 'array') {
let count = 0
let timer = null
this.bgImg = bodyBgImg[count]
clearInterval(timer)
timer = setInterval(() => {
if (++count >= bodyBgImg.length) {
count = 0
}
this.bgImg = bodyBgImg[count]
}, 15000);
}
if (bodyBgImgOpacity !== undefined) {
this.opacity = bodyBgImgOpacity
}
}
}
</script>
<style lang='stylus'>
.body-bg
position fixed
left 0
top 0
z-index -999999
height 100vh
width 100vw
transition background 0.5s
</style>

View File

@@ -0,0 +1,262 @@
<template>
<div class="buttons">
<transition name="fade">
<div
title="返回顶部"
class="button blur go-to-top iconfont icon-fanhuidingbu"
v-show="showToTop"
@click="scrollToTop"
/>
</transition>
<div
title="去评论"
class="button blur go-to-comment iconfont icon-pinglun"
v-show="showCommentBut"
@click="scrollToComment"
/>
<div
title="主题模式"
class="button blur theme-mode-but iconfont icon-zhuti"
@mouseenter="showModeBox = true"
@mouseleave="showModeBox = false"
@click="showModeBox = true"
>
<transition name="mode">
<ul
class="select-box"
ref="modeBox"
v-show="showModeBox"
@click.stop
@touchstart.stop
>
<li
v-for="item in modeList"
:key="item.KEY"
class="iconfont"
:class="[item.icon, {active: item.KEY === currentMode}]"
@click="toggleMode(item.KEY)"
>{{item.name}}</li>
</ul>
</transition>
</div>
</div>
</template>
<script>
import debounce from 'lodash.debounce'
import storage from 'good-storage' // 本地存储
const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl
export default {
data () {
return {
threshold: 100,
scrollTop: null,
showCommentBut: false,
commentTop: null,
currentMode: null,
showModeBox: false,
modeList: [
{
name: '跟随系统',
icon: 'icon-zidong',
KEY: 'auto'
},
{
name: '浅色模式',
icon: 'icon-rijianmoshi',
KEY: 'light'
},
{
name: '深色模式',
icon: 'icon-yejianmoshi',
KEY: 'dark'
},
{
name: '阅读模式',
icon: 'icon-yuedu',
KEY: 'read'
}
],
_scrollTimer: null,
_textareaEl: null,
_recordScrollTop: null,
COMMENT_SELECTOR_1: '#vuepress-plugin-comment', // 评论区元素的选择器1
COMMENT_SELECTOR_2: '#valine-vuepress-comment', // 评论区元素的选择器2
COMMENT_SELECTOR_3: '.vssue' // 评论区元素的选择器3
}
},
mounted () {
this.currentMode = storage.get('mode') || 'auto'
this.scrollTop = this.getScrollTop()
window.addEventListener('scroll', debounce(() => {
this.scrollTop = this.getScrollTop()
}, 100))
window.addEventListener('load', () => {
this.getCommentTop()
})
// 小屏时选择主题模式后关闭选择框
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
const modeBox = this.$refs.modeBox
modeBox.onclick = () => {
this.showModeBox = false
}
window.addEventListener('scroll', debounce(() => {
if (this.showModeBox) {
this.showModeBox = false
}
}, 100))
}
// 移动端对类似:hover效果的处理
const buttons = document.querySelectorAll('.buttons .button')
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i]
button.addEventListener('touchstart', function () {
button.classList.add('hover')
})
button.addEventListener('touchend', function () {
setTimeout(() => {
button.classList.remove('hover')
}, 150)
})
}
},
computed: {
showToTop () {
return this.scrollTop > this.threshold
}
},
methods: {
toggleMode (key) {
this.currentMode = key
this.$emit('toggle-theme-mode', key)
},
getScrollTop () {
return window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop || 0
},
scrollToTop () {
window.scrollTo({ top: 0, behavior: 'smooth' })
this.scrollTop = 0
},
getCommentTop () {
setTimeout(() => {
let commentEl = document.querySelector(this.COMMENT_SELECTOR_1) || document.querySelector(this.COMMENT_SELECTOR_2) || document.querySelector(this.COMMENT_SELECTOR_3)
if (commentEl) {
this.showCommentBut = this.$frontmatter.comment !== false && this.$frontmatter.home !== true
this.commentTop = commentEl.offsetTop - 58
}
}, 500)
},
scrollToComment () {
window.scrollTo({ top: this.commentTop, behavior: 'smooth' })
this._textareaEl = document.querySelector(this.COMMENT_SELECTOR_1 + ' textarea') || document.querySelector(this.COMMENT_SELECTOR_2 + ' input') || document.querySelector(this.COMMENT_SELECTOR_3 + ' textarea')
if (this._textareaEl && this.getScrollTop() !== this._recordScrollTop) {
document.addEventListener("scroll", this._handleListener)
} else if (this._textareaEl && this.getScrollTop() === this._recordScrollTop) {
this._handleFocus()
}
},
_handleListener () {
clearTimeout(this._scrollTimer)
this._scrollTimer = setTimeout(() => {
document.removeEventListener('scroll', this._handleListener)
this._recordScrollTop = this.getScrollTop()
this._handleFocus()
}, 30)
},
_handleFocus () {
this._textareaEl.focus()
this._textareaEl.classList.add('yellowBorder')
setTimeout(() => {
this._textareaEl.classList.remove('yellowBorder')
}, 500)
}
},
watch: {
'$route.path' () {
this.showCommentBut = false
this.getCommentTop()
}
}
}
</script>
<style lang='stylus'>
.yellowBorder
// border: #FFE089 1px solid!important
border-radius 5px
box-shadow 0 0 15px #FFE089 !important
.buttons
position fixed
right 2rem
bottom 2.5rem
z-index 11
@media (max-width $MQNarrow)
right 1rem
bottom 1.5rem
.button
width 2.2rem
height 2.2rem
line-height 2.2rem
border-radius 50%
box-shadow 0 2px 6px rgba(0, 0, 0, 0.15)
margin-top 0.9rem
text-align center
cursor pointer
transition all 0.5s
background var(--blurBg)
&.hover
background $accentColor
box-shadow 0 0 15px $accentColor
&:before
color #fff
@media (any-hover hover)
&:hover
background $accentColor
box-shadow 0 0 15px $accentColor
&:before
color #fff
.select-box
margin 0
padding 0.8rem 0
position absolute
bottom 0rem
right 1.5rem
background var(--mainBg)
border 1px solid var(--borderColor)
width 120px
border-radius 6px
box-shadow 0 0 15px rgba(255, 255, 255, 0.2)
li
list-style none
line-height 2rem
font-size 0.95rem
&:hover
color $accentColor
&.active
background-color rgba(150, 150, 150, 0.2)
color $accentColor
.mode-enter-active, .mode-leave-active
transition all 0.3s
.mode-enter, .mode-leave-to
opacity 0
transform scale(0.8)
.fade-enter-active, .fade-leave-active
transition opacity 0.2s
.fade-enter, .fade-leave-to
opacity 0
</style>

View File

@@ -0,0 +1,204 @@
<template>
<div class="theme-vdoing-content">
<div class="column-wrapper">
<img :src="$withBase(pageData.imgUrl)" />
<dl class="column-info">
<dt class="title">{{pageData.title}}</dt>
<dd
class="description"
v-html="pageData.description"
></dd>
</dl>
</div>
<div
class="catalogue-wrapper"
v-if="isStructuring"
>
<div class="catalogue-title">目录</div>
<div class="catalogue-content">
<template v-for="(item, index) in getCatalogueList()">
<dl
v-if="type(item) === 'array'"
:key="index"
class="inline"
>
<dt>
<router-link :to="item[2]">{{`${index+1}. ${item[1]}`}}</router-link>
</dt>
</dl>
<dl
v-else-if="type(item) === 'object'"
:key="index"
>
<!-- 一级目录 -->
<dt :id="anchorText = item.title">
<a
:href="`#${anchorText}`"
class="header-anchor"
>#</a>
{{`${index+1}. ${item.title}`}}
</dt>
<dd>
<!-- 二级目录 -->
<template
v-for="(c, i) in item.children"
>
<template
v-if="type(c) === 'array'"
>
<router-link
:to="c[2]"
:key="i"
>{{`${index+1}-${i+1}. ${c[1]}`}}</router-link>
</template>
<!-- 三级目录 -->
<div
v-else-if="type(c) === 'object'"
:key="i"
class="sub-cat-wrap"
>
<div :id="anchorText = c.title" class="sub-title">
<a
:href="`#${anchorText}`"
class="header-anchor"
>#</a>
{{`${index+1}-${i+1}. ${c.title}`}}
</div>
<router-link
v-for="(cc, ii) in c.children"
:to="cc[2]"
:key="`${index+1}-${i+1}-${ii+1}`"
>
{{`${index+1}-${i+1}-${ii+1}. ${cc[1]}`}}
</router-link>
</div>
</template>
</dd>
</dl>
</template>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
pageData: null,
isStructuring: true
}
},
created () {
this.getPageData()
const sidebar = this.$themeConfig.sidebar
if (!sidebar || sidebar === 'auto') {
this.isStructuring = false
console.error("目录页数据依赖于结构化的侧边栏数据,请在主题设置中将侧边栏字段设置为'structuring',否则无法获取目录数据。")
}
},
methods: {
getPageData () {
const pageComponent = this.$frontmatter.pageComponent
if (pageComponent && pageComponent.data) {
this.pageData = {
...pageComponent.data,
title: this.$frontmatter.title
}
} else {
console.error('请在front matter中设置pageComponent和pageComponent.data数据')
}
},
getCatalogueList () {
const { sidebar } = this.$site.themeConfig
const key = this.$frontmatter.pageComponent.data.key
const catalogueList = sidebar[`/${key}/`]
if (!catalogueList) {
console.error('未获取到目录数据请查看front matter中设置的key是否正确。')
}
return catalogueList
},
type (o) { // 数据类型检查
return Object.prototype.toString.call(o).match(/\[object (.*?)\]/)[1].toLowerCase()
}
},
watch: {
'$route.path' () {
this.getPageData()
}
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
dl, dd
margin 0
.column-wrapper
margin-top 1rem
display flex
padding-bottom 2rem
border-bottom 1px solid var(--borderColor)
img
width 80px
height 80px
border-radius 2px
margin-right 1rem
.column-info
.title
font-size 1.6rem
.description
color var(--textColor)
opacity 0.8
margin 0.5rem 0
.catalogue-wrapper
.catalogue-title
font-size 1.45rem
margin 2rem 0
.catalogue-content
dl
margin-bottom 1.8rem
&.inline
display inline-block
width 50%
margin-bottom 1rem
@media (max-width $MQMobileNarrow)
width 100%
a
width 100%
&:not(.inline)
dt
margin-top -($navbarHeight)
padding-top $navbarHeight
dt
font-size 1.1rem
&:hover .header-anchor
opacity 1
dd
margin-top 0.7rem
margin-left 1rem
a:not(.header-anchor)
margin-bottom 0.5rem
display inline-block
width 50%
&:hover
color $activeColor
text-decoration none
@media (max-width $MQMobileNarrow)
width 100%
.sub-cat-wrap
margin 5px 0 8px 0
font-size .95rem
&> a
padding-left 1rem
box-sizing border-box
.sub-title
margin-top -($navbarHeight)
padding-top $navbarHeight
margin-bottom 6px
font-size 1rem
&:hover
.header-anchor
opacity 1
</style>

View File

@@ -0,0 +1,113 @@
<template>
<div class="categories-wrapper card-box">
<router-link
to="/categories/"
class="title iconfont icon-wenjianjia"
title="全部分类"
>{{ length === 'all' ? '全部分类' : '文章分类' }}</router-link>
<div class="categories">
<router-link
:to="`/categories/?category=${encodeURIComponent(item.key)}`"
v-for="(item, index) in categories"
:key="index"
:class="{active: item.key === category}"
>
{{item.key}}
<span>{{item.length}}</span>
</router-link>
<router-link
to="/categories/"
v-if="length !== 'all' && length < categoriesData.length"
class="more"
>更多 ...</router-link>
</div>
</div>
</template>
<script>
export default {
props: {
category: {
type: String,
default: ''
},
categoriesData: {
type: Array,
default: []
},
length: {
type: [String, Number],
default: 'all'
}
},
computed: {
categories () {
if (this.length === 'all') {
return this.categoriesData
} else {
return this.categoriesData.slice(0, this.length)
}
}
}
}
</script>
<style lang='stylus'>
.categories-wrapper
.title
color var(--textColor)
opacity 0.9
font-size 1.2rem
padding 0 .95rem
&::before
margin-right 0.3rem
.categories
margin-top 0.6rem
a
display block
padding 8px .95rem 7px .95rem
color var(--textColor)
opacity 0.8
font-size 0.95rem
line-height 0.95rem
position relative
transition all .2s
border-left 2px solid transparent
margin-top -1px
overflow hidden
white-space nowrap
text-overflow ellipsis
@media (max-width $MQMobile)
font-weight 400
&.more
// color $accentColor
&:not(.active):hover
color $accentColor
background #f8f8f8
border-color $accentColor
span
opacity 0.8
span
float right
background-color var(--textColor)
color var(--mainBg)
border-radius 8px
padding 0 0.13rem
min-width 1rem
height 1rem
line-height 1rem
font-size 0.6rem
text-align center
opacity 0.6
transition opacity 0.3s
&.active
background $accentColor
color var(--mainBg)
padding-left 0.8rem
border-radius 1px
border-color transparent
.theme-mode-dark .categories-wrapper .categories a:not(.active):hover,
.theme-mode-read .categories-wrapper .categories a:not(.active):hover
background var(--customBlockBg)
</style>

View File

@@ -0,0 +1,131 @@
<template>
<div class="custom-page categories-page">
<MainLayout>
<template #mainLeft>
<CategoriesBar
v-if="$categoriesAndTags.categories.length"
:categoriesData="$categoriesAndTags.categories"
:category="category"
/>
<PostList
:currentPage="currentPage"
:perPage="perPage"
:category="category"
/>
<Pagination
:total="total"
:perPage="perPage"
:currentPage="currentPage"
@getCurrentPage="handlePagination"
v-show="Math.ceil(total / perPage) > 1"
/>
</template>
<template #mainRight>
<CategoriesBar
v-if="$categoriesAndTags.categories.length"
:categoriesData="$categoriesAndTags.categories"
:category="category"
/>
</template>
</MainLayout>
</div>
</template>
<script>
import MainLayout from '@theme/components/MainLayout'
import PostList from '@theme/components/PostList'
import Pagination from '@theme/components/Pagination'
import CategoriesBar from '@theme/components/CategoriesBar'
export default {
data () {
return {
category: '',
total: 0, // 总长
perPage: 10, // 每页长
currentPage: 1// 当前页
}
},
components: { MainLayout, PostList, Pagination, CategoriesBar },
mounted () {
const queryCategory = this.$route.query.category
if (queryCategory) {
this.category = queryCategory
this.total = this.$groupPosts.categories[queryCategory].length
} else {
this.total = this.$sortPosts.length
}
if (this.$route.query.p) {
this.currentPage = Number(this.$route.query.p)
}
// 滚动条定位到当前分类(增强用户体验)
const cateEl = document.querySelector('.categories')
if (cateEl) {
setTimeout(() => {
const activeEl = cateEl.querySelector('.active')
const topVal = activeEl ? activeEl.offsetTop : 0
cateEl.scrollTo({ top: topVal, behavior: 'smooth' })
}, 300)
}
},
methods: {
handlePagination (i) { // 分页
this.currentPage = i
}
},
watch: {
'$route.query.category' (category) {
this.category = category ? decodeURIComponent(category) : ''
if (this.category) {
this.total = this.$groupPosts.categories[this.category].length
} else {
this.total = this.$sortPosts.length
}
this.currentPage = 1
}
}
}
</script>
<style lang='stylus'>
.categories-page
.categories-wrapper
position sticky
top ($navbarHeight + 0.9rem)
max-height calc(100vh - 10rem)
min-height 4.2rem
@media (max-width $MQMobile)
display none
.categories
padding-right 0.5rem
max-height calc(100vh - 14rem)
min-height 2.2rem
overflow-y auto
transition all 0.2s
position relative
&::-webkit-scrollbar-track-piece
background-color rgba(0, 0, 0, 0.05)
&::-webkit-scrollbar-thumb:vertical
background-color rgba(0, 0, 0, 0.15)
&:hover
&::-webkit-scrollbar-track-piece
background-color rgba(0, 0, 0, 0.1)
&::-webkit-scrollbar-thumb:vertical
background-color rgba(0, 0, 0, 0.25)
.categories-page
.main-left
.categories-wrapper
position relative
top 0
padding 0.9rem 1.5rem
margin-bottom 0.9rem
max-height 15rem
border-radius 0
display none
@media (max-width $MQMobile)
display block
.categories
max-height 12.3rem
</style>

View File

@@ -0,0 +1,245 @@
<template>
<div
class="dropdown-wrapper"
:class="{ open }"
>
<button
class="dropdown-title"
type="button"
:aria-label="dropdownAriaLabel"
@click="toggle"
>
<router-link
v-if="item.link"
:to="item.link"
class="link-title"
>{{ item.text }}</router-link>
<span
class="title"
v-show="!item.link"
>{{ item.text }}</span>
<span
class="arrow"
:class="open ? 'down' : 'right'"
></span>
</button>
<DropdownTransition>
<ul
class="nav-dropdown"
v-show="open"
>
<li
class="dropdown-item"
:key="subItem.link || index"
v-for="(subItem, index) in item.items"
>
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
<ul
class="dropdown-subitem-wrapper"
v-if="subItem.type === 'links'"
>
<li
class="dropdown-subitem"
:key="childSubItem.link"
v-for="childSubItem in subItem.items"
>
<NavLink
@focusout="
isLastItemOfArray(childSubItem, subItem.items) &&
isLastItemOfArray(subItem, item.items) &&
toggle()
"
:item="childSubItem"
/>
</li>
</ul>
<NavLink
v-else
@focusout="isLastItemOfArray(subItem, item.items) && toggle()"
:item="subItem"
/>
</li>
</ul>
</DropdownTransition>
</div>
</template>
<script>
import NavLink from '@theme/components/NavLink.vue'
import DropdownTransition from '@theme/components/DropdownTransition.vue'
import last from 'lodash/last'
export default {
components: { NavLink, DropdownTransition },
data () {
return {
open: false,
isMQMobile: false
}
},
props: {
item: {
required: true
}
},
computed: {
dropdownAriaLabel () {
return this.item.ariaLabel || this.item.text
}
},
beforeMount () {
this.isMQMobile = window.innerWidth < 720 ? true : false;
window.addEventListener('resize', () => {
this.isMQMobile = window.innerWidth < 720 ? true : false;
})
},
methods: {
toggle () {
if (this.isMQMobile) {
this.open = !this.open
}
},
isLastItemOfArray (item, array) {
return last(array) === item
}
},
watch: {
$route () {
this.open = false
}
}
}
</script>
<style lang="stylus">
.dropdown-wrapper
cursor pointer
.dropdown-title
display block
font-size 0.9rem
font-family inherit
cursor inherit
padding inherit
line-height 1.4rem
background transparent
border none
font-weight 500
color var(--textColor)
&:hover
border-color transparent
.arrow
vertical-align middle
margin-top -1px
margin-left 0.4rem
.nav-dropdown
.dropdown-item
color inherit
line-height 1.7rem
h4
margin 0.45rem 0 0
border-top 1px solid var(--borderColor)
padding 0.45rem 1.5rem 0 1.25rem
.dropdown-subitem-wrapper
padding 0
list-style none
.dropdown-subitem
font-size 0.9em
a
display block
line-height 1.7rem
position relative
border-bottom none
font-weight 400
margin-bottom 0
padding 0 1.5rem 0 1.25rem
&:hover
color $accentColor
&.router-link-active
color $accentColor
&::after
content ''
width 0
height 0
border-left 5px solid $accentColor
border-top 3px solid transparent
border-bottom 3px solid transparent
position absolute
top calc(50% - 2px)
left 9px
&:first-child h4
margin-top 0
padding-top 0
border-top 0
@media (max-width $MQMobile)
.dropdown-wrapper
&.open .dropdown-title
margin-bottom 0.5rem
.dropdown-title
font-weight 600
font-size inherit
&:hover
color $accentColor
.link-title
display none
.title
display inline-block !important
.nav-dropdown
transition height 0.1s ease-out
overflow hidden
.dropdown-item
h4
border-top 0
margin-top 0
padding-top 0
h4, & > a
font-size 15px
line-height 2rem
.dropdown-subitem
font-size 14px
padding-left 1rem
@media (min-width $MQMobile)
.dropdown-wrapper
height 1.8rem
&:hover .nav-dropdown, &.open .nav-dropdown
// override the inline style.
display block !important
&.open:blur
display none
.dropdown-title .arrow
// make the arrow always down at desktop
border-left 4px solid transparent
border-right 4px solid transparent
border-top 6px solid $arrowBgColor
border-bottom 0
.nav-dropdown
display none
// Avoid height shaked by clicking
height auto !important
box-sizing border-box
max-height calc(100vh - 2.7rem)
overflow-y auto
position absolute
top 100%
right 0
background-color var(--mainBg)
padding 0.6rem 0
border 1px solid var(--borderColor)
border-bottom-color var(--borderColor)
text-align left
border-radius 0.25rem
white-space nowrap
margin 0
.nav-item .dropdown-title a
&:hover, &.router-link-active
margin-bottom -2px
border-bottom 2px solid lighten($accentColor, 8%)
</style>

View File

@@ -0,0 +1,32 @@
<template>
<transition
name="dropdown"
@enter="setHeight"
@after-enter="unsetHeight"
@before-leave="setHeight"
>
<slot />
</transition>
</template>
<script>
export default {
name: 'DropdownTransition',
methods: {
setHeight (items) {
// explicitly set height so that it can be transitioned
items.style.height = items.scrollHeight + 'px'
},
unsetHeight (items) {
items.style.height = ''
}
}
}
</script>
<style lang="stylus">
.dropdown-enter, .dropdown-leave-to
height 0 !important
</style>

View File

@@ -0,0 +1,74 @@
<template>
<div class="footer">
<div
class="icons"
v-if="social && social.icons"
>
<a
:href="item.link"
:title="item.title"
:class="['iconfont', item.iconClass]"
v-for="(item, index) in social.icons"
:key="index"
target="_blank"
></a>
</div>
<!--Vdoing主题遵循MIT协议完全开源且免费如果您对主题的修改并不大希望您保留主题的链接-->
Theme by
<a
href="https://github.com/xugaoyi/vuepress-theme-vdoing"
target="_blank"
title="本站主题"
>Vdoing</a>
<template v-if="footer">
| Copyright © {{ footer.createYear }}-{{ new Date().getFullYear() }}
<span
v-html="footer.copyrightInfo"
></span>
</template>
</div>
</template>
<script>
export default {
computed: {
social () {
return this.$themeConfig.social
},
footer () {
return this.$themeConfig.footer
}
}
}
</script>
<style lang='stylus'>
// $mobileSidebarWidth = $sidebarWidth * 0.82
.footer
padding 5rem 1.5rem 2.5rem
text-align center
color #666
box-sizing border-box
font-size 0.85rem
transition all 0.2s ease
.icons
margin-bottom 12px
.iconfont
padding 0 10px
font-size 1.3rem
a
color inherit
&:hover
color $accentColor
@media (min-width ($MQMobile + 1px))
.sidebar-open .footer
width auto
padding-left ($sidebarWidth + 1.5rem)
@media (min-width 1520px)
.have-rightmenu .footer
padding-right ($rightMenuWidth + 1.5rem)
.no-sidebar .footer
width auto
padding-left 1.5rem
</style>

View File

@@ -0,0 +1,540 @@
<template>
<div class="home-wrapper">
<!-- banner块 s -->
<div
class="banner"
:class="{ 'hide-banner': !showBanner }"
:style="bannerBgStyle"
>
<div
class="banner-conent"
:style="
!homeData.features && !homeData.heroImage && `padding-top: 7rem`
"
>
<header class="hero">
<img
v-if="homeData.heroImage"
:src="$withBase(homeData.heroImage)"
:alt="homeData.heroAlt"
/>
<h1 v-if="homeData.heroText" id="main-title">
{{ homeData.heroText }}
</h1>
<p v-if="homeData.tagline" class="description">
{{ homeData.tagline }}
</p>
<p class="action" v-if="homeData.actionText && homeData.actionLink">
<NavLink class="action-button" :item="actionLink" />
</p>
</header>
<!-- PC端features块 s -->
<div
class="features"
v-if="homeData.features && homeData.features.length && !isMQMobile"
>
<div
class="feature"
v-for="(feature, index) in homeData.features"
:key="index"
>
<router-link v-if="feature.link" :to="feature.link">
<img
class="feature-img"
v-if="feature.imgUrl"
:src="$withBase(feature.imgUrl)"
:alt="feature.title"
/>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</router-link>
<a v-else href="javascript:;">
<img
class="feature-img"
v-if="feature.imgUrl"
:src="$withBase(feature.imgUrl)"
:alt="feature.title"
/>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</a>
</div>
</div>
<!-- PC端features块 e -->
</div>
<!-- 移动端features块 s -->
<!-- isMQMobile放到v-if上线后会报错 -->
<div
class="slide-banner"
v-if="homeData.features && homeData.features.length"
v-show="isMQMobile"
>
<div class="banner-wrapper">
<div class="slide-banner-scroll" ref="slide">
<div class="slide-banner-wrapper">
<div
class="slide-item"
v-for="(feature, index) in homeData.features"
:key="index"
>
<router-link v-if="feature.link" :to="feature.link">
<img
class="feature-img"
v-if="feature.imgUrl"
:src="$withBase(feature.imgUrl)"
:alt="feature.title"
/>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</router-link>
<a v-else href="javascript:;">
<img
class="feature-img"
v-if="feature.imgUrl"
:src="$withBase(feature.imgUrl)"
:alt="feature.title"
/>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</a>
</div>
</div>
</div>
<div class="docs-wrapper">
<span
class="doc"
v-for="(item, index) in homeData.features.length"
:key="index"
:class="{ active: currentPageIndex === index }"
></span>
</div>
</div>
</div>
<!-- 移动端features块 e -->
</div>
<!-- banner块 e -->
<MainLayout>
<template #mainLeft>
<!-- 简约版文章列表 -->
<UpdateArticle
class="card-box"
v-if="homeData.postList === 'simple'"
:length="homeData.simplePostListLength || 10"
/>
<!-- 详情版文章列表 -->
<template
v-else-if="!homeData.postList || homeData.postList === 'detailed'"
>
<PostList :currentPage="currentPage" :perPage="perPage" />
<Pagination
:total="total"
:perPage="perPage"
:currentPage="currentPage"
@getCurrentPage="handlePagination"
v-show="Math.ceil(total / perPage) > 1"
/>
</template>
<Content class="theme-vdoing-content custom card-box" />
</template>
<template #mainRight>
<BloggerBar v-if="$themeConfig.blogger" />
<CategoriesBar
v-if="
$themeConfig.category !== false &&
$categoriesAndTags.categories.length
"
:categoriesData="$categoriesAndTags.categories"
:length="10"
/>
<TagsBar
v-if="$themeConfig.tag !== false && $categoriesAndTags.tags.length"
:tagsData="$categoriesAndTags.tags"
:length="30"
/>
<div
class="custom-html-box card-box"
v-if="homeSidebarB"
v-html="homeSidebarB"
></div>
</template>
</MainLayout>
</div>
</template>
<script>
import NavLink from "@theme/components/NavLink";
import BScroll from "@better-scroll/core"
import Slide from "@better-scroll/slide"
import MainLayout from '@theme/components/MainLayout'
import PostList from '@theme/components/PostList'
import UpdateArticle from '@theme/components/UpdateArticle'
import Pagination from '@theme/components/Pagination'
import BloggerBar from '@theme/components/BloggerBar'
import CategoriesBar from '@theme/components/CategoriesBar'
import TagsBar from '@theme/components/TagsBar'
const MOBILE_DESKTOP_BREAKPOINT = 720 // refer to config.styl
BScroll.use(Slide)
export default {
data () {
return {
isMQMobile: false,
slide: null,
currentPageIndex: 0,
playTimer: 0,
mark: 0,
total: 0, // 总长
perPage: 10, // 每页长
currentPage: 1// 当前页
}
},
computed: {
homeSidebarB () {
const { htmlModules } = this.$themeConfig
return htmlModules ? htmlModules.homeSidebarB : ''
},
showBanner () { // 当分页不在第一页时隐藏banner栏
return this.$route.query.p
&& this.$route.query.p != 1
&& (!this.homeData.postList || this.homeData.postList === 'detailed')
? false : true
},
bannerBgStyle () {
let bannerBg = this.homeData.bannerBg
if (!bannerBg || bannerBg === 'auto') { // 默认
if (this.$themeConfig.bodyBgImg) { // 当有bodyBgImg时不显示背景
return ''
} else { // 网格纹背景
return 'background: rgb(40,40,45) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABOSURBVFhH7c6xCQAgDAVRR9A6E4hLu4uLiWJ7tSnuQcIvr2TRYsw3/zOGGEOMIcYQY4gxxBhiDDGGGEOMIcYQY4gxxBhiDLkx52W4Gn1tuslCtHJvL54AAAAASUVORK5CYII=)'
}
} else if (bannerBg === 'none') { // 无背景
if (this.$themeConfig.bodyBgImg) {
return ''
} else {
return 'background: var(--mainBg);color: var(--textColor)'
}
} else if (bannerBg.indexOf('background') > -1) { // 自定义背景样式
return bannerBg
} else if (bannerBg.indexOf('.') > -1) { // 大图
return `background: url(${this.$withBase(bannerBg)}) center center / cover no-repeat`
}
},
homeData () {
return {
...this.$page.frontmatter
}
},
actionLink () {
return {
link: this.homeData.actionLink,
text: this.homeData.actionText
};
}
},
components: { NavLink, MainLayout, PostList, UpdateArticle, BloggerBar, CategoriesBar, TagsBar, Pagination },
created () {
this.total = this.$sortPosts.length
},
beforeMount () {
this.isMQMobile = window.innerWidth < MOBILE_DESKTOP_BREAKPOINT ? true : false; // vupress在打包时不能在beforeCreate(),created()访问浏览器api如window
},
mounted () {
if (this.$route.query.p) {
this.currentPage = Number(this.$route.query.p)
}
if (this.isMQMobile && (!this.$route.query.p || this.$route.query.p == 1)) {
this.init()
}
window.addEventListener('resize', () => {
this.isMQMobile = window.innerWidth < MOBILE_DESKTOP_BREAKPOINT ? true : false;
if (this.isMQMobile && !this.slide && !this.mark) {
this.mark++
setTimeout(() => {
this.init()
}, 60)
}
})
},
beforeDestroy () {
clearTimeout(this.playTimer)
this.slide && this.slide.destroy()
},
watch: {
'$route.query.p' () {
if (!this.$route.query.p) {
this.currentPage = 1
} else {
this.currentPage = Number(this.$route.query.p)
}
if (this.currentPage === 1 && this.isMQMobile) {
setTimeout(() => {
this.slide && this.slide.destroy()
this.init()
}, 0)
}
}
},
methods: {
init () {
clearTimeout(this.playTimer)
this.slide = new BScroll(this.$refs.slide, {
scrollX: true, // x轴滚动
scrollY: false, // y轴滚动
slide: {
loop: true,
threshold: 100
},
useTransition: true, // 使用css3 transition动画
momentum: false,
bounce: false, // 回弹
stopPropagation: false, // 是否阻止事件冒泡
probeType: 2,
preventDefault: false
})
// user touches the slide area
this.slide.on('beforeScrollStart', () => {
clearTimeout(this.playTimer)
})
// user touched the slide done
this.slide.on('scrollEnd', () => {
this.autoGoNext()
})
this.slide.on('slideWillChange', (page) => {
this.currentPageIndex = page.pageX
})
this.autoGoNext()
},
autoGoNext () {
clearTimeout(this.playTimer)
this.playTimer = setTimeout(() => {
this.slide.next()
}, 4000)
},
handlePagination (i) { // 分页
this.currentPage = i
},
getScrollTop () {
return window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop
},
},
};
</script>
<style lang="stylus" scoped>
.home-wrapper
.banner
width 100%
min-height 450px
margin-top $navbarHeight
color $bannerTextColor
position relative
overflow hidden
.banner-conent
max-width $homePageWidth
margin 0px auto
position relative
z-index 1
overflow hidden
.hero
text-align center
margin-top 3rem
img
max-width 100%
max-height 240px
display block
margin 2rem auto 1.5rem
h1
margin 0
font-size 3.2rem
.description, .action
margin 1.5rem auto
.description
max-width 40rem
font-size 1.1rem
line-height 1.3
opacity 0.9
.action-button
display inline-block
font-size 1.2rem
background-color $accentColor
padding 0.8rem 1.6rem
border-radius 4px
transition background-color 0.1s ease
box-sizing border-box
border-bottom 1px solid darken($accentColor, 10%)
color #fff
&:hover
background-color lighten($accentColor, 10%)
// pc端features
.features
padding 2rem 0
margin-top 2.5rem
display flex
flex-wrap wrap
align-items flex-start
align-content stretch
justify-content space-between
.feature
flex-grow 1
flex-basis 30%
max-width 30%
text-align center
a
// color lighten($bannerTextColor,10%)
color inherit
.feature-img
width 10rem
height 10rem
animation heart 1.2s ease-in-out 0s infinite alternate
animation-play-state paused
h2
font-weight 500
font-size 1.3rem
border-bottom none
padding-bottom 0
p
opacity 0.8
padding 0 0.8rem
.feature:hover
.feature-img
animation-play-state running
h2, p
color $accentColor
// 移动端滑动图标
.slide-banner
margin-top 2rem
.banner-wrapper
position relative
.slide-banner-scroll
min-height 1px
overflow hidden
.slide-banner-wrapper
height 300px
.slide-item
display inline-block
height 300px
width 100%
text-align center
a
// color lighten($bannerTextColor,10%)
color inherit
.feature-img
width 10rem
height 10rem
h2
font-size 1.1rem
font-weight 500
border-bottom none
padding-bottom 0
p
opacity 0.8
padding 0 0.8rem
.docs-wrapper
position absolute
bottom 25px
left 50%
transform translateX(-50%)
.doc
display inline-block
margin 0 4px
width 8px
height 8px
border-radius 50%
background var(--textColor)
opacity 0.9
&.active
opacity 0.5
// 分页不在第一页时隐藏banner栏
.banner.hide-banner
display none
& + .main-wrapper
margin-top ($navbarHeight + 0.9rem)
.main-wrapper
margin-top 2rem
.main-left
.card-box
margin-bottom 0.9rem
.pagination
margin-bottom 4rem
.theme-vdoing-content
padding 0 2rem
overflow hidden
&>:first-child
padding-top 2rem
&>:last-child
padding-bottom 2rem
.main-right
.custom-html-box
padding 0
overflow hidden
@keyframes heart
from
transform translate(0, 0)
to
transform translate(0, 8px)
// 1025px以下
@media (max-width 1025px)
.home-wrapper
.banner
.banner-conent
.hero
h1
font-size 2.5rem
.description
font-size 1rem
.feature
a
h2
font-size 1.1rem
.feature-img
width 9rem
height 9rem
// 719px以下
@media (max-width $MQMobile)
.home-wrapper
.banner
.banner-conent
.features
display none !important
// 419px以下
@media (max-width $MQMobileNarrow)
.home-wrapper
.banner-conent
padding-left 1.5rem
padding-right 1.5rem
.hero
img
max-height 210px
margin 2rem auto 1.2rem
h1
font-size 2rem
h1, .description, .action
margin 1.2rem auto
.description
font-size 1.2rem
.action-button
font-size 1rem
padding 0.6rem 1.2rem
.feature
h2
font-size 1.25rem
</style>

View File

@@ -0,0 +1,59 @@
<template>
<div class="main-wrapper">
<div class="main-left">
<slot name="mainLeft" />
</div>
<div class="main-right">
<slot name="mainRight" />
</div>
</div>
</template>
<style lang="stylus">
.main-wrapper
margin 1.5rem auto 0 auto
max-width $homePageWidth
padding 0 0.9rem
box-sizing border-box
position relative
display flex
.main-left
flex 1
// width 72%
.theme-vdoing-content.card-box
padding 1rem 1.5rem
margin-bottom 0.9rem
.home-content
padding 1rem 1.5rem 0
.main-right
>*
width 245px
box-sizing border-box
@media (max-width 900px)
width 235px
.card-box
margin 0 0 0.9rem 0.9rem
padding-top 0.95rem
padding-bottom 0.95rem
// 719px以下
@media (max-width $MQMobile)
.main-wrapper
margin 0.9rem 0
padding 0
display block
.main-left
width 100%
.post-list
margin-bottom 3rem
.post
border-radius 0
.pagination
margin-bottom 3rem
.main-right
.blogger-wrapper
display none
.card-box
margin 0 0 0.9rem 0
border-radius 0
width 100%
</style>

View File

@@ -0,0 +1,54 @@
<template>
<router-link
class="nav-link"
:to="link"
@focusout.native="focusoutAction"
v-if="!isExternal(link)"
:exact="exact"
>{{ item.text }}</router-link>
<a
v-else
:href="link"
@focusout="focusoutAction"
class="nav-link external"
:target="isMailto(link) || isTel(link) ? null : '_blank'"
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
>
{{ item.text }}
<OutboundLink />
</a>
</template>
<script>
import { isExternal, isMailto, isTel, ensureExt } from '../util'
export default {
props: {
item: {
required: true
}
},
computed: {
link () {
return ensureExt(this.item.link)
},
exact () {
if (this.$site.locales) {
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
}
return this.link === '/'
}
},
methods: {
isExternal,
isMailto,
isTel,
focusoutAction () {
this.$emit('focusout')
}
}
}
</script>

View File

@@ -0,0 +1,154 @@
<template>
<nav
class="nav-links"
v-if="userLinks.length || repoLink"
>
<!-- user links -->
<div
class="nav-item"
v-for="item in userLinks"
:key="item.link"
>
<DropdownLink
v-if="item.type === 'links'"
:item="item"
/>
<NavLink
v-else
:item="item"
/>
</div>
<!-- repo link -->
<a
v-if="repoLink"
:href="repoLink"
class="repo-link"
target="_blank"
rel="noopener noreferrer"
>
{{ repoLabel }}
<OutboundLink />
</a>
</nav>
</template>
<script>
import DropdownLink from '@theme/components/DropdownLink.vue'
import { resolveNavLinkItem } from '../util'
import NavLink from '@theme/components/NavLink.vue'
export default {
components: { NavLink, DropdownLink },
computed: {
userNav () {
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
},
nav () {
const { locales } = this.$site
if (locales && Object.keys(locales).length > 1) {
const currentLink = this.$page.path
const routes = this.$router.options.routes
const themeLocales = this.$site.themeConfig.locales || {}
const languageDropdown = {
text: this.$themeLocaleConfig.selectText || 'Languages',
ariaLabel: this.$themeLocaleConfig.ariaLabel || 'Select language',
items: Object.keys(locales).map(path => {
const locale = locales[path]
const text = themeLocales[path] && themeLocales[path].label || locale.lang
let link
// Stay on the current page
if (locale.lang === this.$lang) {
link = currentLink
} else {
// Try to stay on the same page
link = currentLink.replace(this.$localeConfig.path, path)
// fallback to homepage
if (!routes.some(route => route.path === link)) {
link = path
}
}
return { text, link }
})
}
return [...this.userNav, languageDropdown]
}
return this.userNav
},
userLinks () {
return (this.nav || []).map(link => {
return Object.assign(resolveNavLinkItem(link), {
items: (link.items || []).map(resolveNavLinkItem)
})
})
},
repoLink () {
const { repo } = this.$site.themeConfig
if (repo) {
return /^https?:/.test(repo)
? repo
: `https://github.com/${repo}`
}
return null
},
repoLabel () {
if (!this.repoLink) return
if (this.$site.themeConfig.repoLabel) {
return this.$site.themeConfig.repoLabel
}
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
const platforms = ['GitHub', 'GitLab', 'Bitbucket']
for (let i = 0; i < platforms.length; i++) {
const platform = platforms[i]
if (new RegExp(platform, 'i').test(repoHost)) {
return platform
}
}
return 'Source'
}
}
}
</script>
<style lang="stylus">
.nav-links
display inline-block
a
line-height 1.4rem
color inherit
&:hover, &.router-link-active
color $accentColor
.nav-item
position relative
display inline-block
margin-left 1.5rem
line-height 2rem
&:first-child
margin-left 0
.repo-link
margin-left 1.5rem
// 959
@media (max-width $MQNarrow)
.nav-links
.nav-item
margin-left 1.2rem
@media (max-width $MQMobile)
.nav-links
.nav-item, .repo-link
margin-left 0
@media (min-width $MQMobile)
.nav-links a
&:hover, &.router-link-active
color var(--textColor)
.nav-item > a:not(.external)
&:hover, &.router-link-active
margin-bottom -2px
border-bottom 2px solid lighten($accentColor, 8%)
</style>

View File

@@ -0,0 +1,141 @@
<template>
<header class="navbar blur">
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')" />
<router-link
:to="$localePath"
class="home-link"
>
<img
class="logo"
v-if="$site.themeConfig.logo"
:src="$withBase($site.themeConfig.logo)"
:alt="$siteTitle"
/>
<!-- <span-->
<!-- ref="siteName"-->
<!-- class="site-name"-->
<!-- v-if="$siteTitle"-->
<!-- :class="{ 'can-hide': $site.themeConfig.logo }"-->
<!-- >{{ $siteTitle }}</span>-->
</router-link>
<div
class="links"
:style="linksWrapMaxWidth ? {
'max-width': linksWrapMaxWidth + 'px'
} : {}"
>
<AlgoliaSearchBox
v-if="isAlgoliaSearch"
:options="algolia"
/>
<SearchBox
v-else-if="$site.themeConfig.search !== false && $page.frontmatter.search !== false"
/>
<NavLinks class="can-hide" />
</div>
</header>
</template>
<script>
import AlgoliaSearchBox from '@AlgoliaSearchBox'
import SearchBox from '@SearchBox'
import SidebarButton from '@theme/components/SidebarButton.vue'
import NavLinks from '@theme/components/NavLinks.vue'
export default {
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
data () {
return {
linksWrapMaxWidth: null
}
},
mounted () {
const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl
const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight'))
const handleLinksWrapWidth = () => {
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
this.linksWrapMaxWidth = null
} else {
this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING
- (this.$refs.siteName && this.$refs.siteName.offsetWidth || 0)
}
}
handleLinksWrapWidth()
window.addEventListener('resize', handleLinksWrapWidth, false)
},
computed: {
algolia () {
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
},
isAlgoliaSearch () {
return this.algolia && this.algolia.apiKey && this.algolia.indexName
}
}
}
function css (el, property) {
// NOTE: Known bug, will return 'auto' if style value is 'auto'
const win = el.ownerDocument.defaultView
// null means not to return pseudo styles
return win.getComputedStyle(el, null)[property]
}
</script>
<style lang="stylus">
$navbar-vertical-padding = 0.7rem
$navbar-horizontal-padding = 1.5rem
.navbar
padding $navbar-vertical-padding $navbar-horizontal-padding
line-height $navbarHeight - 1.4rem
transition transform 0.3s
a, span, img
display inline-block
.logo
height $navbarHeight - 1.4rem
min-width $navbarHeight - 1.4rem
margin-right 0.8rem
vertical-align top
.site-name
font-size 1.3rem
font-weight 600
color var(--textColor)
position relative
.links
padding-left 1.5rem
box-sizing border-box
white-space nowrap
font-size 0.9rem
position absolute
right $navbar-horizontal-padding
top $navbar-vertical-padding
display flex
.search-box
flex 0 0 auto
vertical-align top
.hide-navbar
.navbar
transform translateY(-100%)
// 959
@media (max-width $MQNarrow)
.navbar
.site-name
display none
@media (max-width $MQMobile)
.navbar
padding-left 4rem
.can-hide
display none
.links
padding-left 1.5rem
.site-name
width calc(100vw - 9.4rem)
overflow hidden
white-space nowrap
text-overflow ellipsis
</style>

View File

@@ -0,0 +1,174 @@
<template>
<div>
<main class="page">
<div :class="`theme-vdoing-wrapper ${bgStyle}`">
<ArticleInfo v-if="isArticle()" />
<component
class="theme-vdoing-content"
v-if="pageComponent"
:is="pageComponent"
/>
<div class="content-wrapper">
<RightMenu v-if="showRightMenu" />
<h1 v-if="showTitle">
<img
:src="currentBadge"
v-if="$themeConfig.titleBadge === false ? false : true"
/>
{{this.$page.title}}
</h1>
<slot name="top" v-if="isShowSlotT" />
<Content class="theme-vdoing-content" />
</div>
<slot name="bottom" v-if="isShowSlotB" />
<PageEdit />
<PageNav v-bind="{ sidebarItems }" />
</div>
<UpdateArticle
:length="3"
:moreArticle="updateBarConfig && updateBarConfig.moreArticle"
v-if="isShowUpdateBar"
/>
</main>
</div>
</template>
<script>
import PageEdit from '@theme/components/PageEdit.vue'
import PageNav from '@theme/components/PageNav.vue'
import ArticleInfo from './ArticleInfo.vue'
import Catalogue from './Catalogue.vue'
import UpdateArticle from './UpdateArticle.vue'
import RightMenu from './RightMenu.vue'
import TitleBadgeMixin from '../mixins/titleBadge'
export default {
mixins: [TitleBadgeMixin],
data () {
return {
updateBarConfig: null
}
},
props: ['sidebarItems'],
components: { PageEdit, PageNav, ArticleInfo, Catalogue, UpdateArticle, RightMenu },
created () {
this.updateBarConfig = this.$themeConfig.updateBar
},
computed: {
bgStyle () {
const { contentBgStyle } = this.$themeConfig
return contentBgStyle ? 'bg-style-' + contentBgStyle : ''
},
isShowUpdateBar () {
return this.updateBarConfig && this.updateBarConfig.showToArticle === false ? false : true
},
showTitle () {
return !this.$frontmatter.pageComponent
},
showRightMenu () {
const { $frontmatter, $themeConfig, $page } = this
const { sidebar } = $frontmatter
return (
$themeConfig.rightMenuBar !== false &&
$page.headers &&
($frontmatter && sidebar && sidebar !== false) !== false
)
},
pageComponent () {
return this.$frontmatter.pageComponent ? this.$frontmatter.pageComponent.name : false
},
isShowSlotT() {
return this.getShowStatus('pageTshowMode')
},
isShowSlotB() {
return this.getShowStatus('pageBshowMode')
}
},
methods: {
getShowStatus(prop) {
const { htmlModules } = this.$themeConfig
if(!htmlModules) return false
if (htmlModules[prop] === 'article') { // 仅文章页显示
return this.isArticle()
} else if (htmlModules[prop] === 'custom'){ // 仅自定义页显示
return !(this.isArticle())
} else { // 全部显示
return true
}
},
isArticle () {
return this.$frontmatter.article !== false
}
}
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl'
.page
padding-bottom 2rem
display block
@media (max-width $MQMobile)
padding-top $navbarHeight
@media (min-width $MQMobile)
padding-top ($navbarHeight + 1.5rem)
>*
@extend $vdoing-wrapper
.theme-vdoing-wrapper
.content-wrapper
position relative
h1 img
margin-bottom -0.2rem
max-width 2.2rem
max-height 2.2rem
.theme-vdoing-wrapper
--linesColor rgba(50, 0, 0, 0.05)
&.bg-style-1 // 方格
background-image linear-gradient(90deg, var(--linesColor) 3%, transparent 3%), linear-gradient(0deg, var(--linesColor) 3%, transparent 3%)
background-position center center
background-size 20px 20px
&.bg-style-2 // 横线
background-image repeating-linear-gradient(0, var(--linesColor) 0, var(--linesColor) 1px, transparent 0, transparent 50%)
background-size 30px 30px
&.bg-style-3 // 竖线
background-image repeating-linear-gradient(90deg, var(--linesColor) 0, var(--linesColor) 1px, transparent 0, transparent 50%)
background-size 30px 30px
&.bg-style-4 // 左斜线
background-image repeating-linear-gradient(-45deg, var(--linesColor) 0, var(--linesColor) 1px, transparent 0, transparent 50%)
background-size 20px 20px
&.bg-style-5 // 右斜线
background-image repeating-linear-gradient(45deg, var(--linesColor) 0, var(--linesColor) 1px, transparent 0, transparent 50%)
background-size 20px 20px
&.bg-style-6 // 点状
background-image radial-gradient(var(--linesColor) 1px, transparent 1px)
background-size 10px 10px
// 背景纹适应深色模式
.theme-mode-dark
.theme-vdoing-wrapper
--linesColor rgba(125, 125, 125, 0.05)
/**
* 右侧菜单的自适应
*/
@media (min-width 720px) and (max-width 1279px)
.have-rightmenu
.page
padding-right 0.8rem !important
@media (max-width 1279px)
.have-rightmenu
.right-menu-wrapper
display none
@media (min-width 1280px)
.have-rightmenu
.sidebar .sidebar-sub-headers
display none
</style>

View File

@@ -0,0 +1,168 @@
<template>
<div class="page-edit">
<div class="edit-link" v-if="editLink">
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{
editLinkText
}}</a>
<OutboundLink />
</div>
<div class="tags" v-if="$themeConfig.tag !== false && tags && tags[0]">
<router-link
:to="`/tags/?tag=${encodeURIComponent(item)}`"
v-for="(item, index) in tags"
:key="index"
title="标签"
>#{{ item }}</router-link
>
</div>
<div class="last-updated" v-if="lastUpdated">
<span class="prefix">{{ lastUpdatedText }}:</span>
<span class="time">{{ lastUpdated }}</span>
</div>
</div>
</template>
<script>
import isNil from 'lodash/isNil'
import { endingSlashRE, outboundRE } from '../util'
export default {
name: 'PageEdit',
computed: {
tags () {
return this.$frontmatter.tags
},
lastUpdated () {
return this.$page.lastUpdated
},
lastUpdatedText () {
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
return this.$themeLocaleConfig.lastUpdated
}
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
return this.$site.themeConfig.lastUpdated
}
return 'Last Updated'
},
editLink () {
const showEditLink = isNil(this.$page.frontmatter.editLink)
? this.$site.themeConfig.editLinks
: this.$page.frontmatter.editLink
const {
repo,
docsDir = '',
docsBranch = 'master',
docsRepo = repo
} = this.$site.themeConfig
if (showEditLink && docsRepo && this.$page.relativePath) {
return this.createEditLink(
repo,
docsRepo,
docsDir,
docsBranch,
this.$page.relativePath
)
}
return null
},
editLinkText () {
return (
this.$themeLocaleConfig.editLinkText
|| this.$site.themeConfig.editLinkText
|| `Edit this page`
)
}
},
methods: {
createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
const bitbucket = /bitbucket.org/
if (bitbucket.test(docsRepo)) {
const base = docsRepo
return (
base.replace(endingSlashRE, '')
+ `/src`
+ `/${docsBranch}/`
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+ path
+ `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
)
}
const gitlab = /gitlab.com/
if (gitlab.test(docsRepo)) {
const base = docsRepo
return (
base.replace(endingSlashRE, '')
+ `/-/edit`
+ `/${docsBranch}/`
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+ path
)
}
const base = outboundRE.test(docsRepo)
? docsRepo
: `https://github.com/${docsRepo}`
return (
base.replace(endingSlashRE, '')
+ `/edit`
+ `/${docsBranch}/`
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+ path
)
}
}
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl'
.page-edit
@extend $wrapper
padding-top 1rem
padding-bottom 1rem
overflow auto
.edit-link
display inline-block
float left
margin 0 2rem 0.5rem 0
a
margin-right 0.25rem
.tags
float left
a
margin 0 0.8rem 0.5rem 0
display inline-block
color var(--textLightenColor)
padding 0.2rem 0.7rem
font-size 0.9em
background-color rgba(128, 128, 128, 0.08)
border-radius 3px
opacity 0.8
.last-updated
float right
font-size 0.9em
.prefix
font-weight 500
color var(--textColor)
opacity 0.8
.time
font-weight 400
color #aaa
@media (max-width $MQMobile)
.page-edit
.edit-link, .tags
margin-bottom 0.5rem
.last-updated
width 100%
font-size 0.8em
text-align left
</style>

View File

@@ -0,0 +1,237 @@
<template>
<div class="page-nav-wapper">
<!-- 页面中间左右翻页 -->
<div
class="page-nav-centre-wrap"
v-if="$themeConfig.pageButton !== false && (prev || next)"
>
<router-link
class="page-nav-centre page-nav-centre-prev"
v-if="prev"
:to="prev.path"
@mouseenter.native="showTooltip($event)"
@mousemove.native="showTooltip($event)"
>
<div class="tooltip">{{ prev.title || prev.path }}</div>
</router-link>
<router-link
class="page-nav-centre page-nav-centre-next"
v-if="next"
:to="next.path"
@mouseenter.native="showTooltip($event)"
@mousemove.native="showTooltip($event)"
>
<div class="tooltip">{{ next.title || next.path }}</div>
</router-link>
</div>
<!-- 底部翻页按钮 -->
<div
class="page-nav"
v-if="prev || next"
>
<p class="inner">
<span
v-if="prev"
class="prev"
>
<router-link
v-if="prev"
class="prev"
:to="prev.path"
>{{ prev.title || prev.path }}</router-link>
</span>
<span
v-if="next"
class="next"
>
<router-link
v-if="next"
:to="next.path"
>{{ next.title || next.path }}</router-link>
</span>
</p>
</div>
</div>
</template>
<script>
import { resolvePage } from '../util'
import isString from 'lodash/isString'
import isNil from 'lodash/isNil'
export default {
name: 'PageNav',
props: ['sidebarItems'],
computed: {
prev () {
return resolvePageLink(LINK_TYPES.PREV, this)
},
next () {
return resolvePageLink(LINK_TYPES.NEXT, this)
}
},
methods: {
showTooltip (e) {
const clientW = document.body.clientWidth
const X = e.clientX
const tooltipEle = e.target.querySelector('.tooltip')
if (!tooltipEle) {
return
}
const tooltipEleStyle = tooltipEle.style
if (X < clientW / 2) {
tooltipEleStyle.right = null
tooltipEleStyle.left = X + 10 + 'px'
} else {
tooltipEleStyle.left = null
tooltipEleStyle.right = clientW - X + 10 + 'px'
}
tooltipEleStyle.top = e.clientY + 10 + 'px'
}
}
}
function resolvePrev (page, items) {
return find(page, items, -1)
}
function resolveNext (page, items) {
return find(page, items, 1)
}
const LINK_TYPES = {
NEXT: {
resolveLink: resolveNext,
getThemeLinkConfig: ({ nextLinks }) => nextLinks,
getPageLinkConfig: ({ frontmatter }) => frontmatter.next
},
PREV: {
resolveLink: resolvePrev,
getThemeLinkConfig: ({ prevLinks }) => prevLinks,
getPageLinkConfig: ({ frontmatter }) => frontmatter.prev
}
}
function resolvePageLink (
linkType,
{ $themeConfig, $page, $route, $site, sidebarItems }
) {
const { resolveLink, getThemeLinkConfig, getPageLinkConfig } = linkType
// Get link config from theme
const themeLinkConfig = getThemeLinkConfig($themeConfig)
// Get link config from current page
const pageLinkConfig = getPageLinkConfig($page)
// Page link config will overwrite global theme link config if defined
const link = isNil(pageLinkConfig) ? themeLinkConfig : pageLinkConfig
if (link === false) {
return
} else if (isString(link)) {
return resolvePage($site.pages, link, $route.path)
} else {
return resolveLink($page, sidebarItems)
}
}
function find (page, items, offset) {
const res = []
flatten(items, res)
for (let i = 0; i < res.length; i++) {
const cur = res[i]
if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) {
return res[i + offset]
}
}
}
function flatten (items, res) {
for (let i = 0, l = items.length; i < l; i++) {
if (items[i].type === 'group') {
flatten(items[i].children || [], res)
} else {
res.push(items[i])
}
}
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl'
.page-nav
@extend $wrapper
padding-top 1rem
padding-bottom 0
.inner
min-height 2rem
margin-top 0
border-top 1px solid var(--borderColor)
padding-top 1rem
overflow auto // clear float
.next
float right
.page-nav-centre-wrap
.page-nav-centre
position fixed
top 50%
width 80px
height 70px
margin-top -35px
outline 0
transition all 0.2s
border-radius 3px
opacity 0.55
z-index 99
@media (max-width 1340px)
width 50px
@media (max-width 960px)
display none
&:hover
background rgba(153, 153, 153, 0.15)
opacity 1
.tooltip
display block
&:before
content ''
display block
width 10px
height 10px
border-top 2px solid #999
border-right 2px solid #999
position absolute
top 0
right 0
bottom 0
left 0
margin auto
.tooltip
display none
background rgba(0, 0, 0, 0.5)
color #fff
padding 4px 8px
font-size 13px
border-radius 3px
position fixed
max-width 200px
z-index 99
.page-nav-centre-prev
left 0
&:before
transform rotate(-135deg)
.page-nav-centre-next
right 0
&:before
transform rotate(45deg)
.sidebar-open .page-nav-centre-wrap .page-nav-centre-prev
left $sidebarWidth
.no-sidebar .page-nav-centre-wrap .page-nav-centre-prev
left 0
</style>

View File

@@ -0,0 +1,233 @@
<template>
<div class="pagination">
<span
class="card-box prev iconfont icon-jiantou-zuo"
:class="{disabled: currentPage === 1}"
@click="goPrex()"
>
<p>上一页</p>
</span>
<!-- 分页在5页及以下时 -->
<div
class="pagination-list"
v-if="pages <= 5"
>
<span
class="card-box"
v-for="item in pages"
:key="item"
:class="{active: currentPage === item}"
@click="goIndex(item)"
>{{item}}</span>
</div>
<!-- 分页在5页以上 -->
<div
class="pagination-list"
v-else
>
<!-- 一号位 -->
<span
class="card-box"
:class="{active: currentPage === 1}"
@click="goIndex(1)"
>1</span>
<!-- 二号位 -->
<span
class="ellipsis ell-two"
v-show="currentPage > 3"
@click="goIndex(currentPage - 2)"
title="上两页"
/>
<!--这里没有使用v-if的原因是因为部署版本在当前页大于3时刷新页面出现了一些bug-->
<span
class="card-box"
v-show="currentPage <= 3"
:class="{active: currentPage === 2}"
@click="goIndex(2)"
>2</span>
<!-- 三号位 -->
<span
class="card-box"
:class="{active: currentPage >= 3 && currentPage <= (pages - 2)}"
@click="goIndex(threeNum())"
>{{ threeNum() }}</span>
<!-- 四号位 -->
<span
class="ellipsis ell-four"
v-show="currentPage < (pages - 2)"
@click="goIndex(currentPage + 2)"
title="下两页"
/>
<span
class="card-box"
v-show="currentPage >= (pages - 2)"
:class="{active: currentPage === pages-1}"
@click="goIndex(pages-1)"
>{{ pages-1 }}</span>
<!-- 五号位 -->
<span
class="card-box"
:class="{active: currentPage === pages}"
@click="goIndex(pages)"
>{{pages}}</span>
</div>
<span
class="card-box next iconfont icon-jiantou-you"
:class="{disabled: currentPage === pages}"
@click="goNext()"
>
<p>下一页</p>
</span>
</div>
</template>
<script>
export default {
props: {
total: { // 总长度
type: Number,
default: 10
},
perPage: { // 每页长
type: Number,
default: 10
},
currentPage: { // 当前页
type: Number,
default: 1
}
},
computed: {
pages () { // 总页数
return Math.ceil(this.total / this.perPage)
}
},
methods: {
threeNum () { // 三号位页码计算
let num = 3
const currentPage = this.currentPage
const pages = this.pages
if (currentPage < 3) {
num = 3
} else if (currentPage > (pages - 3)) {
num = pages - 2
} else {
num = currentPage
}
return num
},
goPrex () {
let currentPage = this.currentPage
if (currentPage > 1) {
this.handleEmit(--currentPage)
}
},
goNext () {
let currentPage = this.currentPage
if (currentPage < this.pages) {
this.handleEmit(++currentPage)
}
},
goIndex (i) {
if (i !== this.currentPage) {
this.handleEmit(i)
}
},
handleEmit (i) {
this.$emit('getCurrentPage', i)
}
}
}
</script>
<style lang='stylus'>
.pagination
position relative
height 60px
text-align center
span
line-height 1rem
opacity 0.9
cursor pointer
&:hover
color $accentColor
&.ellipsis
opacity 0.5
&::before
content '...'
font-size 1.2rem
@media (any-hover hover)
&.ell-two
&:hover
&::before
content '«'
&.ell-four
&:hover
&::before
content '»'
> span
position absolute
top 0
padding 1rem 1.2rem
font-size 0.95rem
&.disabled
color rgba(125, 125, 125, 0.5)
&.prev
left 0
border-top-right-radius 32px
border-bottom-right-radius 32px
&.next
right 0
border-top-left-radius 32px
border-bottom-left-radius 32px
&::before
float right
margin-left 0.3rem
p
display inline
line-height 0.95rem
.pagination-list
span
display inline-block
width 2.5rem
height 2.5rem
line-height 2.5rem
margin 0.3rem
&.active
background $accentColor
color var(--mainBg)
@media (max-width 800px)
.pagination
> span
padding 1rem 1.5rem
p
display none
// 719px
@media (max-width $MQMobile)
.pagination
> span // 左右按钮
padding 0.9rem 1.5rem
.pagination-list
span
width 2.3rem
height 2.3rem
line-height 2.3rem
margin 0.25rem
@media (max-width 390px)
.pagination
> span // 左右按钮
padding 0.8rem 1.3rem
.pagination-list
span
width 2rem
height 2rem
line-height 2rem
margin 0.1rem
margin-top 0.3rem
</style>

View File

@@ -0,0 +1,229 @@
<template>
<div
class="post-list"
ref="postList"
>
<transition-group
tag="div"
name="post"
>
<div
class="post card-box"
:class="item.frontmatter.sticky && 'iconfont icon-zhiding'"
v-for="item in sortPosts"
:key="item.key"
>
<div class="title-wrapper">
<h2>
<router-link :to="item.path">{{item.title}}</router-link>
</h2>
<div class="article-info">
<a
title="作者"
class="iconfont icon-touxiang"
target="_blank"
v-if="item.author && item.author.href"
:href="item.author.href"
>{{ item.author.name ? item.author.name : item.author }}</a>
<span
title="作者"
class="iconfont icon-touxiang"
v-else-if="item.author"
>{{ item.author.name ? item.author.name : item.author }}</span>
<span
title="创建时间"
class="iconfont icon-riqi"
v-if="item.frontmatter.date"
>{{ item.frontmatter.date.split(' ')[0]}}</span>
<span
title="分类"
class="iconfont icon-wenjian"
v-if="$themeConfig.category !== false && item.frontmatter.categories"
>
<router-link
:to="`/categories/?category=${encodeURIComponent(c)}`"
v-for="(c, index) in item.frontmatter.categories"
:key="index"
>{{c}}</router-link>
</span>
<span
title="标签"
class="iconfont icon-biaoqian tags"
v-if="$themeConfig.tag !== false && item.frontmatter.tags && item.frontmatter.tags[0]"
>
<router-link
:to="`/tags/?tag=${encodeURIComponent(t)}`"
v-for="(t, index) in item.frontmatter.tags"
:key="index"
>{{t}}</router-link>
</span>
</div>
</div>
<div
class="excerpt-wrapper"
v-if="item.excerpt"
>
<div
class="excerpt"
v-html="item.excerpt"
></div>
<router-link
:to="item.path"
class="readmore iconfont icon-jiantou-you"
>阅读全文</router-link>
</div>
</div>
</transition-group>
</div>
</template>
<script>
export default {
props: {
category: {
type: String,
default: ''
},
tag: {
type: String,
default: ''
},
currentPage: {
type: Number,
default: 1
},
perPage: {
type: Number,
default: 10
}
},
data () {
return {
sortPosts: [],
postListOffsetTop: 0
}
},
created () {
this.setPosts()
},
mounted () {
// this.postListOffsetTop = this.getElementToPageTop(this.$refs.postList) - 240
},
watch: {
currentPage () {
if (this.$route.query.p != this.currentPage) { // 此判断防止添加相同的路由信息(如浏览器回退时触发的)
this.$router.push({
query: {
...this.$route.query,
p: this.currentPage
}
})
}
// setTimeout(() => {
// window.scrollTo({ top: this.postListOffsetTop }) // behavior: 'smooth'
// },0)
this.setPosts()
},
category () {
this.setPosts()
},
tag () {
this.setPosts()
}
},
methods: {
setPosts () {
const currentPage = this.currentPage
const perPage = this.perPage
let posts = []
if (this.category) {
posts = this.$groupPosts.categories[this.category]
} else if (this.tag) {
posts = this.$groupPosts.tags[this.tag]
} else {
posts = this.$sortPosts
}
this.sortPosts = posts.slice((currentPage - 1) * perPage, currentPage * perPage)
},
// getElementToPageTop(el) {
// if(el && el.parentElement) {
// return this.getElementToPageTop(el.parentElement) + el.offsetTop
// }
// return el.offsetTop
// }
}
}
</script>
<style lang='stylus'>
.post-list
margin-bottom 4rem
.post
position relative
padding 1rem 1.5rem
margin-bottom 0.9rem
transition all 0.3s
&.post-leave-active
display none
&.post-enter
opacity 0
transform translateX(-20px)
&::before
position absolute
top -1px
right 0
font-size 2.5rem
color $activeColor
opacity 0.85
.title-wrapper
a
color var(--textColor)
&:hover
color $accentColor
h2
margin 0.5rem 0
font-size 1.4rem
border none
a
@media (max-width $MQMobile)
font-weight 400
.article-info
> a, > span
opacity 0.7
font-size 0.8rem
margin-right 1rem
cursor pointer
&::before
margin-right 0.3rem
a
margin 0
&:not(:first-child)
&::before
content '/'
.tags a:not(:first-child)::before
content '、'
.excerpt-wrapper
border-top 1px solid var(--borderColor)
margin 0.5rem 0
overflow hidden
.excerpt
margin-bottom 0.3rem
font-size 0.92rem
h1, h2, h3
display none
img
max-height 280px
max-width 100% !important
margin 0 auto
.readmore
float right
margin-right 1rem
line-height 1rem
&::before
float right
font-size 0.8rem
margin 0.1rem 0 0 0.2rem
</style>

View File

@@ -0,0 +1,95 @@
<template>
<div class="right-menu-wrapper">
<div class="right-menu-margin">
<div class="right-menu-content">
<div
:class="['right-menu-item', 'level'+item.level, { active: item.slug === hashText }]"
v-for="(item, i) in headers"
:key="i"
>
<a :href="'#'+item.slug">{{item.title}}</a>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
headers: [],
hashText: ''
}
},
mounted () {
this.getHeadersData()
this.getHashText()
},
watch: {
$route () {
this.headers = this.$page.headers
this.getHashText()
}
},
methods: {
getHeadersData () {
this.headers = this.$page.headers
},
getHashText () {
this.hashText = decodeURIComponent(window.location.hash.slice(1))
}
}
}
</script>
<style lang='stylus'>
.right-menu-wrapper
width $rightMenuWidth
float right
margin-right -($rightMenuWidth + 60px)
position sticky
top 0
font-size 0.9rem
.right-menu-margin
margin-top ($navbarHeight + 1rem)
.right-menu-content
max-height 80vh
position relative
overflow hidden
&::-webkit-scrollbar-track-piece
background none
&::-webkit-scrollbar-thumb:vertical
background-color hsla(0, 0%, 49%, 0.3)
&:hover
overflow-y auto
.right-menu-item
padding 4px 15px
border-left 0.13rem solid var(--borderColor)
&.level3
padding-left 28px
&.active
border-color $accentColor
a
color $accentColor
opacity 1
a
color var(--textColor)
opacity 0.75
display block
width ($rightMenuWidth - 30px)
&:hover
color $accentColor
.have-body-img
.right-menu-wrapper
.right-menu-margin
padding 0.3rem 0
background var(--sidebarBg)
border-radius 5px
.right-menu-item
border-color transparent
&.active
border-left 0.2rem solid $accentColor
&:hover
border-left 0.2rem solid $accentColor
</style>

View File

@@ -0,0 +1,121 @@
<template>
<aside class="sidebar">
<div
class="blogger"
v-if="blogger"
>
<img :src="blogger.avatar" />
<div class="blogger-info">
<h3>{{blogger.name}}</h3>
<div
class="icons"
v-if="blogger.social"
>
<a
:href="item.link"
:title="item.title"
:class="['iconfont', item.iconClass]"
v-for="(item, index) in blogger.social.icons"
:key="index"
target="_blank"
></a>
</div>
<span v-else>{{blogger.slogan}}</span>
</div>
</div>
<!-- 移动端Nav -->
<NavLinks />
<slot name="top" />
<SidebarLinks
:depth="0"
:items="items"
/>
<slot name="bottom" />
</aside>
</template>
<script>
import SidebarLinks from '@theme/components/SidebarLinks.vue'
import NavLinks from '@theme/components/NavLinks.vue'
export default {
name: 'Sidebar',
components: { SidebarLinks, NavLinks },
props: ['items'],
computed: {
blogger () {
return this.$themeConfig.blogger
}
}
}
</script>
<style lang="stylus">
.sidebar
ul
padding 0
margin 0
list-style-type none
a
display inline-block
.nav-links
display none
border-bottom 1px solid var(--borderColor)
padding 0.5rem 0 0.75rem 0
a
font-weight 600
.nav-item, .repo-link
display block
line-height 1.25rem
font-size 1.1em
padding 0.5rem 0 0.5rem 1.5rem
& > .sidebar-links
padding 1.5rem 0
& > li > a.sidebar-link
font-size 1.1em
line-height 1.7
font-weight bold
& > li:not(:first-child)
margin-top 0.75rem
.blogger
display none
border-bottom 1px solid var(--borderColor)
img
width 60px
height 60px
border-radius 5px
margin 0.75rem 1rem
.blogger-info
flex 1
h3
margin 0.95rem 0 0.7rem
font-size 1.1rem
.icons .iconfont
font-size 1.2rem
padding-right 0.6rem
color #777
.sidebar-slot
margin-bottom: -.5rem;
font-size: .85rem;
&.sidebar-slot-top
padding: 1.5rem 1.5rem 0;
&.sidebar-slot-bottom
padding: 0 1.5rem 1.5rem;
@media (max-width $MQMobile)
.sidebar
.blogger
display flex
.nav-links
display block
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
top calc(1rem - 2px)
& > .sidebar-links
padding 1rem 0
</style>

View File

@@ -0,0 +1,64 @@
<template>
<div
class="sidebar-button"
@click="$emit('toggle-sidebar')"
title="目录"
>
<svg
class="icon"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
role="img"
viewBox="0 0 448 512"
>
<path
fill="currentColor"
d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"
class
/>
</svg>
</div>
</template>
<style lang="stylus">
.sidebar-button
cursor pointer
display none
width 1.25rem
height 1.25rem
position absolute
padding 0.6rem
top 0.6rem
left 1rem
@media (max-width $MQMobile)
display block
.icon
display block
width 1.25rem
height 1.25rem
@media (min-width ($MQMobile + 1px))
$mobileSidebarWidth = $sidebarWidth * 0.82
.sidebar-button
width 40px
height 40px
display inline-block
position fixed
left 0
top ($navbarHeight + 1rem)
text-align center
line-height 44px
margin 5px 8px
color #888
border-radius 50%
padding 0
// transition left 0.2s ease
transition all .2s
&:hover
background $accentColor
color #fff
box-shadow 0 0 6px $accentColor
.icon
display inline
width 1rem
height 1rem
</style>

View File

@@ -0,0 +1,128 @@
<template>
<section
class="sidebar-group"
:class="[
{
collapsable,
'is-sub-group': depth !== 0
},
`depth-${depth}`
]"
>
<router-link
v-if="item.path"
class="sidebar-heading clickable"
:class="{
open,
active: isActive($route, item.path)
}"
:to="item.path"
@click.native="$emit('toggle')"
>
<span>{{ item.title }}</span>
<span
class="arrow"
v-if="collapsable"
:class="open ? 'down' : 'right'"
></span>
</router-link>
<p
v-else
class="sidebar-heading"
:class="{ open }"
@click="$emit('toggle')"
>
<span>{{ item.title }}</span>
<span
class="arrow"
v-if="collapsable"
:class="open ? 'down' : 'right'"
></span>
</p>
<DropdownTransition>
<SidebarLinks
class="sidebar-group-items"
:items="item.children"
v-if="open || !collapsable"
:sidebar-depth="item.sidebarDepth"
:initial-open-group-index="item.initialOpenGroupIndex"
:depth="depth + 1"
/>
</DropdownTransition>
</section>
</template>
<script>
import { isActive } from '../util'
import DropdownTransition from '@theme/components/DropdownTransition.vue'
export default {
name: 'SidebarGroup',
props: ['item', 'open', 'collapsable', 'depth'],
components: { DropdownTransition },
// ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
beforeCreate () {
this.$options.components.SidebarLinks = require('./SidebarLinks.vue').default
},
methods: { isActive }
}
</script>
<style lang="stylus">
.sidebar-group
.sidebar-group
padding-left 0.5em
&:not(.collapsable)
.sidebar-heading:not(.clickable)
cursor auto
color inherit
// refine styles of nested sidebar groups
&.is-sub-group
padding-left 0
& > .sidebar-heading
font-size 1.01em
line-height 1.4
font-weight bold
padding-left 2rem
&:not(.clickable)
// opacity 0.9
& > .sidebar-group-items
padding-left 1rem
& > li > .sidebar-link
font-size 0.98em
border-left none
&.depth-2
& > .sidebar-heading
border-left none
.sidebar-heading
color var(--textColor)
transition color 0.15s ease
cursor pointer
font-size 1.1em
font-weight bold
// text-transform uppercase
padding 0.35rem 1.5rem 0.35rem 1.25rem
width 100%
box-sizing border-box
margin 0
border-left 0.25rem solid transparent
&.open, &:hover
color inherit
.arrow
position relative
top -0.12em
left 0.5em
&.clickable
&.active
font-weight 600
color $accentColor
border-left-color $accentColor
&:hover
color $accentColor
.sidebar-group-items
transition height 0.1s ease-out
font-size 0.95em
overflow hidden
</style>

View File

@@ -0,0 +1,124 @@
<script>
import { isActive, hashRE, groupHeaders } from '../util'
export default {
functional: true,
props: ['item', 'sidebarDepth'],
render (h,
{
parent: {
$page,
$site,
$route,
$themeConfig,
$themeLocaleConfig
},
props: {
item,
sidebarDepth
}
}) {
// use custom active class matching logic
// due to edge case of paths ending with / + hash
const selfActive = isActive($route, item.path)
// for sidebar: auto pages, a hash link should be active if one of its child
// matches
const active = item.type === 'auto'
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
: selfActive
const link = item.type === 'external'
? renderExternal(h, item.path, item.title || item.path)
: renderLink(h, item.path, item.title || item.path, active)
const maxDepth = [
$page.frontmatter.sidebarDepth,
sidebarDepth,
$themeLocaleConfig.sidebarDepth,
$themeConfig.sidebarDepth,
1
].find(depth => depth !== undefined)
const displayAllHeaders = $themeLocaleConfig.displayAllHeaders
|| $themeConfig.displayAllHeaders
if (item.type === 'auto') {
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
const children = groupHeaders(item.headers)
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
} else {
return link
}
}
}
function renderLink (h, to, text, active) {
return h('router-link', {
props: {
to,
activeClass: '',
exactActiveClass: ''
},
class: {
active,
'sidebar-link': true
}
}, text)
}
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
if (!children || depth > maxDepth) return null
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
const active = isActive(route, path + '#' + c.slug)
return h('li', { class: 'sidebar-sub-header' }, [
renderLink(h, path + '#' + c.slug, c.title, active),
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
])
}))
}
function renderExternal (h, to, text) {
return h('a', {
attrs: {
href: to,
target: '_blank',
rel: 'noopener noreferrer'
},
class: {
'sidebar-link': true
}
}, [text, h('OutboundLink')])
}
</script>
<style lang="stylus">
.sidebar .sidebar-sub-headers
padding-left 1rem
font-size 0.95em
a.sidebar-link
font-size 1em
font-weight 400
display inline-block
color var(--textColor)
border-left 0.25rem solid transparent
padding 0.35rem 1rem 0.35rem 1.25rem
line-height 1.4
width 100%
box-sizing border-box
&:hover
color $accentColor
&.active
font-weight 600
color $accentColor
border-left-color $accentColor
.sidebar-group &
padding-left 2rem
.sidebar-sub-headers &
padding-top 0.25rem
padding-bottom 0.25rem
border-left none
&.active
font-weight 500
</style>

View File

@@ -0,0 +1,93 @@
<template>
<ul class="sidebar-links" v-if="items.length">
<li v-for="(item, i) in items" :key="i">
<SidebarGroup
v-if="item.type === 'group'"
:item="item"
:open="i === openGroupIndex"
:collapsable="item.collapsable || item.collapsible"
:depth="depth"
@toggle="toggleGroup(i)"
/>
<SidebarLink v-else :sidebarDepth="sidebarDepth" :item="item" />
</li>
</ul>
</template>
<script>
import SidebarGroup from '@theme/components/SidebarGroup.vue'
import SidebarLink from '@theme/components/SidebarLink.vue'
import { isActive } from '../util'
export default {
name: 'SidebarLinks',
components: { SidebarGroup, SidebarLink },
props: [
'items',
'depth', // depth of current sidebar links
'sidebarDepth', // depth of headers to be extracted
'initialOpenGroupIndex'
],
data () {
return {
openGroupIndex: this.initialOpenGroupIndex || 0
}
},
created () {
this.refreshIndex()
},
watch: {
'$route' () {
this.refreshIndex()
}
},
methods: {
refreshIndex () {
const index = resolveOpenGroupIndex(
this.$route,
this.items
)
if (index > -1) {
this.openGroupIndex = index
}
},
toggleGroup (index) {
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
},
isActive (page) {
return isActive(this.$route, page.regularPath)
}
}
}
function resolveOpenGroupIndex (route, items) {
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (descendantIsActive(route, item)) {
return i
}
}
return -1
}
function descendantIsActive (route, item) {
if (item.type === 'group') {
return item.children.some(child => {
if (child.type === 'group') {
return descendantIsActive(route, child)
} else {
return child.type === 'page' && isActive(route, child.path)
}
})
}
return false
}
</script>

Some files were not shown because too many files have changed in this diff Show More