initial commit

This commit is contained in:
陈文彬
2020-09-28 20:19:10 +08:00
commit 2f6253cfb6
436 changed files with 26843 additions and 0 deletions

0
src/views/biz/.gitkeep Normal file
View File

View File

@@ -0,0 +1,496 @@
@import (reference) '../../../design/index.less';
.house-wrap {
position: relative;
width: 600px;
height: 600px;
transform: scale(0.5);
.house {
position: absolute;
position: relative;
top: 50%;
left: 50%;
display: flex;
width: 400px;
height: 300px;
transform: translateX(-50%) translateY(-13%);
justify-content: center;
perspective: 200px;
}
.floor {
position: absolute;
bottom: 0;
display: flex;
width: 95%;
height: 30px;
background-color: #e1f6fd;
border: 4px solid #314b70;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
box-shadow: inset 4px 4px 0 #fffdff;
justify-content: center;
}
.floor::before,
.floor::after {
position: absolute;
bottom: 0;
width: 32%;
height: 60%;
background-image: linear-gradient(to bottom, #e0f5fc 50%, #aac4d0 50%);
border-top: 4px solid #314b70;
border-right: 4px solid #314b70;
border-left: 4px solid #314b70;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
content: '';
box-shadow: 4px 0 0 #aac4d0;
}
.floor::after {
top: 0;
width: 25%;
height: 40%;
border-top: none;
border-top-right-radius: 0;
border-top-left-radius: 0;
}
.wall {
position: absolute;
bottom: 30px;
display: flex;
width: 91%;
height: 175px;
overflow: hidden;
background: #c3e0e7;
border-right: 4px solid #314b70;
border-left: 4px solid #314b70;
justify-content: space-between;
align-items: flex-end;
}
.window {
position: relative;
width: 34%;
height: 125px;
background: #aac4d0;
border-top: 4px solid #314b70;
border-right: 4px solid #314b70;
border-bottom: none;
border-left: none;
border-top-right-radius: 8px;
box-shadow: inset 0 4px 2px #e0f5fc;
}
.window::before {
position: absolute;
top: 6%;
left: 0;
width: 94%;
height: 88%;
background-image: linear-gradient(to top, #f3f6fa 47%, #9ab2d3 47%, #9ab2d3 50%, #f3f6fa 50%);
border-top: 4px solid #314b70;
border-right: 4px solid #314b70;
border-bottom: 4px solid #314b70;
border-left: none;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
content: '';
}
.window::after {
position: absolute;
top: 19%;
left: 20%;
width: 30px;
height: 40px;
background-color: #f9aabe;
border: 4px solid #9ab2d3;
content: '';
}
.window:nth-of-type(3) {
border-top: none;
border-right: 4px solid #314b70;
border-bottom: 4px solid #314b70;
border-left: none;
border-top-right-radius: 0;
border-bottom-right-radius: 8px;
transform: rotateZ(180deg);
box-shadow: inset 0 -4px 2px #e0f5fc;
}
.window:nth-of-type(3)::after {
content: none;
}
.door {
display: flex;
width: 20%;
height: 130px;
padding-left: 8px;
background-color: #ffc26b;
border: 4px solid #314b70;
border-bottom: none;
border-top-right-radius: 10px;
border-top-left-radius: 10px;
box-shadow: inset 3px 3px #ffe0ad, inset -10px -8px #ffad61, 4px 0 #aac4d0;
flex-direction: column;
justify-content: space-evenly;
align-items: flex-start;
}
.door__square {
width: 85%;
height: 47px;
border: 4px solid #314b70;
border-radius: 4px;
box-shadow: inset 3px 3px #ffe0ad;
}
.door__line {
width: 25%;
height: 4px;
background: #314b70;
border-radius: 4px;
}
.top {
position: absolute;
width: 82%;
height: 30px;
background-color: #aac4d0;
border: 4px solid #314b70;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
box-shadow: inset 4px 4px 0 #e1f6fd;
}
.circle {
position: absolute;
top: -10%;
display: flex;
width: 115px;
height: 115px;
background-color: #e0f5fc;
border: 4px solid #314b70;
border-radius: 50%;
content: '';
box-shadow: inset 4px 4px 0 #fffdff, inset 4px -4px 0 #fffdff, inset -4px 4px 0 #fffdff,
inset -4px -4px 0 #fffdff;
justify-content: center;
align-items: center;
}
.circle::before,
.circle::after {
position: absolute;
top: 35%;
width: 70%;
height: 4px;
background-color: #314b70;
content: '';
}
.circle::after {
top: 20%;
width: 35%;
}
.plastic {
position: absolute;
top: 30%;
z-index: 100;
width: 100%;
height: 30px;
overflow: hidden;
}
.plastic__g {
display: flex;
justify-content: center;
width: 100%;
overflow: hidden;
transform: translateY(-22px);
}
.plastic__item {
width: 43px;
height: 43px;
margin-bottom: 4px;
border: 3px solid #314b70;
border-radius: 50%;
box-shadow: 0 4px 0 #aac4d0;
}
.plastic__item:nth-child(odd) {
background: #0792d9;
box-shadow: 0 4px 0 #aac4d0, inset 4px 4px 0 #66c8fa;
}
.plastic__item:nth-child(even) {
background: #fffdff;
}
.plastic__item:nth-of-type(1),
.plastic__item:nth-last-of-type(1) {
width: 45px;
height: 45px;
box-shadow: none;
box-shadow: inset 4px 4px 0 #66c8fa;
}
.plastic__item:nth-of-type(5) {
width: 45px;
height: 45px;
}
.line {
position: absolute;
top: 15px;
display: flex;
width: 90%;
height: 85px;
background-color: #e1f6fd;
border-right: 4px solid #314b70;
border-bottom: 4px solid #314b70;
border-left: 4px solid #314b70;
border-radius: 4px;
transform: rotateX(25deg);
transform-style: preserve-3d;
}
.line__item {
height: 100%;
flex-grow: 1;
border-right: 4px solid #314b70;
}
.line__item:nth-child(odd) {
background: #00affa;
box-shadow: inset 4px 4px 0 #66c8fa;
}
.line__item:nth-child(even) {
background: #fffdff;
}
.line__item:nth-last-of-type(1) {
border-right: none;
}
.line__item:nth-child(4),
.line__item:nth-child(5),
.line__item:nth-child(6) {
border-top: 6px solid #314b70;
}
.tree {
position: absolute;
bottom: 19%;
left: 10%;
display: flex;
width: 100px;
height: 165px;
background-color: #00d398;
border: 4px solid #314b70;
border-radius: 50px;
box-shadow: inset 4px 0 0 #77e4c6, inset -4px 0 0 #00a073;
animation: tree 1s linear alternate infinite;
justify-content: center;
transform-origin: 0% 100%;
}
.tree__item {
position: absolute;
bottom: -80px;
width: 4px;
height: 140px;
background: #314b70;
}
.tree__item:nth-of-type(2) {
bottom: 80px;
height: 40px;
border-radius: 20px;
box-shadow: 0 0 0 8px #77e4c6;
}
.tree__item:nth-of-type(2)::before {
position: absolute;
bottom: -45px;
left: -30px;
width: 20px;
height: 35px;
background-color: #77e4c6;
border-radius: 15px;
content: '';
}
.tree__item:nth-of-type(3) {
bottom: 20px;
left: 36%;
width: 4px;
height: 30px;
background-color: #314b70;
transform: rotateZ(-45deg);
}
.dot {
position: absolute;
bottom: 38px;
width: 100%;
height: 4px;
background-image: linear-gradient(
to right,
#314b70 10%,
transparent 10%,
transparent 11%,
#314b70 11%,
#314b70 85%,
transparent 85%,
transparent 86%,
#314b70 86%
);
}
.bush__item {
position: absolute;
bottom: 40px;
left: 18%;
width: 80px;
height: 60px;
background-color: #00d398;
border: 1px solid red;
border: 4px solid #314b70;
border-bottom: none;
border-top-right-radius: 100px;
border-top-left-radius: 50px;
box-shadow: inset 4px 0 0 #77e4c6, inset -4px 0 0 #00a073;
animation: bush 2s alternate infinite;
transform-origin: bottom center;
}
.bush__item:nth-of-type(2) {
left: 13%;
width: 50px;
height: 40px;
border-top-right-radius: 10px;
border-top-left-radius: 50px;
animation: tree 2s alternate reverse infinite 0.5s;
}
.bush__item::before {
position: absolute;
top: 10px;
left: 10px;
width: 20px;
height: 20px;
background: #77e4c6;
border-radius: 50%;
content: '';
}
.cloud {
position: absolute;
top: 200px;
left: 60px;
display: flex;
justify-content: center;
width: 85px;
height: 20px;
border-bottom: 4px solid #e1e8f2;
animation: cloud 4s infinite alternate;
}
.cloud:nth-of-type(2) {
top: 150px;
left: 50%;
animation: cloud 4s infinite reverse alternate 0.5s;
}
.cloud:nth-of-type(3) {
top: 250px;
left: 80%;
animation: cloud 4s ease infinite alternate 0.75s;
}
.cloud__item {
position: relative;
border-top: 20px solid #e1e8f2;
border-right: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 20px solid #e1e8f2;
border-radius: 50%;
transform: rotateZ(45deg);
}
.cloud__item:nth-of-type(2) {
margin-top: 5px;
margin-left: -7px;
border-top: 15px solid #e1e8f2;
border-right: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #e1e8f2;
}
.bird {
position: absolute;
right: 10%;
bottom: 40%;
z-index: -1;
width: 20px;
height: 20px;
border-top: 4px solid #becde2;
border-left: 4px solid #becde2;
transform: rotateZ(-135deg);
animation: bird 1s ease alternate infinite;
}
.bird:nth-of-type(2) {
right: 20%;
bottom: 30%;
width: 15px;
height: 15px;
}
@keyframes bird {
0% {
transform: scaleY(0.7) rotateZ(-135deg) translateX(0) translateY(0) skew(-10deg, -10deg);
}
100% {
transform: scaleY(1) rotateZ(-135deg) translateX(50%) translateY(50%) skew(-10deg, -10deg);
}
}
@keyframes tree {
0% {
transform: scaleY(1);
}
100% {
transform: scaleY(0.975);
}
}
@keyframes bush {
0% {
transform: skewX(-2deg);
}
100% {
transform: skewX(5deg);
}
}
@keyframes cloud {
0% {
transform: translateX(-10%);
}
100% {
transform: translateX(20%);
}
}
}

View File

@@ -0,0 +1,89 @@
<template>
<div class="house-wrap">
<div class="house">
<div class="floor"></div>
<div class="wall">
<div class="window"></div>
<div class="door">
<div class="door__square"></div>
<div class="door__line"></div>
<div class="door__square"></div>
</div>
<div class="window"></div>
</div>
<div class="top"></div>
<div class="circle"></div>
<div class="plastic">
<div class="plastic__g">
<div class="plastic__item" />
<div class="plastic__item" />
<div class="plastic__item" />
<div class="plastic__item" />
<div class="plastic__item" />
<div class="plastic__item" />
<div class="plastic__item" />
<div class="plastic__item" />
</div>
</div>
<div class="line">
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
<div class="line__item" />
</div>
</div>
<div class="clouds">
<div class="cloud">
<div class="cloud__item"></div>
<div class="cloud__item"></div>
</div>
<div class="cloud">
<div class="cloud__item" />
<div class="cloud__item" />
</div>
<div class="cloud">
<div class="cloud__item" />
<div class="cloud__item" />
</div>
<div class="bird" />
</div>
<div class="birds">
<div class="bird" />
<div class="bird" />
</div>
<div class="tree">
<div class="tree__item" />
<div class="tree__item" />
<div class="tree__item" />
</div>
<div class="bush">
<div class="bush__item" />
<div class="bush__item" />
</div>
<div class="dot" />
</div>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'House',
props: {
size: {
type: Number as PropType<number>,
default: 600,
},
},
setup() {
return {};
},
});
</script>
<style lang="less" scoped>
@import './index.less';
</style>

View File

@@ -0,0 +1,16 @@
<template>
<div class="h-full w-full flex justify-center items-center">
<House />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import House from '../house/index.vue';
export default defineComponent({
name: 'Welcome',
components: { House },
setup() {
return {};
},
});
</script>

0
src/views/demo/.gitkeep Normal file
View File

View File

@@ -0,0 +1,89 @@
<template>
<div class="p-4">
<Alert
message="温馨提醒"
description="基础组件依赖于 ant-design-vue,组件库已有的基础组件,项目中不会再次进行demo展示二次封装组件除外"
type="info"
show-icon
/>
<Alert message="按钮扩展" type="info" show-icon class="mt-4" />
<div class="my-2">
<h3>success</h3>
<a-button color="success">成功</a-button>
<a-button color="success" class="ml-2" disabled> 禁用 </a-button>
<a-button color="success" class="ml-2" loading> loading </a-button>
<a-button color="success" type="link" class="ml-2"> link </a-button>
<a-button color="success" type="link" class="ml-2" loading> loading link </a-button>
<a-button color="success" type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>warning</h3>
<a-button color="warning">警告</a-button>
<a-button color="warning" class="ml-2" disabled> 禁用 </a-button>
<a-button color="warning" class="ml-2" loading> loading </a-button>
<a-button color="warning" type="link" class="ml-2"> link </a-button>
<a-button color="warning" type="link" class="ml-2" loading> loading link </a-button>
<a-button color="warning" type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>error</h3>
<a-button color="error">错误</a-button>
<a-button color="error" class="ml-2" disabled> 禁用 </a-button>
<a-button color="error" class="ml-2" loading> loading </a-button>
<a-button color="error" type="link" class="ml-2"> link </a-button>
<a-button color="error" type="link" class="ml-2" loading> loading link </a-button>
<a-button color="error" type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>ghost</h3>
<a-button ghost>幽灵</a-button>
<a-button ghost class="ml-2" disabled> 禁用 </a-button>
<a-button ghost class="ml-2" loading> loading </a-button>
<a-button ghost type="link" class="ml-2"> link </a-button>
<a-button ghost type="link" class="ml-2" loading> loading link </a-button>
<a-button ghost type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>primary</h3>
<a-button type="primary">主按钮</a-button>
<a-button type="primary" class="ml-2" disabled> 禁用 </a-button>
<a-button type="primary" class="ml-2" loading> loading </a-button>
<a-button type="link" class="ml-2"> link </a-button>
<a-button type="link" class="ml-2" loading> loading link </a-button>
<a-button type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>default</h3>
<a-button type="default">默认</a-button>
<a-button type="default" class="ml-2" disabled> 禁用 </a-button>
<a-button type="default" class="ml-2" loading> loading </a-button>
<a-button type="link" class="ml-2"> link </a-button>
<a-button type="link" class="ml-2" loading> loading link </a-button>
<a-button type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>dashed</h3>
<a-button type="dashed">dashed</a-button>
<a-button type="dashed" class="ml-2" disabled> 禁用 </a-button>
<a-button type="dashed" class="ml-2" loading> loading </a-button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
export default defineComponent({
components: { Alert },
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,32 @@
<template>
<div class="px-64">
<Alert message="点内外部触发事件" show-icon class="mt-4"></Alert>
<ClickOutSide @clickOutside="handleClickOutside" class="flex justify-center mt-10">
<div
@click="innerClick"
class="bg-primary w-full h-64 flex justify-center items-center text-2xl text-white rounded-lg shadow-lg"
>
{{ text }}
</div>
</ClickOutSide>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { Alert } from 'ant-design-vue';
import ClickOutSide from '/@/components/ClickOutSide/index.vue';
export default defineComponent({
components: { ClickOutSide, Alert },
setup() {
const text = ref('Click');
function handleClickOutside() {
text.value = 'Click Out Side';
}
function innerClick() {
text.value = 'Click Inner';
}
return { innerClick, handleClickOutside, text };
},
});
</script>

View File

@@ -0,0 +1,83 @@
<template>
<div class="p-4">
<Description
title="基础示例"
:collapseOptions="{ canExpand: true, helpMessage: 'help me' }"
:column="3"
:data="mockData"
:schema="schema"
/>
<Description
class="mt-4"
title="垂直示例"
layout="vertical"
:collapseOptions="{ canExpand: true, helpMessage: 'help me' }"
:column="2"
:data="mockData"
:schema="schema"
/>
<Description @register="register" class="mt-4" />
<Description @register="register1" class="mt-4" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import { Description, DescItem, useDescription } from '/@/components/Description/index';
const mockData: any = {
username: 'test',
nickName: 'VB',
age: 25,
phone: '15695909xxx',
email: '190848757@qq.com',
addr: '厦门市思明区',
sex: '男',
certy: '3504256199xxxxxxxxx',
tag: 'orange',
};
const schema: DescItem[] = [
{
field: 'username',
label: '用户名',
},
{
field: 'nickName',
label: '昵称',
render: (curVal, data) => {
return `${data.username}-${curVal}`;
},
},
{
field: 'phone',
label: '联系电话',
},
{
field: 'email',
label: '邮箱',
},
{
field: 'addr',
label: '地址',
},
];
export default defineComponent({
components: { Description, Alert },
setup() {
const [register] = useDescription({
title: 'useDescription',
data: mockData,
schema: schema,
});
const [register1] = useDescription({
title: '无边框',
bordered: false,
data: mockData,
schema: schema,
});
return { mockData, schema, register, register1 };
},
});
</script>

View File

@@ -0,0 +1,13 @@
<template>
<BasicDrawer v-bind="$attrs" title="Drawer Title" width="50%"> Drawer Info. </BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,17 @@
<template>
<BasicDrawer v-bind="$attrs" @register="register" title="Drawer Title" width="50%">
Drawer Info.
<a-button type="primary" @click="closeDrawer">内部关闭drawer</a-button>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
const [register, { closeDrawer }] = useDrawerInner();
return { register, closeDrawer };
},
});
</script>

View File

@@ -0,0 +1,15 @@
<template>
<BasicDrawer v-bind="$attrs" title="Modal Title" width="50%" showFooter>
<p class="h-20" v-for="index in 20" :key="index">根据屏幕高度自适应</p>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,16 @@
<template>
<BasicDrawer v-bind="$attrs" @register="register" title="Drawer Title" width="50%">
<p class="h-20">外部传递数据: {{ receiveDrawerDataRef }}</p>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
const [register, { receiveDrawerDataRef }] = useDrawerInner();
return { register, receiveDrawerDataRef };
},
});
</script>

View File

@@ -0,0 +1,15 @@
<template>
<BasicDrawer v-bind="$attrs" :drawerType="DrawerType.DETAIL" title="Drawer Title5">
<p class="h-20">Content Message</p>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer, DrawerType } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
return { DrawerType };
},
});
</script>

View File

@@ -0,0 +1,73 @@
<template>
<div class="px-10">
<Alert message="使用 useDrawer 进行抽屉操作" show-icon class="my-4" />
<a-button type="primary" class="mr-2" @click="openDrawerLoading">打开Drawer</a-button>
<Alert message="内外同时同时显示隐藏" show-icon class="my-4" />
<a-button type="primary" class="mr-2" @click="openDrawer2">打开Drawer</a-button>
<Alert message="自适应高度/显示footer" show-icon class="my-4" />
<a-button type="primary" class="mr-2" @click="openDrawer3">打开Drawer</a-button>
<Alert
message="内外数据交互,外部通过 transferModalData 发送,内部通过 receiveDrawerDataRef 接收。该数据具有响应式"
show-icon
class="my-4"
/>
<a-button type="primary" class="mr-2" @click="send">打开Drawer并传递数据</a-button>
<Alert message="详情页模式" show-icon class="my-4" />
<a-button type="primary" class="mr-2" @click="openDrawer5">打开详情Drawer</a-button>
<Drawer1 @register="register1" />
<Drawer2 @register="register2" />
<Drawer3 @register="register3" />
<Drawer4 @register="register4" />
<Drawer5 @register="register5" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import { useDrawer } from '/@/components/Drawer';
import Drawer1 from './Drawer1.vue';
import Drawer2 from './Drawer2.vue';
import Drawer3 from './Drawer3.vue';
import Drawer4 from './Drawer4.vue';
import Drawer5 from './Drawer5.vue';
export default defineComponent({
components: { Alert, Drawer1, Drawer2, Drawer3, Drawer4, Drawer5 },
setup() {
const [register1, { openDrawer: openDrawer1, setDrawerProps }] = useDrawer();
const [register2, { openDrawer: openDrawer2 }] = useDrawer();
const [register3, { openDrawer: openDrawer3 }] = useDrawer();
const [register4, { openDrawer: openDrawer4, transferDrawerData }] = useDrawer();
const [register5, { openDrawer: openDrawer5 }] = useDrawer();
function send() {
transferDrawerData({
data: 'content',
info: 'Info',
});
openDrawer4(true);
}
function openDrawerLoading() {
openDrawer1();
setDrawerProps({ loading: true });
setTimeout(() => {
setDrawerProps({ loading: false });
}, 2000);
}
return {
register1,
openDrawer1,
register2,
openDrawer2,
register3,
openDrawer3,
register4,
register5,
openDrawer5,
send,
openDrawerLoading,
};
},
});
</script>

View File

@@ -0,0 +1,70 @@
<template>
<div class="m-4">
<CollapseContainer title="Antv Icon使用 (直接按需引入相应组件即可)">
<div class="flex justify-around">
<GithubFilled :style="{ fontSize: '30px' }" />
<QqCircleFilled :style="{ fontSize: '30px' }" />
<WechatFilled :style="{ fontSize: '30px' }" />
<AlipayCircleFilled :style="{ fontSize: '30px' }" />
<IeCircleFilled :style="{ fontSize: '30px' }" />
<TaobaoCircleFilled :style="{ fontSize: '30px' }" />
<CodepenCircleFilled :style="{ fontSize: '30px' }" />
</div>
</CollapseContainer>
<CollapseContainer title="IconIfy 组件使用" class="mt-5">
<div class="flex justify-around flex-wrap">
<Icon icon="fa-solid:address-book" :size="30" />
<Icon icon="mdi-light:bank" :size="30" />
<Icon icon="jam:alien-f" :size="30" />
<Icon icon="jam:android" :size="30" />
</div>
</CollapseContainer>
<Alert
show-icon
class="mt-5"
message="推荐使用Iconify组件"
description="Icon组件基本包含所有的图标,在下面网址内你可以查询到你想要的任何图标。并且打包只会打包所用到的图标。唯一不足的可能就是需要连接外网进行使用。"
/>
<a-button type="link" @click="toIconify">Iconify 图标大全</a-button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { Alert } from 'ant-design-vue';
import {
QqCircleFilled,
GithubFilled,
WechatFilled,
AlipayCircleFilled,
IeCircleFilled,
TaobaoCircleFilled,
CodepenCircleFilled,
} from '@ant-design/icons-vue';
import Icon from '/@/components/Icon/index';
export default defineComponent({
components: {
CollapseContainer,
GithubFilled,
QqCircleFilled,
WechatFilled,
AlipayCircleFilled,
IeCircleFilled,
TaobaoCircleFilled,
CodepenCircleFilled,
Icon,
Alert,
},
setup() {
return {
toIconify: () => {
window.open('https://iconify.design/', '__blank');
},
};
},
});
</script>

View File

@@ -0,0 +1,15 @@
<template>
<BasicModal v-bind="$attrs" title="Modal Title" :helpMessage="['提示1', '提示2']">
Modal Info.
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,29 @@
<template>
<BasicModal
v-bind="$attrs"
@register="register"
title="Modal Title"
:helpMessage="['提示1', '提示2']"
>
<a-button type="primary" @click="closeModal" class="mr-2">从内部关闭弹窗</a-button>
<a-button type="primary" @click="setModalProps">从内部修改title</a-button>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
const [register, { closeModal, setModalProps }] = useModalInner();
return {
register,
closeModal,
setModalProps: () => {
setModalProps({ title: 'Modal New Title' });
},
};
},
});
</script>

View File

@@ -0,0 +1,15 @@
<template>
<BasicModal v-bind="$attrs" title="Modal Title" :helpMessage="['提示1', '提示2']" width="700px">
<p class="h-20" v-for="index in 20" :key="index">根据屏幕高度自适应</p>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,16 @@
<template>
<BasicModal v-bind="$attrs" @register="register" title="Modal Title">
<p class="h-20">外部传递数据: {{ receiveModalDataRef }}</p>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
const [register, { receiveModalDataRef }] = useModalInner();
return { register, receiveModalDataRef };
},
});
</script>

View File

@@ -0,0 +1,74 @@
<template>
<div class="px-10">
<Alert
message="使用 useModal 进行弹窗操作,默认可以拖动,可以通过 draggable
参数进行控制是否可以拖动/全屏"
show-icon
class="my-4"
/>
<a-button type="primary" class="mr-2" @click="openModalLoading"
>打开弹窗 默认可以拖动/全屏</a-button
>
<Alert message="内外同时同时显示隐藏" show-icon class="my-4" />
<a-button type="primary" class="mr-2" @click="openModal2">打开弹窗</a-button>
<Alert message="自适应高度" show-icon class="my-4" />
<a-button type="primary" class="mr-2" @click="openModal3">打开弹窗</a-button>
<Alert
message="内外数据交互,外部通过 transferModalData 发送,内部通过 receiveDrawerDataRef 接收。该数据具有响应式"
show-icon
class="my-4"
/>
<a-button type="primary" class="mr-2" @click="send">打开弹窗并传递数据</a-button>
<Modal1 @register="register1" />
<Modal2 @register="register2" />
<Modal3 @register="register3" />
<Modal4 @register="register4" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import { useModal } from '/@/components/Modal';
import Modal1 from './Modal1.vue';
import Modal2 from './Modal2.vue';
import Modal3 from './Modal3.vue';
import Modal4 from './Modal4.vue';
export default defineComponent({
components: { Alert, Modal1, Modal2, Modal3, Modal4 },
setup() {
const [register1, { openModal: openModal1, setModalProps }] = useModal();
const [register2, { openModal: openModal2 }] = useModal();
const [register3, { openModal: openModal3 }] = useModal();
const [register4, { openModal: openModal4, transferModalData }] = useModal();
function send() {
transferModalData({
data: 'content',
info: 'Info',
});
openModal4(true);
}
function openModalLoading() {
openModal1();
setModalProps({ loading: true });
setTimeout(() => {
setModalProps({ loading: false });
}, 2000);
}
return {
register1,
openModal1,
register2,
openModal2,
register3,
openModal3,
register4,
openModal4,
send,
openModalLoading,
};
},
});
</script>

View File

@@ -0,0 +1,87 @@
<template>
<div class="p-4 flex flex-wrap">
<CollapseContainer title="基础示例" :canExpan="true" class="text-center mb-6 qrcode-demo-item">
<QrCode :value="qrCodeUrl" />
</CollapseContainer>
<CollapseContainer title="渲染成img标签示例" class="text-center mb-6 qrcode-demo-item">
<QrCode :value="qrCodeUrl" tag="img" />
</CollapseContainer>
<CollapseContainer title="配置样式示例" class="text-center mb-6 qrcode-demo-item">
<QrCode
:value="qrCodeUrl"
:options="{
color: { dark: '#55D187' },
}"
/>
</CollapseContainer>
<CollapseContainer title="本地logo示例" class="text-center mb-6 qrcode-demo-item">
<QrCode :value="qrCodeUrl" :logo="LogoImg" />
</CollapseContainer>
<CollapseContainer title="在线logo示例" class="text-center mb-6 qrcode-demo-item">
<QrCode
:value="qrCodeUrl"
logo="https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png"
:options="{
color: { dark: '#55D187' },
}"
/>
</CollapseContainer>
<CollapseContainer title="logo配置示例" class="text-center mb-6 qrcode-demo-item">
<QrCode
:value="qrCodeUrl"
:logo="{
src: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png',
logoSize: 0.2,
borderSize: 0.05,
borderRadius: 50,
bgColor: 'blue',
}"
/>
</CollapseContainer>
<CollapseContainer title="下载示例" class="text-center qrcode-demo-item">
<QrCode :value="qrCodeUrl" ref="qrRef" :logo="LogoImg" />
<a-button class="mb-2" type="primary" @click="download"> 下载 </a-button>
<div class="msg">(在线logo会导致图片跨域需要下载图片需要自行解决跨域问题)</div>
</CollapseContainer>
<CollapseContainer title="配置大小示例" class="text-center qrcode-demo-item">
<QrCode :value="qrCodeUrl" :width="300" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import { QrCode, QrCodeActionType } from '/@/components/Qrcode/index';
import LogoImg from '/@/assets/images/logo.png';
import { CollapseContainer } from '/@/components/Container/index';
const qrCodeUrl = 'https://www.vvbin.cn';
export default defineComponent({
components: { CollapseContainer, QrCode },
setup() {
const qrRef = ref<Nullable<QrCodeActionType>>(null);
function download() {
const qrEl = unref(qrRef);
if (!qrEl) return;
qrEl.download('文件名');
}
return {
qrCodeUrl,
LogoImg,
download,
qrRef,
};
},
});
</script>
<style scoped>
.qrcode-demo-item {
width: 30%;
margin-right: 1%;
}
</style>

View File

@@ -0,0 +1,52 @@
<template>
<div class="p-4">
<Alert message="抽取el-scrollbar并对其进行扩展,滚动条美化,适用于各个浏览器" type="info" />
<div class="my-4">
<a-button @click="scrollTo(100)" class="mr-2">滚动到100px位置</a-button>
<a-button @click="scrollTo(800)" class="mr-2">滚动到800px位置</a-button>
<a-button @click="scrollTo(0)" class="mr-2">滚动到顶部</a-button>
<a-button @click="scrollBottom()" class="mr-2">滚动到底部</a-button>
</div>
<div class="w-1/2 h-64 bg-white">
<ScrollContainer class="mt-4" ref="scrollRef">
<ul class="p-3">
<template v-for="index in 100" :key="index">
<li class="leading-8 px-2" :style="{ border: '1px solid #eee' }">{{ index }}</li>
</template>
</ul>
</ScrollContainer>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { ScrollContainer, ScrollActionType } from '/@/components/Container/index';
import { Alert } from 'ant-design-vue';
export default defineComponent({
components: { CollapseContainer, ScrollContainer, Alert },
setup() {
const scrollRef = ref<Nullable<ScrollActionType>>(null);
const getScroll = () => {
const scroll = unref(scrollRef);
if (!scroll) {
throw new Error('scroll is Null');
}
return scroll;
};
function scrollTo(top: number) {
getScroll().scrollTo(top);
}
function scrollBottom() {
getScroll().scrollBottom();
}
return {
scrollTo,
scrollRef,
scrollBottom,
};
},
});
</script>

View File

@@ -0,0 +1,59 @@
<template>
<div class="p-4 virtual-scroll-demo">
<Divider>基础滚动示例</Divider>
<div class="virtual-scroll-demo-wrap">
<VirtualScroll :itemHeight="41" :items="data" :height="300" :width="300">
<template v-slot="{ item }">
<div class="virtual-scroll-demo__item">{{ item.title }}</div>
</template>
</VirtualScroll>
</div>
<Divider>即使不可见也预先加载30条数据防止空白</Divider>
<div class="virtual-scroll-demo-wrap">
<VirtualScroll :itemHeight="41" :items="data" :height="300" :width="300" :bench="30">
<template v-slot="{ item }">
<div class="virtual-scroll-demo__item">{{ item.title }}</div>
</template>
</VirtualScroll>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { VirtualScroll } from '/@/components/VirtualScroll/index';
import { Divider } from 'ant-design-vue';
const data: any[] = (() => {
const arr: any[] = [];
for (let index = 1; index < 20000; index++) {
arr.push({
title: '列表项' + index,
});
}
return arr;
})();
export default defineComponent({
components: { VirtualScroll, Divider },
setup() {
return { data: data };
},
});
</script>
<style lang="less" scoped>
.virtual-scroll-demo {
&-wrap {
display: flex;
margin: 0 30%;
background: #fff;
justify-content: center;
}
/deep/ &__item {
height: 40px;
padding: 0 20px;
line-height: 40px;
border-bottom: 1px solid #ddd;
}
}
</style>

View File

@@ -0,0 +1,26 @@
<template>
<div class="p-4">
<Alert message="抽取el-scrollbar并对其进行扩展,滚动条美化,适用于各个浏览器" type="info" />
<div class="w-1/2 h-64 bg-white">
<ScrollContainer class="mt-4">
<ul class="p-3">
<template v-for="index in 100" :key="index">
<li class="leading-8 px-2" :style="{ border: '1px solid #eee' }">{{ index }}</li>
</template>
</ul>
</ScrollContainer>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { ScrollContainer } from '/@/components/Container/index';
import { Alert } from 'ant-design-vue';
export default defineComponent({
components: { CollapseContainer, ScrollContainer, Alert },
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,23 @@
<template>
<div class="p-4 flex justify-center">
<div class="w-1/2 bg-white p-10 rounded-md">
<StrengthMeter placeholder="默认" />
<StrengthMeter placeholder="禁用" disabled />
<br />
<StrengthMeter placeholder="隐藏input" :show-input="false" value="!@#qwe12345" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import StrengthMeter from '/@/components/StrengthMeter/index';
export default defineComponent({
components: {
StrengthMeter,
},
setup() {
return {};
},
});
</script>

View File

@@ -0,0 +1,23 @@
<template>
<div class="p-10">
<div class="flex justify-center p-4 items-center bg-gray-700">
<RotateDragVerify src="/@/assets/images/header.jpg" ref="el" @success="handleSuccess" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { RotateDragVerify } from '/@/components/Verify/index';
export default defineComponent({
components: { RotateDragVerify },
setup() {
const handleSuccess = () => {
console.log('success!');
};
return {
handleSuccess,
};
},
});
</script>

View File

@@ -0,0 +1,90 @@
<template>
<div class="p-10">
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el1" @success="handleSuccess" />
<a-button color="primary" class="ml-2" @click="handleBtnClick(el1)">还原</a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el2" @success="handleSuccess" circle />
<a-button color="primary" class="ml-2" @click="handleBtnClick(el2)">还原</a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify
ref="el3"
@success="handleSuccess"
text="拖动以进行校验"
successText="校验成功"
:barStyle="{
background: '#018ffb',
}"
/>
<a-button color="primary" class="ml-2" @click="handleBtnClick(el3)">还原</a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el4" @success="handleSuccess">
<template v-slot:actionIcon="isPassing">
<BugOutlined v-if="isPassing" />
<RightOutlined v-else />
</template>
</BasicDragVerify>
<a-button color="primary" class="ml-2" @click="handleBtnClick(el4)">还原</a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el5" @success="handleSuccess">
<template v-slot:text="isPassing">
<div v-if="isPassing">
<BugOutlined />
成功
</div>
<div v-else>
拖动
<RightOutlined />
</div>
</template>
</BasicDragVerify>
<a-button color="primary" class="ml-2" @click="handleBtnClick(el5)">还原</a-button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { BasicDragVerify, DragVerifyActionType, PassingData } from '/@/components/Verify/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { BugOutlined, RightOutlined } from '@ant-design/icons-vue';
export default defineComponent({
components: { BasicDragVerify, BugOutlined, RightOutlined },
setup() {
const { createMessage } = useMessage();
const el1 = ref<RefInstanceType<DragVerifyActionType>>(null);
const el2 = ref<RefInstanceType<DragVerifyActionType>>(null);
const el3 = ref<RefInstanceType<DragVerifyActionType>>(null);
const el4 = ref<RefInstanceType<DragVerifyActionType>>(null);
const el5 = ref<RefInstanceType<DragVerifyActionType>>(null);
function handleSuccess(data: PassingData) {
const { time } = data;
createMessage.success(`校验成功,耗时${time}`);
}
function handleBtnClick(elRef: RefInstanceType<DragVerifyActionType>) {
if (!elRef) {
return;
}
elRef.$.resume();
}
return {
handleSuccess,
el1,
el2,
el3,
el4,
el5,
handleBtnClick,
};
},
});
</script>

View File

@@ -0,0 +1,42 @@
<template>
<div class="p-4">
<CollapseContainer title="Simple">
<a-button type="primary" @contextmenu="handleContext">Right Click on me</a-button>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useContextMenu } from '/@/hooks/web/useContextMenu';
import { CollapseContainer } from '/@/components/Container';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
components: { CollapseContainer },
setup() {
const [createContextMenu] = useContextMenu();
const { createMessage } = useMessage();
function handleContext(e: MouseEvent) {
createContextMenu({
event: e,
items: [
{
label: 'New',
icon: 'ant-design:plus-outlined',
handler: () => {
createMessage.success('click new');
},
},
{
label: 'Open',
icon: 'ant-design:folder-open-filled',
handler: () => {
createMessage.success('click open');
},
},
],
});
}
return { handleContext };
},
});
</script>

View File

@@ -0,0 +1,38 @@
<template>
<div class="p-4">
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md" title="Copy Example">
<div class="flex justify-center">
<a-input placeholder="请输入" v-model:value="value" />
<a-button type="primary" @click="handleCopy">Copy</a-button>
</div>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, unref, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
components: { CollapseContainer },
setup() {
const valueRef = ref('');
const { createMessage } = useMessage();
const { clipboardRef, copiedRef } = useCopyToClipboard();
function handleCopy() {
const value = unref(valueRef);
if (!value) {
createMessage.warning('请输入要拷贝的内容!');
return;
}
clipboardRef.value = value;
if (unref(copiedRef)) {
createMessage.warning('copy success');
}
}
return { handleCopy, value: valueRef };
},
});
</script>

View File

@@ -0,0 +1,56 @@
<template>
<div class="p-4">
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md" title="Window Full Screen">
<a-button type="primary" @click="enterFullscreen" class="mr-2">
Enter Window Full Screen
</a-button>
<a-button color="success" @click="toggleFullscreen" class="mr-2">
Toggle Window Full Screen
</a-button>
<a-button color="error" @click="exitFullscreen" class="mr-2">
Exit Window Full Screen
</a-button>
Current State: {{ isFullscreenRef }}
</CollapseContainer>
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md mt-5" title="Dom Full Screen">
<a-button type="primary" @click="toggleDom" class="mr-2"> Enter Dom Full Screen </a-button>
</CollapseContainer>
<div
ref="domRef"
class="w-1/2 h-64 flex justify-center rounded-md items-center bg-white mx-auto mt-10"
>
<a-button type="primary" @click="toggleDom" class="mr-2"> Exit Dom Full Screen </a-button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useFullscreen } from '/@/hooks/web/useFullScreen';
export default defineComponent({
components: { CollapseContainer },
setup() {
const domRef = ref<Nullable<HTMLElement>>(null);
const {
enterFullscreen,
toggleFullscreen,
isFullscreenRef,
exitFullscreen,
} = useFullscreen();
const { toggleFullscreen: toggleDom } = useFullscreen(domRef);
return {
enterFullscreen,
toggleDom,
toggleFullscreen,
isFullscreenRef,
exitFullscreen,
domRef,
};
},
});
</script>

View File

@@ -0,0 +1,38 @@
<template>
<div class="p-4">
<Alert message="国际化方式,没有进行全局国际化,有需要可以自行处理。" type="info" />
<Divider />
国际化信息: {{ t('hello') }}
<Divider />
<a-button :type="localeRef === 'zhCN' ? 'primary' : ''" @click="localeRef = 'zhCN'">
中文
</a-button>
<a-button :type="localeRef === 'en' ? 'primary' : ''" @click="localeRef = 'en'">
英文
</a-button>
<Divider />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert, Divider } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({
components: { Alert, Divider },
setup() {
const { t, localeRef } = useI18n({
locale: 'zhCN',
messages: {
en: {
hello: 'hello',
},
zhCN: {
hello: '你好',
},
},
});
return { localeRef, t };
},
});
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div class="p-4">
<Alert message="有预览图" type="info" />
<div class="flex justify-center mt-4">
<img :src="img" v-for="img in imgList" :key="img" class="mr-2" @click="handleClick(img)" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import { createImgPreview } from '/@/components/Preview/index';
const imgList: string[] = [
'https://picsum.photos/id/66/346/216',
'https://picsum.photos/id/67/346/216',
'https://picsum.photos/id/68/346/216',
];
export default defineComponent({
components: { Alert },
setup() {
function handleClick(img: string) {
createImgPreview({ imageList: [img] });
}
return { imgList, handleClick };
},
});
</script>

View File

@@ -0,0 +1,97 @@
<template>
<div class="p-4">
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md" title="Message">
<a-button @click="infoMsg('Info message')" class="mr-2">Info</a-button>
<a-button @click="successMsg('Success message')" class="mr-2" color="success">
Success
</a-button>
<a-button @click="warningMsg('Warning message')" class="mr-2" color="warning">
Warning
</a-button>
<a-button @click="errorMsg('Error message')" class="mr-2" color="error"> Error </a-button>
<a-button @click="handleLoading" class="mr-2" type="primary"> Loading </a-button>
</CollapseContainer>
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md mt-5" title="Comfirm">
<a-button @click="handleConfirm('warning')" color="warning" class="mr-2">Warning</a-button>
<a-button @click="handleConfirm('success')" color="success" class="mr-2">Success</a-button>
<a-button @click="handleConfirm('error')" color="error" class="mr-2">Error</a-button>
</CollapseContainer>
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md mt-5" title="Modal">
<a-button @click="handleInfoModal" class="mr-2">Info</a-button>
<a-button @click="handleSuccessModal" color="success" class="mr-2">Success</a-button>
<a-button @click="handleErrorModal" color="error" class="mr-2">Error</a-button>
<a-button @click="handleWarningModal" color="warning" class="mr-2">Warning</a-button>
</CollapseContainer>
<CollapseContainer
class="px-20 bg-white w-full h-32 rounded-md mt-5"
title="Notification 用法与上面一致"
>
<a-button @click="handleNotify" color="success" class="mr-2">Success</a-button>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
components: { CollapseContainer },
setup() {
const {
createMessage,
createConfirm,
createSuccessModal,
createInfoModal,
createErrorModal,
createWarningModal,
notification,
} = useMessage();
const { info, success, warning, error } = createMessage;
function handleLoading() {
createMessage.loading('Loading...');
}
function handleConfirm(type: 'warning' | 'error' | 'success') {
createConfirm({
iconType: type,
title: 'Tip',
content: 'content message...',
});
}
function handleSuccessModal() {
createSuccessModal({ title: 'Tip', content: 'content message...' });
}
function handleErrorModal() {
createErrorModal({ title: 'Tip', content: 'content message...' });
}
function handleWarningModal() {
createWarningModal({ title: 'Tip', content: 'content message...' });
}
function handleInfoModal() {
createInfoModal({ title: 'Tip', content: 'content message...' });
}
function handleNotify() {
notification.success({
message: 'Tip',
description: 'content message...',
});
}
return {
infoMsg: info,
successMsg: success,
warningMsg: warning,
errorMsg: error,
handleLoading,
handleConfirm,
handleSuccessModal,
handleErrorModal,
handleWarningModal,
handleInfoModal,
handleNotify,
};
},
});
</script>

View File

@@ -0,0 +1,32 @@
<template>
<div class="p-4">
<CollapseContainer
class="px-20 bg-white w-full h-32 rounded-md"
title="在下面输入框输入文本,切换后回来内容会保存"
>
<a-input placeholder="请输入" />
</CollapseContainer>
<CollapseContainer class="px-20 mt-10 bg-white w-full h-32 rounded-md" title="标签页操作">
<a-button class="mr-2" @click="closeAll">关闭所有</a-button>
<a-button class="mr-2" @click="closeLeft">关闭左侧</a-button>
<a-button class="mr-2" @click="closeRight">关闭右侧</a-button>
<a-button class="mr-2" @click="closeOther">关闭其他</a-button>
<a-button class="mr-2" @click="closeCurrent">关闭当前</a-button>
<a-button class="mr-2" @click="refreshPage">刷新当前</a-button>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useTabs } from '/@/hooks/web/useTabs';
export default defineComponent({
name: 'TabsDemo',
components: { CollapseContainer },
setup() {
const { closeAll, closeLeft, closeRight, closeOther, closeCurrent, refreshPage } = useTabs();
return { closeAll, closeLeft, closeRight, closeOther, closeCurrent, refreshPage };
},
});
</script>

View File

@@ -0,0 +1,31 @@
<template>
<div class="p-4">
<CollapseContainer class="px-20 bg-white w-full h-32 rounded-md" title="Global WaterMark">
<a-button type="primary" class="mr-2" @click="setWatermark('WaterMark Info')">
Create
</a-button>
<a-button color="error" class="mr-2" @click="clear">Clear</a-button>
<a-button color="warning" class="mr-2" @click="setWatermark('WaterMark Info New')">
Reset
</a-button>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useWatermark } from '/@/hooks/web/useWatermark';
export default defineComponent({
components: { CollapseContainer },
setup() {
const areaRef = ref<Nullable<HTMLElement>>(null);
const { setWatermark, clear } = useWatermark();
return {
setWatermark,
clear,
areaRef,
};
},
});
</script>

View File

@@ -0,0 +1,178 @@
<template>
<div class="m-4">
<div class="mb-4"> </div>
<CollapseContainer title="基础收缩示例">
<BasicForm @register="register" />
</CollapseContainer>
<CollapseContainer title="超过3行自动收起" class="mt-4">
<BasicForm @register="register1" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
const getSchamas = (): FormSchema[] => {
return [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
componentProps: {
placeholder: '自定义placeholder',
onChange: (e: any) => {
console.log(e);
},
},
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
},
{
field: 'field3',
component: 'DatePicker',
label: '字段3',
colProps: {
span: 8,
},
},
{
field: 'field4',
component: 'Select',
label: '字段4',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
key: '1',
},
{
label: '选项2',
value: '2',
key: '2',
},
],
},
},
{
field: 'field5',
component: 'CheckboxGroup',
label: '字段5',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
// {
// field: 'field7',
// component: 'RadioGroup',
// label: '字段7',
// colProps: {
// span: 8,
// },
// componentProps: {
// options: [
// {
// label: '选项1',
// value: '1',
// },
// {
// label: '选项2',
// value: '2',
// },
// ],
// },
// },
];
};
function getAppendSchemas(): FormSchema[] {
return [
{
field: 'field10',
component: 'Input',
label: '字段10',
colProps: {
span: 8,
},
},
{
field: 'field11',
component: 'Input',
label: '字段11',
colProps: {
span: 8,
},
},
{
field: 'field12',
component: 'Input',
label: '字段12',
colProps: {
span: 8,
},
},
{
field: 'field13',
component: 'Input',
label: '字段13',
colProps: {
span: 8,
},
},
];
}
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const [register] = useForm({
labelWidth: 120,
schemas: getSchamas(),
actionColOptions: {
span: 24,
},
compact: true,
showAdvancedButton: true,
});
const [register1] = useForm({
labelWidth: 120,
schemas: [...getSchamas(), ...getAppendSchemas()],
actionColOptions: {
span: 24,
},
compact: true,
showAdvancedButton: true,
});
return {
register,
register1,
};
},
});
</script>

View File

@@ -0,0 +1,70 @@
<template>
<div class="m-4">
<div class="mb-4"> </div>
<CollapseContainer title="自定义表单">
<BasicForm @register="register" @submit="handleSubmit" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, h } from 'vue';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { Input } from 'ant-design-vue';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
rules: [{ required: true }],
render: ({ model, field }) => {
return h(Input, {
placeholder: '请输入',
value: model[field],
onChange: (e: ChangeEvent) => {
model[field] = e.target.value;
},
});
},
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
rules: [{ required: true }],
renderComponentContent: () => {
return {
suffix: () => 'suffix',
};
},
},
];
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const { createMessage } = useMessage();
const [register, { setProps }] = useForm({
labelWidth: 120,
schemas,
actionColOptions: {
span: 24,
},
});
return {
register,
schemas,
handleSubmit: (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
},
setProps,
};
},
});
</script>

View File

@@ -0,0 +1,183 @@
<template>
<div class="m-4">
<div class="mb-4">
<a-button @click="changeLabel3" class="mr-2">更改字段3label</a-button>
<a-button @click="changeLabel34" class="mr-2">同时更改字段3,4label</a-button>
<a-button @click="appendField" class="mr-2">往字段3后面插入字段10</a-button>
<a-button @click="deleteField" class="mr-2">删除字段11</a-button>
</div>
<div class="mb-4"> </div>
<CollapseContainer title="动态表单示例,动态根据表单内其他值改变">
<BasicForm @register="register" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
show: ({ values }) => {
return !!values.field5;
},
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
ifShow: ({ values }) => {
return !!values.field6;
},
},
{
field: 'field3',
component: 'DatePicker',
label: '字段3',
colProps: {
span: 8,
},
dynamicDisabled: ({ values }) => {
return !!values.field7;
},
},
{
field: 'field4',
component: 'Select',
label: '字段4',
colProps: {
span: 8,
},
dynamicRules: ({ values }) => {
return values.field8 ? [{ required: true, message: '字段4必填' }] : [];
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
key: '1',
},
{
label: '选项2',
value: '2',
key: '2',
},
],
},
},
{
field: 'field11',
component: 'DatePicker',
label: '字段11',
colProps: {
span: 8,
},
},
{
field: 'field5',
component: 'Switch',
label: '是否显示字段1(css控制)',
colProps: {
span: 8,
},
labelWidth: 200,
},
{
field: 'field6',
component: 'Switch',
label: '是否显示字段2(dom控制)',
colProps: {
span: 8,
},
labelWidth: 200,
},
{
field: 'field7',
component: 'Switch',
label: '是否禁用字段3',
colProps: {
span: 8,
},
labelWidth: 200,
},
{
field: 'field8',
component: 'Switch',
label: '字段4是否必填',
colProps: {
span: 8,
},
labelWidth: 200,
},
];
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const [
register,
{ setProps, updateSchema, appendSchemaByField, removeSchemaByFiled },
] = useForm({
labelWidth: 120,
schemas,
actionColOptions: {
span: 24,
},
});
function changeLabel3() {
updateSchema({
field: 'field3',
label: '字段3 New',
});
}
function changeLabel34() {
updateSchema([
{
field: 'field3',
label: '字段3 New++',
},
{
field: 'field4',
label: '字段4 New++',
},
]);
}
function appendField() {
appendSchemaByField(
{
field: 'field10',
label: '字段10',
component: 'Input',
colProps: {
span: 8,
},
},
'field3'
);
}
function deleteField() {
removeSchemaByFiled('field11');
}
return {
register,
schemas,
setProps,
changeLabel3,
changeLabel34,
appendField,
deleteField,
};
},
});
</script>

View File

@@ -0,0 +1,186 @@
<template>
<div class="m-4">
<div class="mb-4">
<a-button @click="setProps({ labelWidth: 150 })" class="mr-2">更改labelWidth</a-button>
<a-button @click="setProps({ labelWidth: 120 })" class="mr-2">还原labelWidth</a-button>
<a-button @click="setProps({ size: 'large' })" class="mr-2">更改Size</a-button>
<a-button @click="setProps({ size: 'default' })" class="mr-2">还原Size</a-button>
<a-button @click="setProps({ disabled: true })" class="mr-2">禁用表单</a-button>
<a-button @click="setProps({ disabled: false })" class="mr-2">解除禁用</a-button>
<a-button @click="setProps({ compact: true })" class="mr-2">紧凑表单</a-button>
<a-button @click="setProps({ compact: false })" class="mr-2">还原正常间距</a-button>
<a-button @click="setProps({ actionColOptions: { span: 8 } })" class="mr-2">
操作按钮位置
</a-button>
</div>
<div class="mb-4">
<a-button @click="setProps({ showActionButtonGroup: false })" class="mr-2">
隐藏操作按钮
</a-button>
<a-button @click="setProps({ showActionButtonGroup: true })" class="mr-2">
显示操作按钮
</a-button>
<a-button @click="setProps({ showResetButton: false })" class="mr-2"> 隐藏重置按钮 </a-button>
<a-button @click="setProps({ showResetButton: true })" class="mr-2"> 显示重置按钮 </a-button>
<a-button @click="setProps({ showSubmitButton: false })" class="mr-2">
隐藏查询按钮
</a-button>
<a-button @click="setProps({ showSubmitButton: true })" class="mr-2"> 显示查询按钮 </a-button>
<a-button
@click="
setProps({
resetButtonOptions: {
disabled: true,
text: '重置New',
},
})
"
class="mr-2"
>
修改重置按钮
</a-button>
<a-button
@click="
setProps({
submitButtonOptions: {
disabled: true,
loading: true,
},
})
"
class="mr-2"
>
修改查询按钮
</a-button>
</div>
<div class="mb-4"> </div>
<CollapseContainer title="使用ref调用表单内部函数示例">
<BasicForm
:schemas="schemas"
ref="formElRef"
@submit="handleSubmit"
:actionColOptions="{ span: 24 }"
/>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { BasicForm, FormSchema, FormActionType, FormProps } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
componentProps: {
placeholder: '自定义placeholder',
onChange: (e: any) => {
console.log(e);
},
},
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
},
{
field: 'field3',
component: 'DatePicker',
label: '字段3',
colProps: {
span: 8,
},
},
{
field: 'field4',
component: 'Select',
label: '字段4',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
key: '1',
},
{
label: '选项2',
value: '2',
key: '2',
},
],
},
},
{
field: 'field5',
component: 'CheckboxGroup',
label: '字段5',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
{
field: 'field7',
component: 'RadioGroup',
label: '字段7',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
];
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const formElRef = ref<Nullable<FormActionType>>(null);
const { createMessage } = useMessage();
return {
formElRef,
schemas,
handleSubmit: (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
},
setProps(props: FormProps) {
const formEl = formElRef.value;
if (!formEl) return;
formEl.setProps(props);
},
};
},
});
</script>

View File

@@ -0,0 +1,163 @@
<template>
<div class="m-4">
<div class="mb-4">
<a-button @click="validateForm" class="mr-2">手动校验表单</a-button>
<a-button @click="resetValidate" class="mr-2">清空校验信息</a-button>
<a-button @click="getFormValues" class="mr-2">获取表单值</a-button>
<a-button @click="setFormValues" class="mr-2">设置表单值</a-button>
</div>
<div class="mb-4"> </div>
<CollapseContainer title="表单校验">
<BasicForm @register="register" @submit="handleSubmit" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
rules: [{ required: true }],
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
rules: [{ required: true }],
},
{
field: 'field3',
component: 'DatePicker',
label: '字段3',
colProps: {
span: 8,
},
rules: [{ required: true }],
},
{
field: 'field4',
component: 'Select',
label: '字段4',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
key: '1',
},
{
label: '选项2',
value: '2',
key: '2',
},
],
},
rules: [{ required: true }],
},
{
field: 'field5',
component: 'CheckboxGroup',
label: '字段5',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
rules: [{ required: true }],
},
{
field: 'field7',
component: 'RadioGroup',
label: '字段7',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
rules: [{ required: true, message: '覆盖默认生成的校验信息' }],
},
];
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const { createMessage } = useMessage();
const [register, { validateFields, clearValidate, getFieldsValue, setFieldsValue }] = useForm(
{
labelWidth: 120,
schemas,
actionColOptions: {
span: 24,
},
}
);
async function validateForm() {
try {
const res = await validateFields();
console.log('passing', res);
} catch (error) {
console.log('not passing', error);
}
}
async function resetValidate() {
clearValidate();
}
function getFormValues() {
const values = getFieldsValue();
createMessage.success('values:' + JSON.stringify(values));
}
function setFormValues() {
setFieldsValue({
field1: '1111',
field5: ['1'],
field7: '1',
});
}
return {
register,
schemas,
handleSubmit: (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
},
getFormValues,
setFormValues,
validateForm,
resetValidate,
};
},
});
</script>

View File

@@ -0,0 +1,183 @@
<template>
<div class="m-4">
<div class="mb-4">
<a-button @click="setProps({ labelWidth: 150 })" class="mr-2">更改labelWidth</a-button>
<a-button @click="setProps({ labelWidth: 120 })" class="mr-2">还原labelWidth</a-button>
<a-button @click="setProps({ size: 'large' })" class="mr-2">更改Size</a-button>
<a-button @click="setProps({ size: 'default' })" class="mr-2">还原Size</a-button>
<a-button @click="setProps({ disabled: true })" class="mr-2">禁用表单</a-button>
<a-button @click="setProps({ disabled: false })" class="mr-2">解除禁用</a-button>
<a-button @click="setProps({ compact: true })" class="mr-2">紧凑表单</a-button>
<a-button @click="setProps({ compact: false })" class="mr-2">还原正常间距</a-button>
<a-button @click="setProps({ actionColOptions: { span: 8 } })" class="mr-2">
操作按钮位置
</a-button>
</div>
<div class="mb-4">
<a-button @click="setProps({ showActionButtonGroup: false })" class="mr-2">
隐藏操作按钮
</a-button>
<a-button @click="setProps({ showActionButtonGroup: true })" class="mr-2">
显示操作按钮
</a-button>
<a-button @click="setProps({ showResetButton: false })" class="mr-2"> 隐藏重置按钮 </a-button>
<a-button @click="setProps({ showResetButton: true })" class="mr-2"> 显示重置按钮 </a-button>
<a-button @click="setProps({ showSubmitButton: false })" class="mr-2">
隐藏查询按钮
</a-button>
<a-button @click="setProps({ showSubmitButton: true })" class="mr-2"> 显示查询按钮 </a-button>
<a-button
@click="
setProps({
resetButtonOptions: {
disabled: true,
text: '重置New',
},
})
"
class="mr-2"
>
修改重置按钮
</a-button>
<a-button
@click="
setProps({
submitButtonOptions: {
disabled: true,
loading: true,
},
})
"
class="mr-2"
>
修改查询按钮
</a-button>
</div>
<div class="mb-4"> </div>
<CollapseContainer title="useForm示例">
<BasicForm @register="register" @submit="handleSubmit" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
componentProps: {
placeholder: '自定义placeholder',
onChange: (e: any) => {
console.log(e);
},
},
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
},
{
field: 'field3',
component: 'DatePicker',
label: '字段3',
colProps: {
span: 8,
},
},
{
field: 'field4',
component: 'Select',
label: '字段4',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
key: '1',
},
{
label: '选项2',
value: '2',
key: '2',
},
],
},
},
{
field: 'field5',
component: 'CheckboxGroup',
label: '字段5',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
{
field: 'field7',
component: 'RadioGroup',
label: '字段7',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
];
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const { createMessage } = useMessage();
const [register, { setProps }] = useForm({
labelWidth: 120,
schemas,
actionColOptions: {
span: 24,
},
});
return {
register,
schemas,
handleSubmit: (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
},
setProps,
};
},
});
</script>

View File

@@ -0,0 +1,126 @@
<template>
<div class="m-4">
<CollapseContainer title="基础示例">
<BasicForm
:labelWidth="100"
:schemas="schemas"
:actionColOptions="{ span: 24 }"
@submit="handleSubmit"
/>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicForm, FormSchema } from '/@/components/Form/index';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 8,
},
defaultValue: '111',
componentProps: {
placeholder: '自定义placeholder',
onChange: (e: any) => {
console.log(e);
},
},
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 8,
},
},
{
field: 'field3',
component: 'DatePicker',
label: '字段3',
colProps: {
span: 8,
},
},
{
field: 'field4',
component: 'Select',
label: '字段4',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
key: '1',
},
{
label: '选项2',
value: '2',
key: '2',
},
],
},
},
{
field: 'field5',
component: 'CheckboxGroup',
label: '字段5',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
{
field: 'field7',
component: 'RadioGroup',
label: '字段7',
colProps: {
span: 8,
},
componentProps: {
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
},
},
];
export default defineComponent({
components: { BasicForm, CollapseContainer },
setup() {
const { createMessage } = useMessage();
return {
schemas,
handleSubmit: (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
},
};
},
});
</script>

View File

@@ -0,0 +1,33 @@
<template>
<div class="mt-2">
当前权限模式
<a-button type="link">
{{ permissionMode === PermissionModeEnum.BACK ? '后台权限模式' : '前端角色权限模式' }}
</a-button>
<a-button class="ml-4" @click="togglePermissionMode" type="primary"> 切换权限模式 </a-button>
<Divider />
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { appStore } from '/@/store/modules/app';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { Divider } from 'ant-design-vue';
import { usePermission } from '/@/hooks/web/usePermission';
export default defineComponent({
name: 'CurrentPermissionMode',
components: { Divider },
setup() {
const permissionMode = computed(() => {
return appStore.getProjectConfig.permissionMode;
});
const { togglePermissionMode } = usePermission();
return {
permissionMode,
PermissionModeEnum,
togglePermissionMode,
};
},
});
</script>

View File

@@ -0,0 +1,85 @@
<template>
<div class="p-10 m-4 rounded-md bg-white">
<Alert message="刷新后会还原" show-icon />
<CurrentPermissionMode />
<p>
当前拥有的code列表: <a> {{ permissionStore.getPermCodeListState }} </a>
</p>
<Divider />
<Alert class="mt-4" type="info" message="点击后请查看按钮变化" show-icon />
<Divider />
<a-button type="primary" class="mr-2" @click="changePermissionCode('2')">
点击切换按钮权限(用户id为2)
</a-button>
<a-button type="primary" @click="changePermissionCode('1')">
点击切换按钮权限(用户id为1,默认)
</a-button>
<Divider>组件方式判断权限</Divider>
<Authority :value="'1000'">
<a-button type="primary" class="mx-4">拥有code ['1000']权限可见</a-button>
</Authority>
<Authority :value="'2000'">
<a-button color="success" class="mx-4">拥有code ['2000']权限可见</a-button>
</Authority>
<Authority :value="['1000', '2000']">
<a-button color="error" class="mx-4">拥有code ['1000','2000']角色权限可见</a-button>
</Authority>
<Divider>函数方式方式判断权限</Divider>
<a-button v-if="hasPermission('1000')" type="primary" class="mx-4">
拥有code ['1000']权限可见
</a-button>
<a-button v-if="hasPermission('2000')" color="success" class="mx-4">
拥有code ['2000']权限可见
</a-button>
<a-button v-if="hasPermission(['1000', '2000'])" color="error" class="mx-4">
拥有code ['1000','2000']角色权限可见
</a-button>
<Divider>指令方式方式判断权限(该方式不能动态修改权限.)</Divider>
<a-button v-auth="'1000'" type="primary" class="mx-4"> 拥有code ['1000']权限可见 </a-button>
<a-button v-auth="'2000'" color="success" class="mx-4"> 拥有code ['2000']权限可见 </a-button>
<a-button v-auth="['1000', '2000']" color="error" class="mx-4">
拥有code ['1000','2000']角色权限可见
</a-button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert, Divider } from 'ant-design-vue';
import CurrentPermissionMode from '../CurrentPermissionMode.vue';
import { usePermission } from '/@/hooks/web/usePermission';
import Authority from '/@/components/Authority';
import { getPermCodeByUserId } from '/@/api/sys/user';
import { permissionStore } from '/@/store/modules/permission';
import { PermissionModeEnum } from '/@/enums/appEnum';
export default defineComponent({
components: { Alert, CurrentPermissionMode, Divider, Authority },
setup() {
const { hasPermission } = usePermission();
// !模拟从后台获取权限编码, 该函数可能只需要执行一次,实际项目可以自行放到合适的时机
async function changePermissionCode(userId: string) {
const codeList = await getPermCodeByUserId({ userId });
permissionStore.commitPermCodeListState(codeList);
}
// 默认初始化为1
changePermissionCode('1');
return {
hasPermission,
permissionStore,
changePermissionCode,
PermissionModeEnum,
};
},
});
</script>

View File

@@ -0,0 +1,37 @@
<template>
<div class="p-10 m-4 rounded-md bg-white">
<Alert
message="目前mock了两组数据 id为1 和 2 具体返回的菜单可以在mock/sys/menu.ts内查看"
show-icon
/>
<CurrentPermissionMode />
<Alert class="mt-4" type="info" message="点击后请查看左侧菜单变化" show-icon />
<div class="mt-4">
权限切换(请先切换权限模式为后台权限模式):
<a-button-group>
<a-button @click="changeMenu('1')"> 获取用户id为1的菜单 </a-button>
<a-button @click="changeMenu('2')"> 获取用户id为2的菜单 </a-button>
</a-button-group>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import CurrentPermissionMode from '../CurrentPermissionMode.vue';
import { RoleEnum } from '/@/enums/roleEnum';
import { usePermission } from '/@/hooks/web/usePermission';
export default defineComponent({
components: { Alert, CurrentPermissionMode },
setup() {
const { changeMenu } = usePermission();
return {
RoleEnum,
changeMenu,
};
},
});
</script>

View File

@@ -0,0 +1,9 @@
<template>
<div class="m-10 bg-primary text-2xl h-64 rounded-lg flex justify-center items-center text-white">
Super 角色可见
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({});
</script>

View File

@@ -0,0 +1,9 @@
<template>
<div class="m-10 bg-primary text-2xl h-64 rounded-lg flex justify-center items-center text-white">
Test 角色可见
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({});
</script>

View File

@@ -0,0 +1,84 @@
<template>
<div class="p-10 m-4 rounded-md bg-white">
<Alert
message="由于刷新的时候会请求用户信息接口,会根据接口重置角色信息,所以刷新后界面会恢复原样,如果不需要,可以注释 src/layout/default/index内的获取用户信息接口"
show-icon
/>
<CurrentPermissionMode />
<p>
当前角色: <a> {{ userStore.getRoleListState }} </a>
</p>
<Alert class="mt-4" type="info" message="点击后请查看按钮变化" show-icon />
<div class="mt-4">
权限切换(请先切换权限模式为前端角色权限模式):
<a-button-group>
<a-button @click="changeRole(RoleEnum.SUPER)" :type="isSuper ? 'primary' : ''">
{{ RoleEnum.SUPER }}
</a-button>
<a-button @click="changeRole(RoleEnum.TEST)" :type="isTest ? 'primary' : ''">
{{ RoleEnum.TEST }}
</a-button>
</a-button-group>
</div>
<Divider>组件方式判断权限(有需要可以自行全局注册)</Divider>
<Authority :value="RoleEnum.SUPER">
<a-button type="primary" class="mx-4">拥有super角色权限可见</a-button>
</Authority>
<Authority :value="RoleEnum.TEST">
<a-button color="success" class="mx-4">拥有test角色权限可见</a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]">
<a-button color="error" class="mx-4">拥有[test,super]角色权限可见</a-button>
</Authority>
<Divider>函数方式方式判断权限(适用于函数内部过滤)</Divider>
<a-button v-if="hasPermission(RoleEnum.SUPER)" type="primary" class="mx-4">
拥有super角色权限可见
</a-button>
<a-button v-if="hasPermission(RoleEnum.TEST)" color="success" class="mx-4">
拥有test角色权限可见
</a-button>
<a-button v-if="hasPermission([RoleEnum.TEST, RoleEnum.SUPER])" color="error" class="mx-4">
拥有[test,super]角色权限可见
</a-button>
<Divider>指令方式方式判断权限(该方式不能动态修改权限.)</Divider>
<a-button v-auth="RoleEnum.SUPER" type="primary" class="mx-4"> 拥有super角色权限可见 </a-button>
<a-button v-auth="RoleEnum.TEST" color="success" class="mx-4"> 拥有test角色权限可见 </a-button>
<a-button v-auth="[RoleEnum.TEST, RoleEnum.SUPER]" color="error" class="mx-4">
拥有[test,super]角色权限可见
</a-button>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { Alert, Divider } from 'ant-design-vue';
import CurrentPermissionMode from '../CurrentPermissionMode.vue';
import { userStore } from '/@/store/modules/user';
import { RoleEnum } from '/@/enums/roleEnum';
import { usePermission } from '/@/hooks/web/usePermission';
import Authority from '/@/components/Authority';
export default defineComponent({
components: { Alert, CurrentPermissionMode, Divider, Authority },
setup() {
const { changeRole, hasPermission } = usePermission();
return {
userStore,
RoleEnum,
isSuper: computed(() => userStore.getRoleListState.includes(RoleEnum.SUPER)),
isTest: computed(() => userStore.getRoleListState.includes(RoleEnum.TEST)),
changeRole,
hasPermission,
};
},
});
</script>

View File

@@ -0,0 +1,48 @@
<template>
<div class="p-10 m-4 rounded-md bg-white">
<Alert
message="由于刷新的时候会请求用户信息接口,会根据接口重置角色信息,所以刷新后界面会恢复原样,如果不需要,可以注释 src/layout/default/index内的获取用户信息接口"
show-icon
/>
<CurrentPermissionMode />
<p>
当前角色: <a> {{ userStore.getRoleListState }} </a>
</p>
<Alert class="mt-4" type="info" message="点击后请查看左侧菜单变化" show-icon />
<div class="mt-4">
权限切换(请先切换权限模式为前端角色权限模式):
<a-button-group>
<a-button @click="changeRole(RoleEnum.SUPER)" :type="isSuper ? 'primary' : ''">
{{ RoleEnum.SUPER }}
</a-button>
<a-button @click="changeRole(RoleEnum.TEST)" :type="isTest ? 'primary' : ''">
{{ RoleEnum.TEST }}
</a-button>
</a-button-group>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import CurrentPermissionMode from '../CurrentPermissionMode.vue';
import { userStore } from '/@/store/modules/user';
import { RoleEnum } from '/@/enums/roleEnum';
import { usePermission } from '/@/hooks/web/usePermission';
export default defineComponent({
components: { Alert, CurrentPermissionMode },
setup() {
const { changeRole } = usePermission();
return {
userStore,
RoleEnum,
isSuper: computed(() => userStore.getRoleListState.includes(RoleEnum.SUPER)),
isTest: computed(() => userStore.getRoleListState.includes(RoleEnum.TEST)),
changeRole,
};
},
});
</script>

View File

@@ -0,0 +1,116 @@
<template>
<div class="p-4">
<div class="mb-4">
<a-button @click="handleLevel(2)" class="mr-2">显示到第2级</a-button>
<a-button @click="handleLevel(1)" class="mr-2">显示到第1级</a-button>
<a-button @click="handleSetCheckData" class="mr-2">设置勾选数据</a-button>
<a-button @click="handleGetCheckData" class="mr-2">获取勾选数据</a-button>
<a-button @click="handleSetSelectData" class="mr-2">设置选中数据</a-button>
<a-button @click="handleGetSelectData" class="mr-2">获取选中数据</a-button>
<a-button @click="handleSetExpandData" class="mr-2">设置展开数据</a-button>
<a-button @click="handleGetExpandData" class="mr-2">获取展开数据</a-button>
</div>
<div class="mb-4">
<a-button @click="appendNodeByKey(null)" class="mr-2">添加根节点</a-button>
<a-button @click="appendNodeByKey('2-2')" class="mr-2">添加在parent3内添加节点</a-button>
<a-button @click="deleteNodeByKey('2-2')" class="mr-2">删除parent3节点</a-button>
<a-button @click="updateNodeByKey('1-1')" class="mr-2">更新parent2节点</a-button>
</div>
<CollapseContainer title="函数操作" class="w-1/3 mr-4" :canExpan="false">
<BasicTree :treeData="treeData" ref="treeRef" :checkable="true" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import { BasicTree, TreeActionType } from '/@/components/Tree/index';
import { treeData } from './data';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
components: { BasicTree, CollapseContainer },
setup() {
const treeRef = ref<RefInstanceType<TreeActionType>>(null);
const { createMessage } = useMessage();
function getTree() {
const tree = unref(treeRef);
if (!tree) {
throw new Error('tree is null!');
}
return tree.$;
}
function handleLevel(level: number) {
getTree().filterByLevel(level);
}
function handleSetCheckData() {
getTree().setCheckedKeys(['0-0']);
}
function handleGetCheckData() {
const keys = getTree().getCheckedKeys();
createMessage.success(JSON.stringify(keys));
}
function handleSetSelectData() {
getTree().setSelectedKeys(['0-0']);
}
function handleGetSelectData() {
const keys = getTree().getSelectedKeys();
createMessage.success(JSON.stringify(keys));
}
function handleSetExpandData() {
getTree().setExpandedKeys(['0-0']);
}
function handleGetExpandData() {
const keys = getTree().getExpandedKeys();
createMessage.success(JSON.stringify(keys));
}
function appendNodeByKey(parentKey: string | null = null) {
getTree().insertNodeByKey({
parentKey: parentKey,
node: {
title: '新增节点',
key: '2-2-2',
},
// 往后插入
push: 'push',
// 往前插入
// push:'unshift'
});
}
function deleteNodeByKey(key: string) {
getTree().deleteNodeByKey(key);
}
function updateNodeByKey(key: string) {
getTree().updateNodeByKey(key, {
title: 'parent2-new',
});
}
return {
treeData,
treeRef,
handleLevel,
handleSetCheckData,
handleGetCheckData,
handleSetSelectData,
handleGetSelectData,
handleSetExpandData,
handleGetExpandData,
appendNodeByKey,
deleteNodeByKey,
updateNodeByKey,
};
},
});
</script>

View File

@@ -0,0 +1,63 @@
<template>
<div class="flex p-4">
<CollapseContainer title="右侧操作按钮" class="w-1/3 mr-4">
<BasicTree :treeData="treeData" :actionList="actionList" />
</CollapseContainer>
<CollapseContainer title="右键菜单" class="w-1/3 mr-4">
<BasicTree :treeData="treeData" :beforeRightClick="getRightMenuList" />
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent, h } from 'vue';
import { BasicTree, ActionItem, ContextMenuItem } from '/@/components/Tree/index';
import { treeData } from './data';
import { CollapseContainer } from '/@/components/Container/index';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
export default defineComponent({
components: { BasicTree, CollapseContainer },
setup() {
function handlePlus(node: any) {
console.log(node);
}
function getRightMenuList(node: any): ContextMenuItem[] {
return [
{
label: '新增',
handler: () => {
console.log('点击了新增', node);
},
icon: 'ant-design:plus-outlined',
},
{
label: '删除',
handler: () => {
console.log('点击了删除', node);
},
icon: 'ant-design:folder-open-filled',
},
];
}
const actionList: ActionItem[] = [
{
render: (node) => {
return h(PlusOutlined, {
class: 'ml-2',
onClick: () => {
handlePlus(node);
},
});
},
},
{
render: () => {
return h(DeleteOutlined);
},
},
];
return { treeData, actionList, getRightMenuList };
},
});
</script>

View File

@@ -0,0 +1,38 @@
import { TreeItem } from '/@/components/Tree/index';
export const treeData: TreeItem[] = [
{
title: 'parent 1',
key: '0-0',
icon: 'home|svg',
children: [
{ title: 'leaf', key: '0-0-0' },
{
title: 'leaf',
key: '0-0-1',
children: [
{ title: 'leaf', key: '0-0-0-0' },
{ title: 'leaf', key: '0-0-0-1' },
],
},
],
},
{
title: 'parent 2',
key: '1-1',
icon: 'home|svg',
children: [
{ title: 'leaf', key: '1-1-0' },
{ title: 'leaf', key: '1-1-1' },
],
},
{
title: 'parent 3',
key: '2-2',
icon: 'home|svg',
children: [
{ title: 'leaf', key: '2-2-0' },
{ title: 'leaf', key: '2-2-1' },
],
},
];

View File

@@ -0,0 +1,33 @@
<template>
<div class="flex p-4">
<CollapseContainer title="基础示例" class="w-1/3 mr-4">
<BasicTree :treeData="treeData" />
</CollapseContainer>
<CollapseContainer title="可勾选" class="w-1/3 mr-4">
<BasicTree :treeData="treeData" :checkable="true" />
</CollapseContainer>
<CollapseContainer title="默认展开/勾选示例" class="w-1/3">
<BasicTree
:treeData="treeData"
:checkable="true"
:expandedKeys="['0-0']"
:checkedKeys="['0-0']"
/>
</CollapseContainer>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTree } from '/@/components/Tree/index';
import { treeData } from './data';
import { CollapseContainer } from '/@/components/Container/index';
export default defineComponent({
components: { BasicTree, CollapseContainer },
setup() {
return { treeData };
},
});
</script>

110
src/views/index.vue Normal file
View File

@@ -0,0 +1,110 @@
<template>
<h1>{{ msg }}</h1>
<a-button @click="test">change </a-button>
<div class="sw">
<Scrollbar ref="a">
<div class="ss">13123</div>
</Scrollbar>
</div>
<a-button @click="test1" type="primary">change</a-button>
<ScrollYTransition>
<div class="box" v-show="show"> 1 </div>
</ScrollYTransition>
<!-- <BasicModal /> -->
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { Scrollbar } from '/@/components/Scrollbar/index';
import { ScrollContainer } from '/@/components/Container/index';
import { defHttp } from '/@/utils/http/axios';
import { useThemeMode } from '/@/useApp';
import { useMessage } from '/@/hooks/web/useMessage';
import {
CollapseTransition,
ExpandXTransition,
ScaleTransition,
ScaleRotateTransition,
ScrollYTransition,
} from '/@/components/Transition';
import { ThemeModeEnum } from '../enums/appEnum';
// import { BasicModal } from '/@/components/modal';
export default defineComponent({
name: 'Home',
components: {
Scrollbar,
CollapseTransition,
ExpandXTransition,
ScaleTransition,
ScaleRotateTransition,
ScrollYTransition,
ScrollContainer,
// BasicModal
},
setup() {
const { createMessage } = useMessage();
createMessage.success({
content: '123',
duration: 999999,
});
// createMessage.error('123');
// createMessage.info('123');
// createMessage.warning('123');
// createConfirm({
// iconType: 'success',
// title: '123',
// content: '123',
// });
const { runChangeThemeMode } = useThemeMode(ThemeModeEnum.DARK);
let msg = ref('hello Home');
const show = ref(true);
function test() {
msg.value = 'hello Home1';
}
defHttp.request({
method: 'post',
url: '/login',
params: {
username: 'vben',
password: '123456',
},
});
const a = ref(null);
function test1() {
runChangeThemeMode();
// show.value = !show.value;
// a.value.scrollTo(200);
}
return {
a,
msg,
test,
show,
test1,
};
},
});
</script>
<style lang="less" scoped>
.sw {
width: 300px;
height: 300px;
border: 1px solid red;
.scrollbar {
height: 100%;
}
.ss {
height: 500px;
}
}
.box {
width: 200px;
height: 200px;
background: #000;
}
</style>

View File

@@ -0,0 +1,30 @@
<script lang="tsx">
import { defineComponent, PropOptions } from 'compatible-vue';
import { BasicModal } from '@/components/modal/index';
import { ErrorInfo } from '@/store/modules/error';
import { Description, useDescription } from '@/components/description/index';
import { getDescSchema } from './data';
export default defineComponent({
name: 'ErrorLogDetailModal',
props: {
info: {
type: Object,
default: null,
} as PropOptions<ErrorInfo>,
},
setup(props, { listeners }) {
const [register] = useDescription({
column: 2,
schema: getDescSchema(),
});
return () => {
return (
<BasicModal width={800} title="错误详情" on={listeners}>
<Description data={props.info} onRegister={register} />
</BasicModal>
);
};
},
});
</script>

View File

@@ -0,0 +1,66 @@
import { Tag } from 'ant-design-vue';
import { BasicColumn } from '@/components/table/index';
import { ErrorTypeEnum } from '@/store/modules/error';
import { DescItem } from '@/components/description/index';
export function getColumns(): BasicColumn[] {
return [
{
dataIndex: 'type',
title: '类型',
width: 80,
customRender: (text: string) => {
const color =
text === ErrorTypeEnum.VUE
? 'green'
: text === ErrorTypeEnum.RESOURCE
? 'cyan'
: text === ErrorTypeEnum.PROMISE
? 'blue'
: ErrorTypeEnum.AJAX
? 'red'
: 'purple';
return <Tag color={color}>{text}</Tag>;
},
},
{
dataIndex: 'url',
title: '地址',
width: 200,
},
{
dataIndex: 'time',
title: '时间',
width: 160,
},
{
dataIndex: 'file',
title: '文件',
width: 200,
},
{
dataIndex: 'name',
title: 'Name',
width: 200,
},
{
dataIndex: 'message',
title: '错误信息',
width: 300,
},
{
dataIndex: 'stack',
title: 'stack信息',
width: 300,
},
];
}
export function getDescSchema(): DescItem[] {
return getColumns().map((column) => {
return {
field: column.dataIndex!,
label: column.title,
};
});
}

View File

@@ -0,0 +1,108 @@
<script lang="tsx">
import { defineComponent, watch, ref, unref } from 'compatible-vue';
import DetailModal from './DetailModal.vue';
import { useModal } from '@/components/modal/index';
import { useDesign } from '@/hooks/core/useDesign';
import { BasicTable, useTable } from '@/components/table/index';
import { errorStore, ErrorInfo } from '@/store/modules/error';
import { fireErrorApi } from '@/api/demo/error';
import { getColumns } from './data';
const { prefixCls } = useDesign('error-handle');
export default defineComponent({
name: 'ErrorHandler',
setup() {
const rowInfoRef = ref<ErrorInfo>();
const imgListRef = ref<string[]>([]);
const [register, { setTableData }] = useTable({
titleHelpMessage: '只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效',
title: '错误日志列表',
columns: getColumns(),
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
customRender: (text: string, recoed: ErrorInfo) => {
return (
<a-button type="link" size="small" onClick={handleDetail.bind(null, recoed)}>
详情
</a-button>
);
},
},
});
const [registerModal, { openModal }] = useModal();
watch(
() => errorStore.getErrorInfoState,
(list: any[]) => {
setTableData(list);
},
{
immediate: true,
}
);
// 查看详情
function handleDetail(row: ErrorInfo) {
rowInfoRef.value = row;
openModal({
visible: true,
});
}
function fireVueError() {
throw new Error('fire vue error!');
}
function fireResourceError() {
imgListRef.value.push(`${new Date().getTime()}.png`);
}
async function fireAjaxError() {
await fireErrorApi();
}
return () => (
<div class={[prefixCls, 'p-4']}>
{unref(imgListRef).map((src) => {
return <img src={src} key={src} class="hidden" />;
})}
<DetailModal info={unref(rowInfoRef)} onRegister={registerModal} />
<BasicTable onRegister={register} class={`${prefixCls}-table`}>
<template slot="toolbar">
<a-button onClick={fireVueError} type="primary">
点击触发vue错误
</a-button>
<a-button onClick={fireResourceError} type="primary">
点击触发resource错误
</a-button>
<a-button onClick={fireAjaxError} type="primary">
点击触发ajax错误
</a-button>
</template>
</BasicTable>
</div>
);
},
});
</script>
<style scoped lang="less">
@import (reference) '~@design';
@prefix-cls: ~'@{namespace}-error-handle';
.@{prefix-cls} {
&-table {
background: #fff;
}
}
</style>

View File

@@ -0,0 +1,125 @@
import type { PropType } from 'vue';
import { Result, Button } from 'ant-design-vue';
import { defineComponent, ref, computed, unref } from 'vue';
import { ExceptionEnum } from '/@/enums/exceptionEnum';
import netWorkImg from '/@/assets/images/exception/net-work.png';
import error404 from '/@/assets/images/exception/404.png';
import error500 from '/@/assets/images/exception/500.png';
import notDataImg from '/@/assets/images/no-data.png';
import { useRoute } from 'vue-router';
import { useGo, useRedo } from '/@/hooks/web/usePage';
import { PageEnum } from '/@/enums/pageEnum';
interface MapValue {
title: string;
subTitle: string;
btnText?: string;
icon?: string;
handler?: Fn;
}
export default defineComponent({
name: 'ErrorPage',
props: {
// 状态码
status: {
type: Number as PropType<number>,
default: ExceptionEnum.PAGE_NOT_FOUND,
},
title: {
type: String as PropType<string>,
},
subTitle: {
type: String as PropType<string>,
},
full: {
type: Boolean as PropType<boolean>,
default: false,
},
},
setup(props) {
const statusMapRef = ref(new Map<string | number, MapValue>());
const { query } = useRoute();
const go = useGo();
const redo = useRedo();
const getStatus = computed(() => {
const { status: routeStatus } = query;
const { status } = props;
return Number(routeStatus) || status;
});
const getMapValue = computed(
(): MapValue => {
return unref(statusMapRef).get(unref(getStatus)) as MapValue;
}
);
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, {
title: '404',
subTitle: '抱歉,您访问的页面不存在!',
btnText: props.full ? '返回登录' : '返回首页',
handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
icon: error404,
});
unref(statusMapRef).set(ExceptionEnum.ERROR, {
title: '500',
subTitle: '抱歉,服务器出现异常!',
btnText: '返回首页',
handler: () => go(),
icon: error500,
});
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
title: '当前页面无数据',
subTitle: '',
btnText: '刷新',
handler: () => redo(),
icon: notDataImg,
});
unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, {
title: '网络错误',
subTitle: '抱歉,您的网络连接已断开,请检查您的网络!',
btnText: '刷新',
handler: () => redo(),
icon: netWorkImg,
});
unref(statusMapRef).set(ExceptionEnum.PAGE_TIMEOUT, {
title: '页面加载失败',
subTitle: '抱歉,您的页面加载出错或者过久未响应,请检查您的网络!',
btnText: '刷新',
handler: () => redo(),
icon: netWorkImg,
});
return () => {
const { title, subTitle, btnText, icon, handler } = unref(getMapValue) || {};
return (
<Result
class="flex items-center flex-col"
title={props.title || title}
sub-title={props.subTitle || subTitle}
>
{{
extra: () =>
btnText && (
<Button type="primary" onClick={handler}>
{() => btnText}
</Button>
),
icon: () => icon && <img src={icon} />,
}}
</Result>
);
};
},
});

View File

@@ -0,0 +1,17 @@
<template>
<Exception :status="ExceptionEnum.PAGE_TIMEOUT" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Exception } from '/@/views/sys/exception';
import { ExceptionEnum } from '/@/enums/exceptionEnum';
export default defineComponent({
name: 'LoadTimeout',
components: { Exception },
setup() {
return { ExceptionEnum };
},
});
</script>

View File

@@ -0,0 +1,2 @@
export { default as Exception } from './Exception';
export { default as LoadTimeOut } from './LoadTimeOut.vue';

View File

@@ -0,0 +1,9 @@
<template>
<div />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'FrameBlank',
});
</script>

View File

@@ -0,0 +1,114 @@
<template>
<div class="iframe-page" :style="getWrapStyle">
<Spin :spinning="loading" size="large" :style="getWrapStyle">
<div class="iframe-page__mask" v-show="menuStore.getDragStartState" />
<iframe :src="frameSrc" class="iframe-page__main" ref="frameRef" />
</Spin>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, unref, onMounted, nextTick, computed } from 'vue';
import { Spin } from 'ant-design-vue';
import { getViewportOffset } from '/@/utils/domUtils';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
import { menuStore } from '/@/store/modules/menu';
export default defineComponent({
name: 'IFrame',
components: { Spin },
props: {
frameSrc: {
type: String as PropType<string>,
},
},
setup() {
const loadingRef = ref(false);
const topRef = ref(50);
const heightRef = ref(window.innerHeight);
const frameRef = ref<HTMLElement | null>(null);
function calcHeight() {
const iframe = unref(frameRef);
if (!iframe) {
return;
}
let { top } = getViewportOffset(iframe);
top += 20;
topRef.value = top;
heightRef.value = window.innerHeight - top;
const clientHeight = document.documentElement.clientHeight - top;
iframe.style.height = `${clientHeight}px`;
}
useWindowSizeFn(calcHeight, 150, { immediate: true });
function hideLoading() {
loadingRef.value = false;
calcHeight();
}
function init() {
nextTick(() => {
const iframe = unref(frameRef);
if (!iframe) {
return;
}
if ((iframe as any).attachEvent) {
(iframe as any).attachEvent('onload', () => {
hideLoading();
});
} else {
iframe.onload = () => {
hideLoading();
};
}
});
}
onMounted(() => {
loadingRef.value = true;
init();
});
return {
getWrapStyle: computed(() => {
return {
height: `${unref(heightRef)}px`,
};
}),
loading: loadingRef,
frameRef,
menuStore,
};
},
});
</script>
<style lang="less" scoped>
.iframe-page {
.ant-spin-nested-loading {
position: relative;
height: 100%;
.ant-spin-container {
width: 100%;
height: 100%;
padding: 10px;
}
}
&__mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
&__main {
width: 100%;
height: 100%;
overflow: hidden;
background: #fff;
border: 0;
box-sizing: border-box;
}
}
</style>

View File

@@ -0,0 +1,149 @@
<template>
<div class="lock-page">
<div class="lock-page__entry">
<div class="lock-page__header">
<img src="../../../assets/images/header.jpg" class="lock-page__header-img" />
<p class="lock-page__header-name">{{ realName }}</p>
</div>
<BasicForm @register="register" v-if="!getIsNotPwd" />
<Alert v-if="errMsgRef" type="error" message="锁屏密码错误" banner />
<div class="lock-page__footer">
<a-button type="default" class="mt-2 mr-2" @click="goLogin" v-if="!getIsNotPwd">
返回登录
</a-button>
<a-button type="primary" class="mt-2" @click="unLock(!getIsNotPwd)" :loading="loadingRef">
进入系统
</a-button>
</div>
</div>
</div>
</template>
<script lang="ts">
// 组件相关
import { defineComponent, ref, computed } from 'vue';
import { Alert } from 'ant-design-vue';
// hook
import { BasicForm, useForm } from '/@/components/Form';
import { userStore } from '/@/store/modules/user';
import { appStore } from '/@/store/modules/app';
export default defineComponent({
name: 'LockPage',
components: { Alert, BasicForm },
setup() {
// 获取配置文件
// 样式前缀
const loadingRef = ref(false);
const errMsgRef = ref(false);
const [register, { validateFields }] = useForm({
showActionButtonGroup: false,
schemas: [
{
field: 'password',
label: '',
component: 'InputPassword',
componentProps: {
style: { width: '100%' },
placeholder: '请输入锁屏密码或者用户密码',
},
rules: [{ required: true }],
},
],
});
const realName = computed(() => {
const { realName } = userStore.getUserInfoState || {};
return realName;
});
/**
* @description: unLock
*/
async function unLock(valid = true) {
let password = '';
if (valid) {
try {
const values = (await validateFields()) as any;
password = values.password;
} catch (error) {
return;
}
}
try {
loadingRef.value = true;
const res = await appStore.unLockAction({ password, valid });
errMsgRef.value = !res;
} finally {
loadingRef.value = false;
}
}
function goLogin() {
userStore.loginOut(true);
appStore.resetLockInfo();
}
const getIsNotPwd = computed(() => {
if (!appStore.getLockInfo) {
return true;
}
return appStore.getLockInfo.pwd === undefined;
});
// 账号密码登录
return {
register,
getIsNotPwd,
goLogin,
realName,
unLock,
errMsgRef,
loadingRef,
};
},
});
</script>
<style lang="less" scoped>
@import (reference) '../../../design/index.less';
.lock-page {
position: fixed;
top: 0;
left: 0;
z-index: 999999;
display: flex;
width: 100vw;
height: 100vh;
background: url(../../../assets/images/lock-page.jpg) no-repeat;
background-size: 100% 100%;
align-items: center;
justify-content: flex-end;
&__entry {
position: relative;
width: 400px;
height: 260px;
padding: 80px 50px 0 50px;
margin-right: 50px;
background: #fff;
border-radius: 6px;
}
&__header {
position: absolute;
top: -35px;
left: calc(50% - 45px);
width: auto;
text-align: center;
&-img {
width: 70px;
border-radius: 50%;
}
&-name {
margin-top: 5px;
}
}
&__footer {
text-align: center;
}
}
</style>

View File

@@ -0,0 +1,137 @@
<template>
<div class="login h-screen relative">
<div class="login-mask h-full hidden lg:block" />
<div
class="h-full absolute right-0 top-0 w-full lg:w-2/5 xl:w-1/3 flex justify-center items-center"
>
<div class="login-form bg-white w-full rounded-sm border-solid bg-clip-padding mx-6 xl:mx-14">
<div class="w-full h-full border border-gray-600 px-2 py-10 rounded-sm">
<header class="flex justify-center items-center">
<img src="/@/assets/images/logo.png" class="w-12 mr-4 inline-block" />
<h1 class="text-2xl text-center text-primary tracking-wide">Vben Admin 2.0</h1>
</header>
<a-form class="w-4/5 mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef">
<a-form-item name="account">
<a-input size="large" v-model:value="formData.account" placeholder="vben" />
</a-form-item>
<a-form-item name="password">
<a-input-password
autofocus="autofocus"
size="large"
visibilityToggle
v-model:value="formData.password"
placeholder="123456"
/>
</a-form-item>
<a-form-item name="verify" v-if="openLoginVerify">
<BasicDragVerify v-model:value="formData.verify" ref="verifyRef" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
size="large"
class="rounded-sm"
block
@click="login"
:loading="formState.loading"
>登录</a-button
>
</a-form-item>
</a-form>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, unref, toRaw, computed } from 'vue';
import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
import { userStore } from '/@/store/modules/user';
import { appStore } from '/@/store/modules/app';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
components: { BasicDragVerify },
setup() {
const { notification } = useMessage();
const formRef = ref<any>(null);
const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null);
const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify);
const formData = reactive({
account: '',
password: '',
verify: undefined,
});
const formState = reactive({
loading: false,
});
const formRules = reactive({
account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [],
});
function resetVerify() {
const verify = unref(verifyRef);
if (!verify) return;
formData.verify && verify.$.resume();
formData.verify = undefined;
}
async function handleLogin() {
const form = unref(formRef);
if (!form) return;
formState.loading = true;
try {
const data = await form.validate();
const userInfo = await userStore.login(
toRaw({
password: data.password,
username: data.account,
})
);
if (userInfo) {
notification.success({
message: '登录成功',
description: `欢迎回来: ${userInfo.realName}`,
duration: 3,
});
}
} catch (error) {
} finally {
resetVerify();
formState.loading = false;
}
}
return {
formRef,
verifyRef,
formData,
formState,
formRules,
login: handleLogin,
openLoginVerify: openLoginVerifyRef,
};
},
});
</script>
<style lang="less" scoped>
.login {
background: url(../../../assets/images/login/login-bg.png) no-repeat;
background-size: 100% 100%;
&-mask {
background: url(../../../assets/images/login/login-in.png) no-repeat;
background-size: 100% 100%;
}
&-form {
border-color: rgba(255, 255, 255, 0.5);
border-width: 10px;
}
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<div />
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, unref } from 'vue';
import { appStore } from '/@/store/modules/app';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'Redirect',
setup() {
const { currentRoute, replace } = useRouter();
onBeforeMount(() => {
const { params, query } = unref(currentRoute);
const { path } = params;
const _path = Array.isArray(path) ? path.join('/') : path;
replace({
path: '/' + _path,
query,
});
const { openRouterTransition, openPageLoading } = appStore.getProjectConfig;
if (openRouterTransition && openPageLoading) {
setTimeout(() => {
appStore.setPageLoadingAction(false);
}, 0);
}
});
return {};
},
});
</script>