创建文档
18
.gitignore
vendored
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
33
docs/.vuepress/components/Bit.vue
Normal 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>
|
||||
306
docs/.vuepress/components/Bpmn/bpmn.vue
Normal 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>
|
||||
38
docs/.vuepress/components/Bpmn/bpmnView.vue
Normal 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>
|
||||
@@ -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 + '}'
|
||||
})
|
||||
}
|
||||
@@ -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': '变量'
|
||||
}
|
||||
205
docs/.vuepress/components/Co/formCo.vue
Normal 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>
|
||||
|
||||
104
docs/.vuepress/components/Co/searchCo.vue
Normal 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>
|
||||
127
docs/.vuepress/components/Co/tableHeadCo.vue
Normal 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>
|
||||
15
docs/.vuepress/components/Foo/Bar.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<p class="demo">
|
||||
{{ msg }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: 'Hello this is <Foo-Bar>'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
5
docs/.vuepress/components/OtherComponent.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<p class="demo">
|
||||
This is another component
|
||||
</p>
|
||||
</template>
|
||||
35
docs/.vuepress/components/UpgradePath.vue
Normal 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>
|
||||
17
docs/.vuepress/components/Web/Bpmn.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<bpmn />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import bpmn from '../Bpmn/bpmn'
|
||||
export default {
|
||||
components: {
|
||||
bpmn
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
20
docs/.vuepress/components/Web/BpmnNameForm.vue
Normal 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>
|
||||
65
docs/.vuepress/components/Web/EditBpmn.vue
Normal 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>
|
||||
168
docs/.vuepress/components/Web/Form.vue
Normal 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>
|
||||
81
docs/.vuepress/components/Web/OnlyShowBpmn.vue
Normal 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>
|
||||
134
docs/.vuepress/components/Web/SearchForm.vue
Normal 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>
|
||||
48
docs/.vuepress/components/Web/Table.vue
Normal 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>
|
||||
|
||||
56
docs/.vuepress/components/Web/TableCheckBox.vue
Normal 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>
|
||||
|
||||
66
docs/.vuepress/components/Web/TableSlot.vue
Normal 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>
|
||||
|
||||
15
docs/.vuepress/components/demo-1.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<p class="demo">
|
||||
{{ msg }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: 'Hello this is <demo-1>'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
204
docs/.vuepress/components/diagram-markdown-slot-relationship.vue
Normal 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>
|
||||
13
docs/.vuepress/components/svg-container.vue
Normal 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
@@ -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,
|
||||
}
|
||||
22
docs/.vuepress/config/head.js
Normal 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格式广告支持
|
||||
]
|
||||
110
docs/.vuepress/config/plugins.js
Normal 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')
|
||||
},
|
||||
},
|
||||
],
|
||||
]
|
||||
86
docs/.vuepress/config/themeConfig.js
Normal 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(广告)模块
|
||||
}
|
||||
106
docs/.vuepress/config/themeConfig/htmlModules.js
Normal 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 和 windowRB:1.展示区块宽高最大是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>`,
|
||||
// }
|
||||
42
docs/.vuepress/config/themeConfig/nav.js
Normal 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/',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
105
docs/.vuepress/config/themeConfig/sidebar.js
Normal 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',
|
||||
// // ]
|
||||
// // }
|
||||
// ],
|
||||
}
|
||||
25
docs/.vuepress/enhanceApp.js
Normal 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生效)
|
||||
}
|
||||
12
docs/.vuepress/plugins/love-me/index.js
Normal 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;
|
||||
62
docs/.vuepress/plugins/love-me/love-me.js
Normal 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)
|
||||
}
|
||||
}
|
||||
BIN
docs/.vuepress/public/img/EB-logo.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
docs/.vuepress/public/img/bg.jpeg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
docs/.vuepress/public/img/bg.jpg
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
docs/.vuepress/public/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
docs/.vuepress/public/img/git.png
Normal file
|
After Width: | Height: | Size: 1021 KiB |
BIN
docs/.vuepress/public/img/more.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/.vuepress/public/img/other.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/.vuepress/public/img/panda-waving.png
Normal file
|
After Width: | Height: | Size: 306 KiB |
BIN
docs/.vuepress/public/img/python.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/.vuepress/public/img/ui.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/.vuepress/public/img/web.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
59
docs/.vuepress/styles/index.styl
Normal 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;
|
||||
68
docs/.vuepress/styles/palette.styl
Normal 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()
|
||||
16
docs/00.目录页/01.指南.md
Normal 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
|
||||
---
|
||||
|
||||
16
docs/00.目录页/02.前端.md
Normal 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
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: 介绍
|
||||
date: 2021-02-03 09:26:32
|
||||
permalink: /pages/a3c9a2/
|
||||
---
|
||||
# 介绍
|
||||
|
||||
通用框架组件是由前端及后台两部分组成,通过对单表操作代码的封装,使使用者在较小的开发工作下,完成业务系统的开发。
|
||||
|
||||
## 前端用到的技术
|
||||
|
||||
1. [**Vue**](https://cn.vuejs.org/) Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
|
||||
|
||||
2. [**Vuex**](https://vuex.vuejs.org/zh-cn/) 前端状态管理
|
||||
|
||||
3. [**Vue-router**](https://router.vuejs.org/zh-cn/) 管理页面路由跳转。
|
||||
|
||||
4. [**axios**](https://github.com/axios/axios) 前端请求后台获取数据的工具,类似于ajax。
|
||||
|
||||
5. [**Vue-cli**](https://github.com/vuejs/vue-cli) Vue项目的手脚架搭建客户端。
|
||||
|
||||
6. [**Typescript**](https://www.tslang.cn/) 是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。TypeScript添加了可选的静态类型系统、很多尚未正式发布的ECMAScript新特性(如装饰器 [1] )。2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript。
|
||||
|
||||
7. [**vue-typescript-admin-template**](https://github.com/Armour/vue-typescript-admin-template) 一套封装较为成熟的管理系统Vue模板,包含较多的实用性组件及示例。
|
||||
|
||||
8. [**elementUI**](https://element.eleme.cn/#/zh-CN) ,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库
|
||||
|
||||
9. [**vue-class-component**](https://github.com/vuejs/vue-class-component) 使用typescript语法及注解简化代码插件
|
||||
|
||||
10. [**vue-property-decorator**](https://github.com/kaorun343/vue-property-decorator) 使用 typescript 语法及注解简化代码的插件
|
||||
|
||||
## 后台用到的技术
|
||||
|
||||
1. [**SpringBoot**](https://spring.io/projects/spring-boot/) 快速开发,自动装配的Spring家族的框架
|
||||
|
||||
2. [**Mybatis**](https://mybatis.org/mybatis-3/zh/index.html) 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
|
||||
|
||||
3. [**Apache Shiro**](http://shiro.apache.org/) 是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
|
||||
|
||||
4. [**Lombok**](https://projectlombok.org/) 项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。
|
||||
|
||||
5. [**Druid**](https://github.com/alibaba/druid/) Java语言中被广泛应用的的数据库连接池。Druid能够提供强大的监控和扩展功能。
|
||||
|
||||
6. [**knife4j**](https://doc.xiaominfo.com/) 是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍!
|
||||
|
||||
7. [**tkMybaits**](https://github.com/abel533/Mapper) 通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。极其方便的使用MyBatis单表的增删改查。支持单表操作,不支持通用的多表联合查询。通用 Mapper 支持 Mybatis-3.2.4 及以上版本。
|
||||
|
||||
## 它是如何工作的?
|
||||
|
||||
通用框架,通过前端及后台对单表操作代码的封装,省去了单表操作代码的开发工作量,仅通过配置,即可生成相应的功能。
|
||||
使开发者更加专注于需求、页面展示及关联查询等复杂需求的开发。
|
||||
|
||||
|
||||
首先,使用前需要将业务表的信息录入到配置表当中。在配置表中,我们设计了table表,field表,及select表。
|
||||
将表信息录入到配置表中以后,通过单表通用接口中的getTableInfo接口进行表信息的获取,即可获取当前业务表的全部信息。
|
||||
|
||||
**table表:**
|
||||
|
||||

|
||||
|
||||
**field表:**
|
||||

|
||||
|
||||
**select表:**
|
||||

|
||||
|
||||
|
||||
后台中如何做到单表操作通过一套接口进行封装?最初的项目中,我们通过将表别名设置成一个变量,由前端传入。
|
||||
同时摒弃了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接口获取当前页面需要渲染的表的表信息。
|
||||
然后前端组件通过读取表信息后,自动生成表格,搜索表单,新增及编辑表单,按钮等页面内容。
|
||||
最后通过读取单表通用接口数据,将数据加载入表格当中完成整个页面渲染。
|
||||
|
||||
**前端通用页面示例:**
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
125
docs/01.指南/02.快速开始.md
Normal 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
|
||||
```
|
||||
|
||||
|
||||
75
docs/01.指南/03.打包部署.md
Normal 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文件夹
|
||||
|
||||

|
||||
|
||||
4. 打开dist目录,将除了 `index.html` 的文件全部考入后端项目 `src>main>resources>static` 目录下
|
||||
,将 `index.html` 文件考入到 `src>main>resources>templates` 目录下
|
||||
|
||||

|
||||
|
||||
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文件夹
|
||||
|
||||

|
||||
|
||||
4. 将生成的dist文件夹里面内容上传至Nginx或Apache静态服务器
|
||||
|
||||
5. 进入后台代码目录执行maven打包命令
|
||||
|
||||
``` bash
|
||||
mvn clean package
|
||||
```
|
||||
|
||||
6. 将生成的war包上传到tomcat等服务器进行部署。
|
||||
37
docs/02.前端/01.说明/01.简介.md
Normal 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
|
||||
|
||||
88
docs/02.前端/01.说明/02.功能.md
Normal 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
|
||||
```
|
||||
63
docs/02.前端/01.说明/03.如何设置以及启动项目.md
Normal 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/).
|
||||
58
docs/02.前端/01.说明/04.目录结构.md
Normal 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 配置
|
||||
```
|
||||
70
docs/02.前端/02.配置/01.BaseURL配置.md
Normal 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'
|
||||
```
|
||||
126
docs/02.前端/02.配置/02.vueconfig配置.md
Normal 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')
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
54
docs/02.前端/03.代码示例/01.Api示例.md
Normal 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
|
||||
```
|
||||
263
docs/02.前端/03.代码示例/02.新增页面示例.md
Normal 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
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
....
|
||||
]
|
||||
```
|
||||
|
||||
此时可以看到目录上多了刚刚我们添加的路由
|
||||
|
||||

|
||||
|
||||
5. 此时目录还未显示中文,由于框架使用i18n插件,此时我们需要进入语言目录下进行设置,进入 `src>lang>zh.ts` 中设置显示中文。
|
||||
|
||||
::: warning 注意
|
||||
此处设置需要添加到router对象下,且key值需要与路由中的title设置一致
|
||||
:::
|
||||
|
||||
``` ts {3}
|
||||
export default {
|
||||
route: {
|
||||
userExample:'用户示例',
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后我们就可以看到显示目录变成中文。
|
||||
|
||||

|
||||
|
||||
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>
|
||||
```
|
||||
|
||||
然后我们刷新页面,就可以看到刚刚引入的组件了。
|
||||
|
||||

|
||||
|
||||
7. 页面实例讲解完毕,最后,请按照使用者按照需要进行页面相关的开发或引入其他组件进行开发。
|
||||
|
||||
457
docs/02.前端/04.组件/01.Table组件.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值
|
||||
| :---| :---- | :---- |:---- |:---- |
|
||||
| check-box-visible | 是否显示复选框 | Boolean |-|false|
|
||||
| table-heads | 表头 | Array |-|-|
|
||||
| form-list | 需要显示的数据 | Array |-|-|
|
||||
| loading | 表格加载动画 | Boolean |-|false|
|
||||
| formatter | 格式化表格数据 | Function(row, column, cellValue, index) |-|-|
|
||||
| cellAlign | 表格对齐方式 | String | left/center/right | center |
|
||||
| slotVisible | 是否显示操作列 | Boolean |-|false|
|
||||
| width | 操作列的宽度 | Number |-|200|
|
||||
|
||||
## Table Events
|
||||
|
||||
|
||||
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| :---| :---- | :---- |
|
||||
| handle-selection-change | 当用户手动勾选数据行的 Checkbox 时触发的事件 | row |
|
||||
| handle-sort-change | 当表格的排序条件发生变化的时候会触发该事件 | column |
|
||||
|
||||
## Table Slot
|
||||
|
||||
|
||||
|
||||
| name | 说明 |
|
||||
| :---| :---- |
|
||||
| - | 用于放置在操作列上的按钮等元素,需要从scope.item中获取当前行的值 |
|
||||
|
||||
## Table Header style
|
||||
|
||||
|
||||
|
||||
| name | 说明 |
|
||||
| :---| :---- |
|
||||
| header-cell-style | 用于设置表头样式 |
|
||||
672
docs/02.前端/04.组件/02.Form组件.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值
|
||||
| :---| :---- | :---- |:---- |:---- |
|
||||
| form-title | 表单标题 | String |-|-|
|
||||
| form-visible | 展示/关闭表单 | Boolean |-|-|
|
||||
| form-sub-botton | 表单提交按钮显示文字 | String |-|-|
|
||||
| form-key | 表单key值 | Array |-|-|
|
||||
| form-data | 表单数据(可为空) | Object |-|-|
|
||||
| form-rules | 表单校验规则 | Object | - | - |
|
||||
| form-type | 表单提交类型 1为新增,2为编辑 | Number |-|-|
|
||||
| disable-field | 表单不可编辑的部分 | Array |-|-|
|
||||
|
||||
## Form Events
|
||||
|
||||
|
||||
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| :---| :---- | :---- |
|
||||
| form-submit | 当用户手动点击提交按钮时触发的事件 | - |
|
||||
| cancel-form | 当用户手动点击取消按钮时触发的事件 | - |
|
||||
| search-submit | 表单提交触发的事件 | - |
|
||||
| reset-search-form | 重置搜索表单触发的事件 | - |
|
||||
274
docs/02.前端/04.组件/03.流程图组件.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值
|
||||
| :---| :---- | :---- |:---- |:---- |
|
||||
| updateBpmnFlag | 是否为编辑流程图 | Boolean |-|false|
|
||||
| updateDate | 数据(编辑时传入String) | String/Object |-|-|
|
||||
|
||||
|
||||
## Bpmn Components Events
|
||||
|
||||
|
||||
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| :---| :---- | :---- |
|
||||
| openBpmn | 当用户手动点击文件icon时触发的事件,打开bpmn格式文件 | file |
|
||||
| downloadBpmn | 当用户手动点击下载icon时触发的事件,下载bpmn格式文件 | - |
|
||||
| downloadSvg | 当用户手动点击图片icon时触发的事件,下载svg格式文件 | - |
|
||||
| handleUpdateData | 当用户手动点击上传icon时触发的事件,上传已有流程图 | - |
|
||||
|
||||
|
||||
185
docs/02.前端/04.组件/04.流程图组件之编辑.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值
|
||||
| :---| :---- | :---- |:---- |:---- |
|
||||
| updateBpmnFlag | 是否为编辑流程图 | Boolean |-|false|
|
||||
| updateDate | 数据(编辑时传入String) | String/Object |-|-|
|
||||
|
||||
|
||||
## Bpmn Components Events
|
||||
|
||||
|
||||
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| :---| :---- | :---- |
|
||||
| openBpmn | 当用户手动点击文件icon时触发的事件,打开bpmn格式文件 | file |
|
||||
| downloadBpmn | 当用户手动点击下载icon时触发的事件,下载bpmn格式文件 | - |
|
||||
| downloadSvg | 当用户手动点击图片icon时触发的事件,下载svg格式文件 | - |
|
||||
| handleUpdateData | 当用户手动点击上传icon时触发的事件,上传已有流程图 | - |
|
||||
49
docs/index.md
Normal 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
@@ -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
@@ -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
@@ -0,0 +1,7 @@
|
||||
# vuepress-theme-vdoing
|
||||
|
||||
vuepress-theme-vdoing for vuepress
|
||||
|
||||
一个基于VuePress的 知识管理兼博客 主题。
|
||||
|
||||
[More](https://github.com/xugaoyi/vuepress-theme-vdoing#readme).
|
||||
165
theme-vdoing/components/AlgoliaSearchBox.vue
Normal 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>
|
||||
153
theme-vdoing/components/ArchivesPage.vue
Normal 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>
|
||||
206
theme-vdoing/components/ArticleInfo.vue
Normal 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>
|
||||
83
theme-vdoing/components/BloggerBar.vue
Normal 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>
|
||||
53
theme-vdoing/components/BodyBgImg.vue
Normal 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>
|
||||
262
theme-vdoing/components/Buttons.vue
Normal 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>
|
||||
204
theme-vdoing/components/Catalogue.vue
Normal 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>
|
||||
113
theme-vdoing/components/CategoriesBar.vue
Normal 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>
|
||||
131
theme-vdoing/components/CategoriesPage.vue
Normal 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>
|
||||
245
theme-vdoing/components/DropdownLink.vue
Normal 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>
|
||||
32
theme-vdoing/components/DropdownTransition.vue
Normal 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>
|
||||
74
theme-vdoing/components/Footer.vue
Normal 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>
|
||||
540
theme-vdoing/components/Home.vue
Normal 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>
|
||||
59
theme-vdoing/components/MainLayout.vue
Normal 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>
|
||||
54
theme-vdoing/components/NavLink.vue
Normal 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>
|
||||
154
theme-vdoing/components/NavLinks.vue
Normal 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>
|
||||
141
theme-vdoing/components/Navbar.vue
Normal 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>
|
||||
174
theme-vdoing/components/Page.vue
Normal 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>
|
||||
168
theme-vdoing/components/PageEdit.vue
Normal 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>
|
||||
237
theme-vdoing/components/PageNav.vue
Normal 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>
|
||||
233
theme-vdoing/components/Pagination.vue
Normal 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>
|
||||
229
theme-vdoing/components/PostList.vue
Normal 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>
|
||||
95
theme-vdoing/components/RightMenu.vue
Normal 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>
|
||||
121
theme-vdoing/components/Sidebar.vue
Normal 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>
|
||||
64
theme-vdoing/components/SidebarButton.vue
Normal 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>
|
||||
128
theme-vdoing/components/SidebarGroup.vue
Normal 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>
|
||||
124
theme-vdoing/components/SidebarLink.vue
Normal 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>
|
||||
93
theme-vdoing/components/SidebarLinks.vue
Normal 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>
|
||||