feat: 初始化项目结构并添加基础配置
添加前后端基础项目结构,包括.gitignore、package.json等配置文件 实现前端基础功能模块,包括路由、状态管理、API请求封装等 添加前端UI组件库和样式体系 配置开发环境Mock系统和构建工具链
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<button>Bubble</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
transition: color 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 100%;
|
||||
z-index: -1;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
transform: translate3d(50%, -50%, 0) scale3d(0, 0, 0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.45s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: translate3d(50%, -50%, 0) scale3d(15, 15, 15);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<button>Bubble</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
transition: color 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
z-index: -1;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
transform: translate3d(-50%, -50%, 0) scale3d(0, 0, 0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.45s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: translate3d(-50%, -50%, 0) scale3d(15, 15, 15);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<button>Bubble</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
transition: color 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
bottom: 100%;
|
||||
z-index: -1;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
transform: translate3d(50%, 50%, 0) scale3d(0, 0, 0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.45s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: translate3d(50%, 50%, 0) scale3d(15, 15, 15);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<button>Bubble</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
transition: color 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 100%;
|
||||
z-index: -1;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
transform: translate3d(-50%, 50%, 0) scale3d(0, 0, 0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.45s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: translate3d(-50%, 50%, 0) scale3d(15, 15, 15);
|
||||
}
|
||||
</style>
|
||||
43
front-end/src/views/other/cssfx/components/button-bubble.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<button>Bubble</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
transition: color 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: -1;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
transform: translate3d(-50%, -50%, 0) scale3d(0, 0, 0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.45s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: translate3d(-50%, -50%, 0) scale3d(15, 15, 15);
|
||||
}
|
||||
</style>
|
||||
40
front-end/src/views/other/cssfx/components/button-jelly.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<button>Jelly</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
animation: jelly 0.5s;
|
||||
}
|
||||
|
||||
@keyframes jelly {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: scale(0.9, 1.1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.1, 0.9);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: scale(0.95, 1.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
38
front-end/src/views/other/cssfx/components/button-pulse.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<button>Pulse</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
border: 4px solid hsl(236deg 32% 26%);
|
||||
transform: scale(1);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
opacity: 0;
|
||||
transform: scale(1.75);
|
||||
transform-origin: center;
|
||||
transition: all 0.75s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
35
front-end/src/views/other/cssfx/components/button-shine.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<button>Shine</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button::after {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
bottom: -50%;
|
||||
z-index: -1;
|
||||
width: 1.25em;
|
||||
content: '';
|
||||
background-color: hsl(0deg 0% 100% / 20%);
|
||||
transform: translate3d(-525%, 0, 0) rotate(35deg);
|
||||
}
|
||||
|
||||
button:hover::after {
|
||||
transform: translate3d(200%, 0, 0) rotate(35deg);
|
||||
transition: transform 0.45s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<button>Slide</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(0);
|
||||
transform-origin: bottom center;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: scaleY(1);
|
||||
transform-origin: center top;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<button>Slide</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left center;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: scaleX(1);
|
||||
transform-origin: right center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<button>Slide</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: right center;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<button>Slide</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0.5em 1em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
outline: none;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(0);
|
||||
transform-origin: top center;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover::before {
|
||||
transform: scaleY(1);
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
</style>
|
||||
24
front-end/src/views/other/cssfx/components/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const requireEffect = require.context('./', false, /\.vue$/)
|
||||
const effectList = requireEffect.keys()
|
||||
const effects = {}
|
||||
const components = {}
|
||||
for (const filename of effectList) {
|
||||
const name = filename.replace('./', '').replace('.vue', '')
|
||||
const type = name.slice(0, Math.max(0, name.indexOf('-')))
|
||||
const raw = require(`!!raw-loader!./${name}`).default
|
||||
const component = requireEffect(filename).default
|
||||
const html = /<template>(.*?)<\/template>/g
|
||||
.exec(JSON.stringify(raw))[0]
|
||||
.replace(/<\/?template>/g, '')
|
||||
.replace(/^\\n/, '')
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\\"/g, '"')
|
||||
const css = /<style scoped>(.*?)<\/style>/g
|
||||
.exec(JSON.stringify(raw))[0]
|
||||
.replace(/<\/?style(?: scoped)?>/g, '')
|
||||
.replace(/^\\n/, '')
|
||||
.replace(/\\n/g, '\n')
|
||||
effects[name] = { name, type, html, css }
|
||||
components[name] = component
|
||||
}
|
||||
export { effects, components }
|
||||
96
front-end/src/views/other/cssfx/components/input-outline.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div>
|
||||
<input placeholder="Input Outline" type="text" />
|
||||
<span class="bottom" />
|
||||
<span class="right" />
|
||||
<span class="top" />
|
||||
<span class="left" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 6.5em;
|
||||
padding: 0.35em 0.45em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: 1px solid transparent;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: hsl(0deg 0% 100% / 60%);
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
background-color: #1890ff;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.bottom,
|
||||
.top {
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
|
||||
.bottom {
|
||||
bottom: 0;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
input:focus ~ .bottom {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.right {
|
||||
right: 0;
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
input:focus ~ .right {
|
||||
transform: scaleY(1);
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.top {
|
||||
top: 0;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
input:focus ~ .top {
|
||||
transform: scaleX(1);
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.left {
|
||||
left: 0;
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
input:focus ~ .left {
|
||||
transform: scaleY(1);
|
||||
transform-origin: top left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<input placeholder="Input Outline" type="text" />
|
||||
<span class="bottom" />
|
||||
<span class="right" />
|
||||
<span class="top" />
|
||||
<span class="left" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 6.5em;
|
||||
padding: 0.35em 0.45em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: 1px solid transparent;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: hsl(0deg 0% 100% / 60%);
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
background-color: #1890ff;
|
||||
transform-origin: center;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.bottom,
|
||||
.top {
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
|
||||
.top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
input:focus ~ .top,
|
||||
input:focus ~ .bottom {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
input:focus ~ .left,
|
||||
input:focus ~ .right {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
</style>
|
||||
99
front-end/src/views/other/cssfx/components/input-trace.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div>
|
||||
<input placeholder="Input Trace" type="text" />
|
||||
<span class="bottom" />
|
||||
<span class="right" />
|
||||
<span class="top" />
|
||||
<span class="left" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 6.5em;
|
||||
padding: 0.35em 0.45em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
background-color: hsl(236deg 32% 26%);
|
||||
border: 1px solid transparent;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: hsl(0deg 0% 100% / 60%);
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
background-color: #1890ff;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.bottom,
|
||||
.top {
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
|
||||
.bottom {
|
||||
bottom: 0;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
input:focus ~ .bottom {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.right {
|
||||
right: 0;
|
||||
transform-origin: top right;
|
||||
transition-delay: 0.05s;
|
||||
}
|
||||
|
||||
input:focus ~ .right {
|
||||
transform: scaleY(1);
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.top {
|
||||
top: 0;
|
||||
transform-origin: top left;
|
||||
transition-delay: 0.15s;
|
||||
}
|
||||
|
||||
input:focus ~ .top {
|
||||
transform: scaleX(1);
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.left {
|
||||
left: 0;
|
||||
transform-origin: bottom left;
|
||||
transition-delay: 0.25s;
|
||||
}
|
||||
|
||||
input:focus ~ .left {
|
||||
transform: scaleY(1);
|
||||
transform-origin: top left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<input placeholder="Input Underline" type="text" />
|
||||
<span />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 6.5em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-bottom-color: hsl(185deg 100% 62% / 20%);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: hsl(0deg 0% 100% / 60%);
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: bottom right;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
input:focus ~ span {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<input placeholder="Input Underline" type="text" />
|
||||
<span />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 6.5em;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-bottom-color: hsl(341deg 97% 59% / 20%);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: hsl(0deg 0% 100% / 60%);
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #1890ff;
|
||||
opacity: 0;
|
||||
transform: translate(-50%, 0) scaleX(0);
|
||||
transform-origin: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
input:focus ~ span {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0) scaleX(1);
|
||||
}
|
||||
</style>
|
||||
36
front-end/src/views/other/cssfx/components/text-bars.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<span>Bars</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before,
|
||||
span::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: right center;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
span::after {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
span:hover::before,
|
||||
span:hover::after {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left center;
|
||||
}
|
||||
</style>
|
||||
41
front-end/src/views/other/cssfx/components/text-bars2.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<span>Bars</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before,
|
||||
span::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
top: 0;
|
||||
transform-origin: right center;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
span::after {
|
||||
bottom: 0;
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
span:hover::after {
|
||||
transform: scaleX(1);
|
||||
transform-origin: right center;
|
||||
}
|
||||
</style>
|
||||
35
front-end/src/views/other/cssfx/components/text-barsr3.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<span>Bars</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before,
|
||||
span::after {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: translateX(-50%) scaleX(0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
span::after {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
span:hover::before,
|
||||
span:hover::after {
|
||||
transform: translateX(-50%) scaleX(1);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<span>Highlight</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
inset: 0 -0.25em;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: right center;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<span>Highlight</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
inset: 0 -0.25em;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(0.1);
|
||||
transform-origin: bottom center;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
</style>
|
||||
27
front-end/src/views/other/cssfx/components/text-overline.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<span>Overline</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: bottom right;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<span>Overline</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
opacity: 0;
|
||||
transform: translate(-50%, 0) scaleX(0);
|
||||
transform-origin: center;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0) scaleX(1);
|
||||
}
|
||||
</style>
|
||||
36
front-end/src/views/other/cssfx/components/text-pillars.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<span>Pillars</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before,
|
||||
span::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(0);
|
||||
transform-origin: center top;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
span::after {
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
span:hover::before,
|
||||
span:hover::after {
|
||||
transform: scaleY(1);
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
</style>
|
||||
41
front-end/src/views/other/cssfx/components/text-pillars2.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<span>Pillars</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before,
|
||||
span::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(0);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
left: -8px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleY(1);
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
span::after {
|
||||
right: -8px;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
span:hover::after {
|
||||
transform: scaleY(1);
|
||||
transform-origin: center top;
|
||||
}
|
||||
</style>
|
||||
35
front-end/src/views/other/cssfx/components/text-pillars3.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<span>Pillars</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before,
|
||||
span::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleY(0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
span::after {
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
span:hover::before,
|
||||
span:hover::after {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<span>Strikethrough</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 3px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0) translateY(-50%);
|
||||
transform-origin: right center;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
span:hover {
|
||||
color: hsl(0deg 0% 100% / 80%);
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleX(1) translateY(-50%);
|
||||
transform-origin: left center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<span>Underline</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(0);
|
||||
transform-origin: bottom right;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<span>Underline</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: scaleX(0);
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<span>Underline</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span::before {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background-color: #1890ff;
|
||||
transform: translate(-50%, 0) scaleX(0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
span:hover::before {
|
||||
transform: translate(-50%, 0) scaleX(1);
|
||||
}
|
||||
</style>
|
||||
50
front-end/src/views/other/cssfx/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="cssfx-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="(item, index) in effects"
|
||||
:key="item.name"
|
||||
:lg="4"
|
||||
:md="4"
|
||||
:sm="12"
|
||||
:xl="3"
|
||||
:xs="12"
|
||||
>
|
||||
<div class="cssfx-container-card">
|
||||
<component :is="index" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { components, effects } from './components'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Cssfx',
|
||||
components: { ...components },
|
||||
setup() {
|
||||
return {
|
||||
effects,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cssfx-container {
|
||||
&-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100px;
|
||||
margin-bottom: $base-margin;
|
||||
color: var(--el-color-white);
|
||||
background: #090821;
|
||||
border-radius: $base-border-radius;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
122
front-end/src/views/other/drag/cardDrag.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="card-drag-container">
|
||||
<vab-query-form>
|
||||
<vab-query-form-left-panel>
|
||||
<el-button type="danger" @click="sort">重置顺序</el-button>
|
||||
</vab-query-form-left-panel>
|
||||
</vab-query-form>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<vab-draggable
|
||||
v-model="iconList"
|
||||
item-key="icon"
|
||||
v-bind="dragOptions"
|
||||
>
|
||||
<template #item="{ element: item }">
|
||||
<el-col :lg="3" :md="3" :sm="6" :xl="3" :xs="12">
|
||||
<vab-card class="icon-panel">
|
||||
<vab-icon
|
||||
:icon="item.icon"
|
||||
:style="{ color: item.color }"
|
||||
/>
|
||||
<p>按住拖拽</p>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</vab-draggable>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import _ from 'lodash'
|
||||
import VabDraggable from 'vuedraggable'
|
||||
import { getIconList } from '@/api/defaultIcon'
|
||||
|
||||
defineOptions({
|
||||
name: 'CardDrag',
|
||||
})
|
||||
|
||||
const iconList = ref<any>([])
|
||||
const randomHexColor = () => {
|
||||
return _.shuffle([
|
||||
'#1890FF',
|
||||
'#36CBCB',
|
||||
'#4ECB73',
|
||||
'#FBD437',
|
||||
'#F2637B',
|
||||
'#975FE5',
|
||||
])
|
||||
}
|
||||
const fetchData = async () => {
|
||||
const { data } = await getIconList({
|
||||
pageNo: 1,
|
||||
pageSize: 89,
|
||||
})
|
||||
iconList.value = data.list
|
||||
.filter((icon: any) => icon.includes('-line'))
|
||||
.map((icon: any, index: any) => {
|
||||
return { icon, color: randomHexColor(), order: index + 1 }
|
||||
})
|
||||
}
|
||||
const sort = () => {
|
||||
iconList.value = iconList.value.sort(
|
||||
(a: any, b: any) => a.order - b.order
|
||||
)
|
||||
}
|
||||
const dragOptions = computed(() => {
|
||||
return {
|
||||
animation: 600,
|
||||
group: 'description',
|
||||
disabled: false,
|
||||
ghostClass: 'ghost',
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-drag-container {
|
||||
:deep() {
|
||||
.el-row {
|
||||
display: block;
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-panel {
|
||||
height: 120px;
|
||||
text-align: center;
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
i {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: auto;
|
||||
font-size: 40px;
|
||||
transition: all ease-in-out 0.3s;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
37
front-end/src/views/other/drag/components/DialogDemo.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<vab-dialog
|
||||
v-model="dialogVisible"
|
||||
show-fullscreen
|
||||
title="温馨提示"
|
||||
width="600px"
|
||||
>
|
||||
昨夜西风凋碧树,独上高楼望尽天涯路
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">
|
||||
确 定
|
||||
</el-button>
|
||||
</template>
|
||||
</vab-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabDialog from '@/plugins/VabDialog'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DialogDemo',
|
||||
components: {
|
||||
VabDialog,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
})
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
29
front-end/src/views/other/drag/dialogDrag.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="dialog-drag-container">
|
||||
<el-button type="primary" @click="handleClick">可拖拽弹窗</el-button>
|
||||
<dialog-demo ref="dialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'DialogDrag',
|
||||
components: {
|
||||
DialogDemo: defineAsyncComponent(
|
||||
() => import('./components/DialogDemo')
|
||||
),
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
dialogRef: '',
|
||||
})
|
||||
const handleClick = () => {
|
||||
state['dialogRef'].dialogVisible = true
|
||||
}
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleClick,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
40
front-end/src/views/other/dynamicAnchor/index.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="dynamic-anchor-container">
|
||||
<vab-anchor :floor-list="floorList">
|
||||
<!--TODO 待优化 这里不会动态循环,想做成饿了么那种 el-menu >>> el-menu-item 这种嵌套并且可循环的組件形式 -->
|
||||
<template #floor0>用户管理</template>
|
||||
<template #floor1>配置管理</template>
|
||||
<template #floor2>角色管理</template>
|
||||
<template #floor3>定时任务补偿</template>
|
||||
</vab-anchor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabAnchor from '@/plugins/VabAnchor'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicAnchor',
|
||||
components: { VabAnchor },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
floorList: [
|
||||
{ title: '用户管理' },
|
||||
{ title: '配置管理' },
|
||||
{ title: '角色管理' },
|
||||
{ title: '定时任务补偿' },
|
||||
],
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dynamic-anchor-container {
|
||||
// TODO
|
||||
}
|
||||
</style>
|
||||
153
front-end/src/views/other/dynamicMeta/index.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="dynamic-meta-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>动态标题</span>
|
||||
</template>
|
||||
<el-button
|
||||
@click="
|
||||
handleMeta('DynamicMeta', { title: 'vab-demo' })
|
||||
"
|
||||
>
|
||||
标题变更为 vab-demo
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="
|
||||
handleMeta('DynamicMeta', { title: '动态Meta' })
|
||||
"
|
||||
>
|
||||
还原为默认标题
|
||||
</el-button>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>动态徽章</span>
|
||||
</template>
|
||||
<el-button @click="handleBadge('DynamicMeta')">
|
||||
徽章+ 1
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="resetBadge('DynamicMeta', { badge: '0' })"
|
||||
>
|
||||
徽章清零
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="resetBadge('DynamicMeta', { badge: false })"
|
||||
>
|
||||
移除徽章
|
||||
</el-button>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>动态图标</span>
|
||||
</template>
|
||||
<el-popover
|
||||
popper-class="icon-selector-popper"
|
||||
trigger="hover"
|
||||
:width="292"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button>
|
||||
<vab-icon :icon="icon" />
|
||||
修改图标
|
||||
<vab-icon icon="arrow-down-s-line" />
|
||||
</el-button>
|
||||
</template>
|
||||
<vab-icon-selector @handle-icon="handleIcon" />
|
||||
</el-popover>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>动态高亮菜单</span>
|
||||
</template>
|
||||
<el-button @click="handleActiveMenu('/other/notice')">
|
||||
修改高亮菜单至通知组件
|
||||
</el-button>
|
||||
<el-button @click="handleActiveMenu('/other/dynamicMeta')">
|
||||
还原默认高亮菜单
|
||||
</el-button>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
import { useRoutesStore } from '@/store/modules/routes'
|
||||
import getPageTitle, { setCustomTitle } from '@/utils/pageTitle'
|
||||
import VabIconSelector from '@/plugins/VabIconSelector'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicMeta',
|
||||
components: { VabIconSelector },
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
|
||||
const tabsStore = useTabsStore()
|
||||
const routesStore = useRoutesStore()
|
||||
|
||||
const { changeTabsMeta } = tabsStore
|
||||
const { changeActiveMenu, changeMenuMeta } = routesStore
|
||||
|
||||
const state = reactive({
|
||||
badge: 0,
|
||||
icon: route.meta.icon,
|
||||
defaultTitle: route.meta.title,
|
||||
})
|
||||
const handleBadge = (name) => {
|
||||
state.badge = state.badge + 1
|
||||
changeMenuMeta({
|
||||
name,
|
||||
meta: { badge: state.badge },
|
||||
})
|
||||
}
|
||||
const resetBadge = (name, meta) => {
|
||||
state.badge = 0
|
||||
changeMenuMeta({ name, meta })
|
||||
}
|
||||
const handleMeta = (name, meta) => {
|
||||
if (meta.title) {
|
||||
// 设置自定义标题并更新浏览器标题
|
||||
setCustomTitle(meta.title)
|
||||
document.title = getPageTitle(meta.title)
|
||||
}
|
||||
changeMenuMeta({ name, meta })
|
||||
changeTabsMeta({ name, meta })
|
||||
}
|
||||
const handleIcon = (item) => {
|
||||
state.icon = item
|
||||
changeMenuMeta({ name: 'DynamicMeta', meta: { icon: item } })
|
||||
changeTabsMeta({ name: 'DynamicMeta', meta: { icon: item } })
|
||||
}
|
||||
const handleActiveMenu = (activeMenu) => {
|
||||
changeActiveMenu(activeMenu)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleBadge,
|
||||
resetBadge,
|
||||
handleMeta,
|
||||
handleIcon,
|
||||
handleActiveMenu,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$base: '.dynamic-meta';
|
||||
#{$base}-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
60
front-end/src/views/other/dynamicSegment/test1.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="test1-container">
|
||||
<el-alert :closable="false" show-icon title="params" type="success" />
|
||||
<vab-json-viewer copyable :expand-depth="5" sort :value="route" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabJsonViewer from 'vue-json-viewer'
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Test1',
|
||||
components: { VabJsonViewer },
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
|
||||
const tabsStore = useTabsStore()
|
||||
const { changeTabsMeta } = tabsStore
|
||||
|
||||
const state = reactive({
|
||||
route: {},
|
||||
})
|
||||
const handleParams = () => {
|
||||
nextTick(() => {
|
||||
changeTabsMeta({
|
||||
title: 'Query',
|
||||
meta: {
|
||||
title: `Query Id=${route.query.id}`,
|
||||
},
|
||||
})
|
||||
|
||||
const _route = route.matched[0].children.find(
|
||||
(item) => item.name === 'Test1'
|
||||
)
|
||||
const id = route.path.substring(
|
||||
route.path.lastIndexOf('/') + 1,
|
||||
route.path.length
|
||||
)
|
||||
|
||||
state.route = {
|
||||
path: _route.path,
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
name: _route.name,
|
||||
meta: _route.meta,
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
handleParams()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
49
front-end/src/views/other/dynamicSegment/test2.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="test2-container">
|
||||
<el-alert :closable="false" show-icon title="query" type="success" />
|
||||
<vab-json-viewer copyable :expand-depth="5" sort :value="route" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabJsonViewer from 'vue-json-viewer'
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Test2',
|
||||
components: { VabJsonViewer },
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
|
||||
const tabsStore = useTabsStore()
|
||||
const { changeTabsMeta } = tabsStore
|
||||
|
||||
const state = reactive({
|
||||
route: {},
|
||||
})
|
||||
const handleQuery = () => {
|
||||
nextTick(() => {
|
||||
changeTabsMeta({
|
||||
title: 'Query',
|
||||
meta: {
|
||||
title: `Query Id=${route.query.id}`,
|
||||
},
|
||||
})
|
||||
state.route = {
|
||||
path: route.path,
|
||||
query: route.query,
|
||||
name: route.name,
|
||||
meta: route.meta,
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
handleQuery()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
57
front-end/src/views/other/echarts/components/VabChartBar.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartBar',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: {
|
||||
data: [120, 200, 150, 80, 70, 110, 130],
|
||||
type: 'bar',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartCandlestick',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
xAxis: {
|
||||
data: [
|
||||
'2017-10-24',
|
||||
'2017-10-25',
|
||||
'2017-10-26',
|
||||
'2017-10-27',
|
||||
],
|
||||
},
|
||||
yAxis: {},
|
||||
series: [
|
||||
{
|
||||
type: 'k',
|
||||
data: [
|
||||
[20, 34, 10, 38],
|
||||
[40, 35, 30, 50],
|
||||
[31, 38, 33, 44],
|
||||
[38, 15, 5, 42],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartFunnel',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c}%',
|
||||
},
|
||||
series: {
|
||||
name: '漏斗图',
|
||||
type: 'funnel',
|
||||
left: '20%',
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
width: '60%',
|
||||
min: 0,
|
||||
max: 100,
|
||||
minSize: '0%',
|
||||
maxSize: '100%',
|
||||
sort: 'descending',
|
||||
gap: 2,
|
||||
labelLine: {
|
||||
length: 10,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
type: 'solid',
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: 60, name: '访问' },
|
||||
{ value: 40, name: '咨询' },
|
||||
{ value: 20, name: '订单' },
|
||||
{ value: 80, name: '点击' },
|
||||
{ value: 100, name: '展现' },
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartGauge',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
},
|
||||
tooltip: {
|
||||
formatter: '{a} <br/>{b} : {c}%',
|
||||
},
|
||||
series: {
|
||||
name: 'Pressure',
|
||||
type: 'gauge',
|
||||
radius: '100%',
|
||||
progress: {
|
||||
show: true,
|
||||
},
|
||||
detail: {
|
||||
formatter: '{value}',
|
||||
valueAnimation: true,
|
||||
fontSize: 14,
|
||||
offsetCenter: [0, '70%'],
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: 50,
|
||||
name: 'SCORE',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartLine',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: {
|
||||
data: [150, 230, 224, 218, 135, 147, 260],
|
||||
type: 'line',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
82
front-end/src/views/other/echarts/components/VabChartPie.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartPie',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
radius: ['40%', '80%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '14',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [
|
||||
{ value: 1048, name: '搜索引擎' },
|
||||
{ value: 735, name: '直接访问' },
|
||||
{ value: 580, name: '邮件营销' },
|
||||
{ value: 484, name: '联盟广告' },
|
||||
{ value: 300, name: '视频广告' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartRadar',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
radar: {
|
||||
indicator: [
|
||||
{ name: '销售' },
|
||||
{ name: '管理' },
|
||||
{ name: '信息技术' },
|
||||
{ name: '客服' },
|
||||
{ name: '研发' },
|
||||
],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '预算 vs 开销',
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: [4200, 3000, 20000, 35000, 50000],
|
||||
name: '预算分配',
|
||||
},
|
||||
{
|
||||
value: [5000, 14000, 28000, 26000, 42000],
|
||||
name: '实际开销',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartScatter',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
xAxis: {},
|
||||
yAxis: {},
|
||||
series: {
|
||||
symbolSize: 10,
|
||||
data: [
|
||||
[10, 8.04],
|
||||
[8.07, 6.95],
|
||||
[13, 7.58],
|
||||
[9.05, 8.81],
|
||||
[11, 8.33],
|
||||
[14, 7.66],
|
||||
[13.4, 6.81],
|
||||
[10, 6.33],
|
||||
[14, 8.96],
|
||||
[12.5, 6.82],
|
||||
[9.15, 7.2],
|
||||
[11.5, 7.2],
|
||||
[3.03, 4.23],
|
||||
[12.2, 7.83],
|
||||
[2.02, 4.47],
|
||||
[1.05, 3.33],
|
||||
[4.05, 4.96],
|
||||
[6.03, 7.24],
|
||||
[12, 6.26],
|
||||
[12, 8.84],
|
||||
[7.08, 5.82],
|
||||
[5.02, 5.68],
|
||||
],
|
||||
type: 'scatter',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartSunburst',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
},
|
||||
series: {
|
||||
type: 'sunburst',
|
||||
data: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 5,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
children: [
|
||||
{
|
||||
value: 6,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 9,
|
||||
children: [
|
||||
{
|
||||
value: 4,
|
||||
children: [
|
||||
{
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 3,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 7,
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 6,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
radius: ['10%', '100%'],
|
||||
label: {
|
||||
rotate: 'radial',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartThemeRiver',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 40,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
color: 'rgba(0,0,0,0.2)',
|
||||
width: 1,
|
||||
type: 'solid',
|
||||
},
|
||||
},
|
||||
},
|
||||
singleAxis: {
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
axisTick: {},
|
||||
axisLabel: {},
|
||||
type: 'time',
|
||||
axisPointer: {
|
||||
animation: true,
|
||||
label: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
series: {
|
||||
type: 'themeRiver',
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 20,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.8)',
|
||||
},
|
||||
},
|
||||
data: [
|
||||
['2015/11/08', 10, 'DQ'],
|
||||
['2015/11/09', 15, 'DQ'],
|
||||
['2015/11/10', 35, 'DQ'],
|
||||
['2015/11/11', 38, 'DQ'],
|
||||
['2015/11/12', 22, 'DQ'],
|
||||
['2015/11/13', 16, 'DQ'],
|
||||
['2015/11/14', 7, 'DQ'],
|
||||
['2015/11/15', 2, 'DQ'],
|
||||
['2015/11/16', 17, 'DQ'],
|
||||
['2015/11/17', 33, 'DQ'],
|
||||
['2015/11/18', 40, 'DQ'],
|
||||
['2015/11/19', 32, 'DQ'],
|
||||
['2015/11/20', 26, 'DQ'],
|
||||
['2015/11/21', 35, 'DQ'],
|
||||
['2015/11/22', 40, 'DQ'],
|
||||
['2015/11/23', 32, 'DQ'],
|
||||
['2015/11/24', 26, 'DQ'],
|
||||
['2015/11/25', 22, 'DQ'],
|
||||
['2015/11/26', 16, 'DQ'],
|
||||
['2015/11/27', 22, 'DQ'],
|
||||
['2015/11/28', 10, 'DQ'],
|
||||
['2015/11/08', 35, 'TY'],
|
||||
['2015/11/09', 36, 'TY'],
|
||||
['2015/11/10', 37, 'TY'],
|
||||
['2015/11/11', 22, 'TY'],
|
||||
['2015/11/12', 24, 'TY'],
|
||||
['2015/11/13', 26, 'TY'],
|
||||
['2015/11/14', 34, 'TY'],
|
||||
['2015/11/15', 21, 'TY'],
|
||||
['2015/11/16', 18, 'TY'],
|
||||
['2015/11/17', 45, 'TY'],
|
||||
['2015/11/18', 32, 'TY'],
|
||||
['2015/11/19', 35, 'TY'],
|
||||
['2015/11/20', 30, 'TY'],
|
||||
['2015/11/21', 28, 'TY'],
|
||||
['2015/11/22', 27, 'TY'],
|
||||
['2015/11/23', 26, 'TY'],
|
||||
['2015/11/24', 15, 'TY'],
|
||||
['2015/11/25', 30, 'TY'],
|
||||
['2015/11/26', 35, 'TY'],
|
||||
['2015/11/27', 42, 'TY'],
|
||||
['2015/11/28', 42, 'TY'],
|
||||
['2015/11/08', 21, 'SS'],
|
||||
['2015/11/09', 25, 'SS'],
|
||||
['2015/11/10', 27, 'SS'],
|
||||
['2015/11/11', 23, 'SS'],
|
||||
['2015/11/12', 24, 'SS'],
|
||||
['2015/11/13', 21, 'SS'],
|
||||
['2015/11/14', 35, 'SS'],
|
||||
['2015/11/15', 39, 'SS'],
|
||||
['2015/11/16', 40, 'SS'],
|
||||
['2015/11/17', 36, 'SS'],
|
||||
['2015/11/18', 33, 'SS'],
|
||||
['2015/11/19', 43, 'SS'],
|
||||
['2015/11/20', 40, 'SS'],
|
||||
['2015/11/21', 34, 'SS'],
|
||||
['2015/11/22', 28, 'SS'],
|
||||
['2015/11/23', 26, 'SS'],
|
||||
['2015/11/24', 37, 'SS'],
|
||||
['2015/11/25', 41, 'SS'],
|
||||
['2015/11/26', 46, 'SS'],
|
||||
['2015/11/27', 47, 'SS'],
|
||||
['2015/11/28', 41, 'SS'],
|
||||
['2015/11/08', 10, 'QG'],
|
||||
['2015/11/09', 15, 'QG'],
|
||||
['2015/11/10', 35, 'QG'],
|
||||
['2015/11/11', 38, 'QG'],
|
||||
['2015/11/12', 22, 'QG'],
|
||||
['2015/11/13', 16, 'QG'],
|
||||
['2015/11/14', 7, 'QG'],
|
||||
['2015/11/15', 2, 'QG'],
|
||||
['2015/11/16', 17, 'QG'],
|
||||
['2015/11/17', 33, 'QG'],
|
||||
['2015/11/18', 40, 'QG'],
|
||||
['2015/11/19', 32, 'QG'],
|
||||
['2015/11/20', 26, 'QG'],
|
||||
['2015/11/21', 35, 'QG'],
|
||||
['2015/11/22', 40, 'QG'],
|
||||
['2015/11/23', 32, 'QG'],
|
||||
['2015/11/24', 26, 'QG'],
|
||||
['2015/11/25', 22, 'QG'],
|
||||
['2015/11/26', 16, 'QG'],
|
||||
['2015/11/27', 22, 'QG'],
|
||||
['2015/11/28', 10, 'QG'],
|
||||
['2015/11/08', 10, 'SY'],
|
||||
['2015/11/09', 15, 'SY'],
|
||||
['2015/11/10', 35, 'SY'],
|
||||
['2015/11/11', 38, 'SY'],
|
||||
['2015/11/12', 22, 'SY'],
|
||||
['2015/11/13', 16, 'SY'],
|
||||
['2015/11/14', 7, 'SY'],
|
||||
['2015/11/15', 2, 'SY'],
|
||||
['2015/11/16', 17, 'SY'],
|
||||
['2015/11/17', 33, 'SY'],
|
||||
['2015/11/18', 40, 'SY'],
|
||||
['2015/11/19', 32, 'SY'],
|
||||
['2015/11/20', 26, 'SY'],
|
||||
['2015/11/21', 35, 'SY'],
|
||||
['2015/11/22', 4, 'SY'],
|
||||
['2015/11/23', 32, 'SY'],
|
||||
['2015/11/24', 26, 'SY'],
|
||||
['2015/11/25', 22, 'SY'],
|
||||
['2015/11/26', 16, 'SY'],
|
||||
['2015/11/27', 22, 'SY'],
|
||||
['2015/11/28', 10, 'SY'],
|
||||
['2015/11/08', 10, 'DD'],
|
||||
['2015/11/09', 15, 'DD'],
|
||||
['2015/11/10', 35, 'DD'],
|
||||
['2015/11/11', 38, 'DD'],
|
||||
['2015/11/12', 22, 'DD'],
|
||||
['2015/11/13', 16, 'DD'],
|
||||
['2015/11/14', 7, 'DD'],
|
||||
['2015/11/15', 2, 'DD'],
|
||||
['2015/11/16', 17, 'DD'],
|
||||
['2015/11/17', 33, 'DD'],
|
||||
['2015/11/18', 4, 'DD'],
|
||||
['2015/11/19', 32, 'DD'],
|
||||
['2015/11/20', 26, 'DD'],
|
||||
['2015/11/21', 35, 'DD'],
|
||||
['2015/11/22', 40, 'DD'],
|
||||
['2015/11/23', 32, 'DD'],
|
||||
['2015/11/24', 26, 'DD'],
|
||||
['2015/11/25', 22, 'DD'],
|
||||
['2015/11/26', 16, 'DD'],
|
||||
['2015/11/27', 22, 'DD'],
|
||||
['2015/11/28', 10, 'DD'],
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
<vab-chart
|
||||
:init-options="initOptions"
|
||||
:option="option"
|
||||
theme="vab-echarts-theme"
|
||||
/>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChart from '@/plugins/VabChart'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VabChartTreemap',
|
||||
components: {
|
||||
VabChart,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initOptions: {
|
||||
renderer: 'svg',
|
||||
},
|
||||
option: {
|
||||
grid: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 60,
|
||||
left: 40,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'treemap',
|
||||
data: [
|
||||
{
|
||||
name: 'nodeA', // First tree
|
||||
value: 10,
|
||||
children: [
|
||||
{
|
||||
name: 'nodeAa', // First leaf of first tree
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
name: 'nodeAb', // Second leaf of first tree
|
||||
value: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'nodeB', // Second tree
|
||||
value: 20,
|
||||
children: [
|
||||
{
|
||||
name: 'nodeBa', // Son of first tree
|
||||
value: 20,
|
||||
children: [
|
||||
{
|
||||
name: 'nodeBa1', // Granson of first tree
|
||||
value: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
70
front-end/src/views/other/echarts/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="echarts-container">
|
||||
<el-row :gutter="20">
|
||||
<vab-chart-line title="折线图" />
|
||||
<vab-chart-bar title="柱状图" />
|
||||
<vab-chart-pie title="饼状图" />
|
||||
<vab-chart-scatter title="散点图" />
|
||||
<vab-chart-candlestick title="K线图" />
|
||||
<vab-chart-radar title="雷达图" />
|
||||
<vab-chart-treemap title="矩形树图" />
|
||||
<vab-chart-sunburst title="旭日图" />
|
||||
<vab-chart-funnel title="漏斗图" />
|
||||
<vab-chart-gauge title="仪表图" />
|
||||
<vab-chart-theme-river title="河流流向图" />
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabChartLine from './components/VabChartLine'
|
||||
import VabChartBar from './components/VabChartBar'
|
||||
import VabChartPie from './components/VabChartPie'
|
||||
import VabChartScatter from './components/VabChartScatter'
|
||||
import VabChartCandlestick from './components/VabChartCandlestick'
|
||||
import VabChartRadar from './components/VabChartRadar'
|
||||
import VabChartTreemap from './components/VabChartTreemap'
|
||||
import VabChartSunburst from './components/VabChartSunburst'
|
||||
import VabChartFunnel from './components/VabChartFunnel'
|
||||
import VabChartGauge from './components/VabChartGauge'
|
||||
import VabChartThemeRiver from './components/VabChartThemeRiver'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Echarts',
|
||||
components: {
|
||||
VabChartLine,
|
||||
VabChartBar,
|
||||
VabChartPie,
|
||||
VabChartScatter,
|
||||
VabChartCandlestick,
|
||||
VabChartRadar,
|
||||
VabChartTreemap,
|
||||
VabChartSunburst,
|
||||
VabChartFunnel,
|
||||
VabChartGauge,
|
||||
VabChartThemeRiver,
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.echarts-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.vab-card {
|
||||
height: 300px;
|
||||
|
||||
.echarts {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
10
front-end/src/views/other/errorLog/components/ErrorTest.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<!-- js代码错误实时提醒测试 -->
|
||||
<div>{{ zxwk1998jiayou.zxwk1998jiayou }}</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'ErrorTest',
|
||||
})
|
||||
</script>
|
||||
39
front-end/src/views/other/errorLog/index.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="errorLog-container">
|
||||
<el-button type="primary" @click="handleError">
|
||||
点击模拟一个zxwk1998jiayou的错误
|
||||
</el-button>
|
||||
<error-test v-if="show" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ErrorTest from './components/ErrorTest'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ErrorLog',
|
||||
components: { ErrorTest },
|
||||
setup() {
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const state = reactive({
|
||||
show: false,
|
||||
})
|
||||
|
||||
const handleError = () => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
$baseMessage(
|
||||
'\u4E3A\u4E86\u9632\u6B62\u5F15\u8D77\u6B67\u4E49\uFF0C\u6F14\u793A\u73AF\u5883\u4E0D\u5141\u8BB8\u6A21\u62DF\u9519\u8BEF\uFF0C\u8BF7\u4E0B\u8F7D\u6E90\u7801\u540E\u4F53\u9A8C\u6B64\u529F\u80FD\u3002',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
}
|
||||
state.show = true
|
||||
}
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleError,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
127
front-end/src/views/other/excel/exportExcel.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div class="export-excel-container">
|
||||
<vab-query-form>
|
||||
<vab-query-form-left-panel :span="24">
|
||||
<el-form inline label-width="100px" @submit.prevent>
|
||||
<el-form-item label="文件名">
|
||||
<el-input
|
||||
v-model="filename"
|
||||
placeholder="请输出要导出文件的名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型">
|
||||
<el-select v-model="bookType">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleDownload">
|
||||
导出 Excel
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</vab-query-form-left-panel>
|
||||
</vab-query-form>
|
||||
|
||||
<el-table v-loading="listLoading" border :data="list">
|
||||
<el-table-column align="center" label="序号" width="55">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="标题">
|
||||
<template #default="{ row }">
|
||||
{{ row.title }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="作者">
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ row.author }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="访问量">
|
||||
<template #default="{ row }">
|
||||
{{ row.pageViews }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="时间">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.datetime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExportExcel',
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
listLoading: true,
|
||||
downloadLoading: false,
|
||||
filename: '',
|
||||
autoWidth: true,
|
||||
bookType: 'xlsx',
|
||||
options: ['xlsx', 'csv', 'txt'],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
this.listLoading = true
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList()
|
||||
this.list = list
|
||||
this.listLoading = false
|
||||
},
|
||||
handleDownload() {
|
||||
this.downloadLoading = true
|
||||
import('@/utils/excel').then((excel) => {
|
||||
const tHeader = [
|
||||
'Id',
|
||||
'Title',
|
||||
'Author',
|
||||
'Readings',
|
||||
'Date',
|
||||
]
|
||||
const filterVal = [
|
||||
'id',
|
||||
'title',
|
||||
'author',
|
||||
'pageViews',
|
||||
'datetime',
|
||||
]
|
||||
const list = this.list
|
||||
const data = this.formatJson(filterVal, list)
|
||||
excel.export_json_to_excel({
|
||||
header: tHeader,
|
||||
data,
|
||||
filename: this.filename,
|
||||
autoWidth: this.autoWidth,
|
||||
bookType: this.bookType,
|
||||
})
|
||||
this.downloadLoading = false
|
||||
})
|
||||
},
|
||||
formatJson(filterVal, jsonData) {
|
||||
return jsonData.map((v) =>
|
||||
filterVal.map((j) => {
|
||||
return v[j]
|
||||
})
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
116
front-end/src/views/other/excel/exportMergeHeaderExcel.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="merge-header-container">
|
||||
<vab-query-form>
|
||||
<vab-query-form-top-panel :span="24">
|
||||
<el-button
|
||||
:loading="downloadLoading"
|
||||
type="primary"
|
||||
@click="handleDownload"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</vab-query-form-top-panel>
|
||||
</vab-query-form>
|
||||
|
||||
<el-table
|
||||
ref="multipleTable"
|
||||
v-loading="listLoading"
|
||||
border
|
||||
:data="list"
|
||||
>
|
||||
<el-table-column align="center" label="序号" width="55">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="Main Information">
|
||||
<el-table-column label="Title">
|
||||
<template #default="{ row }">
|
||||
{{ row.title }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="Author">
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ row.author }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="Readings">
|
||||
<template #default="{ row }">
|
||||
{{ row.pageViews }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="Date">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.datetime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/table'
|
||||
import { parseTime } from '@/utils'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExportMergeHeaderExcel',
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
listLoading: true,
|
||||
downloadLoading: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
this.listLoading = true
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList()
|
||||
this.list = list
|
||||
this.listLoading = false
|
||||
},
|
||||
handleDownload() {
|
||||
this.downloadLoading = true
|
||||
import('@/utils/excel').then((excel) => {
|
||||
const multiHeader = [
|
||||
['Id', 'Main Information', '', '', 'Date'],
|
||||
]
|
||||
const header = ['', 'Title', 'Author', 'Readings', '']
|
||||
const filterVal = [
|
||||
'id',
|
||||
'title',
|
||||
'author',
|
||||
'pageViews',
|
||||
'datetime',
|
||||
]
|
||||
const list = this.list
|
||||
const data = this.formatJson(filterVal, list)
|
||||
const merges = ['A1:A2', 'B1:D1', 'E1:E2']
|
||||
excel.export_json_to_excel({
|
||||
multiHeader,
|
||||
header,
|
||||
merges,
|
||||
data,
|
||||
})
|
||||
this.downloadLoading = false
|
||||
})
|
||||
},
|
||||
formatJson(filterVal, jsonData) {
|
||||
return jsonData.map((v) =>
|
||||
filterVal.map((j) => {
|
||||
if (j === 'timestamp') {
|
||||
return parseTime(v[j])
|
||||
} else {
|
||||
return v[j]
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
139
front-end/src/views/other/excel/exportSelectExcel.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div class="select-excel-container">
|
||||
<vab-query-form>
|
||||
<vab-query-form-left-panel>
|
||||
<el-form inline @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="filename"
|
||||
placeholder="请输出要导出文件的名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleDownload">
|
||||
导出选中行
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</vab-query-form-left-panel>
|
||||
</vab-query-form>
|
||||
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
v-loading="listLoading"
|
||||
border
|
||||
:data="list"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column align="center" type="selection" />
|
||||
<el-table-column align="center" label="序号" width="55">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="标题">
|
||||
<template #default="{ row }">
|
||||
{{ row.title }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="作者">
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ row.author }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="访问量" width="115">
|
||||
<template #default="{ row }">
|
||||
{{ row.pageViews }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="时间">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.datetime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/table'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExportSelectExcel',
|
||||
setup() {
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const state = reactive({
|
||||
multipleTableRef: null,
|
||||
list: [],
|
||||
listLoading: true,
|
||||
multipleSelection: [],
|
||||
downloadLoading: false,
|
||||
filename: '',
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
state.listLoading = true
|
||||
const {
|
||||
data: { list },
|
||||
} = await getList()
|
||||
state.list = list
|
||||
state.listLoading = false
|
||||
}
|
||||
const handleSelectionChange = (val) => {
|
||||
state.multipleSelection = val
|
||||
}
|
||||
const handleDownload = () => {
|
||||
if (state.multipleSelection.length > 0) {
|
||||
state.downloadLoading = true
|
||||
import('@/utils/excel').then((excel) => {
|
||||
const tHeader = [
|
||||
'Id',
|
||||
'Title',
|
||||
'Author',
|
||||
'Readings',
|
||||
'Date',
|
||||
]
|
||||
const filterVal = [
|
||||
'id',
|
||||
'title',
|
||||
'author',
|
||||
'pageViews',
|
||||
'datetime',
|
||||
]
|
||||
const list = state.multipleSelection
|
||||
const data = state.formatJson(filterVal, list)
|
||||
excel.export_json_to_excel({
|
||||
header: tHeader,
|
||||
data,
|
||||
filename: state.filename,
|
||||
})
|
||||
state.multipleTableRef.clearSelection()
|
||||
state.downloadLoading = false
|
||||
})
|
||||
} else {
|
||||
$baseMessage(
|
||||
'请至少选择一行',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
}
|
||||
}
|
||||
const formatJson = (filterVal, jsonData) => {
|
||||
return jsonData.map((v) => filterVal.map((j) => v[j]))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
fetchData,
|
||||
handleSelectionChange,
|
||||
handleDownload,
|
||||
formatJson,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
97
front-end/src/views/other/iframe/search.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="iframe-search-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
:lg="{ span: 12, offset: 6 }"
|
||||
:md="{ span: 20, offset: 2 }"
|
||||
:sm="{ span: 20, offset: 2 }"
|
||||
:xl="{ span: 12, offset: 6 }"
|
||||
:xs="24"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="top"
|
||||
label-width="100px"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="请输入跳转url" prop="url">
|
||||
<el-input v-model="form.url" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="请输入跳转后自动改名的Title"
|
||||
prop="title"
|
||||
>
|
||||
<el-input v-model="form.title" />
|
||||
</el-form-item>
|
||||
<el-button
|
||||
:icon="Search"
|
||||
native-type="submit"
|
||||
type="primary"
|
||||
@click="handleClick"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IframeSearch',
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const validateExternal = (rule, value, callback) => {
|
||||
if (!isExternal(value)) callback(new Error('url输入错误'))
|
||||
else callback()
|
||||
}
|
||||
const state = reactive({
|
||||
formRef: null,
|
||||
rules: {
|
||||
url: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入跳转url',
|
||||
trigger: 'blur',
|
||||
validator: validateExternal,
|
||||
},
|
||||
],
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入跳转后自动改名的Title',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
form: {
|
||||
url: 'https://www.so.com/s?ie=utf-8&fr=none&src=home_suggst_revise&q=vue-admin-better&eci=&nlpv=test_zc_rank1',
|
||||
title: '360搜索',
|
||||
},
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
state['formRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
router.push({
|
||||
path: '/other/iframe/view',
|
||||
query: state.form,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
handleClick,
|
||||
Search,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
44
front-end/src/views/other/iframe/view.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="iframe-container">
|
||||
<iframe frameborder="0" :src="url" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Iframe',
|
||||
data() {
|
||||
return { url: '' }
|
||||
},
|
||||
watch: {
|
||||
$route: 'handleIframe',
|
||||
},
|
||||
created() {
|
||||
this.handleIframe()
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useTabsStore, ['changeTabsMeta']),
|
||||
handleIframe() {
|
||||
this.url = `https://${this.$route.query.url}`
|
||||
const meta = { ...this.$route.meta, ...this.$route.query }
|
||||
this.$nextTick(() => {
|
||||
this.changeTabsMeta({
|
||||
title: 'Iframe',
|
||||
meta,
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iframe-container {
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: $base-keep-alive-height;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="menu1-1-1-1-container">
|
||||
<el-alert :closable="false" title="多级路由 1-1-1-1" type="success">
|
||||
<el-input v-model="value" />
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Menu1111',
|
||||
setup() {
|
||||
const value = ref('')
|
||||
|
||||
return {
|
||||
value,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
37
front-end/src/views/other/noLayout/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts"></script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { handleActivePath } from '@/utils/routes'
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
import type { VabRoute } from '/#/router'
|
||||
|
||||
defineOptions({
|
||||
name: 'NoLayout',
|
||||
})
|
||||
|
||||
const tabsStore = useTabsStore()
|
||||
const route = useRoute()
|
||||
const { delVisitedRoute } = tabsStore
|
||||
const router = useRouter()
|
||||
|
||||
const goBack = async () => {
|
||||
await router.push({ path: '/index' })
|
||||
await delVisitedRoute(handleActivePath(route as VabRoute, true))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="no-layout-container">
|
||||
<el-page-header content="无框" title="返回首页" @back="goBack" />
|
||||
|
||||
<el-alert :closable="false" title="无框示例" type="success" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.no-layout-container {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
</style>
|
||||
277
front-end/src/views/other/notice/index.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<template>
|
||||
<div class="notice-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>成功提示</span>
|
||||
</template>
|
||||
<el-result
|
||||
icon="success"
|
||||
sub-title="请根据提示进行操作"
|
||||
title="成功提示"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="primary">确认</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>警告提示</span>
|
||||
</template>
|
||||
<el-result
|
||||
icon="warning"
|
||||
sub-title="请根据提示进行操作"
|
||||
title="警告提示"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="primary">确认</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>错误提示</span>
|
||||
</template>
|
||||
<el-result
|
||||
icon="error"
|
||||
sub-title="请根据提示进行操作"
|
||||
title="错误提示"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="primary">确认</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>信息提示</span>
|
||||
</template>
|
||||
<el-result
|
||||
icon="info"
|
||||
sub-title="请根据提示进行操作"
|
||||
title="信息提示"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="primary">确认</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>Alert 警告</span>
|
||||
</template>
|
||||
<el-alert show-icon title="成功提示的文案" type="success" />
|
||||
<el-alert show-icon title="消息提示的文案" type="info" />
|
||||
<el-alert show-icon title="警告提示的文案" type="warning" />
|
||||
<el-alert show-icon title="错误提示的文案" type="error" />
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>element-plus风格 Message 消息提示</span>
|
||||
</template>
|
||||
<el-button plain type="primary" @click="open1">
|
||||
消息
|
||||
</el-button>
|
||||
<el-button plain type="success" @click="open2">
|
||||
成功
|
||||
</el-button>
|
||||
<el-button plain type="warning" @click="open3">
|
||||
警告
|
||||
</el-button>
|
||||
<el-button plain type="danger" @click="open4">
|
||||
错误
|
||||
</el-button>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>hey-ui风格 消息提示</span>
|
||||
</template>
|
||||
<el-button plain type="primary" @click="open5">
|
||||
消息
|
||||
</el-button>
|
||||
<el-button plain type="success" @click="open6">
|
||||
成功
|
||||
</el-button>
|
||||
<el-button plain type="warning" @click="open7">
|
||||
警告
|
||||
</el-button>
|
||||
<el-button plain type="danger" @click="open8">
|
||||
错误
|
||||
</el-button>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>Notification 消息提示</span>
|
||||
</template>
|
||||
<el-button plain type="info" @click="open9">消息</el-button>
|
||||
<el-button plain type="success" @click="open10">
|
||||
成功
|
||||
</el-button>
|
||||
<el-button plain type="warning" @click="open11">
|
||||
警告
|
||||
</el-button>
|
||||
<el-button plain type="danger" @click="open12">
|
||||
错误
|
||||
</el-button>
|
||||
</vab-card>
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>Message Box消息提示</span>
|
||||
</template>
|
||||
<el-button plain type="primary" @click="open13">
|
||||
消息提示
|
||||
</el-button>
|
||||
<el-button plain type="primary" @click="open14">
|
||||
确认消息
|
||||
</el-button>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>
|
||||
<span>更新提示</span>
|
||||
</template>
|
||||
<el-button plain @click="open15">更新提示</el-button>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Notice',
|
||||
setup() {
|
||||
const $pub = inject('$pub')
|
||||
const $baseAlert = inject('$baseAlert')
|
||||
const $baseNotify = inject('$baseNotify')
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const state = reactive({
|
||||
vabUpdateRef: null,
|
||||
data: [],
|
||||
toggleIndex: 0,
|
||||
})
|
||||
const open1 = () => {
|
||||
$baseMessage('这是一条消息提示', 'info')
|
||||
}
|
||||
const open2 = () => {
|
||||
$baseMessage('恭喜你,这是一条成功消息', 'success')
|
||||
}
|
||||
const open3 = () => {
|
||||
$baseMessage('警告哦,这是一条警告消息', 'warning')
|
||||
}
|
||||
const open4 = () => {
|
||||
$baseMessage('错了哦,这是一条错误消息', 'error')
|
||||
}
|
||||
const open5 = () => {
|
||||
$baseMessage('这是一条消息提示', 'info', 'vab-hey-message-info')
|
||||
}
|
||||
const open6 = () => {
|
||||
$baseMessage(
|
||||
'恭喜你,这是一条成功消息',
|
||||
'success',
|
||||
'vab-hey-message-success'
|
||||
)
|
||||
}
|
||||
const open7 = () => {
|
||||
$baseMessage(
|
||||
'警告哦,这是一条警告消息',
|
||||
'warning',
|
||||
'vab-hey-message-warning'
|
||||
)
|
||||
}
|
||||
const open8 = () => {
|
||||
$baseMessage(
|
||||
'错了哦,这是一条错误消息',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
}
|
||||
const open9 = () => {
|
||||
$baseNotify(
|
||||
'这是一条消息的提示消息',
|
||||
'提示',
|
||||
'info',
|
||||
'bottom-right'
|
||||
)
|
||||
}
|
||||
const open10 = () => {
|
||||
$baseNotify(
|
||||
'这是一条成功的提示消息',
|
||||
'成功',
|
||||
'success',
|
||||
'bottom-right'
|
||||
)
|
||||
}
|
||||
const open11 = () => {
|
||||
$baseNotify(
|
||||
'这是一条警告的提示消息',
|
||||
'警告',
|
||||
'warning',
|
||||
'bottom-right'
|
||||
)
|
||||
}
|
||||
const open12 = () => {
|
||||
$baseNotify(
|
||||
'这是一条错误的提示消息',
|
||||
'错误',
|
||||
'error',
|
||||
'bottom-right'
|
||||
)
|
||||
}
|
||||
const open13 = () => {
|
||||
$baseAlert('这是一条消息提示')
|
||||
}
|
||||
const open14 = () => {
|
||||
$baseAlert('这是一条确认消息')
|
||||
}
|
||||
const open15 = () => {
|
||||
$pub('vab-update')
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
open1,
|
||||
open2,
|
||||
open3,
|
||||
open4,
|
||||
open5,
|
||||
open6,
|
||||
open7,
|
||||
open8,
|
||||
open9,
|
||||
open10,
|
||||
open11,
|
||||
open12,
|
||||
open13,
|
||||
open14,
|
||||
open15,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$base: '.notice';
|
||||
#{$base}-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
}
|
||||
</style>
|
||||
87
front-end/src/views/other/print/index.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="print-container">
|
||||
<el-button type="primary" @click="print('imageRef')">
|
||||
<vab-icon icon="printer-line" />
|
||||
打印图片
|
||||
</el-button>
|
||||
<el-button type="primary" @click="print('tableRef')">
|
||||
<vab-icon icon="printer-line" />
|
||||
打印表格
|
||||
</el-button>
|
||||
<el-button type="primary" @click="remotePrint">
|
||||
<vab-icon icon="printer-line" />
|
||||
自定义打印
|
||||
</el-button>
|
||||
<img
|
||||
ref="imageRef"
|
||||
:src="
|
||||
'https://gcore.jsdelivr.net/gh/' +
|
||||
'chuzh' +
|
||||
'ixin/image' +
|
||||
'/table/vab-im' +
|
||||
'age-1.jpg'
|
||||
"
|
||||
style="display: block; width: 520px; margin-top: 15px"
|
||||
/>
|
||||
<br />
|
||||
<el-table ref="tableRef" :data="tableData" style="width: 520px">
|
||||
<el-table-column label="姓名" prop="name" width="120px" />
|
||||
<el-table-column label="地址" prop="address" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabPrint from '@/plugins/VabPrint'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Print',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
imageRef: null,
|
||||
tableRef: null,
|
||||
tableData: [
|
||||
{
|
||||
name: '马云',
|
||||
address: '上海市普陀区金沙江路',
|
||||
},
|
||||
{
|
||||
name: '马化腾',
|
||||
address: '上海市普陀区金沙江路',
|
||||
},
|
||||
{
|
||||
name: '李彦宏',
|
||||
address: '上海市普陀区金沙江路',
|
||||
},
|
||||
{
|
||||
name: '刘强东',
|
||||
address: '上海市普陀区金沙江路',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const print = async (val) => {
|
||||
await VabPrint(state[val])
|
||||
}
|
||||
const remotePrint = () => {
|
||||
ElMessageBox.prompt('', '自定义打印', {
|
||||
inputType: 'textarea',
|
||||
inputValue: `<h1>Vue Admin Plus</h1>
|
||||
<p>veujs-core.cn</p>`,
|
||||
inputErrorMessage: 'Invalid Email',
|
||||
confirmButtonText: '打印',
|
||||
})
|
||||
.then(({ value }) => {
|
||||
VabPrint(value)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
print,
|
||||
remotePrint,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
96
front-end/src/views/other/share/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="share-container">
|
||||
<el-form inline :model="form" @submit.prevent>
|
||||
<el-form-item label="URL">
|
||||
<el-input v-model="form.url" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
native-type="submit"
|
||||
type="primary"
|
||||
@click="copy($event)"
|
||||
>
|
||||
<vab-icon icon="file-copy-line" />
|
||||
拷贝URL
|
||||
</el-button>
|
||||
<el-popover
|
||||
placement="bottom-start"
|
||||
title=""
|
||||
trigger="hover"
|
||||
:width="220"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button native-type="submit" type="primary">
|
||||
<vab-icon icon="qr-code-line" />
|
||||
生成二维码
|
||||
</el-button>
|
||||
</template>
|
||||
<vab-qr-code
|
||||
:dot-scale="0.5"
|
||||
:logo-src="require('@/assets/logo.png')"
|
||||
:text="form.url"
|
||||
/>
|
||||
</el-popover>
|
||||
<!-- <el-popover
|
||||
placement="bottom-start"
|
||||
title=""
|
||||
trigger="hover"
|
||||
:width="220"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button native-type="submit" type="primary">
|
||||
<vab-icon icon="qr-code-line" />
|
||||
生成二维码
|
||||
</el-button>
|
||||
</template>
|
||||
<vab-qr-code
|
||||
:dot-scale="0.5"
|
||||
:gif-bg-src="require('vue-qr/src/assets/dog.gif')"
|
||||
:text="form.url"
|
||||
/>
|
||||
</el-popover> -->
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabQrCode from '@/plugins/VabQrCode'
|
||||
import clip from '@/utils/clipboard'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Share',
|
||||
components: {
|
||||
VabQrCode,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
form: {
|
||||
url: '',
|
||||
},
|
||||
})
|
||||
const copy = (event) => {
|
||||
clip(state.form.url, event)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
state.form.url = 'https://vuejs-core.cn/admin-plus'
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
copy,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.share-container {
|
||||
:deep() {
|
||||
.el-input__inner {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
62
front-end/src/views/other/social/index.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="social-container">
|
||||
<el-button type="primary" @click="handleSocialLogin">
|
||||
点击进行Github登录
|
||||
</el-button>
|
||||
<p v-if="data">{{ data }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { login } from '@/utils/social'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Social',
|
||||
setup() {
|
||||
const $baseLoading = inject('$baseLoading')
|
||||
const $baseMessage = inject('$baseMessage')
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const socialLogin = (data) => userStore.socialLogin(data)
|
||||
|
||||
const data = ref()
|
||||
|
||||
const handleSocialLogin = () => {
|
||||
const loading = $baseLoading()
|
||||
login(`https://github.com/login/oauth/authorize`, {
|
||||
client_id:
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'bb8dfd34f6c6a57367e3'
|
||||
: 'e104bdc488d009840c4f',
|
||||
})
|
||||
.then(async (_data) => {
|
||||
data.value = _data
|
||||
await socialLogin(_data)
|
||||
// 登录页面使用,取消注释
|
||||
// const routerPath =
|
||||
// this.redirect === "/404" || this.redirect === "/403"
|
||||
// ? "/"
|
||||
// : this.redirect;
|
||||
// this.$router.push(routerPath).catch(() => {});
|
||||
})
|
||||
.catch(() => {
|
||||
$baseMessage(
|
||||
'第三方登录失败,未返回令牌',
|
||||
'error',
|
||||
'vab-hey-message-error'
|
||||
)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
handleSocialLogin,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
91
front-end/src/views/other/tabs/index.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="tabs-container">
|
||||
<el-button type="primary" @click="closeOthersTabs">关闭其他</el-button>
|
||||
<el-button type="primary" @click="closeLeftTabs">关闭左侧</el-button>
|
||||
<el-button type="primary" @click="closeRightTabs">关闭右侧</el-button>
|
||||
<el-button type="primary" @click="closeAllTabs">关闭全部</el-button>
|
||||
<el-button type="primary" @click="handleTabRemove(route.path)">
|
||||
关闭当前
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { handleActivePath } from '@/utils/routes'
|
||||
import { useTabsStore } from '@/store/modules/tabs'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const tabStore = useTabsStore()
|
||||
const { getVisitedRoutes: visitedRoutes } = storeToRefs(tabStore)
|
||||
const {
|
||||
delVisitedRoute,
|
||||
delOthersVisitedRoutes,
|
||||
delLeftVisitedRoutes,
|
||||
delRightVisitedRoutes,
|
||||
delAllVisitedRoutes,
|
||||
} = tabStore
|
||||
|
||||
const hoverRoute = ref(null)
|
||||
/**
|
||||
* 根据原生路径删除标签中的标签
|
||||
* @param rawPath 原生路径
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const handleTabRemove = async (rawPath) => {
|
||||
if (isActive(rawPath)) await toLastTab()
|
||||
await delVisitedRoute(rawPath)
|
||||
}
|
||||
/**
|
||||
* 删除其他标签页
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const closeOthersTabs = async () => {
|
||||
if (hoverRoute.value) {
|
||||
await router.push(hoverRoute.value)
|
||||
await delOthersVisitedRoutes(hoverRoute.value.path)
|
||||
} else await delOthersVisitedRoutes(handleActivePath(route, true))
|
||||
}
|
||||
/**
|
||||
* 删除左侧标签页
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const closeLeftTabs = async () => {
|
||||
if (hoverRoute.value) {
|
||||
await router.push(hoverRoute.value)
|
||||
await delLeftVisitedRoutes(hoverRoute.value.path)
|
||||
} else await delLeftVisitedRoutes(handleActivePath(route, true))
|
||||
}
|
||||
/**
|
||||
* 删除右侧标签页
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const closeRightTabs = async () => {
|
||||
if (hoverRoute.value) {
|
||||
await router.push(hoverRoute.value)
|
||||
await delRightVisitedRoutes(hoverRoute.value.path)
|
||||
} else await delRightVisitedRoutes(handleActivePath(route, true))
|
||||
}
|
||||
/**
|
||||
* 删除所有标签页
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const closeAllTabs = async () => {
|
||||
await delAllVisitedRoutes()
|
||||
await toLastTab()
|
||||
}
|
||||
/**
|
||||
* 跳转最后一个标签页
|
||||
*/
|
||||
const toLastTab = async () => {
|
||||
const latestView = visitedRoutes.value
|
||||
.filter((_) => _.path !== handleActivePath(route, true))
|
||||
.slice(-1)[0]
|
||||
if (latestView) await router.push(latestView)
|
||||
else await router.push('/')
|
||||
}
|
||||
const isActive = (path) => {
|
||||
return path === handleActivePath(route, true)
|
||||
}
|
||||
</script>
|
||||
209
front-end/src/views/other/timeline/index.vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div class="timeline-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="8" :md="12" :sm="12" :xl="8" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>常规风格</template>
|
||||
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in activities"
|
||||
:key="index"
|
||||
:color="item.color"
|
||||
:timestamp="item.timestamp"
|
||||
>
|
||||
<template v-if="!item.color" #dot>
|
||||
<vab-icon v-if="item.icon" :icon="item.icon" />
|
||||
<span
|
||||
v-if="item.waver"
|
||||
class="vab-dot"
|
||||
:class="{
|
||||
['vab-dot-' + item.waver]: true,
|
||||
}"
|
||||
>
|
||||
<span></span>
|
||||
</span>
|
||||
</template>
|
||||
<vab-card v-if="item.card" shadow="never">
|
||||
{{ item.content }}
|
||||
</vab-card>
|
||||
<template v-else>
|
||||
{{ item.content }}
|
||||
</template>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
<el-col :lg="8" :md="12" :sm="12" :xl="8" :xs="24">
|
||||
<vab-card shadow="never">
|
||||
<template #header>卡片风格</template>
|
||||
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in activities"
|
||||
:key="index"
|
||||
:color="item.color"
|
||||
:timestamp="item.timestamp"
|
||||
>
|
||||
<template v-if="!item.color" #dot>
|
||||
<vab-icon v-if="item.icon" :icon="item.icon" />
|
||||
<span
|
||||
v-if="item.waver"
|
||||
class="vab-dot"
|
||||
:class="{
|
||||
['vab-dot-' + item.waver]: true,
|
||||
}"
|
||||
>
|
||||
<span></span>
|
||||
</span>
|
||||
</template>
|
||||
<div
|
||||
class="vab-info-card"
|
||||
:class="{
|
||||
['vab-info-card-' + item.cardType]: true,
|
||||
}"
|
||||
>
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</vab-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Timeline',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
activities: [
|
||||
{
|
||||
content: '支持使用默认图标',
|
||||
timestamp: '2021-04-12 20:46',
|
||||
icon: 'account-circle-line',
|
||||
cardType: 'warning',
|
||||
},
|
||||
{
|
||||
content: '支持使用默认图标',
|
||||
timestamp: '2021-04-18 20:46',
|
||||
icon: 'archive-line',
|
||||
cardType: 'error',
|
||||
},
|
||||
{
|
||||
content: '支持自定义颜色',
|
||||
timestamp: '2021-04-03 20:46',
|
||||
color: '#13ce66',
|
||||
cardType: 'success',
|
||||
},
|
||||
{
|
||||
content: '支持默认颜色',
|
||||
timestamp: '2021-04-03 20:46',
|
||||
color: '#e4e7ed',
|
||||
},
|
||||
{
|
||||
content: '支持success闪动',
|
||||
timestamp: '2021-04-05 20:46',
|
||||
waver: 'success',
|
||||
},
|
||||
{
|
||||
content: '支持error闪动',
|
||||
timestamp: '2021-04-05 20:46',
|
||||
waver: 'error',
|
||||
},
|
||||
],
|
||||
})
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.timeline-container {
|
||||
padding: 0 !important;
|
||||
background: $base-color-background !important;
|
||||
|
||||
:deep() {
|
||||
.el-timeline-item__dot {
|
||||
[class*='ri'] {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-left: -3px;
|
||||
background: var(--el-color-white);
|
||||
}
|
||||
|
||||
.vab-dot {
|
||||
left: -1px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-card {
|
||||
.el-card__header {
|
||||
position: relative;
|
||||
|
||||
.card-header-radio {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: $base-margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vab-info-card {
|
||||
position: relative;
|
||||
width: 80%;
|
||||
padding: $base-padding;
|
||||
background: #e2e2e2;
|
||||
border-radius: $base-border-radius + 2;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: -10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
content: '';
|
||||
border-color: #e2e2e2 transparent transparent;
|
||||
border-style: solid dashed dashed;
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
&-success {
|
||||
color: var(--el-color-white);
|
||||
background: var(--el-color-success);
|
||||
|
||||
&::after {
|
||||
border-color: var(--el-color-success) transparent
|
||||
transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&-error {
|
||||
color: var(--el-color-white);
|
||||
background: var(--el-color-error);
|
||||
|
||||
&::after {
|
||||
border-color: var(--el-color-error) transparent transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&-warning {
|
||||
color: var(--el-color-white);
|
||||
background: var(--el-color-warning);
|
||||
|
||||
&::after {
|
||||
border-color: var(--el-color-warning) transparent
|
||||
transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
34
front-end/src/views/other/upload/index.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<vab-upload
|
||||
ref="vabUploadRef"
|
||||
:limit="50"
|
||||
name="file"
|
||||
:size="2"
|
||||
url="/upload"
|
||||
/>
|
||||
<el-button type="primary" @click="handleShow()">模拟上传</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabUpload from '@/plugins/VabUpload'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Upload',
|
||||
components: {
|
||||
VabUpload,
|
||||
},
|
||||
setup() {
|
||||
const vabUploadRef = ref()
|
||||
const handleShow = () => {
|
||||
vabUploadRef.value.handleShow()
|
||||
}
|
||||
|
||||
return {
|
||||
vabUploadRef,
|
||||
handleShow,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
47
front-end/src/views/other/watermark/index.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="watermark-container">
|
||||
<el-button type="primary" @click="setWatermark(title)">
|
||||
添加水印
|
||||
</el-button>
|
||||
<el-button type="primary" @click="setCustomWatermark">
|
||||
添加自定义水印
|
||||
</el-button>
|
||||
<el-button type="primary" @click="setWatermark('')">移除水印</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Watermark from '@/utils/watermark'
|
||||
import { useSettingsStore } from '@/store/modules/settings'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Watermark',
|
||||
setup() {
|
||||
const settingsStore = useSettingsStore()
|
||||
const { title } = storeToRefs(settingsStore)
|
||||
|
||||
const setWatermark = (value: string) => {
|
||||
//@ts-ignore
|
||||
Watermark.set(value)
|
||||
}
|
||||
const setCustomWatermark = () => {
|
||||
ElMessageBox.prompt('请输入自定义水印', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
})
|
||||
.then(({ value }) => {
|
||||
if (value) setWatermark(value)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
setWatermark,
|
||||
setCustomWatermark,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
BIN
front-end/src/views/other/workflow/components/background/end.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<el-tabs tab-position="left">
|
||||
<el-tab-pane label="添加动作">
|
||||
<div v-for="item in nodeList" :key="item.type">
|
||||
<el-button
|
||||
class="add-node-btn"
|
||||
type="primary"
|
||||
@click="$_addNode(item)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="添加组">
|
||||
<el-button
|
||||
class="add-node-btn"
|
||||
type="primary"
|
||||
@click="$_addTempalte"
|
||||
>
|
||||
模板
|
||||
</el-button>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'AddPanel',
|
||||
props: {
|
||||
nodeData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lf: {
|
||||
type: Object || String,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
emits: ['addNodeFinish'],
|
||||
data() {
|
||||
return {
|
||||
nodeList: [
|
||||
{
|
||||
type: 'user',
|
||||
label: '用户',
|
||||
},
|
||||
{
|
||||
type: 'push',
|
||||
label: '推送',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
$_addNode(item) {
|
||||
const { lf, nodeData } = this.$props
|
||||
const { id, x, y } = nodeData
|
||||
const nextNode = lf.addNode({
|
||||
type: item.type,
|
||||
x: x + 150,
|
||||
y: y + 150,
|
||||
})
|
||||
const nextId = nextNode.id
|
||||
lf.createEdge({ sourceNodeId: id, targetNodeId: nextId })
|
||||
this.$emit('addNodeFinish')
|
||||
},
|
||||
$_addTempalte() {
|
||||
const { lf, nodeData } = this.$props
|
||||
const { id, x, y } = nodeData
|
||||
const timeNode = lf.addNode({
|
||||
type: 'download',
|
||||
x,
|
||||
y: y + 150,
|
||||
})
|
||||
const userNode = lf.addNode({
|
||||
type: 'user',
|
||||
x: x + 150,
|
||||
y: y + 150,
|
||||
})
|
||||
const pushNode = lf.addNode({
|
||||
type: 'push',
|
||||
x: x + 150,
|
||||
y: y + 300,
|
||||
properties: {},
|
||||
})
|
||||
const endNode = lf.addNode({
|
||||
type: 'end',
|
||||
x: x + 300,
|
||||
y: y + 150,
|
||||
})
|
||||
const endNode2 = lf.addNode({
|
||||
type: 'end',
|
||||
x: x + 300,
|
||||
y: y + 300,
|
||||
})
|
||||
lf.createEdge({ sourceNodeId: id, targetNodeId: timeNode.id })
|
||||
lf.createEdge({
|
||||
sourceNodeId: timeNode.id,
|
||||
targetNodeId: userNode.id,
|
||||
})
|
||||
lf.createEdge({
|
||||
sourceNodeId: userNode.id,
|
||||
targetNodeId: endNode.id,
|
||||
endPoint: { x: x + 280, y: y + 150 },
|
||||
text: {
|
||||
value: 'Y',
|
||||
x: x + 230,
|
||||
y: y + 140,
|
||||
},
|
||||
})
|
||||
lf.createEdge({
|
||||
sourceNodeId: userNode.id,
|
||||
targetNodeId: pushNode.id,
|
||||
text: {
|
||||
value: 'N',
|
||||
x: x + 160,
|
||||
y: y + 230,
|
||||
},
|
||||
})
|
||||
lf.createEdge({
|
||||
sourceNodeId: pushNode.id,
|
||||
targetNodeId: endNode2.id,
|
||||
endPoint: { x: x + 280, y: y + 300 },
|
||||
})
|
||||
this.$emit('addNodeFinish')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.add-node-btn {
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button-group>
|
||||
<el-button plain size="small" @click="$_zoomIn">放大</el-button>
|
||||
<el-button plain size="small" @click="$_zoomOut">缩小</el-button>
|
||||
<el-button plain size="small" @click="$_zoomReset">
|
||||
大小适应
|
||||
</el-button>
|
||||
<el-button plain size="small" @click="$_translateRest">
|
||||
定位还原
|
||||
</el-button>
|
||||
<el-button plain size="small" @click="$_reset">
|
||||
还原(大小&定位)
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="undoDisable"
|
||||
plain
|
||||
size="small"
|
||||
@click="$_undo"
|
||||
>
|
||||
上一步(ctrl+z)
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="redoDisable"
|
||||
plain
|
||||
size="small"
|
||||
@click="$_redo"
|
||||
>
|
||||
下一步(ctrl+y)
|
||||
</el-button>
|
||||
<el-button plain size="small" @click="$_download">
|
||||
下载图片
|
||||
</el-button>
|
||||
<el-button plain size="small" @click="$_catData">
|
||||
查看数据
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'Control',
|
||||
props: {
|
||||
lf: {
|
||||
type: Object || String,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
emits: ['cat-data'],
|
||||
data() {
|
||||
return {
|
||||
undoDisable: true,
|
||||
redoDisable: true,
|
||||
graphData: null,
|
||||
dataVisible: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$props.lf.on(
|
||||
'history:change',
|
||||
({ data: { undoAble, redoAble } }) => {
|
||||
this.$data.undoDisable = !undoAble
|
||||
this.$data.redoDisable = !redoAble
|
||||
}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
$_zoomIn() {
|
||||
this.$props.lf.zoom(true)
|
||||
},
|
||||
$_zoomOut() {
|
||||
this.$props.lf.zoom(false)
|
||||
},
|
||||
$_zoomReset() {
|
||||
this.$props.lf.resetZoom()
|
||||
},
|
||||
$_translateRest() {
|
||||
this.$props.lf.resetTranslate()
|
||||
},
|
||||
$_reset() {
|
||||
this.$props.lf.resetZoom()
|
||||
this.$props.lf.resetTranslate()
|
||||
},
|
||||
$_undo() {
|
||||
this.$props.lf.undo()
|
||||
},
|
||||
$_redo() {
|
||||
this.$props.lf.redo()
|
||||
},
|
||||
$_download() {
|
||||
this.$props.lf.getSnapshot()
|
||||
},
|
||||
$_catData() {
|
||||
this.$emit('cat-data')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<vab-json-viewer copyable :expand-depth="5" sort :value="data" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VabJsonViewer from 'vue-json-viewer'
|
||||
|
||||
export default defineComponent({
|
||||
components: { VabJsonViewer },
|
||||
props: {
|
||||
graphData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.data = JSON.parse(
|
||||
JSON.stringify([
|
||||
{
|
||||
edges: this.graphData.edges,
|
||||
nodes: this.graphData.nodes,
|
||||
},
|
||||
])
|
||||
)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div class="node-panel">
|
||||
<div
|
||||
v-for="item in nodeList"
|
||||
:key="item.text"
|
||||
class="node-item"
|
||||
@mousedown="$_dragNode(item)"
|
||||
>
|
||||
<div class="node-item-icon" :class="item.class">
|
||||
<div
|
||||
v-if="item.type === 'user' || item.type === 'time'"
|
||||
class="shape"
|
||||
></div>
|
||||
</div>
|
||||
<span class="node-label">{{ item.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
name: 'NodePanel',
|
||||
props: {
|
||||
lf: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nodeList: [
|
||||
{
|
||||
text: '开始',
|
||||
type: 'start',
|
||||
class: 'node-start',
|
||||
},
|
||||
{
|
||||
text: '矩形',
|
||||
type: 'rect',
|
||||
class: 'node-rect',
|
||||
},
|
||||
{
|
||||
type: 'user',
|
||||
text: '用户',
|
||||
class: 'node-user',
|
||||
},
|
||||
{
|
||||
type: 'push',
|
||||
text: '推送',
|
||||
class: 'node-push',
|
||||
},
|
||||
{
|
||||
type: 'download',
|
||||
text: '位置',
|
||||
class: 'node-download',
|
||||
},
|
||||
{
|
||||
type: 'end',
|
||||
text: '结束',
|
||||
class: 'node-end',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
$_dragNode(item) {
|
||||
this.$props.lf.dnd.startDrag({
|
||||
type: item.type,
|
||||
text: item.label,
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.node-panel {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: $base-margin * 2;
|
||||
z-index: 101;
|
||||
width: 70px;
|
||||
padding: 20px 10px;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 10px 1px rgb(228 224 219);
|
||||
}
|
||||
|
||||
.node-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.node-item-icon {
|
||||
display: block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: auto;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.node-start {
|
||||
background: url('../background/start.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-rect {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.node-user {
|
||||
background: url('../background/user.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-time {
|
||||
background: url('../background/time.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-push {
|
||||
background: url('../background/push.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-download {
|
||||
background: url('../background/download.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-click {
|
||||
background: url('../background/click.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.node-end {
|
||||
background: url('../background/end.png') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-width="80px" :model="formData">
|
||||
<el-form-item label="名称">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动区域">
|
||||
<el-input v-model="formData.region" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动形式">
|
||||
<el-input v-model="formData.type" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
props: {
|
||||
nodeData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lf: {
|
||||
type: Object || String,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
emits: ['onClose'],
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
name: '',
|
||||
region: '',
|
||||
type: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const { properties } = this.$props.nodeData
|
||||
if (properties) {
|
||||
this.$data.formData = Object.assign(
|
||||
{},
|
||||
this.$data.formData,
|
||||
properties
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
console.log('submit!')
|
||||
const { id } = this.$props.nodeData
|
||||
this.$props.lf.setProperties(id, this.$data.formData)
|
||||
this.$emit('onClose')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="property-dialog">
|
||||
<user
|
||||
v-if="nodeData.type === 'user'"
|
||||
:lf="lf"
|
||||
:node-data="nodeData"
|
||||
@on-close="handleClose"
|
||||
/>
|
||||
<common-property
|
||||
v-else
|
||||
:lf="lf"
|
||||
:node-data="nodeData"
|
||||
@on-close="handleClose"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommonProperty from './CommonProperty'
|
||||
import User from './User.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PropertyDialog',
|
||||
components: {
|
||||
CommonProperty,
|
||||
User,
|
||||
},
|
||||
props: {
|
||||
nodeData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lf: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
emits: ['setPropertiesFinish'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit('setPropertiesFinish')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.property-dialog {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="form" label-width="80px" :model="form">
|
||||
<el-form-item label="活动名称">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动区域">
|
||||
<el-select v-model="form.region" placeholder="请选择活动区域">
|
||||
<el-option label="区域一" value="shanghai" />
|
||||
<el-option label="区域二" value="beijing" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间">
|
||||
<el-col :span="11">
|
||||
<el-date-picker
|
||||
v-model="form.date1"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%"
|
||||
type="date"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col class="line" :span="2">-</el-col>
|
||||
<el-col :span="11">
|
||||
<el-time-picker
|
||||
v-model="form.date2"
|
||||
placeholder="选择时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item label="即时配送">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动性质">
|
||||
<el-checkbox-group v-model="form.type">
|
||||
<el-checkbox
|
||||
label="美食/餐厅线上活动"
|
||||
name="type"
|
||||
value="美食/餐厅线上活动"
|
||||
/>
|
||||
<el-checkbox
|
||||
label="地推活动"
|
||||
name="type"
|
||||
value="地推活动"
|
||||
/>
|
||||
<el-checkbox
|
||||
label="线下主题活动"
|
||||
name="type"
|
||||
value="线下主题活动"
|
||||
/>
|
||||
<el-checkbox
|
||||
label="单纯品牌曝光"
|
||||
name="type"
|
||||
value="单纯品牌曝光"
|
||||
/>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="特殊资源">
|
||||
<el-radio-group v-model="form.resource">
|
||||
<el-radio label="线上品牌商赞助" value="线上品牌商赞助" />
|
||||
<el-radio label="线下场地免费" value="线下场地免费" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动形式">
|
||||
<el-input v-model="form.desc" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
props: {
|
||||
nodeData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lf: {
|
||||
type: Object || String,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
emits: ['onClose'],
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const { properties } = this.$props.nodeData
|
||||
if (properties) {
|
||||
this.$data.form = Object.assign({}, this.$data.form, properties)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
console.log('submit!')
|
||||
const { id } = this.$props.nodeData
|
||||
this.$props.lf.setProperties(id, this.$data.form)
|
||||
this.$emit('onClose')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,10 @@
|
||||
// svg png 图片资源来自阿里字体库
|
||||
// [阿里字体库](https://www.iconfont.cn/collections/index?spm=a313x.7781069.1998910419.4)
|
||||
// svg图标建议使用自己创建的
|
||||
|
||||
export { default as registerStart } from './registerStart'
|
||||
export { default as registerUser } from './registerUser'
|
||||
export { default as registerPush } from './registerPush'
|
||||
export { default as registerEnd } from './registerEnd'
|
||||
export { default as registerPolyline } from './registerPolyline'
|
||||
export { default as registerDownload } from './registerDownload'
|
||||
@@ -0,0 +1,84 @@
|
||||
const NODE_COLOR = '#9932CC'
|
||||
export default function registerDownload(lf) {
|
||||
lf.register('download', ({ PolygonNode, PolygonNodeModel, h }) => {
|
||||
class Node extends PolygonNode {
|
||||
getIconShape() {
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
x: 14,
|
||||
y: 13,
|
||||
width: 23,
|
||||
height: 23,
|
||||
viewBox: '0 0 1024 1024',
|
||||
},
|
||||
h('path', {
|
||||
fill: NODE_COLOR,
|
||||
d: 'M831.513034 319.863005h-95.945189c-17.662265 0-31.980365 14.3181-31.980365 31.980365 0 17.662265 14.3181 31.980365 31.980365 31.980366h64.218604c17.520025 0 31.722492 14.202467 31.722492 31.722492V863.786065c0 17.520025-14.202467 31.722492-31.722492 31.722492H159.66442c-17.520025 0-31.722492-14.202467-31.722493-31.722492V415.546228c0-17.520025 14.202467-31.722492 31.722493-31.722492h64.218603c17.662265 0 31.980365-14.3181 31.980366-31.980366 0-17.662265-14.3181-31.980365-31.980366-31.980365H127.937834c-35.322483 0-63.956637 28.634154-63.956637 63.956637v511.693008c0 35.322483 28.634154 63.956637 63.956637 63.956638h703.5752c35.322483 0 63.956637-28.634154 63.956638-63.956638V383.819642c0-35.32146-28.634154-63.956637-63.956638-63.956637z',
|
||||
}),
|
||||
h('path', {
|
||||
fill: NODE_COLOR,
|
||||
d: 'M310.382073 521.036817c-12.388145-12.388145-32.473599-12.388145-44.862767 0l-0.364297 0.364297c-12.388145 12.388145-12.388145 32.473599 0 44.862767l190.186573 190.186574c5.818519 6.813173 14.465456 11.137665 24.126491 11.137664h0.515746c9.662057 0 18.307971-4.324492 24.12649-11.137664L694.296883 566.263881c12.388145-12.388145 12.388145-32.473599 0-44.862767l-0.364297-0.364297c-12.388145-12.388145-32.473599-12.388145-44.862767 0L511.706311 658.400325V95.743598c0-17.520025-14.202467-31.722492-31.722492-31.722492h-0.515746c-17.520025 0-31.722492 14.202467-31.722493 31.722492v562.656727L310.382073 521.036817z',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getShape() {
|
||||
const { model } = this.props
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
x,
|
||||
y,
|
||||
fillOpacity,
|
||||
strokeOpacity,
|
||||
points,
|
||||
} = model
|
||||
const style = model.getNodeStyle()
|
||||
const transform = `matrix(1 0 0 1 ${x - width / 2} ${y - height / 2})`
|
||||
const pointsPath = points
|
||||
.map((point) => point.join(','))
|
||||
.join(' ')
|
||||
return h(
|
||||
'g',
|
||||
{
|
||||
transform,
|
||||
},
|
||||
[
|
||||
h('polygon', {
|
||||
...style,
|
||||
points: pointsPath,
|
||||
strokeOpacity,
|
||||
fillOpacity,
|
||||
}),
|
||||
this.getIconShape(),
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Model extends PolygonNodeModel {
|
||||
constructor(data, graphModel) {
|
||||
data.text = {
|
||||
value: (data.text && data.text.value) || '',
|
||||
x: data.x,
|
||||
y: data.y + 50,
|
||||
}
|
||||
super(data, graphModel)
|
||||
const lenght = 25
|
||||
this.points = [
|
||||
[lenght, 0],
|
||||
[lenght * 2, lenght],
|
||||
[lenght, lenght * 2],
|
||||
[0, lenght],
|
||||
]
|
||||
this.stroke = NODE_COLOR
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
view: Node,
|
||||
model: Model,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
export default function registerEnd(lf) {
|
||||
lf.register('end', ({ CircleNode, CircleNodeModel, h }) => {
|
||||
class EndNode extends CircleNode {
|
||||
getIconShape() {
|
||||
const { x, y, width, height } = this.props.model
|
||||
const stroke = '#404040'
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
x: x - width / 4,
|
||||
y: y - height / 4,
|
||||
width: width / 2,
|
||||
height: width / 2,
|
||||
viewBox: '0 0 1024 1024',
|
||||
},
|
||||
h('path', {
|
||||
fill: stroke,
|
||||
d: 'M212.992 526.336 212.992 526.336 212.992 526.336 215.04 526.336 212.992 526.336Z',
|
||||
}),
|
||||
// 圆形外框隐藏
|
||||
// h(
|
||||
// 'path',
|
||||
// {
|
||||
// fill: stroke,
|
||||
// d: 'M817.152 202.752 817.152 202.752C737.28 122.88 628.736 75.776 509.952 75.776c-118.784 0-229.376 49.152-307.2 126.976l0 0c-77.824 77.824-126.976 186.368-126.976 307.2 0 118.784 49.152 229.376 126.976 307.2 77.824 79.872 188.416 126.976 307.2 126.976 120.832 0 229.376-49.152 307.2-126.976 79.872-77.824 126.976-186.368 126.976-307.2C946.176 389.12 897.024 280.576 817.152 202.752zM770.048 770.048c-65.536 65.536-157.696 108.544-260.096 108.544-102.4 0-194.56-40.96-260.096-108.544C184.32 704.512 141.312 612.352 141.312 509.952s40.96-194.56 108.544-260.096C317.44 184.32 409.6 141.312 509.952 141.312c100.352 0 192.512 40.96 258.048 106.496l2.048 2.048c65.536 65.536 108.544 157.696 108.544 260.096S837.632 704.512 770.048 770.048z'
|
||||
// }
|
||||
// ),
|
||||
h('path', {
|
||||
fill: stroke,
|
||||
d: 'M724.992 296.96 724.992 296.96 296.96 296.96 296.96 724.992 724.992 724.992 724.992 296.96Z',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getShape() {
|
||||
const { model } = this.props
|
||||
const { x, y } = model
|
||||
const style = model.getNodeStyle()
|
||||
return h('g', {}, [
|
||||
h('circle', {
|
||||
...style,
|
||||
cx: x,
|
||||
cy: y,
|
||||
}),
|
||||
this.getIconShape(),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
class EndModel extends CircleNodeModel {
|
||||
constructor(data, graphModel) {
|
||||
data.text = {
|
||||
value: (data.text && data.text.value) || '',
|
||||
x: data.x,
|
||||
y: data.y + 35,
|
||||
}
|
||||
super(data, graphModel)
|
||||
}
|
||||
|
||||
getConnectedSourceRules() {
|
||||
const rules = super.getConnectedSourceRules()
|
||||
const notAsTarget = {
|
||||
message: '终止节点不能作为连线的起点',
|
||||
validate: () => false,
|
||||
}
|
||||
rules.push(notAsTarget)
|
||||
return rules
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
view: EndNode,
|
||||
model: EndModel,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export default function registerPolyline(lf) {
|
||||
lf.register('polyline', ({ PolylineEdge, PolylineEdgeModel }) => {
|
||||
class ConnnectionModel extends PolylineEdgeModel {
|
||||
constructor(data, graphModel) {
|
||||
super(data, graphModel)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
view: PolylineEdge,
|
||||
model: ConnnectionModel,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
export default function registerPush(lf, clickPlus, mouseDownPlus) {
|
||||
lf.register('push', ({ PolygonNode, PolygonNodeModel, h }) => {
|
||||
class Node extends PolygonNode {
|
||||
getIconShape() {
|
||||
const { model } = this.props
|
||||
const { stroke } = model
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
x: 18,
|
||||
y: 18,
|
||||
width: 30,
|
||||
height: 30,
|
||||
viewBox: '0 0 1024 1024',
|
||||
},
|
||||
h('path', {
|
||||
fill: stroke,
|
||||
d: 'M866.461538 39.384615H393.846154c-43.323077 0-78.769231 35.446154-78.769231 78.769231v1.969231c0 13.784615 7.876923 27.569231 19.692308 35.446154 5.907692 3.938462 80.738462 78.769231 80.738461 78.769231 5.907692 5.907692 15.753846 0 15.753846-7.876924 0-15.753846 13.784615-31.507692 29.538462-31.507692h334.769231c15.753846 0 31.507692 15.753846 31.507692 31.507692v531.692308c0 15.753846-15.753846 27.569231-31.507692 27.569231h-334.769231c-15.753846 0-27.569231-11.815385-27.569231-27.569231v-1.969231c0-7.876923-9.846154-11.815385-15.753846-5.907692 0 0-74.830769 74.830769-82.707692 78.769231-11.815385 7.876923-19.692308 19.692308-19.692308 35.446154v39.384615c0 43.323077 33.476923 78.769231 76.8 78.769231h472.615385c43.323077 0 80.738462-35.446154 80.738461-78.769231V118.153846c0-43.323077-35.446154-78.769231-78.769231-78.769231zM630.153846 945.230769c-33.476923 0-59.076923-25.6-59.076923-59.076923s25.6-59.076923 59.076923-59.076923 59.076923 25.6 59.076923 59.076923-25.6 59.076923-59.076923 59.076923z m-86.646154-474.584615L297.353846 224.492308c-11.815385-11.815385-29.538462-11.815385-41.353846 0l-41.353846 41.353846c-11.815385 11.815385-11.815385 29.538462 0 41.353846l90.584615 90.584615c11.815385 11.815385 3.938462 33.476923-13.784615 33.476923H29.538462c-15.753846 1.969231-29.538462 15.753846-29.538462 31.507693v59.076923c0 15.753846 13.784615 29.538462 29.538462 29.538461h259.938461c17.723077 0 25.6 21.661538 13.784615 33.476923l-90.584615 90.584616c-11.815385 11.815385-11.815385 29.538462 0 41.353846l41.353846 41.353846c11.815385 11.815385 29.538462 11.815385 41.353846 0L543.507692 512c9.846154-9.846154 9.846154-29.538462 0-41.353846z',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getPlusShape() {
|
||||
const { model } = this.props
|
||||
// 判断当前节点是否子节点
|
||||
const graphData = lf.getGraphData()
|
||||
const edges = graphData.edges
|
||||
const hasChildNode = edges.some(
|
||||
(_) => _.sourceNodeId === model.id
|
||||
)
|
||||
if (hasChildNode) {
|
||||
return false
|
||||
}
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
x: 70,
|
||||
y: 20,
|
||||
width: 30,
|
||||
height: 30,
|
||||
viewBox: '0 0 1024 1024',
|
||||
class: 'time-plus',
|
||||
onClick: (e) => clickPlus(e, model),
|
||||
onMousedown: (e) => mouseDownPlus(e, model),
|
||||
onMouseUp: (e) => mouseDownPlus(e, model),
|
||||
},
|
||||
h('path', {
|
||||
fill: '#f17611',
|
||||
d: 'M512 512m-448 0a448 448 0 1 0 896 0 448 448 0 1 0-896 0Z',
|
||||
}),
|
||||
h('path', {
|
||||
fill: '#ffffff',
|
||||
d: 'M448 298.666667h128v426.666666h-128z',
|
||||
}),
|
||||
h('path', {
|
||||
fill: '#ffffff',
|
||||
d: 'M298.666667 448h426.666666v128H298.666667z',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getShape() {
|
||||
const { model } = this.props
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
x,
|
||||
y,
|
||||
fillOpacity,
|
||||
strokeOpacity,
|
||||
points,
|
||||
} = model
|
||||
const style = model.getNodeStyle()
|
||||
const transform = `matrix(1 0 0 1 ${x - width / 2} ${y - height / 2})`
|
||||
const pointsPath = points
|
||||
.map((point) => point.join(','))
|
||||
.join(' ')
|
||||
return h(
|
||||
'g',
|
||||
{
|
||||
transform,
|
||||
},
|
||||
[
|
||||
h('polygon', {
|
||||
...style,
|
||||
points: pointsPath,
|
||||
strokeOpacity,
|
||||
fillOpacity,
|
||||
}),
|
||||
this.getIconShape(),
|
||||
this.getPlusShape(),
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Model extends PolygonNodeModel {
|
||||
constructor(data, graphModel) {
|
||||
data.text = {
|
||||
value: (data.text && data.text.value) || '',
|
||||
x: data.x,
|
||||
y: data.y + 50,
|
||||
}
|
||||
super(data, graphModel)
|
||||
const lenght = 35
|
||||
this.points = [
|
||||
[lenght, 0],
|
||||
[lenght * 2, lenght],
|
||||
[lenght, lenght * 2],
|
||||
[0, lenght],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
view: Node,
|
||||
model: Model,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
export default function registerStart(lf) {
|
||||
lf.register('start', ({ CircleNode, CircleNodeModel, h }) => {
|
||||
class StartNode extends CircleNode {
|
||||
getLabelShape() {
|
||||
const { x, y } = this.props.model
|
||||
return h(
|
||||
'text',
|
||||
{
|
||||
fill: '#000000',
|
||||
fontSize: 12,
|
||||
x: x - 13,
|
||||
y: y + 4,
|
||||
width: 50,
|
||||
height: 25,
|
||||
},
|
||||
'Start'
|
||||
)
|
||||
}
|
||||
|
||||
getShape() {
|
||||
const { model } = this.props
|
||||
const { x, y } = model
|
||||
const style = model.getNodeStyle()
|
||||
return h('g', {}, [
|
||||
h('circle', {
|
||||
...style,
|
||||
cx: x,
|
||||
cy: y,
|
||||
}),
|
||||
this.getLabelShape(),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
class StartModel extends CircleNodeModel {
|
||||
constructor(data, graphModel) {
|
||||
data.text = {
|
||||
value: (data.text && data.text.value) || '',
|
||||
x: data.x,
|
||||
y: data.y + 35,
|
||||
dragable: false,
|
||||
editable: true,
|
||||
}
|
||||
super(data, graphModel)
|
||||
}
|
||||
|
||||
getConnectedTargetRules() {
|
||||
const rules = super.getConnectedTargetRules()
|
||||
const notAsTarget = {
|
||||
message: '起始节点不能作为连线的终点',
|
||||
validate: () => false,
|
||||
}
|
||||
rules.push(notAsTarget)
|
||||
return rules
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
view: StartNode,
|
||||
model: StartModel,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
export default function registerUser(lf) {
|
||||
lf.register('user', ({ PolygonNode, PolygonNodeModel, h }) => {
|
||||
class Node extends PolygonNode {
|
||||
getIconShape() {
|
||||
const { stroke } = this.props.model
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
x: 20,
|
||||
y: 18,
|
||||
width: 30,
|
||||
height: 30,
|
||||
viewBox: '0 0 1126 1024',
|
||||
},
|
||||
h('path', {
|
||||
fill: stroke,
|
||||
d: 'M792.576 379.392a25.6 25.6 0 0 0 25.2928 25.8048h283.2384A25.6 25.6 0 0 0 1126.4 379.392a25.6 25.6 0 0 0-25.2928-25.8048h-283.2384a25.6 25.6 0 0 0-25.344 25.8048z m303.9232 80.7424H761.856c-16.5376 0-29.9008 11.6224-29.9008 25.7536 0 14.1824 13.312 25.7536 29.9008 25.7536h334.6432c16.4864 0 29.9008-11.5712 29.9008-25.7536 0-14.1312-13.4144-25.7536-29.9008-25.7536z m4.608 106.496h-283.2384a25.6 25.6 0 0 0-25.344 25.7536 25.6 25.6 0 0 0 25.344 25.7536h283.2384A25.6 25.6 0 0 0 1126.4 592.384a25.6 25.6 0 0 0-25.2928-25.8048zM543.0272 1024H341.6576C150.8352 1024 0 1024 0 923.648v-20.1216c0-188.16 153.2928-341.1968 341.7088-341.1968h201.2672c188.416 0 341.76 153.0368 341.76 341.1968v20.0704C884.6848 1024 726.3232 1024 542.976 1024z m-203.1616-405.1456c-158.464 0-287.4368 128.4096-287.4368 286.208v20.48c0 40.9088 166.0928 40.9088 287.4368 40.9088h204.9536c100.4544 0 287.4368 0 287.4368-40.96v-20.3776c0-157.8496-128.9728-286.208-287.4368-286.208H339.8656z m92.416-76.7488a271.4112 271.4112 0 0 1-271.2064-271.0528A271.36 271.36 0 0 1 432.2816 0a271.36 271.36 0 0 1 271.2064 271.0528 271.4624 271.4624 0 0 1-271.2064 271.0528z m-215.3472-271.872c0 118.1696 96.6144 214.3232 215.3472 214.3232 118.784 0 215.3984-96.1536 215.3984-214.3232 0-118.2208-96.6144-214.3232-215.3984-214.3232S216.9344 152.0128 216.9344 270.2336z',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getShape() {
|
||||
const { model } = this.props
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
x,
|
||||
y,
|
||||
fillOpacity,
|
||||
strokeOpacity,
|
||||
points,
|
||||
} = model
|
||||
const style = model.getNodeStyle()
|
||||
const transform = `matrix(1 0 0 1 ${x - width / 2} ${y - height / 2})`
|
||||
const pointsPath = points
|
||||
.map((point) => point.join(','))
|
||||
.join(' ')
|
||||
return h(
|
||||
'g',
|
||||
{
|
||||
transform,
|
||||
},
|
||||
[
|
||||
h('polygon', {
|
||||
...style,
|
||||
points: pointsPath,
|
||||
strokeOpacity,
|
||||
fillOpacity,
|
||||
}),
|
||||
this.getIconShape(),
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Model extends PolygonNodeModel {
|
||||
constructor(data, graphModel) {
|
||||
data.text = {
|
||||
value: (data.text && data.text.value) || '',
|
||||
x: data.x,
|
||||
y: data.y + 50,
|
||||
}
|
||||
super(data, graphModel)
|
||||
const lenght = 35
|
||||
this.points = [
|
||||
[lenght, 0],
|
||||
[lenght * 2, lenght],
|
||||
[lenght, lenght * 2],
|
||||
[0, lenght],
|
||||
]
|
||||
// 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
|
||||
this.menu = [
|
||||
{
|
||||
text: '删除',
|
||||
className: 'lf-menu-delete',
|
||||
icon: true,
|
||||
callback(node) {
|
||||
const comfirm = window.confirm('你确定要删除吗?')
|
||||
comfirm && lf.deleteNode(node.id)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '编辑',
|
||||
className: 'lf-menu-item',
|
||||
callback(node) {
|
||||
lf.editText(node.id)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '复制',
|
||||
className: 'lf-menu-item',
|
||||
callback(node) {
|
||||
lf.cloneNode(node.id)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
view: Node,
|
||||
model: Model,
|
||||
}
|
||||
})
|
||||
}
|
||||
303
front-end/src/views/other/workflow/index.vue
Normal file
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<div class="workflow-container">
|
||||
<!-- 辅助工具栏 -->
|
||||
<control v-if="lf" class="vab-control" :lf="lf" @cat-data="catData" />
|
||||
<!-- 节点面板 -->
|
||||
<node-panel :lf="lf" />
|
||||
<!-- 画布 -->
|
||||
<div id="container" ref="container"></div>
|
||||
<!-- 用户节点自定义操作面板 -->
|
||||
<add-panel
|
||||
v-if="showAddPanel"
|
||||
class="add-panel"
|
||||
:lf="lf"
|
||||
:node-data="addClickNode"
|
||||
:style="addPanelStyle"
|
||||
@add-node-finish="hideAddPanel"
|
||||
/>
|
||||
<!-- 属性面板 -->
|
||||
<el-drawer
|
||||
v-model="dialogVisible"
|
||||
:before-close="closeDialog"
|
||||
direction="rtl"
|
||||
size="400px"
|
||||
title="设置节点属性"
|
||||
>
|
||||
<property-dialog
|
||||
v-if="dialogVisible"
|
||||
:lf="lf"
|
||||
:node-data="clickNode"
|
||||
@set-properties-finish="closeDialog"
|
||||
/>
|
||||
</el-drawer>
|
||||
<!-- 数据查看面板 -->
|
||||
<el-dialog v-model="dataVisible" title="数据" width="50%">
|
||||
<data-dialog :graph-data="graphData" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/workflow'
|
||||
import LogicFlow from '@logicflow/core'
|
||||
import '@logicflow/core/lib/style/index.css'
|
||||
import { Menu, Snapshot } from '@logicflow/extension'
|
||||
import '@logicflow/extension/lib/style/index.css'
|
||||
import NodePanel from './components/lFComponents/NodePanel'
|
||||
import AddPanel from './components/lFComponents/AddPanel'
|
||||
import Control from './components/lFComponents/Control'
|
||||
import PropertyDialog from './components/propertySetting/PropertyDialog'
|
||||
import DataDialog from './components/lFComponents/DataDialog'
|
||||
import {
|
||||
registerDownload,
|
||||
registerEnd,
|
||||
registerPolyline,
|
||||
registerPush,
|
||||
registerStart,
|
||||
registerUser,
|
||||
} from './components/registerNode'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Workflow',
|
||||
components: {
|
||||
NodePanel,
|
||||
AddPanel,
|
||||
Control,
|
||||
PropertyDialog,
|
||||
DataDialog,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
lf: null,
|
||||
showAddPanel: false,
|
||||
addPanelStyle: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
},
|
||||
addClickNode: null,
|
||||
clickNode: null,
|
||||
dialogVisible: false,
|
||||
graphData: null,
|
||||
dataVisible: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
const { data } = await getList()
|
||||
this.data = data
|
||||
this.$_initLf()
|
||||
},
|
||||
$_initLf() {
|
||||
const _this = this
|
||||
// 画布配置
|
||||
const config = {
|
||||
container: this.$refs.container,
|
||||
background: {
|
||||
backgroundColor: '#f7f9ff',
|
||||
},
|
||||
grid: {
|
||||
size: 10,
|
||||
visible: false,
|
||||
},
|
||||
keyboard: {
|
||||
enabled: true,
|
||||
},
|
||||
edgeTextDraggable: true,
|
||||
guards: {
|
||||
beforeClone(data) {
|
||||
console.log('beforeClone', data)
|
||||
return true
|
||||
},
|
||||
beforeDelete(data) {
|
||||
// 可以根据data数据判断是否允许删除,允许返回true,不允许返回false
|
||||
// 文档: http://logic-flow.org/guide/basic/keyboard.html#%E5%A6%82%E4%BD%95%E9%98%BB%E6%AD%A2%E5%88%A0%E9%99%A4%E6%88%96%E8%80%85%E6%8B%B7%E8%B4%9D%E8%A1%8C%E4%B8%BA
|
||||
console.log('beforeDelete', data)
|
||||
// _this.$message('不允许删除', 'error')
|
||||
return true
|
||||
},
|
||||
},
|
||||
}
|
||||
// 使用插件
|
||||
LogicFlow.use(Menu)
|
||||
LogicFlow.use(Snapshot)
|
||||
// 渲染画布
|
||||
this.lf = new LogicFlow({ ...config })
|
||||
|
||||
// 菜单配置文档:http://logic-flow.org/guide/extension/extension-components.html#%E8%8F%9C%E5%8D%95
|
||||
// 重置,增加,节点自由配置(以user节点为示例)
|
||||
this.lf.setMenuConfig({
|
||||
nodeMenu: [],
|
||||
edgeMenu: [],
|
||||
})
|
||||
this.lf.addMenuConfig({
|
||||
nodeMenu: [
|
||||
{
|
||||
text: '分享',
|
||||
callback() {
|
||||
_this.$baseAlert('分享成功!')
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '属性',
|
||||
callback(node) {
|
||||
_this.$baseAlert(`
|
||||
节点id:${node.id}
|
||||
节点类型:${node.type}
|
||||
节点坐标:(x: ${node.x}, y: ${node.y})`)
|
||||
},
|
||||
},
|
||||
],
|
||||
edgeMenu: [
|
||||
{
|
||||
text: '属性',
|
||||
callback(edge) {
|
||||
_this.$baseAlert(`
|
||||
边id:${edge.id}
|
||||
边类型:${edge.type}
|
||||
边坐标:(x: ${edge.x}, y: ${edge.y})
|
||||
源节点id:${edge.sourceNodeId}
|
||||
目标节点id:${edge.targetNodeId}`)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
// 设置主题
|
||||
this.lf.setTheme({
|
||||
circle: {
|
||||
r: 20,
|
||||
stroke: '#000000',
|
||||
outlineColor: '#88f',
|
||||
strokeWidth: 1,
|
||||
},
|
||||
rect: {
|
||||
outlineColor: '#88f',
|
||||
strokeWidth: 1,
|
||||
},
|
||||
polygon: {
|
||||
strokeWidth: 1,
|
||||
},
|
||||
polyline: {
|
||||
stroke: '#000000',
|
||||
hoverStroke: '#000000',
|
||||
selectedStroke: '#000000',
|
||||
outlineColor: '#88f',
|
||||
strokeWidth: 1,
|
||||
},
|
||||
nodeText: {
|
||||
color: '#000000',
|
||||
},
|
||||
edgeText: {
|
||||
color: '#000000',
|
||||
background: {
|
||||
fill: '#f7f9ff',
|
||||
},
|
||||
},
|
||||
})
|
||||
this.registerNode()
|
||||
},
|
||||
// 自定义
|
||||
registerNode() {
|
||||
registerStart(this.lf)
|
||||
registerUser(this.lf)
|
||||
registerEnd(this.lf)
|
||||
registerPush(this.lf, this.clickPlus, this.mouseDownPlus)
|
||||
registerDownload(this.lf)
|
||||
registerPolyline(this.lf)
|
||||
this.render()
|
||||
},
|
||||
render() {
|
||||
this.lf.render(this.data)
|
||||
this.event()
|
||||
},
|
||||
getData() {
|
||||
const data = this.lf.getGraphData()
|
||||
console.log(JSON.stringify(data))
|
||||
},
|
||||
event() {
|
||||
this.lf.on('node:click', ({ data }) => {
|
||||
console.log('node:click', data)
|
||||
this.clickNode = data
|
||||
this.dialogVisible = true
|
||||
})
|
||||
this.lf.on('edge:click', ({ data }) => {
|
||||
console.log('edge:click', data)
|
||||
this.clickNode = data
|
||||
this.dialogVisible = true
|
||||
})
|
||||
this.lf.on('element:click', () => {
|
||||
this.hideAddPanel()
|
||||
})
|
||||
this.lf.on('blank:click', () => {
|
||||
this.hideAddPanel()
|
||||
})
|
||||
this.lf.on('connection:not-allowed', (data) => {
|
||||
this.$message({
|
||||
type: 'error',
|
||||
message: data.msg,
|
||||
})
|
||||
})
|
||||
this.lf.on('node:mousemove', () => {
|
||||
console.log('on mousemove')
|
||||
})
|
||||
},
|
||||
clickPlus(e, attributes) {
|
||||
e.stopPropagation()
|
||||
console.log('clickPlus', e, attributes)
|
||||
const { clientX, clientY } = e
|
||||
console.log(clientX, clientY)
|
||||
this.addPanelStyle.top = `${clientY - 40}px`
|
||||
this.addPanelStyle.left = `${clientX}px`
|
||||
this.showAddPanel = true
|
||||
this.addClickNode = attributes
|
||||
},
|
||||
mouseDownPlus(e, attributes) {
|
||||
e.stopPropagation()
|
||||
console.log('mouseDownPlus', e, attributes)
|
||||
},
|
||||
hideAddPanel() {
|
||||
this.showAddPanel = false
|
||||
this.addPanelStyle.top = 0
|
||||
this.addPanelStyle.left = 0
|
||||
this.addClickNode = null
|
||||
},
|
||||
closeDialog() {
|
||||
this.dialogVisible = false
|
||||
},
|
||||
catData() {
|
||||
this.graphData = this.lf.getGraphData()
|
||||
this.dataVisible = true
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.workflow-container {
|
||||
position: relative;
|
||||
|
||||
.vab-control {
|
||||
position: absolute;
|
||||
top: $base-margin * 2;
|
||||
left: $base-margin * 2;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#container {
|
||||
height: calc(#{$base-keep-alive-height} - #{$base-padding} * 2);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.time-plus {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.add-panel {
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
</style>
|
||||