初始化提交
This commit is contained in:
434
web/src/views/chart/chart.scss
Normal file
434
web/src/views/chart/chart.scss
Normal file
@@ -0,0 +1,434 @@
|
||||
.chart-scrollbar {
|
||||
.chart-warp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
.chart-warp-bottom {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
.big-data-down-left,
|
||||
.big-data-down-right {
|
||||
width: 30%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.flex-warp-item {
|
||||
padding: 0 7.5px 15px 15px;
|
||||
width: 100%;
|
||||
height: 33.33%;
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--el-color-white);
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
transition: all ease 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
.flex-title {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.flex-title-small {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.flex-content {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
.flex-content-overflow {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.big-data-down-left {
|
||||
color: var(--el-text-color-primary);
|
||||
.sky {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.sky-left {
|
||||
font-size: 30px;
|
||||
}
|
||||
.sky-center {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 0 10px;
|
||||
font {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.span {
|
||||
background: #22bc76;
|
||||
border-radius: 2px;
|
||||
padding: 0 5px;
|
||||
color: var(--el-color-white);
|
||||
}
|
||||
}
|
||||
.sky-right {
|
||||
span {
|
||||
font-size: 30px;
|
||||
}
|
||||
font {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sky-dd {
|
||||
.sky-dl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
overflow: hidden;
|
||||
div {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.tip {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.sky-dl-first {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.d-states {
|
||||
display: flex;
|
||||
.d-states-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
i {
|
||||
font-size: 20px;
|
||||
height: 33px;
|
||||
width: 33px;
|
||||
line-height: 33px;
|
||||
text-align: center;
|
||||
border-radius: 100%;
|
||||
flex-shrink: 1;
|
||||
color: var(--el-color-white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.i-bg1 {
|
||||
background: #22bc76;
|
||||
}
|
||||
.i-bg2 {
|
||||
background: #e2356d;
|
||||
}
|
||||
.i-bg3 {
|
||||
background: #43bbef;
|
||||
}
|
||||
.d-states-flex {
|
||||
overflow: hidden;
|
||||
padding: 0 10px 0;
|
||||
.d-states-item-label {
|
||||
color: var(--el-color-primary);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.d-states-item-value {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: 3px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.d-btn {
|
||||
margin-top: 5px;
|
||||
.d-btn-item {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-radius: 35px;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
margin-top: 15px;
|
||||
cursor: pointer;
|
||||
transition: all ease 0.3s;
|
||||
color: var(--el-color-primary);
|
||||
.d-btn-item-left {
|
||||
font-size: 20px;
|
||||
border: 1px solid var(--el-color-primary);
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
border-radius: 100%;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
.d-btn-item-center {
|
||||
padding: 0 10px;
|
||||
flex: 1;
|
||||
}
|
||||
.d-btn-item-eight {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.big-data-down-center {
|
||||
width: 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.big-data-down-center-one {
|
||||
height: 66.67%;
|
||||
padding: 0 7.5px 15px;
|
||||
.big-data-down-center-one-content {
|
||||
height: 100%;
|
||||
background: var(--el-color-white);
|
||||
padding: 15px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
transition: all ease 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
.big-data-down-center-two {
|
||||
padding: 0 7.5px 15px;
|
||||
height: 33.33%;
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--el-color-white);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
transition: all ease 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
.flex-title {
|
||||
margin-bottom: 15px;
|
||||
color: var(--el-text-color-primary);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.flex-title-small {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.flex-content {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
height: calc(100% - 30px);
|
||||
.flex-content-left {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 120px;
|
||||
height: 100%;
|
||||
.monitor-item {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.monitor-wave {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
background-color: var(--el-color-primary);
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #f4f4f4;
|
||||
animation: roateOne 10s linear infinite;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
&::before {
|
||||
bottom: 10px;
|
||||
border-radius: 60%;
|
||||
}
|
||||
&::after {
|
||||
bottom: 8px;
|
||||
opacity: 0.7;
|
||||
border-radius: 37%;
|
||||
}
|
||||
.monitor-z-index {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@keyframes roateOne {
|
||||
0% {
|
||||
transform: translate(-50%, 0) rotateZ(0deg);
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%, -2%) rotateZ(180deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, 0%) rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
.monitor-active {
|
||||
background-color: #22bc76;
|
||||
.monitor-z-index {
|
||||
color: #22bc76;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.flex-content-right {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.big-data-down-right {
|
||||
.flex-warp-item {
|
||||
padding: 0 15px 15px 7.5px;
|
||||
.flex-title {
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
.flex-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.task {
|
||||
display: flex;
|
||||
height: 45px;
|
||||
.task-item {
|
||||
flex: 1;
|
||||
color: var(--el-color-white);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.task-item-box {
|
||||
position: relative;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
border-radius: 100%;
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10px 12px 0 rgba(0, 0, 0, 0.3);
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: -2;
|
||||
left: -50%;
|
||||
top: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50% 50%, 50% 50%;
|
||||
background-position: 0 0, 100% 0, 100% 100%, 0 100%;
|
||||
background-image: linear-gradient(#19d4ae, #19d4ae), linear-gradient(#5ab1ef, #5ab1ef), linear-gradient(#fa6e86, #fa6e86),
|
||||
linear-gradient(#ffb980, #ffb980);
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
left: 1px;
|
||||
top: 1px;
|
||||
width: calc(100% - 2px);
|
||||
height: calc(100% - 2px);
|
||||
border-radius: 100%;
|
||||
}
|
||||
.task-item-value {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.task-item-label {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.task1 {
|
||||
&::after {
|
||||
background: #5492be;
|
||||
}
|
||||
}
|
||||
.task2 {
|
||||
&::after {
|
||||
background: #43a177;
|
||||
}
|
||||
}
|
||||
.task3 {
|
||||
&::after {
|
||||
background: #a76077;
|
||||
}
|
||||
}
|
||||
}
|
||||
.task-first-item {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
color: var(--el-color-primary);
|
||||
.task-first {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.progress {
|
||||
color: var(--el-text-color-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
justify-content: space-between;
|
||||
margin-top: 15px;
|
||||
.progress-item {
|
||||
height: 33.33%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.progress-box {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
:deep(.el-progress__text) {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 12px !important;
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-progress-bar__outer) {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
:deep(.el-progress-bar) {
|
||||
margin-right: -22px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
web/src/views/chart/chart.ts
Normal file
59
web/src/views/chart/chart.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* sky 天气
|
||||
* @returns 返回模拟数据
|
||||
*/
|
||||
export const skyList = [
|
||||
{
|
||||
v1: '时间',
|
||||
v2: '天气',
|
||||
v3: '温度',
|
||||
v5: '降水',
|
||||
v7: '风力',
|
||||
type: 'title',
|
||||
},
|
||||
{
|
||||
v1: '今天',
|
||||
v2: 'ele-Sunny',
|
||||
v3: '20°/26°',
|
||||
v5: '50%',
|
||||
v7: '13m/s',
|
||||
},
|
||||
{
|
||||
v1: '明天',
|
||||
v2: 'ele-Lightning',
|
||||
v3: '20°/26°',
|
||||
v5: '50%',
|
||||
v7: '13m/s',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 当前设置状态
|
||||
* @returns 返回模拟数据
|
||||
*/
|
||||
export const dBtnList = [
|
||||
{
|
||||
v2: '阳光玫瑰种植',
|
||||
v3: '126天',
|
||||
v4: '设备在线',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 当前设备监测
|
||||
* @returns 返回模拟数据
|
||||
*/
|
||||
export const chartData4List = [
|
||||
{
|
||||
label: '温度',
|
||||
},
|
||||
{
|
||||
label: '光照',
|
||||
},
|
||||
{
|
||||
label: '湿度',
|
||||
},
|
||||
{
|
||||
label: '风力',
|
||||
},
|
||||
];
|
||||
107
web/src/views/chart/head.vue
Normal file
107
web/src/views/chart/head.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="big-data-up mb15">
|
||||
<div class="up-left">
|
||||
<i class="el-icon-time mr5"></i>
|
||||
<span>{{ time.txt }}</span>
|
||||
</div>
|
||||
<div class="up-center">
|
||||
<span>智慧农业系统平台</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onBeforeMount, onUnmounted, defineComponent } from 'vue';
|
||||
import { formatDate } from '/@/utils/formatTime';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'chartHead',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
time: {
|
||||
txt: '',
|
||||
fun: 0,
|
||||
},
|
||||
});
|
||||
// 初始化时间
|
||||
const initTime = () => {
|
||||
state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ');
|
||||
state.time.fun = window.setInterval(() => {
|
||||
state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ');
|
||||
}, 1000);
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initTime();
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(state.time.fun);
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.big-data-up {
|
||||
height: 55px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
color: var(--el-color-primary);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.up-left {
|
||||
position: absolute;
|
||||
}
|
||||
.up-center {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
letter-spacing: 5px;
|
||||
background-image: -webkit-linear-gradient(
|
||||
left,
|
||||
var(--el-color-primary),
|
||||
var(--el-color-primary-light-3) 25%,
|
||||
var(--el-color-primary) 50%,
|
||||
var(--el-color-primary-light-3) 75%,
|
||||
var(--el-color-primary)
|
||||
);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation-data-v-b02d8052 4s linear infinite;
|
||||
animation: masked-animation-data-v-b02d8052 4s linear infinite;
|
||||
-webkit-box-reflect: below -2px -webkit-gradient(linear, left top, left bottom, from(transparent), to(hsla(0, 0%, 100%, 0.1)));
|
||||
position: relative;
|
||||
@keyframes masked-animation {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -100% 0;
|
||||
}
|
||||
}
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
width: 250px;
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 1px transparent solid;
|
||||
border-image: linear-gradient(to right, var(--el-color-primary-light-9), var(--el-color-primary)) 1 10;
|
||||
}
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
492
web/src/views/chart/index.vue
Normal file
492
web/src/views/chart/index.vue
Normal file
@@ -0,0 +1,492 @@
|
||||
<template>
|
||||
<div class="chart-scrollbar layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
|
||||
<div class="chart-warp">
|
||||
<div class="chart-warp-top">
|
||||
<ChartHead />
|
||||
</div>
|
||||
<div class="chart-warp-bottom">
|
||||
<!-- 左边 -->
|
||||
<div class="big-data-down-left">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">天气预报</div>
|
||||
<div class="flex-content">
|
||||
<div class="sky">
|
||||
<SvgIcon name="ele-Sunny" class="sky-left" />
|
||||
<div class="sky-center">
|
||||
<div class="mb2">
|
||||
<span>多云转晴</span>
|
||||
<span>东南风</span>
|
||||
<span class="span ml5">良</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sky-right">
|
||||
<span>25</span>
|
||||
<span>°C</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sky-dd">
|
||||
<div class="sky-dl" v-for="(v, k) in skyList" :key="k" :class="{ 'sky-dl-first': k === 1 }">
|
||||
<div>{{ v.v1 }}</div>
|
||||
<div v-if="v.type === 'title'">{{ v.v2 }}</div>
|
||||
<div v-else>
|
||||
<SvgIcon :name="v.v2" />
|
||||
</div>
|
||||
<div>{{ v.v3 }}</div>
|
||||
<div class="tip">{{ v.v5 }}</div>
|
||||
<div>{{ v.v7 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">当前设备状态</div>
|
||||
<div class="flex-content flex-content-overflow">
|
||||
<div class="d-states">
|
||||
<div class="d-states-item">
|
||||
<SvgIcon name="ele-Odometer" class="i-bg1" />
|
||||
<div class="d-states-flex">
|
||||
<div class="d-states-item-label">园区设备数</div>
|
||||
<div class="d-states-item-value">99</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-states-item">
|
||||
<SvgIcon name="ele-FirstAidKit" class="i-bg2" />
|
||||
<div class="d-states-flex">
|
||||
<div class="d-states-item-label">预警设备数</div>
|
||||
<div class="d-states-item-value">10</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-states-item">
|
||||
<SvgIcon name="ele-VideoPlay" class="i-bg3" />
|
||||
<div class="d-states-flex">
|
||||
<div class="d-states-item-label">运行设备数</div>
|
||||
<div class="d-states-item-value">20</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-btn">
|
||||
<div class="d-btn-item" v-for="(v, k) in dBtnList" :key="k">
|
||||
<i class="d-btn-item-left el-icon-money"></i>
|
||||
<div class="d-btn-item-center">
|
||||
<div>{{ v.v2 }}|{{ v.v3 }}</div>
|
||||
</div>
|
||||
<div class="d-btn-item-eight">{{ v.v4 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">近30天预警总数</div>
|
||||
<div class="flex-content">
|
||||
<div style="height: 100%" ref="chartsWarningRef"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间 -->
|
||||
<div class="big-data-down-center">
|
||||
<div class="big-data-down-center-one">
|
||||
<div class="big-data-down-center-one-content">
|
||||
<div style="height: 100%" ref="chartsCenterOneRef"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="big-data-down-center-two">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">
|
||||
<span>当前设备监测</span>
|
||||
<span class="flex-title-small">单位:次</span>
|
||||
</div>
|
||||
<div class="flex-content">
|
||||
<div class="flex-content-left">
|
||||
<div class="monitor-item" v-for="(v, k) in chartData4List" :key="k">
|
||||
<div class="monitor-wave">
|
||||
<div class="monitor-z-index">
|
||||
<div class="monitor-item-label">{{ v.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-content-right">
|
||||
<div style="height: 100%" ref="chartsMonitorRef"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右边 -->
|
||||
<div class="big-data-down-right">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">
|
||||
<span>近7天产品追溯扫码统计</span>
|
||||
<span class="flex-title-small">单位:次</span>
|
||||
</div>
|
||||
<div class="flex-content">
|
||||
<div style="height: 100%" ref="chartsSevenDaysRef"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">当前任务统计</div>
|
||||
<div class="flex-content">
|
||||
<div class="task">
|
||||
<div class="task-item task-first-item">
|
||||
<div class="task-item-value task-first">25</div>
|
||||
<div class="task-item-label">待办任务</div>
|
||||
</div>
|
||||
<div class="task-item">
|
||||
<div class="task-item-box task1">
|
||||
<div class="task-item-value">12</div>
|
||||
<div class="task-item-label">施肥</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-item">
|
||||
<div class="task-item-box task2">
|
||||
<div class="task-item-value">3</div>
|
||||
<div class="task-item-label">施药</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-item">
|
||||
<div class="task-item-box task3">
|
||||
<div class="task-item-value">5</div>
|
||||
<div class="task-item-label">农事</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-item">
|
||||
<span>施肥率</span>
|
||||
<div class="progress-box">
|
||||
<el-progress :percentage="70" color="#43bdf0"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-item">
|
||||
<span>施药率</span>
|
||||
<div class="progress-box">
|
||||
<el-progress :percentage="36" color="#43bdf0"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-item">
|
||||
<span>农事率</span>
|
||||
<div class="progress-box">
|
||||
<el-progress :percentage="91" color="#43bdf0"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="flex-title">
|
||||
<span>近7天投入品记录</span>
|
||||
<span class="flex-title-small">单位:件</span>
|
||||
</div>
|
||||
<div class="flex-content">
|
||||
<div style="height: 100%" ref="chartsInvestmentRef"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, onMounted, getCurrentInstance, watch, nextTick, onActivated, defineComponent } from 'vue';
|
||||
import ChartHead from '/@/views/chart/head.vue';
|
||||
import * as echarts from 'echarts';
|
||||
import 'echarts-wordcloud';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import { skyList, dBtnList, chartData4List } from '/@/views/chart/chart';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'chartIndex',
|
||||
components: { ChartHead },
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const state = reactive({
|
||||
skyList,
|
||||
dBtnList,
|
||||
chartData4List,
|
||||
myCharts: [],
|
||||
});
|
||||
// 设置主内容的高度
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// 初始化中间图表1
|
||||
const initChartsCenterOne = () => {
|
||||
const myChart = echarts.init(proxy.$refs.chartsCenterOneRef);
|
||||
const option = {
|
||||
grid: {
|
||||
top: 15,
|
||||
right: 15,
|
||||
bottom: 20,
|
||||
left: 30,
|
||||
},
|
||||
tooltip: {},
|
||||
series: [
|
||||
{
|
||||
type: 'wordCloud',
|
||||
sizeRange: [12, 40],
|
||||
rotationRange: [0, 0],
|
||||
rotationStep: 45,
|
||||
gridSize: Math.random() * 20 + 5,
|
||||
shape: 'circle',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
textStyle: {
|
||||
fontFamily: 'sans-serif',
|
||||
fontWeight: 'bold',
|
||||
color: function () {
|
||||
return `rgb(${[Math.round(Math.random() * 160), Math.round(Math.random() * 160), Math.round(Math.random() * 160)].join(',')})`;
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ name: 'vue-next-admin', value: 520 },
|
||||
{ name: 'lyt', value: 520 },
|
||||
{ name: 'next-admin', value: 500 },
|
||||
{ name: '更名', value: 420 },
|
||||
{ name: '智慧农业', value: 520 },
|
||||
{ name: '男神', value: 2.64 },
|
||||
{ name: '好身材', value: 4.03 },
|
||||
{ name: '校草', value: 24.95 },
|
||||
{ name: '酷', value: 4.04 },
|
||||
{ name: '时尚', value: 5.27 },
|
||||
{ name: '阳光活力', value: 5.8 },
|
||||
{ name: '初恋', value: 3.09 },
|
||||
{ name: '英俊潇洒', value: 24.71 },
|
||||
{ name: '霸气', value: 6.33 },
|
||||
{ name: '腼腆', value: 2.55 },
|
||||
{ name: '蠢萌', value: 3.88 },
|
||||
{ name: '青春', value: 8.04 },
|
||||
{ name: '网红', value: 5.87 },
|
||||
{ name: '萌', value: 6.97 },
|
||||
{ name: '认真', value: 2.53 },
|
||||
{ name: '古典', value: 2.49 },
|
||||
{ name: '温柔', value: 3.91 },
|
||||
{ name: '有个性', value: 3.25 },
|
||||
{ name: '可爱', value: 9.93 },
|
||||
{ name: '幽默诙谐', value: 3.65 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
(<any>state.myCharts).push(myChart);
|
||||
};
|
||||
// 初始化近7天产品追溯扫码统计
|
||||
const initChartsSevenDays = () => {
|
||||
const myChart = echarts.init(proxy.$refs.chartsSevenDaysRef);
|
||||
const option = {
|
||||
grid: {
|
||||
top: 15,
|
||||
right: 15,
|
||||
bottom: 20,
|
||||
left: 30,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['1天', '2天', '3天', '4天', '5天', '6天', '7天'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [12, 32, 11, 34, 90, 23, 21],
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [22, 82, 91, 24, 90, 30, 30],
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [50, 32, 18, 14, 90, 30, 50],
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
(<any>state.myCharts).push(myChart);
|
||||
};
|
||||
// 初始化近30天预警总数
|
||||
const initChartsWarning = () => {
|
||||
const myChart = echarts.init(proxy.$refs.chartsWarningRef);
|
||||
const option = {
|
||||
grid: {
|
||||
top: 50,
|
||||
right: 20,
|
||||
bottom: 30,
|
||||
left: 30,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '面积模式',
|
||||
type: 'pie',
|
||||
radius: [20, 50],
|
||||
center: ['50%', '50%'],
|
||||
roseType: 'area',
|
||||
itemStyle: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
data: [
|
||||
{ value: 40, name: '监测设备预警' },
|
||||
{ value: 38, name: '天气预警' },
|
||||
{ value: 32, name: '任务预警' },
|
||||
{ value: 30, name: '病虫害预警' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
(<any>state.myCharts).push(myChart);
|
||||
};
|
||||
// 初始化当前设备监测
|
||||
const initChartsMonitor = () => {
|
||||
const myChart = echarts.init(proxy.$refs.chartsMonitorRef);
|
||||
const option = {
|
||||
grid: {
|
||||
top: 15,
|
||||
right: 15,
|
||||
bottom: 20,
|
||||
left: 30,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
itemStyle: {
|
||||
color: '#289df5',
|
||||
borderColor: '#289df5',
|
||||
areaStyle: {
|
||||
type: 'default',
|
||||
opacity: 0.1,
|
||||
},
|
||||
},
|
||||
data: [20, 32, 31, 34, 12, 13, 20],
|
||||
type: 'line',
|
||||
areaStyle: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
(<any>state.myCharts).push(myChart);
|
||||
};
|
||||
// 初始化近7天投入品记录
|
||||
const initChartsInvestment = () => {
|
||||
const myChart = echarts.init(proxy.$refs.chartsInvestmentRef);
|
||||
const option = {
|
||||
grid: {
|
||||
top: 15,
|
||||
right: 15,
|
||||
bottom: 20,
|
||||
left: 30,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['1天', '2天', '3天', '4天', '5天', '6天', '7天'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [10, 20, 15, 80, 70, 11, 30],
|
||||
type: 'bar',
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
(<any>state.myCharts).push(myChart);
|
||||
};
|
||||
// 批量设置 echarts resize
|
||||
const initEchartsResizeFun = () => {
|
||||
nextTick(() => {
|
||||
for (let i = 0; i < state.myCharts.length; i++) {
|
||||
(<any>state.myCharts[i]).resize();
|
||||
}
|
||||
});
|
||||
};
|
||||
// 批量设置 echarts resize
|
||||
const initEchartsResize = () => {
|
||||
window.addEventListener('resize', initEchartsResizeFun);
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initChartsCenterOne();
|
||||
initChartsSevenDays();
|
||||
initChartsWarning();
|
||||
initChartsMonitor();
|
||||
initChartsInvestment();
|
||||
initEchartsResize();
|
||||
});
|
||||
// 由于页面缓存原因,keep-alive
|
||||
onActivated(() => {
|
||||
initEchartsResizeFun();
|
||||
});
|
||||
// 监听 vuex 中的 tagsview 开启全屏变化,重新 resize 图表,防止不出现/大小不变等
|
||||
watch(
|
||||
() => isTagsViewCurrenFull.value,
|
||||
() => {
|
||||
initEchartsResizeFun();
|
||||
}
|
||||
);
|
||||
return {
|
||||
initTagViewHeight,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './chart.scss';
|
||||
</style>
|
||||
89
web/src/views/demo/demo1.vue
Normal file
89
web/src/views/demo/demo1.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud } from "@fast-crud/fast-crud";
|
||||
import { useExpose } from "@fast-crud/fast-crud";
|
||||
import _ from 'lodash-es'
|
||||
|
||||
//此处为crudOptions配置
|
||||
const createCrudOptions = function ({ expose }) {
|
||||
const records = [{id:1,name:'Hello World'}]
|
||||
const pageRequest = async (query) => {
|
||||
return {
|
||||
records, currentPage:1,pageSize:20,total:records.length
|
||||
}
|
||||
};
|
||||
const editRequest = async ({ form, row }) => {
|
||||
const target = _.find(records,item=>{return row.id === item.id})
|
||||
_.merge(target,form)
|
||||
return target;
|
||||
};
|
||||
const delRequest = async ({ row }) => {
|
||||
_.remove(records,item=>{return item.id === row.id})
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }) => {
|
||||
const maxRecord = _.maxBy(records,item=>{return item.id})
|
||||
form.id = (maxRecord?.id||0)+1
|
||||
records.push(form)
|
||||
return form
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
name: {
|
||||
title: "姓名",
|
||||
type: "text",
|
||||
search: {show: true},
|
||||
form: {
|
||||
component: {
|
||||
maxlength: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//此处为组件定义
|
||||
export default defineComponent({
|
||||
name: "FsCrudFirst",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
// 暴露的方法
|
||||
const { expose } = useExpose({ crudRef, crudBinding });
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ expose });
|
||||
// 初始化crud配置
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||
const { resetCrudOptions } = useCrud({ expose, crudOptions });
|
||||
// 你可以调用此方法,重新初始化crud配置
|
||||
// resetCrudOptions(options)
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
expose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
118
web/src/views/error/401.vue
Normal file
118
web/src/views/error/401.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
|
||||
<div class="error-flex">
|
||||
<div class="left">
|
||||
<div class="left-item">
|
||||
<div class="left-item-animation left-item-num">401</div>
|
||||
<div class="left-item-animation left-item-title">{{ $t('message.noAccess.accessTitle') }}</div>
|
||||
<div class="left-item-animation left-item-msg">{{ $t('message.noAccess.accessMsg') }}</div>
|
||||
<div class="left-item-animation left-item-btn">
|
||||
<el-button type="primary" round @click="onSetAuth">{{ $t('message.noAccess.accessBtn') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<img
|
||||
src="https://img-blog.csdnimg.cn/3333f265772a4fa89287993500ecbf96.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import { Session } from '/@/utils/storage';
|
||||
|
||||
export default defineComponent({
|
||||
name: '401',
|
||||
setup() {
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const onSetAuth = () => {
|
||||
// https://gitee.com/lyt-top/vue-next-admin/issues/I5C3JS
|
||||
// 清除缓存/token等
|
||||
Session.clear();
|
||||
// 使用 reload 时,不需要调用 resetRoute() 重置路由
|
||||
window.location.reload();
|
||||
};
|
||||
// 设置主内容的高度
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
return {
|
||||
onSetAuth,
|
||||
initTagViewHeight,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.error {
|
||||
height: 100%;
|
||||
background-color: var(--el-color-white);
|
||||
display: flex;
|
||||
.error-flex {
|
||||
margin: auto;
|
||||
display: flex;
|
||||
height: 350px;
|
||||
width: 900px;
|
||||
.left {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
.left-item {
|
||||
.left-item-animation {
|
||||
opacity: 0;
|
||||
animation-name: error-num;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
.left-item-num {
|
||||
color: var(--el-color-info);
|
||||
font-size: 55px;
|
||||
}
|
||||
.left-item-title {
|
||||
font-size: 20px;
|
||||
color: var(--el-text-color-primary);
|
||||
margin: 15px 0 5px 0;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.left-item-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
margin-bottom: 30px;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.left-item-btn {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: 1;
|
||||
opacity: 0;
|
||||
animation-name: error-img;
|
||||
animation-duration: 2s;
|
||||
animation-fill-mode: forwards;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
115
web/src/views/error/404.vue
Normal file
115
web/src/views/error/404.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
|
||||
<div class="error-flex">
|
||||
<div class="left">
|
||||
<div class="left-item">
|
||||
<div class="left-item-animation left-item-num">404</div>
|
||||
<div class="left-item-animation left-item-title">{{ $t('message.notFound.foundTitle') }}</div>
|
||||
<div class="left-item-animation left-item-msg">{{ $t('message.notFound.foundMsg') }}</div>
|
||||
<div class="left-item-animation left-item-btn">
|
||||
<el-button type="primary" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<img
|
||||
src="https://img-blog.csdnimg.cn/9eb1d85a417f4ed1ba7107f149ce3da1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: '404',
|
||||
setup() {
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const router = useRouter();
|
||||
const onGoHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
// 设置主内容的高度
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
return {
|
||||
onGoHome,
|
||||
initTagViewHeight,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.error {
|
||||
height: 100%;
|
||||
background-color: var(--el-color-white);
|
||||
display: flex;
|
||||
.error-flex {
|
||||
margin: auto;
|
||||
display: flex;
|
||||
height: 350px;
|
||||
width: 900px;
|
||||
.left {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
.left-item {
|
||||
.left-item-animation {
|
||||
opacity: 0;
|
||||
animation-name: error-num;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
.left-item-num {
|
||||
color: var(--el-color-info);
|
||||
font-size: 55px;
|
||||
}
|
||||
.left-item-title {
|
||||
font-size: 20px;
|
||||
color: var(--el-text-color-primary);
|
||||
margin: 15px 0 5px 0;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.left-item-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
margin-bottom: 30px;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.left-item-btn {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: 1;
|
||||
opacity: 0;
|
||||
animation-name: error-img;
|
||||
animation-duration: 2s;
|
||||
animation-fill-mode: forwards;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
38
web/src/views/fun/clipboard/index.vue
Normal file
38
web/src/views/fun/clipboard/index.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<el-card shadow="hover" header="复制剪切演示">
|
||||
<el-alert
|
||||
title="感谢优秀的 `vue-clipboard3`,项目地址:https://github.com/JamieCurnow/vue-clipboard3`"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<el-input placeholder="请输入内容" v-model="copyVal">
|
||||
<template #append>
|
||||
<el-button @click="copyText(copyVal)">复制链接</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input placeholder="先点击上方 `复制链接` 按钮,然后 `Ctrl + V` 进行粘贴! " v-model="shearVal" class="mt15"> </el-input>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
import commonFunction from '/@/utils/commonFunction';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funClipboard',
|
||||
setup() {
|
||||
const { copyText } = commonFunction();
|
||||
const state = reactive({
|
||||
copyVal: 'https://gitee.com/lyt-top/vue-next-admin',
|
||||
shearVal: '',
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {});
|
||||
return {
|
||||
copyText,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
159
web/src/views/fun/countup/index.vue
Normal file
159
web/src/views/fun/countup/index.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="hover" header="数字滚动演示">
|
||||
<el-alert
|
||||
title="感谢优秀的 `countup.js`,项目地址:https://github.com/inorganik/countUp.js"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="6" class="mb15" v-for="(v, k) in topCardItemList" :key="k">
|
||||
<div class="countup-card-item countup-card-item-box" :style="{ background: `var(${v.color})` }">
|
||||
<div class="countup-card-item-flex" ref="topCardItemRefs">
|
||||
<div class="countup-card-item-title pb3">{{ v.title }}</div>
|
||||
<div class="countup-card-item-title-num pb6"></div>
|
||||
<div class="countup-card-item-tip pb3">{{ v.tip }}</div>
|
||||
<div class="countup-card-item-tip-num"></div>
|
||||
</div>
|
||||
<i :class="v.icon" :style="{ color: v.iconColor }"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default" @click="refreshCurrent">
|
||||
<el-icon>
|
||||
<ele-RefreshRight />
|
||||
</el-icon>
|
||||
重置/刷新数值
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, nextTick, defineComponent } from 'vue';
|
||||
import { CountUp } from 'countup.js';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funCountup',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
topCardItemRefs: null as any,
|
||||
topCardItemList: [
|
||||
{
|
||||
title: '今日访问人数',
|
||||
titleNum: '123',
|
||||
tip: '在场人数',
|
||||
tipNum: '911',
|
||||
color: '--el-color-primary',
|
||||
iconColor: '#ffcb47',
|
||||
icon: 'iconfont icon-jinridaiban',
|
||||
},
|
||||
{
|
||||
title: '实验室总数',
|
||||
titleNum: '123',
|
||||
tip: '使用中',
|
||||
tipNum: '611',
|
||||
color: '--el-color-success',
|
||||
iconColor: '#70cf41',
|
||||
icon: 'iconfont icon-AIshiyanshi',
|
||||
},
|
||||
{
|
||||
title: '申请人数(月)',
|
||||
titleNum: '123',
|
||||
tip: '通过人数',
|
||||
tipNum: '911',
|
||||
color: '--el-color-warning',
|
||||
iconColor: '#dfae64',
|
||||
icon: 'iconfont icon-shenqingkaiban',
|
||||
},
|
||||
{
|
||||
title: '销售情况',
|
||||
titleNum: '123',
|
||||
tip: '销售数',
|
||||
tipNum: '911',
|
||||
color: '--el-color-danger',
|
||||
iconColor: '#e56565',
|
||||
icon: 'iconfont icon-ditu',
|
||||
},
|
||||
],
|
||||
});
|
||||
// 初始化数字滚动
|
||||
const initNumCountUp = () => {
|
||||
nextTick(() => {
|
||||
state.topCardItemRefs.forEach((v: HTMLDivElement) => {
|
||||
new CountUp(v.querySelector('.countup-card-item-title-num') as HTMLDivElement, Math.random() * 10000).start();
|
||||
new CountUp(v.querySelector('.countup-card-item-tip-num') as HTMLDivElement, Math.random() * 1000).start();
|
||||
});
|
||||
});
|
||||
};
|
||||
// 重置/刷新数值
|
||||
const refreshCurrent = () => {
|
||||
initNumCountUp();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initNumCountUp();
|
||||
});
|
||||
return {
|
||||
refreshCurrent,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.countup-card-item {
|
||||
width: 100%;
|
||||
height: 103px;
|
||||
background: var(--el-text-color-secondary);
|
||||
border-radius: 4px;
|
||||
transition: all ease 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
.countup-card-item-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&:hover {
|
||||
i {
|
||||
right: 0px !important;
|
||||
bottom: 0px !important;
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: -10px;
|
||||
font-size: 70px;
|
||||
transform: rotate(-30deg);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
.countup-card-item-flex {
|
||||
padding: 0 20px;
|
||||
color: var(--el-color-white);
|
||||
.countup-card-item-title,
|
||||
.countup-card-item-tip {
|
||||
font-size: 13px;
|
||||
}
|
||||
.countup-card-item-title-num {
|
||||
font-size: 18px;
|
||||
}
|
||||
.countup-card-item-tip-num {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
63
web/src/views/fun/cropper/index.vue
Normal file
63
web/src/views/fun/cropper/index.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="croppers-container">
|
||||
<el-card shadow="hover" header="cropper 图片裁剪">
|
||||
<el-alert
|
||||
title="感谢优秀的 `cropperjs`,项目地址:https://github.com/fengyuanchen/cropperjs"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<div class="cropper-img-warp">
|
||||
<div class="mb15 mt15">
|
||||
<img class="cropper-img" :src="cropperImg" />
|
||||
</div>
|
||||
<el-button type="primary" size="default" @click="onCropperDialogOpen">
|
||||
<el-icon>
|
||||
<ele-Crop />
|
||||
</el-icon>
|
||||
更换头像
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<CropperDialog ref="cropperDialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, defineComponent } from 'vue';
|
||||
import CropperDialog from '/@/components/cropper/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funCropper',
|
||||
components: { CropperDialog },
|
||||
setup() {
|
||||
const cropperDialogRef = ref();
|
||||
const state = reactive({
|
||||
cropperImg: 'https://img1.baidu.com/it/u=2813520958,2218166536&fm=26&fmt=auto&gp=0.jpg',
|
||||
});
|
||||
// 打开裁剪弹窗
|
||||
const onCropperDialogOpen = () => {
|
||||
cropperDialogRef.value.openDialog(state.cropperImg);
|
||||
};
|
||||
return {
|
||||
cropperDialogRef,
|
||||
onCropperDialogOpen,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.croppers-container {
|
||||
.cropper-img-warp {
|
||||
text-align: center;
|
||||
.cropper-img {
|
||||
margin: auto;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
web/src/views/fun/echartsMap/index.vue
Normal file
141
web/src/views/fun/echartsMap/index.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
|
||||
<div class="layout-view-bg-white">
|
||||
<div ref="echartsMap" style="height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, onMounted, defineComponent } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import 'echarts/extension/bmap/bmap';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import { echartsMapList, echartsMapData } from './mock';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funEchartsMap',
|
||||
setup() {
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const state: any = reactive({
|
||||
echartsMap: null,
|
||||
echartsMapList,
|
||||
echartsMapData,
|
||||
});
|
||||
// 设置主内容的高度
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// echartsMap 将坐标信息和对应物理量的值合在一起
|
||||
const convertData = (data: any) => {
|
||||
let res = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let geoCoord = state.echartsMapData[data[i].name];
|
||||
if (geoCoord) {
|
||||
res.push({
|
||||
name: data[i].name,
|
||||
value: geoCoord.concat(data[i].value),
|
||||
});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
// 初始化 echartsMap
|
||||
const initEchartsMap = () => {
|
||||
const myChart = echarts.init(<HTMLElement>state.echartsMap);
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
color: ['#9a60b4', '#ea7ccc'],
|
||||
bmap: {
|
||||
center: [104.114129, 37.550339],
|
||||
zoom: 5,
|
||||
roam: true,
|
||||
mapStyle: {},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'pm2.5',
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'bmap',
|
||||
data: convertData(state.echartsMapList),
|
||||
symbolSize: function (val: any) {
|
||||
return val[2] / 10;
|
||||
},
|
||||
encode: {
|
||||
value: 2,
|
||||
},
|
||||
label: {
|
||||
formatter: '{b}',
|
||||
position: 'right',
|
||||
show: false,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Top 5',
|
||||
type: 'effectScatter',
|
||||
coordinateSystem: 'bmap',
|
||||
data: convertData(
|
||||
state.echartsMapList
|
||||
.sort(function (a: any, b: any) {
|
||||
return b.value - a.value;
|
||||
})
|
||||
.slice(0, 6)
|
||||
),
|
||||
symbolSize: function (val: any) {
|
||||
return val[2] / 10;
|
||||
},
|
||||
encode: {
|
||||
value: 2,
|
||||
},
|
||||
showEffectOn: 'render',
|
||||
rippleEffect: {
|
||||
brushType: 'stroke',
|
||||
},
|
||||
hoverAnimation: true,
|
||||
label: {
|
||||
formatter: '{b}',
|
||||
position: 'right',
|
||||
show: true,
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: '#333',
|
||||
},
|
||||
zlevel: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initEchartsMap();
|
||||
});
|
||||
return {
|
||||
initTagViewHeight,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
387
web/src/views/fun/echartsMap/mock.ts
Normal file
387
web/src/views/fun/echartsMap/mock.ts
Normal file
@@ -0,0 +1,387 @@
|
||||
// 地图模拟数据
|
||||
export const echartsMapList = [
|
||||
{ name: '海门', value: 9 },
|
||||
{ name: '鄂尔多斯', value: 12 },
|
||||
{ name: '招远', value: 12 },
|
||||
{ name: '舟山', value: 12 },
|
||||
{ name: '齐齐哈尔', value: 14 },
|
||||
{ name: '盐城', value: 15 },
|
||||
{ name: '赤峰', value: 16 },
|
||||
{ name: '青岛', value: 18 },
|
||||
{ name: '乳山', value: 18 },
|
||||
{ name: '金昌', value: 19 },
|
||||
{ name: '泉州', value: 21 },
|
||||
{ name: '莱西', value: 21 },
|
||||
{ name: '日照', value: 21 },
|
||||
{ name: '胶南', value: 22 },
|
||||
{ name: '南通', value: 23 },
|
||||
{ name: '拉萨', value: 24 },
|
||||
{ name: '云浮', value: 24 },
|
||||
{ name: '梅州', value: 25 },
|
||||
{ name: '文登', value: 25 },
|
||||
{ name: '上海', value: 25 },
|
||||
{ name: '攀枝花', value: 25 },
|
||||
{ name: '威海', value: 25 },
|
||||
{ name: '承德', value: 25 },
|
||||
{ name: '厦门', value: 26 },
|
||||
{ name: '汕尾', value: 26 },
|
||||
{ name: '潮州', value: 26 },
|
||||
{ name: '丹东', value: 27 },
|
||||
{ name: '太仓', value: 27 },
|
||||
{ name: '曲靖', value: 27 },
|
||||
{ name: '烟台', value: 28 },
|
||||
{ name: '福州', value: 29 },
|
||||
{ name: '瓦房店', value: 30 },
|
||||
{ name: '即墨', value: 30 },
|
||||
{ name: '抚顺', value: 31 },
|
||||
{ name: '玉溪', value: 31 },
|
||||
{ name: '张家口', value: 31 },
|
||||
{ name: '阳泉', value: 31 },
|
||||
{ name: '莱州', value: 32 },
|
||||
{ name: '湖州', value: 32 },
|
||||
{ name: '汕头', value: 32 },
|
||||
{ name: '昆山', value: 33 },
|
||||
{ name: '宁波', value: 33 },
|
||||
{ name: '湛江', value: 33 },
|
||||
{ name: '揭阳', value: 34 },
|
||||
{ name: '荣成', value: 34 },
|
||||
{ name: '连云港', value: 35 },
|
||||
{ name: '葫芦岛', value: 35 },
|
||||
{ name: '常熟', value: 36 },
|
||||
{ name: '东莞', value: 36 },
|
||||
{ name: '河源', value: 36 },
|
||||
{ name: '淮安', value: 36 },
|
||||
{ name: '泰州', value: 36 },
|
||||
{ name: '南宁', value: 37 },
|
||||
{ name: '营口', value: 37 },
|
||||
{ name: '惠州', value: 37 },
|
||||
{ name: '江阴', value: 37 },
|
||||
{ name: '蓬莱', value: 37 },
|
||||
{ name: '韶关', value: 38 },
|
||||
{ name: '嘉峪关', value: 38 },
|
||||
{ name: '广州', value: 38 },
|
||||
{ name: '延安', value: 38 },
|
||||
{ name: '太原', value: 39 },
|
||||
{ name: '清远', value: 39 },
|
||||
{ name: '中山', value: 39 },
|
||||
{ name: '昆明', value: 39 },
|
||||
{ name: '寿光', value: 40 },
|
||||
{ name: '盘锦', value: 40 },
|
||||
{ name: '长治', value: 41 },
|
||||
{ name: '深圳', value: 360 },
|
||||
{ name: '珠海', value: 42 },
|
||||
{ name: '宿迁', value: 43 },
|
||||
{ name: '咸阳', value: 43 },
|
||||
{ name: '铜川', value: 44 },
|
||||
{ name: '平度', value: 44 },
|
||||
{ name: '佛山', value: 44 },
|
||||
{ name: '海口', value: 44 },
|
||||
{ name: '江门', value: 45 },
|
||||
{ name: '章丘', value: 45 },
|
||||
{ name: '肇庆', value: 46 },
|
||||
{ name: '大连', value: 47 },
|
||||
{ name: '临汾', value: 47 },
|
||||
{ name: '吴江', value: 47 },
|
||||
{ name: '石嘴山', value: 49 },
|
||||
{ name: '沈阳', value: 50 },
|
||||
{ name: '苏州', value: 50 },
|
||||
{ name: '茂名', value: 50 },
|
||||
{ name: '嘉兴', value: 51 },
|
||||
{ name: '长春', value: 51 },
|
||||
{ name: '胶州', value: 52 },
|
||||
{ name: '银川', value: 52 },
|
||||
{ name: '张家港', value: 52 },
|
||||
{ name: '三门峡', value: 53 },
|
||||
{ name: '锦州', value: 54 },
|
||||
{ name: '南昌', value: 54 },
|
||||
{ name: '柳州', value: 54 },
|
||||
{ name: '三亚', value: 54 },
|
||||
{ name: '自贡', value: 56 },
|
||||
{ name: '吉林', value: 56 },
|
||||
{ name: '阳江', value: 57 },
|
||||
{ name: '泸州', value: 57 },
|
||||
{ name: '西宁', value: 57 },
|
||||
{ name: '宜宾', value: 58 },
|
||||
{ name: '呼和浩特', value: 58 },
|
||||
{ name: '成都', value: 58 },
|
||||
{ name: '大同', value: 58 },
|
||||
{ name: '镇江', value: 59 },
|
||||
{ name: '桂林', value: 59 },
|
||||
{ name: '张家界', value: 59 },
|
||||
{ name: '宜兴', value: 59 },
|
||||
{ name: '北海', value: 60 },
|
||||
{ name: '西安', value: 61 },
|
||||
{ name: '金坛', value: 62 },
|
||||
{ name: '东营', value: 62 },
|
||||
{ name: '牡丹江', value: 63 },
|
||||
{ name: '遵义', value: 63 },
|
||||
{ name: '绍兴', value: 63 },
|
||||
{ name: '扬州', value: 64 },
|
||||
{ name: '常州', value: 64 },
|
||||
{ name: '潍坊', value: 65 },
|
||||
{ name: '重庆', value: 66 },
|
||||
{ name: '台州', value: 67 },
|
||||
{ name: '南京', value: 67 },
|
||||
{ name: '滨州', value: 70 },
|
||||
{ name: '贵阳', value: 71 },
|
||||
{ name: '无锡', value: 71 },
|
||||
{ name: '本溪', value: 71 },
|
||||
{ name: '克拉玛依', value: 72 },
|
||||
{ name: '渭南', value: 72 },
|
||||
{ name: '马鞍山', value: 72 },
|
||||
{ name: '宝鸡', value: 72 },
|
||||
{ name: '焦作', value: 75 },
|
||||
{ name: '句容', value: 75 },
|
||||
{ name: '北京', value: 79 },
|
||||
{ name: '徐州', value: 79 },
|
||||
{ name: '衡水', value: 80 },
|
||||
{ name: '包头', value: 80 },
|
||||
{ name: '绵阳', value: 80 },
|
||||
{ name: '乌鲁木齐', value: 84 },
|
||||
{ name: '枣庄', value: 84 },
|
||||
{ name: '杭州', value: 84 },
|
||||
{ name: '淄博', value: 85 },
|
||||
{ name: '鞍山', value: 86 },
|
||||
{ name: '溧阳', value: 86 },
|
||||
{ name: '库尔勒', value: 86 },
|
||||
{ name: '安阳', value: 90 },
|
||||
{ name: '开封', value: 90 },
|
||||
{ name: '济南', value: 92 },
|
||||
{ name: '德阳', value: 93 },
|
||||
{ name: '温州', value: 95 },
|
||||
{ name: '九江', value: 96 },
|
||||
{ name: '邯郸', value: 98 },
|
||||
{ name: '临安', value: 99 },
|
||||
{ name: '兰州', value: 99 },
|
||||
{ name: '沧州', value: 100 },
|
||||
{ name: '临沂', value: 103 },
|
||||
{ name: '南充', value: 104 },
|
||||
{ name: '天津', value: 105 },
|
||||
{ name: '富阳', value: 106 },
|
||||
{ name: '泰安', value: 112 },
|
||||
{ name: '诸暨', value: 112 },
|
||||
{ name: '郑州', value: 113 },
|
||||
{ name: '哈尔滨', value: 114 },
|
||||
{ name: '聊城', value: 116 },
|
||||
{ name: '芜湖', value: 117 },
|
||||
{ name: '唐山', value: 119 },
|
||||
{ name: '平顶山', value: 119 },
|
||||
{ name: '邢台', value: 119 },
|
||||
{ name: '德州', value: 120 },
|
||||
{ name: '济宁', value: 120 },
|
||||
{ name: '荆州', value: 127 },
|
||||
{ name: '宜昌', value: 130 },
|
||||
{ name: '义乌', value: 132 },
|
||||
{ name: '丽水', value: 133 },
|
||||
{ name: '洛阳', value: 134 },
|
||||
{ name: '秦皇岛', value: 136 },
|
||||
{ name: '株洲', value: 143 },
|
||||
{ name: '石家庄', value: 147 },
|
||||
{ name: '莱芜', value: 148 },
|
||||
{ name: '常德', value: 152 },
|
||||
{ name: '保定', value: 153 },
|
||||
{ name: '湘潭', value: 154 },
|
||||
{ name: '金华', value: 157 },
|
||||
{ name: '岳阳', value: 169 },
|
||||
{ name: '长沙', value: 175 },
|
||||
{ name: '衢州', value: 177 },
|
||||
{ name: '廊坊', value: 93 },
|
||||
{ name: '菏泽', value: 194 },
|
||||
{ name: '合肥', value: 229 },
|
||||
{ name: '武汉', value: 273 },
|
||||
{ name: '大庆', value: 279 },
|
||||
];
|
||||
|
||||
// 地图经纬度数据
|
||||
export const echartsMapData = {
|
||||
海门: [121.15, 31.89],
|
||||
鄂尔多斯: [109.781327, 39.608266],
|
||||
招远: [120.38, 37.35],
|
||||
舟山: [122.207216, 29.985295],
|
||||
齐齐哈尔: [123.97, 47.33],
|
||||
盐城: [120.13, 33.38],
|
||||
赤峰: [118.87, 42.28],
|
||||
青岛: [120.33, 36.07],
|
||||
乳山: [121.52, 36.89],
|
||||
金昌: [102.188043, 38.520089],
|
||||
泉州: [118.58, 24.93],
|
||||
莱西: [120.53, 36.86],
|
||||
日照: [119.46, 35.42],
|
||||
胶南: [119.97, 35.88],
|
||||
南通: [121.05, 32.08],
|
||||
拉萨: [91.11, 29.97],
|
||||
云浮: [112.02, 22.93],
|
||||
梅州: [116.1, 24.55],
|
||||
文登: [122.05, 37.2],
|
||||
上海: [121.48, 31.22],
|
||||
攀枝花: [101.718637, 26.582347],
|
||||
威海: [122.1, 37.5],
|
||||
承德: [117.93, 40.97],
|
||||
厦门: [118.1, 24.46],
|
||||
汕尾: [115.375279, 22.786211],
|
||||
潮州: [116.63, 23.68],
|
||||
丹东: [124.37, 40.13],
|
||||
太仓: [121.1, 31.45],
|
||||
曲靖: [103.79, 25.51],
|
||||
烟台: [121.39, 37.52],
|
||||
福州: [119.3, 26.08],
|
||||
瓦房店: [121.979603, 39.627114],
|
||||
即墨: [120.45, 36.38],
|
||||
抚顺: [123.97, 41.97],
|
||||
玉溪: [102.52, 24.35],
|
||||
张家口: [114.87, 40.82],
|
||||
阳泉: [113.57, 37.85],
|
||||
莱州: [119.942327, 37.177017],
|
||||
湖州: [120.1, 30.86],
|
||||
汕头: [116.69, 23.39],
|
||||
昆山: [120.95, 31.39],
|
||||
宁波: [121.56, 29.86],
|
||||
湛江: [110.359377, 21.270708],
|
||||
揭阳: [116.35, 23.55],
|
||||
荣成: [122.41, 37.16],
|
||||
连云港: [119.16, 34.59],
|
||||
葫芦岛: [120.836932, 40.711052],
|
||||
常熟: [120.74, 31.64],
|
||||
东莞: [113.75, 23.04],
|
||||
河源: [114.68, 23.73],
|
||||
淮安: [119.15, 33.5],
|
||||
泰州: [119.9, 32.49],
|
||||
南宁: [108.33, 22.84],
|
||||
营口: [122.18, 40.65],
|
||||
惠州: [114.4, 23.09],
|
||||
江阴: [120.26, 31.91],
|
||||
蓬莱: [120.75, 37.8],
|
||||
韶关: [113.62, 24.84],
|
||||
嘉峪关: [98.289152, 39.77313],
|
||||
广州: [113.23, 23.16],
|
||||
延安: [109.47, 36.6],
|
||||
太原: [112.53, 37.87],
|
||||
清远: [113.01, 23.7],
|
||||
中山: [113.38, 22.52],
|
||||
昆明: [102.73, 25.04],
|
||||
寿光: [118.73, 36.86],
|
||||
盘锦: [122.070714, 41.119997],
|
||||
长治: [113.08, 36.18],
|
||||
深圳: [114.07, 22.62],
|
||||
珠海: [113.52, 22.3],
|
||||
宿迁: [118.3, 33.96],
|
||||
咸阳: [108.72, 34.36],
|
||||
铜川: [109.11, 35.09],
|
||||
平度: [119.97, 36.77],
|
||||
佛山: [113.11, 23.05],
|
||||
海口: [110.35, 20.02],
|
||||
江门: [113.06, 22.61],
|
||||
章丘: [117.53, 36.72],
|
||||
肇庆: [112.44, 23.05],
|
||||
大连: [121.62, 38.92],
|
||||
临汾: [111.5, 36.08],
|
||||
吴江: [120.63, 31.16],
|
||||
石嘴山: [106.39, 39.04],
|
||||
沈阳: [123.38, 41.8],
|
||||
苏州: [120.62, 31.32],
|
||||
茂名: [110.88, 21.68],
|
||||
嘉兴: [120.76, 30.77],
|
||||
长春: [125.35, 43.88],
|
||||
胶州: [120.03336, 36.264622],
|
||||
银川: [106.27, 38.47],
|
||||
张家港: [120.555821, 31.875428],
|
||||
三门峡: [111.19, 34.76],
|
||||
锦州: [121.15, 41.13],
|
||||
南昌: [115.89, 28.68],
|
||||
柳州: [109.4, 24.33],
|
||||
三亚: [109.511909, 18.252847],
|
||||
自贡: [104.778442, 29.33903],
|
||||
吉林: [126.57, 43.87],
|
||||
阳江: [111.95, 21.85],
|
||||
泸州: [105.39, 28.91],
|
||||
西宁: [101.74, 36.56],
|
||||
宜宾: [104.56, 29.77],
|
||||
呼和浩特: [111.65, 40.82],
|
||||
成都: [104.06, 30.67],
|
||||
大同: [113.3, 40.12],
|
||||
镇江: [119.44, 32.2],
|
||||
桂林: [110.28, 25.29],
|
||||
张家界: [110.479191, 29.117096],
|
||||
宜兴: [119.82, 31.36],
|
||||
北海: [109.12, 21.49],
|
||||
西安: [108.95, 34.27],
|
||||
金坛: [119.56, 31.74],
|
||||
东营: [118.49, 37.46],
|
||||
牡丹江: [129.58, 44.6],
|
||||
遵义: [106.9, 27.7],
|
||||
绍兴: [120.58, 30.01],
|
||||
扬州: [119.42, 32.39],
|
||||
常州: [119.95, 31.79],
|
||||
潍坊: [119.1, 36.62],
|
||||
重庆: [106.54, 29.59],
|
||||
台州: [121.420757, 28.656386],
|
||||
南京: [118.78, 32.04],
|
||||
滨州: [118.03, 37.36],
|
||||
贵阳: [106.71, 26.57],
|
||||
无锡: [120.29, 31.59],
|
||||
本溪: [123.73, 41.3],
|
||||
克拉玛依: [84.77, 45.59],
|
||||
渭南: [109.5, 34.52],
|
||||
马鞍山: [118.48, 31.56],
|
||||
宝鸡: [107.15, 34.38],
|
||||
焦作: [113.21, 35.24],
|
||||
句容: [119.16, 31.95],
|
||||
北京: [116.46, 39.92],
|
||||
徐州: [117.2, 34.26],
|
||||
衡水: [115.72, 37.72],
|
||||
包头: [110, 40.58],
|
||||
绵阳: [104.73, 31.48],
|
||||
乌鲁木齐: [87.68, 43.77],
|
||||
枣庄: [117.57, 34.86],
|
||||
杭州: [120.19, 30.26],
|
||||
淄博: [118.05, 36.78],
|
||||
鞍山: [122.85, 41.12],
|
||||
溧阳: [119.48, 31.43],
|
||||
库尔勒: [86.06, 41.68],
|
||||
安阳: [114.35, 36.1],
|
||||
开封: [114.35, 34.79],
|
||||
济南: [117, 36.65],
|
||||
德阳: [104.37, 31.13],
|
||||
温州: [120.65, 28.01],
|
||||
九江: [115.97, 29.71],
|
||||
邯郸: [114.47, 36.6],
|
||||
临安: [119.72, 30.23],
|
||||
兰州: [103.73, 36.03],
|
||||
沧州: [116.83, 38.33],
|
||||
临沂: [118.35, 35.05],
|
||||
南充: [106.110698, 30.837793],
|
||||
天津: [117.2, 39.13],
|
||||
富阳: [119.95, 30.07],
|
||||
泰安: [117.13, 36.18],
|
||||
诸暨: [120.23, 29.71],
|
||||
郑州: [113.65, 34.76],
|
||||
哈尔滨: [126.63, 45.75],
|
||||
聊城: [115.97, 36.45],
|
||||
芜湖: [118.38, 31.33],
|
||||
唐山: [118.02, 39.63],
|
||||
平顶山: [113.29, 33.75],
|
||||
邢台: [114.48, 37.05],
|
||||
德州: [116.29, 37.45],
|
||||
济宁: [116.59, 35.38],
|
||||
荆州: [112.239741, 30.335165],
|
||||
宜昌: [111.3, 30.7],
|
||||
义乌: [120.06, 29.32],
|
||||
丽水: [119.92, 28.45],
|
||||
洛阳: [112.44, 34.7],
|
||||
秦皇岛: [119.57, 39.95],
|
||||
株洲: [113.16, 27.83],
|
||||
石家庄: [114.48, 38.03],
|
||||
莱芜: [117.67, 36.19],
|
||||
常德: [111.69, 29.05],
|
||||
保定: [115.48, 38.85],
|
||||
湘潭: [112.91, 27.87],
|
||||
金华: [119.64, 29.12],
|
||||
岳阳: [113.09, 29.37],
|
||||
长沙: [113, 28.21],
|
||||
衢州: [118.88, 28.97],
|
||||
廊坊: [116.7, 39.53],
|
||||
菏泽: [115.480656, 35.23375],
|
||||
合肥: [117.27, 31.86],
|
||||
武汉: [114.31, 30.52],
|
||||
大庆: [125.03, 46.58],
|
||||
};
|
||||
62
web/src/views/fun/gridLayout/index.vue
Normal file
62
web/src/views/fun/gridLayout/index.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="grid-layout-container">
|
||||
<el-card shadow="hover" header="vue-grid-layout 拖拽布局演示">
|
||||
<el-alert
|
||||
title="感谢优秀的 `vue-grid-layout`,项目地址:https://github.com/jbaysolutions/vue-grid-layout"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<grid-layout
|
||||
v-model:layout="layouts"
|
||||
:col-num="12"
|
||||
:row-height="30"
|
||||
:is-draggable="true"
|
||||
:is-resizable="true"
|
||||
:is-mirrored="false"
|
||||
:vertical-compact="true"
|
||||
:margin="[10, 10]"
|
||||
:use-css-transforms="true"
|
||||
>
|
||||
<grid-item v-for="item in layouts" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i" :key="item.i">
|
||||
<div class="w100 h100 flex">
|
||||
<span class="flex-margin font14">{{ item.i }}</span>
|
||||
</div>
|
||||
</grid-item>
|
||||
</grid-layout>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FunGridLayout',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
layouts: [
|
||||
{ x: 0, y: 0, w: 2, h: 2, i: '0' },
|
||||
{ x: 2, y: 0, w: 2, h: 4, i: '1' },
|
||||
{ x: 4, y: 0, w: 2, h: 5, i: '2' },
|
||||
{ x: 6, y: 0, w: 2, h: 3, i: '3' },
|
||||
{ x: 8, y: 0, w: 2, h: 3, i: '4' },
|
||||
{ x: 10, y: 0, w: 2, h: 3, i: '5' },
|
||||
{ x: 0, y: 5, w: 2, h: 5, i: '6' },
|
||||
],
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.grid-layout-container {
|
||||
.vue-grid-item {
|
||||
background: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
44
web/src/views/fun/printJs/index.vue
Normal file
44
web/src/views/fun/printJs/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div ref="printRef">
|
||||
<el-card shadow="hover" header="打印演示">
|
||||
<el-alert
|
||||
title="感谢优秀的 `print-js`,项目地址:https://github.com/crabbly/Print.js。请在打印弹窗 `更多设置` 中开启 `背景图形。`"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<el-button @click="onPrintJs" size="default" type="primary">
|
||||
<SvgIcon name="iconfont icon-dayin" />
|
||||
点击打印演示
|
||||
</el-button>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
import printJs from 'print-js';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funPrintJs',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
printRef: null as null | HTMLDivElement,
|
||||
});
|
||||
// 打印点击
|
||||
const onPrintJs = () => {
|
||||
printJs({
|
||||
printable: state.printRef,
|
||||
type: 'html',
|
||||
css: ['//at.alicdn.com/t/font_2298093_o73r8wjdhlg.css', '//unpkg.com/element-plus/dist/index.css'],
|
||||
scanStyles: false,
|
||||
style: `@media print{.mb15{margin-bottom:15px;}.el-button--small i.iconfont{font-size: 12px !important;margin-right: 5px;}}`,
|
||||
});
|
||||
};
|
||||
return {
|
||||
onPrintJs,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
79
web/src/views/fun/qrcode/index.vue
Normal file
79
web/src/views/fun/qrcode/index.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="qrcode-container">
|
||||
<el-card shadow="hover" header="qrcodejs2 二维码生成">
|
||||
<el-alert
|
||||
title="感谢优秀的 `qrcodejs2`,项目地址:https://github.com/davidshimjs/qrcodejs"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<div class="qrcode-img-warp">
|
||||
<div class="mb30 mt30 qrcode-img">
|
||||
<div class="qrcode" ref="qrcodeRef"></div>
|
||||
</div>
|
||||
<el-button type="primary" size="default" @click="onInitQrcode">
|
||||
<el-icon>
|
||||
<ele-Refresh />
|
||||
</el-icon>
|
||||
重新生成
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent } from 'vue';
|
||||
import QRCode from 'qrcodejs2-fixes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funQrcode',
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const state = reactive({
|
||||
qrcode: '',
|
||||
});
|
||||
// 初始化生成二维码
|
||||
const initQrcode = () => {
|
||||
new QRCode(proxy.$refs.qrcodeRef, {
|
||||
text: `https://lyt-top.gitee.io/vue-next-admin-preview/#/login?t=${new Date().getTime()}`,
|
||||
width: 125,
|
||||
height: 125,
|
||||
colorDark: '#000000',
|
||||
colorLight: '#ffffff',
|
||||
});
|
||||
};
|
||||
// 重新生成
|
||||
const onInitQrcode = () => {
|
||||
proxy.$refs.qrcodeRef.innerHTML = '';
|
||||
initQrcode();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initQrcode();
|
||||
});
|
||||
return {
|
||||
onInitQrcode,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.qrcode-container {
|
||||
.qrcode-img-warp {
|
||||
text-align: center;
|
||||
.qrcode-img {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 125px;
|
||||
.qrcode {
|
||||
margin: auto;
|
||||
width: 125px;
|
||||
height: 125px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
54
web/src/views/fun/splitpanes/index.vue
Normal file
54
web/src/views/fun/splitpanes/index.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="splitpanes-container">
|
||||
<el-card shadow="hover" header="splitpanes 窗格拆分器">
|
||||
<el-alert
|
||||
title="感谢优秀的 `splitpanes`,项目地址:https://github.com/antoniandre/splitpanes"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<splitpanes class="default-theme" @resize="paneSize = $event[0].size" style="height: 500px">
|
||||
<pane :size="32"> 1 </pane>
|
||||
<pane :size="36">
|
||||
<splitpanes class="default-theme" :horizontal="true">
|
||||
<pane :size="100"> 2 </pane>
|
||||
<pane :size="100"> 3 </pane>
|
||||
</splitpanes>
|
||||
</pane>
|
||||
<pane :size="32"> 4 </pane>
|
||||
</splitpanes>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
import 'splitpanes/dist/splitpanes.css';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funSplitpanes',
|
||||
components: { Splitpanes, Pane },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
paneSize: 50,
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.splitpanes__pane {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
position: relative;
|
||||
font-size: 70px;
|
||||
color: var(--el-color-primary-light-5);
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
background-color: var(--el-color-primary) !important;
|
||||
}
|
||||
</style>
|
||||
118
web/src/views/fun/tagsView/index.vue
Normal file
118
web/src/views/fun/tagsView/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="fun-tagsview">
|
||||
<NoticeBar
|
||||
text="已删除非当前页 tagsView 演示,后续有时间可以再加回来!,tagsview 支持多标签(参数不同)、单标签共用(参数不同)"
|
||||
background="#ecf5ff"
|
||||
color="#409eff"
|
||||
/>
|
||||
<el-card shadow="hover" header="tagsView 当前页演示" class="mt15">
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default" @click="refreshCurrentTagsView">
|
||||
<el-icon>
|
||||
<ele-RefreshRight />
|
||||
</el-icon>
|
||||
刷新当前页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default" @click="closeCurrentTagsView">
|
||||
<el-icon>
|
||||
<ele-Close />
|
||||
</el-icon>
|
||||
关闭当前页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="warning" size="default" @click="closeOtherTagsView">
|
||||
<el-icon>
|
||||
<ele-CircleClose />
|
||||
</el-icon>
|
||||
关闭其它
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default" @click="closeAllTagsView">
|
||||
<el-icon>
|
||||
<ele-FolderDelete />
|
||||
</el-icon>
|
||||
全部关闭
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default" @click="openCurrenFullscreen">
|
||||
<el-icon>
|
||||
<ele-FullScreen />
|
||||
</el-icon>
|
||||
当前页全屏
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { getCurrentInstance, reactive, toRefs, defineComponent } from 'vue';
|
||||
import NoticeBar from '/@/components/noticeBar/index.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funTagsView',
|
||||
components: { NoticeBar },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const route = useRoute();
|
||||
const state = reactive({});
|
||||
// 0 刷新当前,1 关闭当前,2 关闭其它,3 关闭全部 4 当前页全屏
|
||||
// 1、刷新当前 tagsView
|
||||
const refreshCurrentTagsView = () => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 0, ...route }));
|
||||
};
|
||||
// 2、关闭当前 tagsView
|
||||
const closeCurrentTagsView = () => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
|
||||
};
|
||||
// 3、关闭其它 tagsView
|
||||
const closeOtherTagsView = () => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 2, ...route }));
|
||||
};
|
||||
// 4、关闭全部 tagsView
|
||||
const closeAllTagsView = () => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 3, ...route }));
|
||||
};
|
||||
// 5、开启当前页面全屏
|
||||
const openCurrenFullscreen = () => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 4, ...route }));
|
||||
};
|
||||
return {
|
||||
refreshCurrentTagsView,
|
||||
closeCurrentTagsView,
|
||||
closeOtherTagsView,
|
||||
closeAllTagsView,
|
||||
openCurrenFullscreen,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.fun-tagsview {
|
||||
.fun-tagsview-from-item {
|
||||
:deep(.el-form-item__content) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
web/src/views/fun/wangEditor/index.vue
Normal file
33
web/src/views/fun/wangEditor/index.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="editor-container">
|
||||
<el-card shadow="hover" header="wangeditor富文本编辑器">
|
||||
<el-alert
|
||||
title="感谢优秀的 `wangeditor`,项目地址:https://github.com/wangeditor-team/wangEditor"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mb15"
|
||||
></el-alert>
|
||||
<Editor :is-disable="false" v-model="editorVal" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import Editor from '/@/components/editor/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'funWangEditor',
|
||||
components: { Editor },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
editorVal: '',
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
643
web/src/views/home/index.vue
Normal file
643
web/src/views/home/index.vue
Normal file
File diff suppressed because one or more lines are too long
37
web/src/views/limits/backEnd/page/index.vue
Normal file
37
web/src/views/limits/backEnd/page/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
title="温馨提示:1、此页面无法模拟后端控制路由,因为 `gitee` 上所请求的 `json` 菜单数据线上会出现跨域的情况(json地址:
|
||||
https://gitee.com/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json)。2、本地接口请求文件位置:`/src/api/menu/index.ts`。
|
||||
3、拉取代码后本地请求查看后端控制页面路由效果:`/src/store/modules/themeConfig.ts`中开启(isRequestRoutes 为 true,则开启后端控制路由)。
|
||||
4、此页面效果只作为演示使用,若出现不可逆转的bug,请尝试 `F5` 刷新页面。5、默认启用的是 `前端控制路由`。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
></el-alert>
|
||||
<el-button type="primary" size="default" class="mt15" @click="onGoToFrontEndPage">
|
||||
<el-icon>
|
||||
<ele-Position />
|
||||
</el-icon>
|
||||
立即前往前端控制路由
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'limitsBackEndEndPage',
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
// 立即前往前端控制路由
|
||||
const onGoToFrontEndPage = () => {
|
||||
router.push('/limits/frontEnd/page');
|
||||
};
|
||||
return {
|
||||
onGoToFrontEndPage,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
388
web/src/views/limits/frontEnd/btn/index.vue
Normal file
388
web/src/views/limits/frontEnd/btn/index.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<div>
|
||||
<LimitsFrontEndPage />
|
||||
<!-- 演示1:组件方式 -->
|
||||
<el-card shadow="hover" header="演示1:组件方式" class="mt15">
|
||||
<el-row class="mb10" style="color: #808080">单个权限验证(:value="xxx"):</el-row>
|
||||
<div class="flex-warp">
|
||||
<Auth :value="'btn.add'">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auth>
|
||||
<Auth :value="'btn.edit'">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auth>
|
||||
<Auth :value="'btn.del'">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auth>
|
||||
<Auth :value="'btn.link'">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default">
|
||||
<el-icon>
|
||||
<ele-Link />
|
||||
</el-icon>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auth>
|
||||
</div>
|
||||
|
||||
<el-row class="mb10 mt10" style="color: #808080">多个权限验证,满足一个则显示(:value="[xxx,xxx]"):</el-row>
|
||||
<div class="flex-warp">
|
||||
<Auths :value="['btn.addsss', 'btn.edit', 'btn.delsss', 'btn.linksss']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auths>
|
||||
<Auths :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auths>
|
||||
<Auths :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auths>
|
||||
<Auths :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default">
|
||||
<el-icon>
|
||||
<ele-Link />
|
||||
</el-icon>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</Auths>
|
||||
</div>
|
||||
|
||||
<el-row class="mb10 mt10" style="color: #808080">多个权限验证,全部满足则显示(:value="[xxx,xxx]"):</el-row>
|
||||
<div class="flex-warp">
|
||||
<AuthAll :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</AuthAll>
|
||||
<AuthAll :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</AuthAll>
|
||||
<AuthAll :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</AuthAll>
|
||||
<AuthAll :value="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default">
|
||||
<el-icon>
|
||||
<ele-Link />
|
||||
</el-icon>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</AuthAll>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 演示2:指令方式 -->
|
||||
<el-card shadow="hover" header="演示2:指令方式(页面初始化时执行)" class="mt15">
|
||||
<el-row class="mb10" style="color: #808080">单个权限验证(v-auth="xxx"):</el-row>
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item" v-auth="'btn.add'">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auth="'btn.edit'">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auth="'btn.del'">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auth="'btn.link'">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default">
|
||||
<el-icon>
|
||||
<ele-Link />
|
||||
</el-icon>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row class="mb10 mt10" style="color: #808080">多个权限验证,满足一个则显示(v-auths="[xxx,xxx]"):</el-row>
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item" v-auths="['btn.addsss', 'btn.edit', 'btn.delsss', 'btn.linksss']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auths="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auths="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auths="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default">
|
||||
<el-icon>
|
||||
<ele-Link />
|
||||
</el-icon>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row class="mb10 mt10" style="color: #808080">多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]"):</el-row>
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item" v-auth-all="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auth-all="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auth-all="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item" v-auth-all="['btn.add', 'btn.edit', 'btn.del', 'btn.link']">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default">
|
||||
<el-icon>
|
||||
<ele-Link />
|
||||
</el-icon>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 演示3:函数方式 -->
|
||||
<el-card shadow="hover" header="演示3:函数方式(点击按钮查看有无权限,用于判断)" class="mt15">
|
||||
<el-row class="mb10" style="color: #808080">auth('xxx')、auths(['xxx','xxx'])、authAll(['xxx','xxx']):</el-row>
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default" @click="onAuthClick">
|
||||
<el-icon>
|
||||
<ele-DocumentAdd />
|
||||
</el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default" @click="onAuthsClick">
|
||||
<el-icon>
|
||||
<ele-Edit />
|
||||
</el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default" @click="onAuthAllClick">
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import LimitsFrontEndPage from '/@/views/limits/frontEnd/page/index.vue';
|
||||
import Auth from '/@/components/auth/auth.vue';
|
||||
import Auths from '/@/components/auth/auths.vue';
|
||||
import AuthAll from '/@/components/auth/authAll.vue';
|
||||
import { auth, auths, authAll } from '/@/utils/authFunction';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'limitsFrontEndBtn',
|
||||
components: { LimitsFrontEndPage, Auth, Auths, AuthAll },
|
||||
setup() {
|
||||
// 单个权限验证
|
||||
const onAuthClick = () => {
|
||||
if (!auth('btn.add')) ElMessage.error('抱歉,您没有权限!');
|
||||
else ElMessage.success('恭喜,您有权限!');
|
||||
};
|
||||
// 多个权限验证,满足一个则为 true
|
||||
const onAuthsClick = () => {
|
||||
if (!auths(['btn.add', 'btn.edit', 'btn.del', 'btn.link'])) ElMessage.error('抱歉,您没有权限!');
|
||||
else ElMessage.success('恭喜,您有权限!');
|
||||
};
|
||||
// 多个权限验证,全部满足则为 true
|
||||
const onAuthAllClick = () => {
|
||||
if (!authAll(['btn.add', 'btn.edit', 'btn.del', 'btn.link'])) ElMessage.error('抱歉,您没有权限!');
|
||||
else ElMessage.success('恭喜,您有权限!');
|
||||
};
|
||||
return {
|
||||
onAuthClick,
|
||||
onAuthsClick,
|
||||
onAuthAllClick,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
66
web/src/views/limits/frontEnd/page/index.vue
Normal file
66
web/src/views/limits/frontEnd/page/index.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
title="温馨提示:此权限页面代码及效果只作为演示使用,若出现不可逆转的bug,请尝试 `F5` 刷新页面。若实际项目中非要实现此用户权限切换功能,
|
||||
请在切换方法 `onRadioChange` 最后面添加刷新代码 `window.location.reload()`。 请注意:按钮权限页面中的演示2(指令模式)、演示3(函数模式)
|
||||
切换用户时无法动态演示,想要动态演示,请按 `F5` 或者添加 `window.location.reload()`。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
></el-alert>
|
||||
<el-alert
|
||||
:title="`当前用户页面权限:[${userInfos.roles}],当前用户按钮权限:[${userInfos.authBtnList}]`"
|
||||
type="success"
|
||||
:closable="false"
|
||||
class="mt15"
|
||||
></el-alert>
|
||||
<el-card shadow="hover" header="切换用户演示,前端控制不同用户显示不同页面、按钮权限" class="mt15">
|
||||
<el-radio-group v-model="userAuth" size="default" @change="onRadioChange">
|
||||
<el-radio-button label="admin"></el-radio-button>
|
||||
<el-radio-button label="common"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import Cookies from 'js-cookie';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
import { frontEndsResetRoute, setAddRoute, setFilterMenuAndCacheTagsViewRoutes } from '/@/router/frontEnd';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'limitsFrontEndPage',
|
||||
setup() {
|
||||
const storesUserInfo = useUserInfo();
|
||||
const { userInfos } = storeToRefs(storesUserInfo);
|
||||
const state = reactive({
|
||||
val: '',
|
||||
userAuth: '',
|
||||
});
|
||||
// 初始化用户权限
|
||||
const initUserAuth = () => {
|
||||
state.userAuth = (<any>userInfos).value.roles[0];
|
||||
};
|
||||
// 用户权限改变时
|
||||
const onRadioChange = async () => {
|
||||
// 模拟数据
|
||||
frontEndsResetRoute();
|
||||
Cookies.set('userName', state.userAuth);
|
||||
// 模拟切换不同权限用户
|
||||
await storesUserInfo.setUserInfos();
|
||||
await setAddRoute();
|
||||
setFilterMenuAndCacheTagsViewRoutes();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initUserAuth();
|
||||
});
|
||||
return {
|
||||
userInfos,
|
||||
onRadioChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
177
web/src/views/login/component/account.vue
Normal file
177
web/src/views/login/component/account.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<el-form size="large" class="login-content-form">
|
||||
<el-form-item class="login-animation1">
|
||||
<el-input type="text" :placeholder="$t('message.account.accountPlaceholder1')" v-model="ruleForm.userName" clearable autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon"><ele-User /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="login-animation2">
|
||||
<el-input
|
||||
:type="isShowPassword ? 'text' : 'password'"
|
||||
:placeholder="$t('message.account.accountPlaceholder2')"
|
||||
v-model="ruleForm.password"
|
||||
autocomplete="off"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon"><ele-Unlock /></el-icon>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<i
|
||||
class="iconfont el-input__icon login-content-password"
|
||||
:class="isShowPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
|
||||
@click="isShowPassword = !isShowPassword"
|
||||
>
|
||||
</i>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="login-animation3">
|
||||
<el-col :span="15">
|
||||
<el-input
|
||||
type="text"
|
||||
maxlength="4"
|
||||
:placeholder="$t('message.account.accountPlaceholder3')"
|
||||
v-model="ruleForm.code"
|
||||
clearable
|
||||
autocomplete="off"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon"><ele-Position /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="1"></el-col>
|
||||
<el-col :span="8">
|
||||
<el-button class="login-content-code">1234</el-button>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item class="login-animation4">
|
||||
<el-button type="primary" class="login-content-submit" round @click="onSignIn" :loading="loading.signIn">
|
||||
<span>{{ $t('message.account.accountBtnText') }}</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Cookies from 'js-cookie';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { initFrontEndControlRoutes } from '/@/router/frontEnd';
|
||||
import { initBackEndControlRoutes } from '/@/router/backEnd';
|
||||
import { Session } from '/@/utils/storage';
|
||||
import { formatAxis } from '/@/utils/formatTime';
|
||||
import { NextLoading } from '/@/utils/loading';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'loginAccount',
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
isShowPassword: false,
|
||||
ruleForm: {
|
||||
userName: 'admin',
|
||||
password: '123456',
|
||||
code: '1234',
|
||||
},
|
||||
loading: {
|
||||
signIn: false,
|
||||
},
|
||||
});
|
||||
// 时间获取
|
||||
const currentTime = computed(() => {
|
||||
return formatAxis(new Date());
|
||||
});
|
||||
// 登录
|
||||
const onSignIn = async () => {
|
||||
state.loading.signIn = true;
|
||||
// 存储 token 到浏览器缓存
|
||||
Session.set('token', Math.random().toString(36).substr(0));
|
||||
// 模拟数据,对接接口时,记得删除多余代码及对应依赖的引入。用于 `/src/stores/userInfo.ts` 中不同用户登录判断(模拟数据)
|
||||
Cookies.set('userName', state.ruleForm.userName);
|
||||
if (!themeConfig.value.isRequestRoutes) {
|
||||
// 前端控制路由,2、请注意执行顺序
|
||||
await initFrontEndControlRoutes();
|
||||
signInSuccess();
|
||||
} else {
|
||||
// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
||||
await initBackEndControlRoutes();
|
||||
// 执行完 initBackEndControlRoutes,再执行 signInSuccess
|
||||
signInSuccess();
|
||||
}
|
||||
};
|
||||
// 登录成功后的跳转
|
||||
const signInSuccess = () => {
|
||||
// 初始化登录成功时间问候语
|
||||
let currentTimeInfo = currentTime.value;
|
||||
// 登录成功,跳到转首页
|
||||
// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
|
||||
if (route.query?.redirect) {
|
||||
router.push({
|
||||
path: <string>route.query?.redirect,
|
||||
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
|
||||
});
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
// 登录成功提示
|
||||
// 关闭 loading
|
||||
state.loading.signIn = true;
|
||||
const signInText = t('message.signInText');
|
||||
ElMessage.success(`${currentTimeInfo},${signInText}`);
|
||||
// 添加 loading,防止第一次进入界面时出现短暂空白
|
||||
NextLoading.start();
|
||||
};
|
||||
return {
|
||||
onSignIn,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.login-content-form {
|
||||
margin-top: 20px;
|
||||
@for $i from 1 through 4 {
|
||||
.login-animation#{$i} {
|
||||
opacity: 0;
|
||||
animation-name: error-num;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: calc($i/10) + s;
|
||||
}
|
||||
}
|
||||
.login-content-password {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
.login-content-code {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
letter-spacing: 5px;
|
||||
}
|
||||
.login-content-submit {
|
||||
width: 100%;
|
||||
letter-spacing: 2px;
|
||||
font-weight: 300;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
84
web/src/views/login/component/mobile.vue
Normal file
84
web/src/views/login/component/mobile.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<el-form size="large" class="login-content-form">
|
||||
<el-form-item class="login-animation1">
|
||||
<el-input type="text" :placeholder="$t('message.mobile.placeholder1')" v-model="ruleForm.userName" clearable autocomplete="off">
|
||||
<template #prefix>
|
||||
<i class="iconfont icon-dianhua el-input__icon"></i>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="login-animation2">
|
||||
<el-col :span="15">
|
||||
<el-input type="text" maxlength="4" :placeholder="$t('message.mobile.placeholder2')" v-model="ruleForm.code" clearable autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon"><ele-Position /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="1"></el-col>
|
||||
<el-col :span="8">
|
||||
<el-button class="login-content-code">{{ $t('message.mobile.codeText') }}</el-button>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item class="login-animation3">
|
||||
<el-button round type="primary" class="login-content-submit">
|
||||
<span>{{ $t('message.mobile.btnText') }}</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<div class="font12 mt30 login-animation4 login-msg">{{ $t('message.mobile.msgText') }}</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface LoginMobileState {
|
||||
userName: any;
|
||||
code: string | number | undefined;
|
||||
}
|
||||
|
||||
// 定义对象与类型
|
||||
const ruleForm: LoginMobileState = {
|
||||
userName: '',
|
||||
code: '',
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'loginMobile',
|
||||
setup() {
|
||||
const state = reactive({ ruleForm });
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.login-content-form {
|
||||
margin-top: 20px;
|
||||
@for $i from 1 through 4 {
|
||||
.login-animation#{$i} {
|
||||
opacity: 0;
|
||||
animation-name: error-num;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: calc($i/10) + s;
|
||||
}
|
||||
}
|
||||
.login-content-code {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
.login-content-submit {
|
||||
width: 100%;
|
||||
letter-spacing: 2px;
|
||||
font-weight: 300;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.login-msg {
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
59
web/src/views/login/component/scan.vue
Normal file
59
web/src/views/login/component/scan.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="login-scan-container">
|
||||
<div ref="qrcodeRef"></div>
|
||||
<div class="font12 mt20 login-msg">{{ $t('message.scan.text') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent, onMounted } from 'vue';
|
||||
import QRCode from 'qrcodejs2-fixes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'loginScan',
|
||||
setup() {
|
||||
const qrcodeRef = ref<HTMLElement | null>(null);
|
||||
// 初始化生成二维码
|
||||
const initQrcode = () => {
|
||||
(qrcodeRef.value as HTMLElement).innerHTML = '';
|
||||
new QRCode(qrcodeRef.value, {
|
||||
text: `https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi`,
|
||||
width: 260,
|
||||
height: 260,
|
||||
colorDark: '#000000',
|
||||
colorLight: '#ffffff',
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initQrcode();
|
||||
});
|
||||
return { qrcodeRef };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.login-scan-animation {
|
||||
opacity: 0;
|
||||
animation-name: error-num;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
.login-scan-container {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
@extend .login-scan-animation;
|
||||
animation-delay: 0.1s;
|
||||
:deep(img) {
|
||||
margin: auto;
|
||||
}
|
||||
.login-msg {
|
||||
color: var(--el-text-color-placeholder);
|
||||
@extend .login-scan-animation;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
200
web/src/views/login/index.vue
Normal file
200
web/src/views/login/index.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<div class="login-icon-group">
|
||||
<div class="login-icon-group-title">
|
||||
<img :src="logoMini" />
|
||||
<div class="login-icon-group-title-text font25">{{ getThemeConfig.globalViceTitle }}</div>
|
||||
</div>
|
||||
<img :src="loginIconTwo" class="login-icon-group-icon" />
|
||||
</div>
|
||||
<div class="login-content">
|
||||
<div class="login-content-main">
|
||||
<h4 class="login-content-title ml15">{{ getThemeConfig.globalTitle }}后台模板</h4>
|
||||
<div v-if="!isScan">
|
||||
<el-tabs v-model="tabsActiveName">
|
||||
<el-tab-pane :label="$t('message.label.one1')" name="account">
|
||||
<Account />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('message.label.two2')" name="mobile">
|
||||
<Mobile />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<Scan v-if="isScan" />
|
||||
<div class="login-content-main-sacn" @click="isScan = !isScan">
|
||||
<i class="iconfont" :class="isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
|
||||
<div class="login-content-main-sacn-delta"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, defineComponent, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import logoMini from '/@/assets/logo-mini.svg';
|
||||
import loginIconTwo from '/@/assets/login-icon-two.svg';
|
||||
import { NextLoading } from '/@/utils/loading';
|
||||
import Account from '/@/views/login/component/account.vue';
|
||||
import Mobile from '/@/views/login/component/mobile.vue';
|
||||
import Scan from '/@/views/login/component/scan.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface LoginState {
|
||||
tabsActiveName: string;
|
||||
isScan: boolean;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'loginIndex',
|
||||
components: { Account, Mobile, Scan },
|
||||
setup() {
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const state = reactive<LoginState>({
|
||||
tabsActiveName: 'account',
|
||||
isScan: false,
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return themeConfig.value;
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
NextLoading.done();
|
||||
});
|
||||
return {
|
||||
logoMini,
|
||||
loginIconTwo,
|
||||
getThemeConfig,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.login-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: var(--el-color-white);
|
||||
.login-icon-group {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.login-icon-group-title {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
&-text {
|
||||
padding-left: 15px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 60%;
|
||||
overflow: hidden;
|
||||
height: 80%;
|
||||
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='1200' height='770' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M58.4 47.77C104.6 59.51 135.26 67.37 162.11 78.04C188.97 88.72 226.33 102.69 265.92 123.55C305.51 144.4 366.96 167.09 441.43 121.52C515.9 75.95 546.48 61.01 577.69 46.27C608.9 31.53 625.86 23.69 680.26 12.28C734.65 0.87 837.29 10.7 867.29 21.8C897.29 32.9 935.51 51.9 962.21 95.45C988.9 139.01 972.91 177.36 951.37 221.39C929.83 265.43 883.49 306 890.44 337.33C897.4 368.66 974.73 412.18 974.73 411.47C974.73 412.18 1066.36 457.62 1106.36 491.06C1146.36 524.5 1178.8 563.36 1184.03 579.63C1189.26 595.9 1200.4 622.49 1181.55 676.88C1162.71 731.26 1127.16 764.32 1115.31 778.64C1103.45 792.96 5.34 783.61 4.32 784.63C3.3 785.65 -172.34 2.38 1.13 35.04L58.4 47.77L58.4 47.77Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
||||
background: var(--el-color-primary-light-5);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
width: 150px;
|
||||
height: 300px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='150' height='300' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M-0.56 -0.28C41.94 36.17 67.73 18.94 93.33 33.96C118.93 48.98 107.58 73.56 101.94 89.76C96.29 105.96 50.09 217.83 47.87 231.18C45.64 244.52 46.02 255.2 64.4 270.05C82.79 284.91 121.99 292.31 111.98 289.81C101.97 287.32 153.96 301.48 151.83 299.9C149.69 298.32 149.98 -1.36 149.71 -1.18C149.98 -1.36 -43.06 -36.74 -0.56 -0.28L-0.56 -0.28Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
||||
background: var(--el-color-primary-light-5);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
&-icon {
|
||||
width: 60%;
|
||||
height: 70%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
.login-content {
|
||||
width: 500px;
|
||||
padding: 20px;
|
||||
position: absolute;
|
||||
right: 200px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) translate3d(0, 0, 0);
|
||||
background-color: var(--el-color-white);
|
||||
border: 5px solid var(--el-color-primary-light-8);
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
height: 460px;
|
||||
.login-content-main {
|
||||
margin: 0 auto;
|
||||
width: 80%;
|
||||
.login-content-title {
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: 500;
|
||||
font-size: 22px;
|
||||
text-align: center;
|
||||
letter-spacing: 4px;
|
||||
margin: 15px 0 30px;
|
||||
white-space: nowrap;
|
||||
z-index: 5;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.login-content-main-sacn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all ease 0.3s;
|
||||
color: var(--el-text-color-primary);
|
||||
&-delta {
|
||||
position: absolute;
|
||||
width: 35px;
|
||||
height: 70px;
|
||||
z-index: 2;
|
||||
top: 2px;
|
||||
right: 21px;
|
||||
background: var(--el-color-white);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
transition: all ease 0.3s;
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
i {
|
||||
width: 47px;
|
||||
height: 50px;
|
||||
display: inline-block;
|
||||
font-size: 48px;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
164
web/src/views/make/noticeBar/index.vue
Normal file
164
web/src/views/make/noticeBar/index.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="notice-bar-container">
|
||||
<el-card shadow="hover" header="滚动通知栏:默认">
|
||||
<NoticeBar
|
||||
text="🎉🎉🔥基于vue3.x 、Typescript、vite、Element plus等,适配手机、平板、pc
|
||||
的后台开源免费模板库(vue2.x请切换vue-prev-admin分支),仓库地址:https://gitee.com/lyt-top/vue-next-admin"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="滚动通知栏:设置样式" class="mt15">
|
||||
<NoticeBar
|
||||
text="🎉🎉🔥基于vue3.x 、Typescript、vite、Element plus等,适配手机、平板、pc
|
||||
的后台开源免费模板库(vue2.x请切换vue-prev-admin分支),仓库地址:https://gitee.com/lyt-top/vue-next-admin"
|
||||
leftIcon="iconfont icon-tongzhi2"
|
||||
rightIcon="ele-ArrowRight"
|
||||
background="#ecf5ff"
|
||||
color="#409eff"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="滚动通知栏:搭配 NoticeBar 和 Carousel 走马灯 组件可以实现垂直滚动的效果" class="mt15">
|
||||
<NoticeBar :scrollable="true">
|
||||
<el-carousel height="40px" direction="vertical" :autoplay="true" indicator-position="none" :interval="3000">
|
||||
<el-carousel-item v-for="v in noticeList" :key="v">{{ v }} </el-carousel-item>
|
||||
</el-carousel>
|
||||
</NoticeBar>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="滚动通知栏:参数" class="mt15">
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column prop="a1" label="参数"> </el-table-column>
|
||||
<el-table-column prop="a2" label="说明"> </el-table-column>
|
||||
<el-table-column prop="a3" label="类型"> </el-table-column>
|
||||
<el-table-column prop="a4" label="可选值"> </el-table-column>
|
||||
<el-table-column prop="a5" label="默认值"> </el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="图标选择器(宽度自动):事件" class="mt15">
|
||||
<el-table :data="tableData1" style="width: 100%">
|
||||
<el-table-column prop="a1" label="事件名称"> </el-table-column>
|
||||
<el-table-column prop="a2" label="说明"> </el-table-column>
|
||||
<el-table-column prop="a3" label="类型"> </el-table-column>
|
||||
<el-table-column prop="a4" label="回调参数"> </el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import NoticeBar from '/@/components/noticeBar/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'makeNoticeBar',
|
||||
components: { NoticeBar },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
noticeList: [
|
||||
'🎉🎉🔥基于vue3.x 、Typescript、vite、Element plus等',
|
||||
'适配手机、平板、pc的后台开源免费模板库(vue2.x请切换vue-prev-admin分支)',
|
||||
'仓库地址:https://gitee.com/lyt-top/vue-next-admin',
|
||||
'演示地址:https://lyt-top.gitee.io/vue-next-admin-preview/#/login',
|
||||
],
|
||||
tableData: [
|
||||
{
|
||||
a1: 'mode',
|
||||
a2: '通知栏模式,用于右侧 icon 图标点击',
|
||||
a3: 'string',
|
||||
a4: 'closeable / link',
|
||||
a5: '',
|
||||
},
|
||||
{
|
||||
a1: 'text',
|
||||
a2: '通知文本内容,scrollable 为 false 时生效',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '',
|
||||
},
|
||||
{
|
||||
a1: 'color',
|
||||
a2: '通知文本颜色',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '#e6a23c',
|
||||
},
|
||||
{
|
||||
a1: 'background',
|
||||
a2: '通知背景色',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '#fdf6ec',
|
||||
},
|
||||
{
|
||||
a1: 'size',
|
||||
a2: '字体大小,单位px',
|
||||
a3: 'number / string',
|
||||
a4: '',
|
||||
a5: '14',
|
||||
},
|
||||
{
|
||||
a1: 'height',
|
||||
a2: '通知栏高度,单位px',
|
||||
a3: 'number / string',
|
||||
a4: '',
|
||||
a5: '40',
|
||||
},
|
||||
{
|
||||
a1: 'delay',
|
||||
a2: '动画延迟时间 (s)',
|
||||
a3: 'number / string',
|
||||
a4: '',
|
||||
a5: '1',
|
||||
},
|
||||
{
|
||||
a1: 'speed',
|
||||
a2: '滚动速率 (px/s)',
|
||||
a3: 'number / string',
|
||||
a4: '',
|
||||
a5: '100',
|
||||
},
|
||||
{
|
||||
a1: 'scrollable',
|
||||
a2: '是否开启垂直滚动',
|
||||
a3: 'boolean',
|
||||
a4: 'true',
|
||||
a5: 'false',
|
||||
},
|
||||
{
|
||||
a1: 'leftIcon',
|
||||
a2: '自定义左侧图标',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '',
|
||||
},
|
||||
{
|
||||
a1: 'rightIcon',
|
||||
a2: '自定义右侧图标',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '',
|
||||
},
|
||||
],
|
||||
tableData1: [
|
||||
{
|
||||
a1: 'close',
|
||||
a2: '通知栏模式(mode)closeable 时回调事件',
|
||||
a3: 'function',
|
||||
a4: '',
|
||||
},
|
||||
{
|
||||
a1: 'link',
|
||||
a2: '通知栏模式(mode)link 时回调事件',
|
||||
a3: 'function',
|
||||
a4: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
126
web/src/views/make/selector/index.vue
Normal file
126
web/src/views/make/selector/index.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="selector-container">
|
||||
<el-card shadow="hover" header="图标选择器(宽度自动):">
|
||||
<IconSelector @get="onGetIcon" @clear="onClearIcon" v-model="modelIcon" />
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="图标选择器(宽度自动):参数" class="mt15">
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column prop="a1" label="参数"> </el-table-column>
|
||||
<el-table-column prop="a2" label="说明"> </el-table-column>
|
||||
<el-table-column prop="a3" label="类型"> </el-table-column>
|
||||
<el-table-column prop="a4" label="可选值"> </el-table-column>
|
||||
<el-table-column prop="a5" label="默认值"> </el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="图标选择器(宽度自动):事件" class="mt15">
|
||||
<el-table :data="tableData1" style="width: 100%">
|
||||
<el-table-column prop="a1" label="事件名称"> </el-table-column>
|
||||
<el-table-column prop="a2" label="说明"> </el-table-column>
|
||||
<el-table-column prop="a3" label="类型"> </el-table-column>
|
||||
<el-table-column prop="a4" label="回调参数"> </el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import IconSelector from '/@/components/iconSelector/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'makeSelector',
|
||||
components: { IconSelector },
|
||||
setup() {
|
||||
const state = reactive({
|
||||
modelIcon: '',
|
||||
tableData: [
|
||||
{
|
||||
a1: 'prepend',
|
||||
a2: '输入框前置内容,只能字体图标',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: 'ele-Pointer',
|
||||
},
|
||||
{
|
||||
a1: 'placeholder',
|
||||
a2: '输入框占位文本',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '请输入内容搜索图标或者选择图标',
|
||||
},
|
||||
{
|
||||
a1: 'size',
|
||||
a2: '尺寸',
|
||||
a3: 'string',
|
||||
a4: 'large / default / small',
|
||||
a5: 'default',
|
||||
},
|
||||
{
|
||||
a1: 'title',
|
||||
a2: '弹窗标题',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '请选择图标',
|
||||
},
|
||||
{
|
||||
a1: 'type',
|
||||
a2: 'icon 图标类型',
|
||||
a3: 'string',
|
||||
a4: 'ali / ele / awe / all',
|
||||
a5: 'ele',
|
||||
},
|
||||
{
|
||||
a1: 'disabled',
|
||||
a2: '禁用',
|
||||
a3: 'boolean',
|
||||
a4: 'true',
|
||||
a5: 'false',
|
||||
},
|
||||
{
|
||||
a1: 'clearable',
|
||||
a2: '是否可清空',
|
||||
a3: 'boolean',
|
||||
a4: 'false',
|
||||
a5: 'true',
|
||||
},
|
||||
{
|
||||
a1: 'emptyDescription',
|
||||
a2: '自定义空状态描述文字',
|
||||
a3: 'String',
|
||||
a4: '',
|
||||
a5: '无相关图标',
|
||||
},
|
||||
],
|
||||
tableData1: [
|
||||
{
|
||||
a1: 'get',
|
||||
a2: '获取当前点击的 icon 图标',
|
||||
a3: 'function',
|
||||
a4: '(icon: string)',
|
||||
},
|
||||
{
|
||||
a1: 'clear',
|
||||
a2: '清空当前点击的 icon 图标',
|
||||
a3: 'function',
|
||||
a4: '(icon: string)',
|
||||
},
|
||||
],
|
||||
});
|
||||
// 获取当前点击的 icon 图标
|
||||
const onGetIcon = (icon: string) => {
|
||||
console.log(icon);
|
||||
};
|
||||
// 清空当前点击的 icon 图标
|
||||
const onClearIcon = (icon: string) => {
|
||||
console.log(icon);
|
||||
};
|
||||
return {
|
||||
onGetIcon,
|
||||
onClearIcon,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
59
web/src/views/make/svgDemo/index.vue
Normal file
59
web/src/views/make/svgDemo/index.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="svg-demo-container">
|
||||
<el-card shadow="hover" header="svgIcon:演示(支持本地svg)">
|
||||
<SvgIcon name="iconfont icon-shuju1" color="red" :size="30" />
|
||||
<SvgIcon name="ele-Trophy" color="var(--el-color-primary)" :size="30" />
|
||||
<SvgIcon name="fa fa-flag-checkered" color="#09f" :size="30" />
|
||||
<SvgIcon :name="logoMini" color="#09f" :size="30" />
|
||||
</el-card>
|
||||
<el-card shadow="hover" header="svgIcon:参数" class="mt15">
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column prop="a1" label="参数"> </el-table-column>
|
||||
<el-table-column prop="a2" label="说明"> </el-table-column>
|
||||
<el-table-column prop="a3" label="类型"> </el-table-column>
|
||||
<el-table-column prop="a4" label="可选值"> </el-table-column>
|
||||
<el-table-column prop="a5" label="默认值"> </el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import logoMini from '/@/assets/logo-mini.svg';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'makeSvgDemo',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
tableData: [
|
||||
{
|
||||
a1: 'name',
|
||||
a2: 'svg 图标组件名字 / svg 路径 url',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '',
|
||||
},
|
||||
{
|
||||
a1: 'size',
|
||||
a2: 'svg 大小',
|
||||
a3: 'number',
|
||||
a4: '',
|
||||
a5: 14,
|
||||
},
|
||||
{
|
||||
a1: 'color',
|
||||
a2: 'svg 颜色',
|
||||
a3: 'string',
|
||||
a4: '',
|
||||
a5: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
return {
|
||||
logoMini,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
web/src/views/menu/menu1/menu11/index.vue
Normal file
21
web/src/views/menu/menu1/menu11/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="val" placeholder="menu11:请输入内容测试路由缓存"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'menu11',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
val: '',
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
web/src/views/menu/menu1/menu12/menu121/index.vue
Normal file
21
web/src/views/menu/menu1/menu12/menu121/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="val" placeholder="menu121:请输入内容测试路由缓存"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'menu121',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
val: '',
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
web/src/views/menu/menu1/menu12/menu122/index.vue
Normal file
21
web/src/views/menu/menu1/menu12/menu122/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="val" placeholder="menu122:请输入内容测试路由缓存"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'menu122',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
val: '',
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
27
web/src/views/menu/menu1/menu13/index.vue
Normal file
27
web/src/views/menu/menu1/menu13/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="val" placeholder="menu13:请输入内容测试路由缓存"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onActivated, onMounted, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'menu13',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
val: '',
|
||||
});
|
||||
onMounted(() => {
|
||||
console.log(2222);
|
||||
});
|
||||
onActivated(() => {
|
||||
console.log(1111);
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
web/src/views/menu/menu2/index.vue
Normal file
21
web/src/views/menu/menu2/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="val" placeholder="menu2:请输入内容测试路由缓存"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'menu2',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
val: '',
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
87
web/src/views/pages/awesome/index.vue
Normal file
87
web/src/views/pages/awesome/index.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="awesome-container">
|
||||
<el-card shadow="hover" :header="`fontawesome 字体图标(自动载入):${sheetsIconList.length - 24}个`">
|
||||
<el-row class="iconfont-row">
|
||||
<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">
|
||||
<div class="iconfont-warp">
|
||||
<div class="flex-margin">
|
||||
<div class="iconfont-warp-value">
|
||||
<i :class="v" class="fa"></i>
|
||||
</div>
|
||||
<div class="iconfont-warp-label mt10">{{ v }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import initIconfont from '/@/utils/getStyleSheets';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesAwesome',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
sheetsIconList: [],
|
||||
});
|
||||
// 初始化获取 css 样式,这里使用fontawesome的图标(记得加上前缀 `fa`),其它第三方请自行做判断
|
||||
const initGetStyleSheets = () => {
|
||||
initIconfont.awe().then((res: any) => (state.sheetsIconList = res));
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initGetStyleSheets();
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.awesome-container {
|
||||
.iconfont-row {
|
||||
border-top: 1px solid var(--next-border-color-light);
|
||||
border-left: 1px solid var(--next-border-color-light);
|
||||
.iconfont-warp {
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--next-border-color-light);
|
||||
border-bottom: 1px solid var(--next-border-color-light);
|
||||
height: 120px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px var(--next-color-dark-hover);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
.iconfont-warp-value {
|
||||
i {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-label {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-value {
|
||||
i {
|
||||
color: #606266;
|
||||
font-size: 32px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-label {
|
||||
color: #99a9bf;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
66
web/src/views/pages/drag/index.vue
Normal file
66
web/src/views/pages/drag/index.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="drag-container">
|
||||
<el-card shadow="hover" header="拖动指令效果(v-drag)作用于 Dialog 对话框">
|
||||
<el-button type="primary" @click="dialogVisible = true" size="default">
|
||||
<el-icon>
|
||||
<ele-Pointer />
|
||||
</el-icon>
|
||||
点击打开 Dialog
|
||||
</el-button>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" header="自定义div" class="mt15">
|
||||
<div class="drag-dom">
|
||||
<div class="drag-header">
|
||||
<el-button type="success" size="default" v-drag="['.drag-container .drag-dom', '.drag-container .drag-header']">
|
||||
<el-icon>
|
||||
<ele-Pointer />
|
||||
</el-icon>
|
||||
按住进行拖动测试
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" width="769px">
|
||||
<template #header>
|
||||
<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']">拖动指令效果(v-drag)</div>
|
||||
</template>
|
||||
<p>鼠标放标题头进行 Dialog 对话框拖动</p>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false" size="default">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesDrag',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.drag-container {
|
||||
.drag-dom {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
.drag-header {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
204
web/src/views/pages/dynamicForm/index.vue
Normal file
204
web/src/views/pages/dynamicForm/index.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="dynamic-form-container">
|
||||
<el-card shadow="hover" header="动态复杂表单">
|
||||
<el-form :model="form" ref="formRulesOneRef" size="default" label-width="100px" class="mt35">
|
||||
<el-row :gutter="35">
|
||||
<el-col
|
||||
:xs="val.xs"
|
||||
:sm="val.sm"
|
||||
:md="val.md"
|
||||
:lg="val.md"
|
||||
:xl="val.xl"
|
||||
class="mb20"
|
||||
v-show="val.isShow"
|
||||
v-for="(val, key) in formData"
|
||||
:key="key"
|
||||
>
|
||||
<template v-if="val.type !== ''">
|
||||
<el-form-item
|
||||
:label="val.label"
|
||||
:prop="val.prop"
|
||||
:rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: val.type === 'input' ? 'blur' : 'change' }]"
|
||||
v-if="val.type !== ''"
|
||||
>
|
||||
<el-input
|
||||
v-model="form[val.prop]"
|
||||
:placeholder="val.placeholder"
|
||||
clearable
|
||||
v-if="val.type === 'input'"
|
||||
style="width: 100%"
|
||||
:disabled="val.disabled"
|
||||
></el-input>
|
||||
<el-date-picker
|
||||
v-model="form[val.prop]"
|
||||
type="date"
|
||||
:placeholder="val.placeholder"
|
||||
v-else-if="val.type === 'date'"
|
||||
style="width: 100%"
|
||||
:disabled="val.disabled"
|
||||
>
|
||||
</el-date-picker>
|
||||
<el-select
|
||||
v-model="form[val.prop]"
|
||||
:placeholder="val.placeholder"
|
||||
v-else-if="val.type === 'select'"
|
||||
style="width: 100%"
|
||||
:disabled="val.disabled"
|
||||
>
|
||||
<el-option v-for="item in val.options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="form[val.prop]"
|
||||
:placeholder="val.placeholder"
|
||||
clearable
|
||||
v-if="val.type === 'textarea'"
|
||||
style="width: 100%"
|
||||
:disabled="val.disabled"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-row :gutter="35" v-for="(v, k) in form.list" :key="k">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
|
||||
<el-form-item label="年度" :prop="`list[${k}].year`" :rules="[{ required: true, message: `年度不能为空`, trigger: 'blur' }]">
|
||||
<template #label>
|
||||
<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
|
||||
<el-icon>
|
||||
<ele-Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<span class="ml10">年度</span>
|
||||
</template>
|
||||
<el-input v-model="form.list[k].year" style="width: 100%" placeholder="请输入"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
|
||||
<el-form-item label="月度" :prop="`list[${k}].month`" :rules="[{ required: true, message: `月度不能为空`, trigger: 'blur' }]">
|
||||
<el-input v-model="form.list[k].month" style="width: 100%" placeholder="请输入"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
|
||||
<el-form-item label="日度" :prop="`list[${k}].day`" :rules="[{ required: true, message: `日度不能为空`, trigger: 'blur' }]">
|
||||
<el-input v-model="form.list[k].day" style="width: 100%" placeholder="请输入"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-row class="flex mt15">
|
||||
<div class="flex-margin">
|
||||
<el-button size="default" @click="onResetForm">
|
||||
<el-icon>
|
||||
<ele-RefreshRight />
|
||||
</el-icon>
|
||||
重置表单
|
||||
</el-button>
|
||||
<el-button size="default" type="primary" @click="onSubmitForm">
|
||||
<SvgIcon name="iconfont icon-shuxing" />
|
||||
验证表单
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent } from 'vue';
|
||||
import { formData } from './mock';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface FormDataOptions {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
interface FormDataState {
|
||||
label: string;
|
||||
prop: string;
|
||||
placeholder: string;
|
||||
clearable: boolean;
|
||||
disabled: boolean;
|
||||
required: boolean;
|
||||
type: string;
|
||||
i18n: boolean;
|
||||
i18nText: string;
|
||||
isShow: boolean;
|
||||
xs: number;
|
||||
sm: number;
|
||||
md: number;
|
||||
lg: number;
|
||||
xl: number;
|
||||
options?: FormDataOptions[];
|
||||
}
|
||||
interface DynamicFormState {
|
||||
formData: FormDataState[];
|
||||
form: any;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesDynamicForm',
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const state = reactive<DynamicFormState>({
|
||||
formData,
|
||||
form: {
|
||||
name: '',
|
||||
email: '',
|
||||
autograph: '',
|
||||
occupation: '',
|
||||
list: [
|
||||
{
|
||||
year: '',
|
||||
month: '',
|
||||
day: '',
|
||||
},
|
||||
],
|
||||
remarks: '',
|
||||
},
|
||||
});
|
||||
// 新增行
|
||||
const onAddRow = () => {
|
||||
state.form.list.push({
|
||||
year: '',
|
||||
month: '',
|
||||
day: '',
|
||||
});
|
||||
};
|
||||
// 删除行
|
||||
const onDelRow = (k: number) => {
|
||||
state.form.list.splice(k, 1);
|
||||
};
|
||||
// 表单验证
|
||||
const onSubmitForm = () => {
|
||||
proxy.$refs.formRulesOneRef.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
proxy.$message.success('验证成功');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
// 重置表单
|
||||
const onResetForm = () => {
|
||||
proxy.$refs.formRulesOneRef.resetFields();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {});
|
||||
return {
|
||||
onAddRow,
|
||||
onDelRow,
|
||||
onSubmitForm,
|
||||
onResetForm,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
119
web/src/views/pages/dynamicForm/mock.ts
Normal file
119
web/src/views/pages/dynamicForm/mock.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
// 表单数据选项(自行扩展)
|
||||
export const formData = [
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
placeholder: '请输入姓名',
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
type: 'input',
|
||||
i18n: false,
|
||||
i18nText: '',
|
||||
isShow: true,
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 4,
|
||||
},
|
||||
{
|
||||
label: '邮箱',
|
||||
prop: 'email',
|
||||
placeholder: '请输入用户邮箱',
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
type: 'input',
|
||||
i18n: false,
|
||||
i18nText: '',
|
||||
isShow: true,
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 4,
|
||||
},
|
||||
{
|
||||
label: '登陆时间',
|
||||
prop: 'autograph',
|
||||
placeholder: '选择时间',
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
type: 'date',
|
||||
i18n: false,
|
||||
i18nText: '',
|
||||
isShow: true,
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 4,
|
||||
},
|
||||
{
|
||||
label: '职务',
|
||||
prop: 'occupation',
|
||||
placeholder: '请选择职务',
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
type: 'select',
|
||||
i18n: false,
|
||||
i18nText: '',
|
||||
options: [
|
||||
{
|
||||
label: '计算机 / 互联网 / 通信',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '生产 / 工艺 / 制造',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '医疗 / 护理 / 制药',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
isShow: true,
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 4,
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
prop: '',
|
||||
placeholder: '',
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
type: '',
|
||||
i18n: false,
|
||||
i18nText: '',
|
||||
isShow: true,
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 24,
|
||||
lg: 24,
|
||||
xl: 24,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remarks',
|
||||
placeholder: '请输入',
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
type: 'textarea',
|
||||
i18n: false,
|
||||
i18nText: '',
|
||||
isShow: true,
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 24,
|
||||
lg: 24,
|
||||
xl: 24,
|
||||
},
|
||||
];
|
||||
89
web/src/views/pages/element/index.vue
Normal file
89
web/src/views/pages/element/index.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="element-container">
|
||||
<el-card shadow="hover" :header="`element plus 字体图标(自动载入,增加了 ele- 前缀,使用时:ele-Aim):${sheetsIconList.length}个`">
|
||||
<el-row class="iconfont-row">
|
||||
<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">
|
||||
<div class="iconfont-warp">
|
||||
<div class="flex-margin">
|
||||
<div class="iconfont-warp-value">
|
||||
<SvgIcon :name="v" :size="30" />
|
||||
</div>
|
||||
<div class="iconfont-warp-label mt10">{{ v }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import initIconfont from '/@/utils/getStyleSheets';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesElement',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
sheetsIconList: [],
|
||||
});
|
||||
// 初始化获取 css 样式,获取 element plus 自带 svg 图标,增加了 ele- 前缀,使用时:ele-Aim
|
||||
const initGetStyleSheets = () => {
|
||||
initIconfont.ele().then((res: any) => {
|
||||
state.sheetsIconList = res;
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initGetStyleSheets();
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.element-container {
|
||||
.iconfont-row {
|
||||
border-top: 1px solid var(--next-border-color-light);
|
||||
border-left: 1px solid var(--next-border-color-light);
|
||||
.iconfont-warp {
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--next-border-color-light);
|
||||
border-bottom: 1px solid var(--next-border-color-light);
|
||||
height: 120px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px var(--next-color-dark-hover);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
.iconfont-warp-value {
|
||||
i {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-label {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-value {
|
||||
i {
|
||||
color: #606266;
|
||||
font-size: 32px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-label {
|
||||
color: #99a9bf;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
39
web/src/views/pages/filtering/details.vue
Normal file
39
web/src/views/pages/filtering/details.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
|
||||
<div class="layout-view-bg-white">
|
||||
<div class="w100 h100 flex">
|
||||
<div class="flex-margin color-primary">filtering-details 测试界面</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFilteringDetails',
|
||||
setup() {
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
// 设置主内容的高度
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
return {
|
||||
initTagViewHeight,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
39
web/src/views/pages/filtering/details1.vue
Normal file
39
web/src/views/pages/filtering/details1.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
|
||||
<div class="layout-view-bg-white">
|
||||
<div class="w100 h100 flex">
|
||||
<div class="flex-margin color-primary">测试界面</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFilteringDetails1',
|
||||
setup() {
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
// 设置主内容的高度
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
return {
|
||||
initTagViewHeight,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
355
web/src/views/pages/filtering/index.vue
Normal file
355
web/src/views/pages/filtering/index.vue
Normal file
@@ -0,0 +1,355 @@
|
||||
<template>
|
||||
<div class="filtering">
|
||||
<el-card
|
||||
shadow="hover"
|
||||
class="filtering-list br-top-no"
|
||||
v-loading="tableData.loading"
|
||||
element-loading-text="加载中..."
|
||||
element-loading-background="rgba(255, 255, 255, 0.1)"
|
||||
:class="{ 'min-h-360': tableData.data.length <= 0 }"
|
||||
>
|
||||
<div
|
||||
v-for="(val, key) in filtering"
|
||||
:key="key"
|
||||
:ref="
|
||||
(el) => {
|
||||
if (el) dlRefs[key] = el;
|
||||
}
|
||||
"
|
||||
class="filtering-list-flex"
|
||||
>
|
||||
<div class="filtering-list-title">{{ val.title }}</div>
|
||||
<div class="filtering-list-item" :style="{ height: val.isMore ? 'auto' : '50px' }">
|
||||
<span class="span" :class="v.active ? 'dd-active' : ''" v-for="(v, k) in val.children" :key="k" @click="onSelItem(val, v)">{{
|
||||
v.label
|
||||
}}</span>
|
||||
<div class="dd-more" v-if="val.isShowMore" @click="val.isMore = !val.isMore">
|
||||
<span>{{ val.isMore ? '收起' : '展开' }}</span>
|
||||
<i :class="val.isMore ? 'el-icon-arrow-down' : 'el-icon-arrow-right'"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp mt15 mb15" v-if="tableData.data.length > 0">
|
||||
<el-row :gutter="15">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb15" v-for="(v, k) in tableData.data" :key="k" @click="onTableItemClick(v)">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="item-img">
|
||||
<img :src="v.img" />
|
||||
</div>
|
||||
<div class="item-txt">
|
||||
<div class="item-txt-title">{{ v.title }}</div>
|
||||
<div class="item-txt-other">
|
||||
<div style="width: 100%">
|
||||
<div class="item-txt-msg mb10">
|
||||
<span>评价 {{ v.evaluate }}</span>
|
||||
<span class="ml10">收藏 {{ v.collection }}</span>
|
||||
</div>
|
||||
<div class="item-txt-msg item-txt-price">
|
||||
<span class="font-price">
|
||||
<span>¥</span>
|
||||
<span class="font">{{ v.price }}</span>
|
||||
</span>
|
||||
<span>月销{{ v.monSales }}笔</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else class="filtering-no-data">
|
||||
<div class="no-data-box">
|
||||
<i class="el-icon-search"></i>
|
||||
<div class="no-txt">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="tableData.data.length > 0">
|
||||
<el-pagination
|
||||
style="text-align: right"
|
||||
background
|
||||
@size-change="onHandleSizeChange"
|
||||
@current-change="onHandleCurrentChange"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:current-page="tableData.param.pageNum"
|
||||
:page-size="tableData.param.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</template>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, onMounted, nextTick, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { filtering, filterList } from './mock';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFiltering',
|
||||
setup() {
|
||||
const dlRefs: any = ref([]);
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
filtering,
|
||||
tableData: {
|
||||
data: filterList,
|
||||
total: 99,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initBtnToggle();
|
||||
window.onresize = () => {
|
||||
initBtnToggle();
|
||||
};
|
||||
});
|
||||
// 初始化 `收起、展开` 按钮
|
||||
const initBtnToggle = () => {
|
||||
nextTick(() => {
|
||||
const els = dlRefs.value;
|
||||
els.map((v: any, k: number) => {
|
||||
v.scrollHeight < v.lastChild.scrollHeight ? (state.filtering[k].isShowMore = true) : (state.filtering[k].isShowMore = false);
|
||||
});
|
||||
});
|
||||
};
|
||||
// 过滤当前选中的数据
|
||||
const onSelItem = (val: any, v: any) => {
|
||||
val.children.map((v: any) => (v.active = false));
|
||||
v.active = true;
|
||||
let arr = [];
|
||||
state.filtering.map((item: any) => {
|
||||
item.children.map((chil: any) => {
|
||||
if (chil.active) {
|
||||
arr.push({
|
||||
...item,
|
||||
children: [{ ...chil }],
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
state.tableData.loading = true;
|
||||
setTimeout(() => {
|
||||
state.tableData.loading = false;
|
||||
}, 500);
|
||||
};
|
||||
// 当前列表项点击
|
||||
const onTableItemClick = (v: any) => {
|
||||
if (v.id === 1) {
|
||||
router.push({
|
||||
path: '/pages/filtering/details',
|
||||
query: { id: v.id },
|
||||
});
|
||||
} else {
|
||||
router.push({
|
||||
path: '/pages/filtering/details1',
|
||||
query: { id: v.id },
|
||||
});
|
||||
}
|
||||
};
|
||||
// 分页点击
|
||||
const onHandleSizeChange = (val: number) => {
|
||||
state.tableData.param.pageSize = val;
|
||||
};
|
||||
// 分页点击
|
||||
const onHandleCurrentChange = (val: number) => {
|
||||
state.tableData.param.pageNum = val;
|
||||
};
|
||||
return {
|
||||
dlRefs,
|
||||
onSelItem,
|
||||
onTableItemClick,
|
||||
onHandleSizeChange,
|
||||
onHandleCurrentChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.filtering {
|
||||
.filtering-list {
|
||||
overflow: hidden;
|
||||
border-bottom: none !important;
|
||||
.filtering-list-flex {
|
||||
&:last-of-type {
|
||||
.filtering-list-item {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
}
|
||||
.filtering-list-title {
|
||||
float: left;
|
||||
width: 64px;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
color: #909399;
|
||||
margin: 15px 0;
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border: 1px solid #909399;
|
||||
border-width: 0 1px 1px 0;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
transform: rotate(-45deg) translateY(-50%);
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
}
|
||||
}
|
||||
.filtering-list-item {
|
||||
border-bottom: 1px dotted var(--next-border-color-light);
|
||||
margin-left: 64px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.span {
|
||||
color: #8d8d91;
|
||||
font-size: 14px;
|
||||
float: left;
|
||||
padding: 0 15px;
|
||||
margin: 15px 0;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.dd-active {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.dd-more {
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 16px;
|
||||
color: #a5a5a5;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: #8d8d91;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.br-top-no {
|
||||
border-top: none;
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
height: 360px;
|
||||
.flex-warp-item-box {
|
||||
border: 1px solid var(--next-border-color-light);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
|
||||
.item-txt-title {
|
||||
color: var(--el-color-primary) !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.item-img {
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
transform: translateZ(0) scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 215px;
|
||||
overflow: hidden;
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.item-txt {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
.item-txt-title {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
color: #666666;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
text-decoration: underline;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.item-txt-other {
|
||||
flex: 1;
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
.item-txt-msg {
|
||||
font-size: 12px;
|
||||
color: #8d8d91;
|
||||
}
|
||||
.item-txt-price {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.font-price {
|
||||
color: #ff5000;
|
||||
.font {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
height: 100%;
|
||||
.filtering-no-data {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.no-data-box {
|
||||
color: #cccccc;
|
||||
margin: auto;
|
||||
i {
|
||||
font-size: 70px;
|
||||
}
|
||||
.no-txt {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.min-h-360 {
|
||||
height: 360px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
201
web/src/views/pages/filtering/mock.ts
Normal file
201
web/src/views/pages/filtering/mock.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
// 导航数据
|
||||
export const filtering = [
|
||||
{
|
||||
title: '权限',
|
||||
isMore: false,
|
||||
isShowMore: false,
|
||||
id: 0,
|
||||
children: [
|
||||
{
|
||||
id: '01',
|
||||
label: '全部',
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
id: '02',
|
||||
label: '普通用户',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: '03',
|
||||
label: '管理员',
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '布局',
|
||||
isMore: false,
|
||||
isShowMore: false,
|
||||
id: 1,
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
label: '全部',
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
label: '默认',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
label: '经典',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
label: '横向',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
label: '分栏',
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '配置',
|
||||
isMore: false,
|
||||
isShowMore: false,
|
||||
id: 2,
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
label: '全部',
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
label: '开启 Breadcrumb',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
label: '开启 Tags-View',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
label: '固定 Header',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
label: '侧边栏 Logo',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
label: '开启折叠 NavMenu',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
label: '开启一个 NavMenu 展开',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
label: '登录用户头像',
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// 列表数据
|
||||
export const filterList = [
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2020-08/14/9d9c9a60-f0af-41aa-b617-683b07c87642.jpg',
|
||||
title: '嘉陵江2020年第1号洪水”在嘉陵江支流涪江形成',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/news/pic/2020-08/13/0ea47d3c-feb9-4bd7-8597-a8a373aa6340c6ec12c7-3b33-4528-91a6-85ec8ca1df67_watermark.png',
|
||||
title: '让《民法典》走近群众 盐田街道开展人民调解宣传活动',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/photo/pic/2020-08/12/a08d6eb0-1d53-4f76-a313-ad3e5d701f98.jpg',
|
||||
title: '记者手记:可可西里,“挪”向“藏羚羊大产房”的14个半小时',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/photo/pic/2020-08/11/43cc0e14-9bca-45b9-9a8b-342e09d6a4c7.jpg',
|
||||
title: '以优异成绩庆祝深圳经济特区建立40周年',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/photo/pic/2020-08/11/a4dc322b-68ec-40e6-8906-3124142c3e49.jpg',
|
||||
title: '草原上的“太阳姑娘”',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/zhuanti/pic/2020-08/07/57f087b4-4812-46cc-adb9-ead73621284e.png',
|
||||
title: '奇观天下|带你走进非洲野生动物观光第一目的地',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2020-09/02/t2_(101X54X600X335)7cd39301-d9cf-45f1-91c3-9575b1e5ce0e.jpg.2',
|
||||
title: '五角大楼发布“中国军力报告” 华春莹: 罔顾事实,充满偏见',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2020-09/02/b8b41d9c-0508-4498-8d37-6e597493769f.jpg',
|
||||
title: '最新地铁消息汇总:4号线北延、2号线三期、8号线一期等今年通车',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 8,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/photo/pic/2020-08/10/1635374c-f4d6-475c-ac47-1334176f365d.png',
|
||||
title: '9月1日深圳新增5例无症状感染者!钟南山这段话冲上热搜!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 9,
|
||||
},
|
||||
{
|
||||
img: 'http://www.sznews.com/news/pic/2020-08/13/646e5458-92b7-4636-9940-9b0799babfe1.png',
|
||||
title: '全能“小福宝” 为文明社区建设添砖加瓦',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 10,
|
||||
},
|
||||
];
|
||||
114
web/src/views/pages/formAdapt/index.vue
Normal file
114
web/src/views/pages/formAdapt/index.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="form-adapt-container">
|
||||
<el-card shadow="hover" header="表单自适应演示(改变窗口查看效果)">
|
||||
<el-form :model="form" size="default" label-width="100px" class="mt35 mb35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="form.name" placeholder="请输入姓名" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="用户归属部门">
|
||||
<el-input v-model="form.email" placeholder="请输入用户归属部门" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="登陆账户名">
|
||||
<el-input v-model="form.autograph" placeholder="请输入登陆账户名" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="职务">
|
||||
<el-select v-model="form.occupation" placeholder="请选择职务" clearable class="w100">
|
||||
<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
|
||||
<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
|
||||
<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="手机">
|
||||
<el-input v-model="form.phone" placeholder="请输入手机" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择性别" clearable class="w100">
|
||||
<el-option label="男" value="1"></el-option>
|
||||
<el-option label="女" value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="登录密码">
|
||||
<el-input v-model="form.phone1" placeholder="请输入登录密码" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="权限角色">
|
||||
<el-input v-model="form.phone2" placeholder="请输入权限角色" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="创建用户">
|
||||
<el-input v-model="form.phone3" placeholder="请输入创建用户" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="修改用户">
|
||||
<el-input v-model="form.phone4" placeholder="请输入修改用户" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="所属用户">
|
||||
<el-input v-model="form.phone5" placeholder="请输入所属用户" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="所属部门">
|
||||
<el-input v-model="form.phone6" placeholder="请输入所属部门" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<el-form-item>
|
||||
<el-button type="primary">
|
||||
<SvgIcon name="iconfont icon-biaodan" />
|
||||
更新个人信息
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFormAdapt',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
form: {
|
||||
name: '',
|
||||
email: '',
|
||||
autograph: '',
|
||||
occupation: '',
|
||||
phone: '',
|
||||
sex: '',
|
||||
phone1: '',
|
||||
phone2: '',
|
||||
phone3: '',
|
||||
phone4: '',
|
||||
phone5: '',
|
||||
phone6: '',
|
||||
},
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
59
web/src/views/pages/formI18n/index.vue
Normal file
59
web/src/views/pages/formI18n/index.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="form-i18n-container">
|
||||
<el-card shadow="hover" header="表单国际化演示(不适用于动态项 form-item)">
|
||||
<div style="text-align: center; margin-top: 15px">
|
||||
<el-radio-group v-model="radio" size="default" @change="onRadioChange">
|
||||
<el-radio-button label="zh-cn">中文简体</el-radio-button>
|
||||
<el-radio-button label="en">英文</el-radio-button>
|
||||
<el-radio-button label="zh-tw">中文繁体</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-form :model="form" size="default" label-width="100px" class="mt35 mb35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
|
||||
<el-form-item :label="$t('message.formI18nLabel.name')">
|
||||
<el-input v-model="form.name" :placeholder="$t('message.formI18nPlaceholder.name')" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
|
||||
<el-form-item :label="$t('message.formI18nLabel.email')">
|
||||
<el-input v-model="form.email" :placeholder="$t('message.formI18nPlaceholder.email')" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
|
||||
<el-form-item :label="$t('message.formI18nLabel.autograph')">
|
||||
<el-input v-model="form.autograph" :placeholder="$t('message.formI18nPlaceholder.autograph')" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, getCurrentInstance } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFormI18n',
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const state = reactive({
|
||||
radio: 'zh-cn',
|
||||
form: {
|
||||
name: '',
|
||||
email: '',
|
||||
autograph: '',
|
||||
},
|
||||
});
|
||||
// 单选框改变时
|
||||
const onRadioChange = () => {
|
||||
proxy.$i18n.locale = state.radio;
|
||||
};
|
||||
return {
|
||||
onRadioChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
68
web/src/views/pages/formRules/component/formRulesOne.vue
Normal file
68
web/src/views/pages/formRules/component/formRulesOne.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="form-rules-one-container">
|
||||
<el-form :model="form" :rules="rules" ref="formRulesOneRef" size="default" label-width="100px" class="mt35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入姓名" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入用户邮箱" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="登陆账户名" prop="autograph">
|
||||
<el-input v-model="form.autograph" placeholder="请输入登陆账户名" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="职务" prop="occupation">
|
||||
<el-select v-model="form.occupation" placeholder="请选择职务" clearable class="w100">
|
||||
<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
|
||||
<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
|
||||
<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFormRulesOne',
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
form: { name: '', email: '', autograph: '', occupation: '' },
|
||||
rules: {
|
||||
name: { required: true, message: '请输入姓名', trigger: 'blur' },
|
||||
email: { required: true, message: '请输入用户邮箱', trigger: 'blur' },
|
||||
autograph: { required: true, message: '请输入登陆账户名', trigger: 'blur' },
|
||||
occupation: { required: true, message: '请选择职务', trigger: 'change' },
|
||||
},
|
||||
});
|
||||
// 赋值回显
|
||||
const initForm = () => {
|
||||
state.form = <any>props.data;
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initForm();
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
50
web/src/views/pages/formRules/component/formRulesThree.vue
Normal file
50
web/src/views/pages/formRules/component/formRulesThree.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="form-rules-three-container">
|
||||
<el-form :model="form" :rules="rules" ref="formRulesThreeRef" size="default" label-width="100px" class="mt35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="创建用户" prop="createUser">
|
||||
<el-input v-model="form.createUser" placeholder="请输入创建用户" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="修改用户" prop="editUser">
|
||||
<el-input v-model="form.editUser" placeholder="请输入修改用户" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="所属用户" prop="user">
|
||||
<el-input v-model="form.user" placeholder="请输入所属用户" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="所属部门" prop="department">
|
||||
<el-input v-model="form.department" placeholder="请输入所属部门" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFormRulesThree',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
form: { createUser: '', editUser: '', user: '', department: '' },
|
||||
rules: {
|
||||
createUser: { required: true, message: '请输入创建用户', trigger: 'blur' },
|
||||
editUser: { required: true, message: '请输入修改用户', trigger: 'blur' },
|
||||
user: { required: true, message: '请输入所属用户', trigger: 'blur' },
|
||||
department: { required: true, message: '请输入所属部门', trigger: 'blur' },
|
||||
},
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
52
web/src/views/pages/formRules/component/formRulesTwo.vue
Normal file
52
web/src/views/pages/formRules/component/formRulesTwo.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="form-rules-two-container">
|
||||
<el-form :model="form" :rules="rules" ref="formRulesTwoRef" size="default" label-width="100px" class="mt35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="手机" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入手机" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择性别" clearable class="w100">
|
||||
<el-option label="男" value="1"></el-option>
|
||||
<el-option label="女" value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="登录密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入登录密码" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="权限角色" prop="auth">
|
||||
<el-input v-model="form.auth" placeholder="请输入权限角色" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFormRulesTwo',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
form: { phone: '', sex: '', password: '', auth: '' },
|
||||
rules: {
|
||||
phone: { required: true, message: '请输入手机', trigger: 'blur' },
|
||||
password: { required: true, message: '请输入登录密码', trigger: 'blur' },
|
||||
auth: { required: true, message: '请输入权限角色', trigger: 'blur' },
|
||||
},
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
80
web/src/views/pages/formRules/index.vue
Normal file
80
web/src/views/pages/formRules/index.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="form-rules-container">
|
||||
<el-card shadow="hover" header="表单组件1"> <FormRulesOne :data="formRulesOneData" ref="pagesFormRulesOneRef" /></el-card>
|
||||
<el-card shadow="hover" header="表单组件2" class="mt15"><FormRulesTwo ref="pagesFormRulesTwoRef" /> </el-card>
|
||||
<el-card shadow="hover" header="表单组件3" class="mt15"> <FormRulesThree ref="pagesFormRulesThreeRef" /></el-card>
|
||||
<el-row class="flex mt15">
|
||||
<div class="flex-margin">
|
||||
<el-button size="default" @click="onResetForm">
|
||||
<SvgIcon name="ele-RefreshRight" />
|
||||
重置表单
|
||||
</el-button>
|
||||
<el-button size="default" type="primary" @click="onSubmitForm">
|
||||
<SvgIcon name="iconfont icon-shuxing" />
|
||||
验证表单
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, getCurrentInstance } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import FormRulesOne from '/@/views/pages/formRules/component/formRulesOne.vue';
|
||||
import FormRulesTwo from '/@/views/pages/formRules/component/formRulesTwo.vue';
|
||||
import FormRulesThree from '/@/views/pages/formRules/component/formRulesThree.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesFormRules',
|
||||
components: {
|
||||
FormRulesOne,
|
||||
FormRulesTwo,
|
||||
FormRulesThree,
|
||||
},
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const state = reactive({
|
||||
formRulesOneData: {
|
||||
name: 'lyt',
|
||||
email: 'lyt123@.com',
|
||||
autograph: 'lyt123456',
|
||||
occupation: '1',
|
||||
},
|
||||
});
|
||||
// 表单组件验证
|
||||
const formRulesValidate = (pageRef: string, sonRef: string) => {
|
||||
return new Promise((resolve) => {
|
||||
proxy.$refs[pageRef].$refs[sonRef].validate((valid: boolean) => {
|
||||
if (valid) resolve(valid);
|
||||
});
|
||||
});
|
||||
};
|
||||
// 表单组件重置
|
||||
const formRulesResetFields = () => {
|
||||
proxy.$refs.pagesFormRulesOneRef.$refs.formRulesOneRef.resetFields();
|
||||
proxy.$refs.pagesFormRulesTwoRef.$refs.formRulesTwoRef.resetFields();
|
||||
proxy.$refs.pagesFormRulesThreeRef.$refs.formRulesThreeRef.resetFields();
|
||||
};
|
||||
// 验证表单
|
||||
const onSubmitForm = () => {
|
||||
Promise.all([
|
||||
formRulesValidate('pagesFormRulesOneRef', 'formRulesOneRef'),
|
||||
formRulesValidate('pagesFormRulesTwoRef', 'formRulesTwoRef'),
|
||||
formRulesValidate('pagesFormRulesThreeRef', 'formRulesThreeRef'),
|
||||
]).then(() => {
|
||||
ElMessage.success('表单全部验证成功');
|
||||
});
|
||||
};
|
||||
// 重置表单
|
||||
const onResetForm = () => {
|
||||
formRulesResetFields();
|
||||
};
|
||||
return {
|
||||
onSubmitForm,
|
||||
onResetForm,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
87
web/src/views/pages/iocnfont/index.vue
Normal file
87
web/src/views/pages/iocnfont/index.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="iconfont-container">
|
||||
<el-card shadow="hover" :header="`iconfont 字体图标(自动载入):${sheetsIconList.length}个`">
|
||||
<el-row class="iconfont-row">
|
||||
<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">
|
||||
<div class="iconfont-warp">
|
||||
<div class="flex-margin">
|
||||
<div class="iconfont-warp-value">
|
||||
<i :class="v" class="iconfont"></i>
|
||||
</div>
|
||||
<div class="iconfont-warp-label mt10">{{ v }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import initIconfont from '/@/utils/getStyleSheets';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesIocnfont',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
sheetsIconList: [],
|
||||
});
|
||||
// 初始化获取 css 样式,这里使用阿里的图标(记得加上前缀 `iconfont`),其它第三方请自行做判断
|
||||
const initGetStyleSheets = () => {
|
||||
initIconfont.ali().then((res: any) => (state.sheetsIconList = res));
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initGetStyleSheets();
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.iconfont-container {
|
||||
.iconfont-row {
|
||||
border-top: 1px solid var(--next-border-color-light);
|
||||
border-left: 1px solid var(--next-border-color-light);
|
||||
.iconfont-warp {
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--next-border-color-light);
|
||||
border-bottom: 1px solid var(--next-border-color-light);
|
||||
height: 120px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px var(--next-color-dark-hover);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
.iconfont-warp-value {
|
||||
i {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-label {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-value {
|
||||
i {
|
||||
color: #606266;
|
||||
font-size: 32px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.iconfont-warp-label {
|
||||
color: #99a9bf;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
194
web/src/views/pages/lazyImg/index.vue
Normal file
194
web/src/views/pages/lazyImg/index.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<div class="lazy-img-container">
|
||||
<el-card shadow="hover" header="图片懒加载演示(F12 切换到 Network Img下进行图片加载查看)">
|
||||
<div class="flex-warp" v-if="tableData.data.length > 0">
|
||||
<el-row :gutter="15">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb15" v-for="(v, k) in tableData.data" :key="k" @click="onTableItemClick(v)">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="item-img" v-loading="v.loading">
|
||||
<img :data-img="v.img" :data-key="k" :data-lazy-img-list="k" />
|
||||
</div>
|
||||
<div class="item-txt">
|
||||
<div class="item-txt-title">{{ v.title }}</div>
|
||||
<div class="item-txt-other">
|
||||
<div style="width: 100%">
|
||||
<div class="item-txt-msg mb10">
|
||||
<span>评价 {{ v.evaluate }}</span>
|
||||
<span class="ml10">收藏 {{ v.collection }}</span>
|
||||
</div>
|
||||
<div class="item-txt-msg item-txt-price">
|
||||
<span class="font-price">
|
||||
<span>¥</span>
|
||||
<span class="font">{{ v.price }}</span>
|
||||
</span>
|
||||
<span>月销{{ v.monSales }}笔</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-empty v-else description="暂无数据"></el-empty>
|
||||
<template v-if="tableData.data.length > 0">
|
||||
<el-pagination
|
||||
style="text-align: right"
|
||||
background
|
||||
@size-change="onHandleSizeChange"
|
||||
@current-change="onHandleCurrentChange"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:current-page="tableData.param.pageNum"
|
||||
:page-size="tableData.param.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</template>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import other from '/@/utils/other';
|
||||
import { filterList } from './mock';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesListAdapt',
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
tableData: {
|
||||
data: filterList,
|
||||
total: 99,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 当前列表项点击
|
||||
const onTableItemClick = (v: any) => {
|
||||
router.push({
|
||||
path: '/pages/filteringDetails',
|
||||
query: { id: v.id },
|
||||
});
|
||||
};
|
||||
// 分页点击
|
||||
const onHandleSizeChange = (val: number) => {
|
||||
state.tableData.param.pageSize = val;
|
||||
};
|
||||
// 分页点击
|
||||
const onHandleCurrentChange = (val: number) => {
|
||||
state.tableData.param.pageNum = val;
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
other.lazyImg('[data-lazy-img-list]', state.tableData.data);
|
||||
});
|
||||
return {
|
||||
onTableItemClick,
|
||||
onHandleSizeChange,
|
||||
onHandleCurrentChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.lazy-img-container {
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
height: 360px;
|
||||
.flex-warp-item-box {
|
||||
border: 1px solid var(--next-border-color-light);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
|
||||
.item-txt-title {
|
||||
color: var(--el-color-primary) !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.item-img {
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
transform: translateZ(0) scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 215px;
|
||||
overflow: hidden;
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.item-txt {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
.item-txt-title {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
color: #666666;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
text-decoration: underline;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.item-txt-other {
|
||||
flex: 1;
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
.item-txt-msg {
|
||||
font-size: 12px;
|
||||
color: #8d8d91;
|
||||
}
|
||||
.item-txt-price {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.font-price {
|
||||
color: #ff5000;
|
||||
.font {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
313
web/src/views/pages/lazyImg/mock.ts
Normal file
313
web/src/views/pages/lazyImg/mock.ts
Normal file
@@ -0,0 +1,313 @@
|
||||
// 列表数据
|
||||
export const filterList = [
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/09/e37326cc-4583-48f3-aa00-ecc2392d319d.jpg',
|
||||
title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 1,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/09/78cf72b6-e2d9-459d-a368-470414a027f4679cf4ea-26fa-48c8-9fee-c2d092a91400.png',
|
||||
title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 2,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/09/1faf3c6e-1250-4e6b-b072-4a331553e027.jpg',
|
||||
title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 3,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/09/9fcf6dd4-1e80-4497-bdc9-83dc7246d170.jpg.2',
|
||||
title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 4,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/09/1bd78227-4126-4a43-bdf6-48ead6edd1bf.jpg.2',
|
||||
title: '深圳:实现“从0到1”源头创新,推进大湾区综合性国家科学中心建设!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 5,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/08/9ea943a3-3ae8-4f49-8296-711ec36ef8c6_watermark.png',
|
||||
title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 6,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/08/a95ba232-1422-4f7e-b85f-c61d486c8659.jpg.2',
|
||||
title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 7,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/08/76816bf0-3899-4c7e-bc6e-079b5ba8725e.jpg',
|
||||
title: '民生小事 | 手机遗落出租车 热心民警帮找回',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 8,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/08/28ed70d4-71f5-4abb-bf7b-0294bece9e43.jpg.2',
|
||||
title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 9,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/05/d13ae31f-fd45-431a-b48e-c5895bbc193e.png',
|
||||
title: '深圳湾公园一女子落水,三名男子接力及时施救',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 10,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/653/w930h523/20210704/d5d2-krwipas6444058.jpg',
|
||||
title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 1,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/766/w930h636/20210704/b1ae-krwipas6332914.jpg',
|
||||
title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 2,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/750/w930h620/20210704/2886-krwipas6264821.jpg',
|
||||
title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 3,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/750/w930h620/20210704/767c-krwipas6387862.jpg',
|
||||
title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 4,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/111/w1024h687/20210704/1f65-krwipas5871436.jpg',
|
||||
title: '盛夏的那考河湿地公园!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 5,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/657/w930h527/20210704/7eae-krwipas5866609.jpg',
|
||||
title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 6,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/760/w930h630/20210703/124e-krwipas5596390.jpg',
|
||||
title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 7,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/27/w930h697/20210703/9630-krwipas5514972.jpg',
|
||||
title: '民生小事 | 手机遗落出租车 热心民警帮找回',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 8,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/750/w930h620/20210703/2fe3-krwipas5388050.jpg',
|
||||
title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 9,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/724/w930h594/20210703/98b6-krwipas5234060.jpg',
|
||||
title: '深圳湾公园一女子落水,三名男子接力及时施救',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 10,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/750/w930h620/20210703/f765-krwipas5194727.jpg',
|
||||
title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 1,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/5dde-krwipas4724976.jpg',
|
||||
title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 2,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/f45e-krwipas4566804.jpg',
|
||||
title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 3,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/5579-krwipas4551382.jpg',
|
||||
title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 4,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/7c75-krwipas4543661.jpg',
|
||||
title: '深圳:实现“从0到1”源头创新,推进大湾区综合性国家科学中心建设!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 5,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/653/w930h523/20210702/ece2-krwipas4411140.jpg',
|
||||
title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 6,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/f5c2-krwipas4215211.jpg',
|
||||
title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 7,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210701/720/w930h590/20210701/eabc-krwipas3509204.jpg',
|
||||
title: '民生小事 | 手机遗落出租车 热心民警帮找回',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 8,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210701/797/w930h667/20210701/4667-krwipas3365057.jpg',
|
||||
title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 9,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210701/750/w930h620/20210701/baea-krwipas2976622.jpg',
|
||||
title: '民众前往中共一大纪念馆参观',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 10,
|
||||
loading: true,
|
||||
},
|
||||
{
|
||||
img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210630/617/w850h567/20210630/5c96-krwipas1819108.jpg',
|
||||
title: '延吉灯光秀美轮美奂 市民徜徉璀璨夜景',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 10,
|
||||
loading: true,
|
||||
},
|
||||
];
|
||||
209
web/src/views/pages/listAdapt/index.vue
Normal file
209
web/src/views/pages/listAdapt/index.vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div class="list-adapt-container">
|
||||
<el-card shadow="hover" header="列表自适应演示(改变窗口查看效果)">
|
||||
<div class="flex-warp" v-if="tableData.data.length > 0">
|
||||
<el-row :gutter="15">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb15" v-for="(v, k) in tableData.data" :key="k" @click="onTableItemClick(v)">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<div class="item-img">
|
||||
<img :src="v.img" />
|
||||
</div>
|
||||
<div class="item-txt">
|
||||
<div class="item-txt-title">{{ v.title }}</div>
|
||||
<div class="item-txt-other">
|
||||
<div style="width: 100%">
|
||||
<div class="item-txt-msg mb10">
|
||||
<span>评价 {{ v.evaluate }}</span>
|
||||
<span class="ml10">收藏 {{ v.collection }}</span>
|
||||
</div>
|
||||
<div class="item-txt-msg item-txt-price">
|
||||
<span class="font-price">
|
||||
<span>¥</span>
|
||||
<span class="font">{{ v.price }}</span>
|
||||
</span>
|
||||
<span>月销{{ v.monSales }}笔</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-empty v-else description="暂无数据"></el-empty>
|
||||
<template v-if="tableData.data.length > 0">
|
||||
<el-pagination
|
||||
style="text-align: right"
|
||||
background
|
||||
@size-change="onHandleSizeChange"
|
||||
@current-change="onHandleCurrentChange"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:current-page="tableData.param.pageNum"
|
||||
:page-size="tableData.param.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</template>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { filterList } from './mock';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface ListAdaptRow {
|
||||
img: string;
|
||||
title: string;
|
||||
evaluate: string;
|
||||
collection: string;
|
||||
price: string;
|
||||
monSales: string;
|
||||
id: number;
|
||||
}
|
||||
interface TableDataState {
|
||||
tableData: {
|
||||
data: Array<ListAdaptRow>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesListAdapt',
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive<TableDataState>({
|
||||
tableData: {
|
||||
data: filterList,
|
||||
total: 99,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 当前列表项点击
|
||||
const onTableItemClick = (v: ListAdaptRow) => {
|
||||
router.push({
|
||||
path: '/pages/filteringDetails',
|
||||
query: { id: v.id },
|
||||
});
|
||||
};
|
||||
// 分页点击
|
||||
const onHandleSizeChange = (val: number) => {
|
||||
state.tableData.param.pageSize = val;
|
||||
};
|
||||
// 分页点击
|
||||
const onHandleCurrentChange = (val: number) => {
|
||||
state.tableData.param.pageNum = val;
|
||||
};
|
||||
return {
|
||||
onTableItemClick,
|
||||
onHandleSizeChange,
|
||||
onHandleCurrentChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
height: 360px;
|
||||
.flex-warp-item-box {
|
||||
border: 1px solid var(--next-border-color-light);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-color-primary);
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
|
||||
.item-txt-title {
|
||||
color: var(--el-color-primary) !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.item-img {
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
transform: translateZ(0) scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 215px;
|
||||
overflow: hidden;
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.item-txt {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
.item-txt-title {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
color: #666666;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
text-decoration: underline;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
.item-txt-other {
|
||||
flex: 1;
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
.item-txt-msg {
|
||||
font-size: 12px;
|
||||
color: #8d8d91;
|
||||
}
|
||||
.item-txt-price {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.font-price {
|
||||
color: #ff5000;
|
||||
.font {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
93
web/src/views/pages/listAdapt/mock.ts
Normal file
93
web/src/views/pages/listAdapt/mock.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// 列表数据
|
||||
export const filterList = [
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/09/e37326cc-4583-48f3-aa00-ecc2392d319d.jpg',
|
||||
title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/09/78cf72b6-e2d9-459d-a368-470414a027f4679cf4ea-26fa-48c8-9fee-c2d092a91400.png',
|
||||
title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/09/1faf3c6e-1250-4e6b-b072-4a331553e027.jpg',
|
||||
title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/09/9fcf6dd4-1e80-4497-bdc9-83dc7246d170.jpg.2',
|
||||
title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/09/1bd78227-4126-4a43-bdf6-48ead6edd1bf.jpg.2',
|
||||
title: '深圳:实现“从0到1”源头创新,推进大湾区综合性国家科学中心建设!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/08/9ea943a3-3ae8-4f49-8296-711ec36ef8c6_watermark.png',
|
||||
title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/08/a95ba232-1422-4f7e-b85f-c61d486c8659.jpg.2',
|
||||
title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/08/76816bf0-3899-4c7e-bc6e-079b5ba8725e.jpg',
|
||||
title: '民生小事 | 手机遗落出租车 热心民警帮找回',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 8,
|
||||
},
|
||||
{
|
||||
img: 'https://news.sznews.com/pic/2021-03/08/28ed70d4-71f5-4abb-bf7b-0294bece9e43.jpg.2',
|
||||
title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 9,
|
||||
},
|
||||
{
|
||||
img: 'http://news.sznews.com/pic/2021-03/05/d13ae31f-fd45-431a-b48e-c5895bbc193e.png',
|
||||
title: '深圳湾公园一女子落水,三名男子接力及时施救',
|
||||
evaluate: (Math.random() * 10).toFixed(2),
|
||||
collection: (Math.random() * 100).toFixed(2),
|
||||
price: (Math.random() * 10).toFixed(2),
|
||||
monSales: (Math.random() * 20).toFixed(2),
|
||||
id: 10,
|
||||
},
|
||||
];
|
||||
28
web/src/views/pages/preview/index.vue
Normal file
28
web/src/views/pages/preview/index.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="preview-container">
|
||||
<el-card shadow="hover" header="element-plus 大图预览">
|
||||
<el-image style="width: 100px; height: 100px; border-radius: 5px" :src="url" :preview-src-list="srcList" title="点击查看大图预览"> </el-image>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesPreview',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
url: 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500',
|
||||
srcList: [
|
||||
'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500',
|
||||
'https://img2.baidu.com/it/u=2370931438,70387529&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
'https://fuss10.elemecdn.com/1/8e/aeffeb4de74e2fde4bd74fc7b4486jpeg.jpeg',
|
||||
],
|
||||
});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
51
web/src/views/pages/steps/index.vue
Normal file
51
web/src/views/pages/steps/index.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="steps-container">
|
||||
<el-card shadow="hover" header="element-plus 步骤条">
|
||||
<el-steps :active="stepsActive">
|
||||
<el-step title="第一步">
|
||||
<template #icon>
|
||||
<SvgIcon name="iconfont icon-0_round_solid" :size="20" />
|
||||
</template>
|
||||
</el-step>
|
||||
<el-step title="第二步">
|
||||
<template #icon>
|
||||
<SvgIcon name="iconfont icon-2_round_solid" :size="20" />
|
||||
</template>
|
||||
</el-step>
|
||||
<el-step title="第三步">
|
||||
<template #icon>
|
||||
<SvgIcon name="iconfont icon-3_round_solid" :size="20" />
|
||||
</template>
|
||||
</el-step>
|
||||
</el-steps>
|
||||
<el-result icon="success" title="成功提示" subTitle="请根据提示进行操作" v-if="stepsActive === 1"> </el-result>
|
||||
<el-result icon="warning" title="警告提示" subTitle="请根据提示进行操作" v-else-if="stepsActive === 2"> </el-result>
|
||||
<el-result icon="error" title="错误提示" subTitle="请根据提示进行操作" v-else-if="stepsActive === 3"> </el-result>
|
||||
<el-button @click="onNextSteps" size="default" class="mt15" type="primary">
|
||||
<SvgIcon name="iconfont icon-step" />
|
||||
下一步
|
||||
</el-button>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesSteps',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
stepsActive: 1,
|
||||
});
|
||||
// 下一步点击
|
||||
const onNextSteps = () => {
|
||||
if (state.stepsActive++ > 2) state.stepsActive = 1;
|
||||
};
|
||||
return {
|
||||
onNextSteps,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
129
web/src/views/pages/tableRules/index.vue
Normal file
129
web/src/views/pages/tableRules/index.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<el-card shadow="hover" header="表单表格验证">
|
||||
<el-form ref="tableRulesRef" :model="tableData" size="default">
|
||||
<el-table :data="tableData.data" border class="module-table-uncollected">
|
||||
<el-table-column
|
||||
v-for="(item, index) in tableData.header"
|
||||
:key="index"
|
||||
show-overflow-tooltip
|
||||
:prop="item.prop"
|
||||
:width="item.width"
|
||||
:label="item.label"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<span v-if="item.isRequired" class="color-danger">*</span>
|
||||
<span class="pl5">{{ item.label }}</span>
|
||||
<el-tooltip v-if="item.isTooltip" effect="dark" content="这是tooltip" placement="top">
|
||||
<i class="iconfont icon-quanxian" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-slot="scope">
|
||||
<el-form-item
|
||||
:prop="`data.${scope.$index}.${item.prop}`"
|
||||
:rules="[{ required: item.isRequired, message: '不能为空', trigger: `${item.type}` == 'input' ? 'blur' : 'change' }]"
|
||||
>
|
||||
<el-select v-if="item.type === 'select'" v-model="scope.row[item.prop]" placeholder="请选择">
|
||||
<el-option v-for="sel in tableData.option" :key="sel.id" :label="sel.label" :value="sel.value" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else-if="item.type === 'date'"
|
||||
v-model="scope.row[item.prop]"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-input v-else-if="item.type === 'input'" v-model="scope.row[item.prop]" placeholder="请输入内容" />
|
||||
<el-input v-else-if="item.type === 'dialog'" v-model="scope.row[item.prop]" readonly placeholder="请输入内容">
|
||||
<template v-slot:suffix>
|
||||
<i class="iconfont icon-shouye_dongtaihui" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<el-row class="flex mt15">
|
||||
<div class="flex-margin">
|
||||
<el-button size="default" type="success" @click="onValidate">表格验证</el-button>
|
||||
<el-button size="default" type="primary" @click="onAddRow">新增一行</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableHeader {
|
||||
prop: string;
|
||||
width: string | number;
|
||||
label: string;
|
||||
isRequired?: boolean;
|
||||
isTooltip?: boolean;
|
||||
type: string;
|
||||
}
|
||||
interface TableRulesState {
|
||||
tableData: {
|
||||
data: any[];
|
||||
header: TableHeader[];
|
||||
option: any[];
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesTableRules',
|
||||
setup() {
|
||||
const tableRulesRef = ref();
|
||||
const state = reactive<TableRulesState>({
|
||||
tableData: {
|
||||
data: [],
|
||||
header: [
|
||||
{ prop: 'a1', width: '', label: '一级分类', isRequired: true, type: 'select' },
|
||||
{ prop: 'a2', width: '', label: '二级分类', isRequired: true, type: 'select' },
|
||||
{ prop: 'a3', width: '', label: '三级分类', isRequired: true, type: 'select' },
|
||||
{ prop: 'a4', width: '', label: '四级分类', isRequired: true, type: 'date' },
|
||||
{ prop: 'a5', width: '', label: '五级分类', isRequired: true, type: 'input' },
|
||||
{ prop: 'a6', width: '', label: '六级分类', isTooltip: true, type: 'dialog' },
|
||||
{ prop: 'a7', width: '', label: '演示级分类', type: 'input' },
|
||||
{ prop: 'a8', width: '', label: '颜色是分类', type: 'input' },
|
||||
],
|
||||
option: [
|
||||
{ value: '选项1', label: '黄金糕' },
|
||||
{ value: '选项2', label: '双皮奶' },
|
||||
{ value: '选项3', label: '蚵仔煎' },
|
||||
],
|
||||
},
|
||||
});
|
||||
// 表格验证
|
||||
const onValidate = () => {
|
||||
if (state.tableData.data.length <= 0) return ElMessage.warning('请先点击增加一行');
|
||||
tableRulesRef.value.validate((valid: any) => {
|
||||
if (!valid) return ElMessage.warning('表格项必填未填');
|
||||
ElMessage.success('全部验证通过');
|
||||
});
|
||||
};
|
||||
// 新增一行
|
||||
const onAddRow = () => {
|
||||
state.tableData.data.push({
|
||||
a1: '',
|
||||
a2: '',
|
||||
a3: '',
|
||||
a4: '',
|
||||
a5: '',
|
||||
a6: '',
|
||||
a7: '',
|
||||
a8: '',
|
||||
});
|
||||
};
|
||||
return {
|
||||
onValidate,
|
||||
onAddRow,
|
||||
tableRulesRef,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
258
web/src/views/pages/tree/index.vue
Normal file
258
web/src/views/pages/tree/index.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="tree-container">
|
||||
<el-card shadow="hover" header="element plus Tree 树形控件改成表格">
|
||||
<div v-loading="treeLoading">
|
||||
<div class="tree-head">
|
||||
<div class="tree-head-check"><el-checkbox v-model="treeCheckAll" @change="onCheckAllChange"></el-checkbox></div>
|
||||
<div class="tree-head-one">商品 ID</div>
|
||||
<div style="flex: 1; display: flex">
|
||||
<div class="tree-head-two">商品名称</div>
|
||||
<div class="tree-head-three">描述</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-tree :data="treeTableData" show-checkbox node-key="id" ref="treeTable" :props="treeDefaultProps" @check="onCheckTree">
|
||||
<template #default="{ node, data }">
|
||||
<span class="tree-custom-node">
|
||||
<span style="flex: 1">{{ node.label }}</span>
|
||||
<span v-if="data.isShow" style="flex: 1; display: flex">
|
||||
<span type="text" size="default" style="flex: 1">{{ data.label1 }}</span>
|
||||
<span type="text" size="default" style="flex: 1">{{ data.label2 }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
<el-button @click="onSelect" class="mt15" size="default" type="primary">
|
||||
<SvgIcon name="iconfont icon-shuxingtu" />
|
||||
选择元素
|
||||
</el-button>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onBeforeMount, getCurrentInstance, defineComponent } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TreeDataState {
|
||||
id: number;
|
||||
label: string;
|
||||
label1: string;
|
||||
label2: string;
|
||||
isShow: boolean;
|
||||
children?: TreeDataState[];
|
||||
}
|
||||
interface TreeSate {
|
||||
treeCheckAll: boolean;
|
||||
treeLoading: boolean;
|
||||
treeTableData: TreeDataState[];
|
||||
treeDefaultProps: {
|
||||
children: string;
|
||||
label: string;
|
||||
};
|
||||
treeSelArr: TreeDataState[];
|
||||
treeLength: number;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesTree',
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const state = reactive<TreeSate>({
|
||||
treeCheckAll: false,
|
||||
treeLoading: false,
|
||||
treeTableData: [],
|
||||
treeDefaultProps: {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
},
|
||||
treeSelArr: [],
|
||||
treeLength: 0,
|
||||
});
|
||||
// 初始化树的长度
|
||||
const initTreeLengh = (arr: TreeDataState[]) => {
|
||||
let count = 0;
|
||||
arr.map((item) => {
|
||||
if (item.children) {
|
||||
count += item.children.length;
|
||||
}
|
||||
});
|
||||
state.treeLength = count + arr.length;
|
||||
};
|
||||
// 全选改变时
|
||||
const onCheckAllChange = () => {
|
||||
if (state.treeCheckAll) {
|
||||
proxy.$refs.treeTable.setCheckedNodes(state.treeTableData);
|
||||
} else {
|
||||
proxy.$refs.treeTable.setCheckedKeys([]);
|
||||
}
|
||||
};
|
||||
// 节点选中状态发生变化时的回调
|
||||
const onCheckTree = () => {
|
||||
state.treeSelArr = [];
|
||||
state.treeSelArr = proxy.$refs.treeTable.getCheckedNodes();
|
||||
state.treeSelArr.length == state.treeLength ? (state.treeCheckAll = true) : (state.treeCheckAll = false);
|
||||
};
|
||||
// 选择元素按钮
|
||||
const onSelect = () => {
|
||||
let treeArr = proxy.$refs.treeTable.getCheckedNodes();
|
||||
if (treeArr.length <= 0) {
|
||||
ElMessage.warning('请选择元素');
|
||||
return;
|
||||
} else {
|
||||
// console.log(proxy.$refs.treeTable.getCheckedNodes());
|
||||
}
|
||||
};
|
||||
// 初始化树模拟数据
|
||||
const getTreeData = () => {
|
||||
state.treeTableData = [
|
||||
{
|
||||
id: 1,
|
||||
label: '12987121',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: true,
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
label: '一级 1-1',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
label: '一级 1-2',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '12987122',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: true,
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
label: '二级 2-1',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
label: '二级 2-2',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: '12987123',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: true,
|
||||
children: [
|
||||
{
|
||||
id: 31,
|
||||
label: '二级 3-1',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
label: '二级 3-2',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
label: '二级 3-3',
|
||||
label1: '好滋好味鸡蛋仔',
|
||||
label2: '荷兰优质淡奶,奶香浓而不腻',
|
||||
isShow: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
initTreeLengh(state.treeTableData);
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
getTreeData();
|
||||
});
|
||||
return {
|
||||
getTreeData,
|
||||
onCheckAllChange,
|
||||
onCheckTree,
|
||||
onSelect,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tree-container {
|
||||
.tree-head {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
border: 1px solid var(--next-border-color-light);
|
||||
border-bottom: none;
|
||||
display: flex;
|
||||
padding-right: 8px;
|
||||
font-weight: bold;
|
||||
color: #909399;
|
||||
.tree-head-check {
|
||||
width: 38px;
|
||||
text-align: right;
|
||||
}
|
||||
.tree-head-one,
|
||||
.tree-head-two,
|
||||
.tree-head-three {
|
||||
flex: 1;
|
||||
}
|
||||
.tree-head-one {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
.el-tree {
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid var(--next-border-color-light);
|
||||
.tree-custom-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-right: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
&:deep(.el-tree-node) {
|
||||
border: 1px solid var(--next-border-color-light);
|
||||
border-bottom: none;
|
||||
color: #606266;
|
||||
.el-tree-node__content {
|
||||
line-height: 57px !important;
|
||||
height: 57px !important;
|
||||
}
|
||||
.el-tree-node__children {
|
||||
.el-tree-node {
|
||||
border: none;
|
||||
}
|
||||
.el-tree-node__content {
|
||||
border-top: 1px solid var(--next-border-color-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
174
web/src/views/pages/waterfall/index.vue
Normal file
174
web/src/views/pages/waterfall/index.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="waterfall-container">
|
||||
<el-card shadow="hover" header="瀑布屏(布局一)" class="mb15">
|
||||
<div class="waterfall-first">
|
||||
<div class="waterfall-first-item" v-for="v in 30" :key="v" v-waves>
|
||||
<div class="w100 h100 flex">
|
||||
<span class="flex-margin">{{ v }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" header="瀑布屏(布局二)">
|
||||
<div class="waterfall-last">
|
||||
<div class="waterfall-last-item" v-for="v in 30" :key="v" v-waves="'light'">
|
||||
<div class="w100 h100 flex">
|
||||
<span class="flex-margin">{{ v }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWaterfall',
|
||||
setup() {
|
||||
const state = reactive({});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.waterfall-container {
|
||||
.waterfall-first {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(188px, 1fr));
|
||||
grid-gap: 0.25em;
|
||||
grid-auto-flow: row dense;
|
||||
grid-auto-rows: 20px;
|
||||
.waterfall-first-item {
|
||||
width: 100%;
|
||||
background: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 3px;
|
||||
&:nth-of-type(3n + 1) {
|
||||
grid-row: auto / span 5;
|
||||
}
|
||||
&:nth-of-type(3n + 2) {
|
||||
grid-row: auto / span 6;
|
||||
}
|
||||
&:nth-of-type(3n + 3) {
|
||||
grid-row: auto / span 8;
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.waterfall-last {
|
||||
display: grid;
|
||||
grid-gap: 0.25em;
|
||||
grid-auto-flow: row dense;
|
||||
grid-auto-rows: minmax(188px, 20vmin);
|
||||
grid-template-columns: 1fr;
|
||||
.waterfall-last-item {
|
||||
height: 100%;
|
||||
background: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
.waterfall-last {
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
.waterfall-last-item {
|
||||
&:nth-of-type(9n + 9) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(9n + 8) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(9n + 7) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(9n + 6) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(9n + 5) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(9n + 4) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(9n + 3) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(9n + 2) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(9n + 1) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (min-width: 576px) and (min-width: 1024px) {
|
||||
.waterfall-last {
|
||||
grid-template-columns: repeat(14, 1fr);
|
||||
.waterfall-last-item {
|
||||
&:nth-of-type(15n + 15) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 14) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 13) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(15n + 12) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 11) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 10) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
&:nth-of-type(15n + 9) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 8) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 7) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 6) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 5) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 4) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 3) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 2) {
|
||||
grid-column: auto / span 3;
|
||||
}
|
||||
&:nth-of-type(15n + 1) {
|
||||
grid-column: auto / span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
134
web/src/views/pages/waves/index.vue
Normal file
134
web/src/views/pages/waves/index.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="preview-container">
|
||||
<el-card shadow="hover" header="波浪指令效果(v-waves)作用于 btn">
|
||||
<el-row class="mb10" style="color: #808080">可选参数 v-waves=" |light|red|orange|purple|green|teal"</el-row>
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button size="default" v-waves>
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
默认效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default" v-waves="'light'">
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
light 效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="success" size="default" v-waves="'red'">
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
red 效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="default" v-waves="'orange'">
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
orange 效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="warning" size="default" v-waves="'purple'">
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
purple 效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="default" v-waves="'green'">
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
green 效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="default" v-waves="'teal'">
|
||||
<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
|
||||
teal 效果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" header="波浪指令效果(v-waves)作用于 div" class="mt15">
|
||||
<div class="waterfall-first">
|
||||
<div class="waterfall-first-item" v-for="v in 12" :key="v" v-waves>
|
||||
<div class="w100 h100 flex">
|
||||
<span class="flex-margin">{{ v }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWaves',
|
||||
setup() {
|
||||
const state = reactive({});
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.preview-container {
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.waterfall-first {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(188px, 1fr));
|
||||
grid-gap: 0.25em;
|
||||
grid-auto-flow: row dense;
|
||||
grid-auto-rows: 20px;
|
||||
.waterfall-first-item {
|
||||
width: 100%;
|
||||
background: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 3px;
|
||||
&:nth-of-type(3n + 1) {
|
||||
grid-row: auto / span 5;
|
||||
}
|
||||
&:nth-of-type(3n + 2) {
|
||||
grid-row: auto / span 6;
|
||||
}
|
||||
&:nth-of-type(3n + 3) {
|
||||
grid-row: auto / span 8;
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
107
web/src/views/pages/workflow/component/contextmenu/index.vue
Normal file
107
web/src/views/pages/workflow/component/contextmenu/index.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<transition name="el-zoom-in-center">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="el-dropdown__popper el-popper is-light is-pure custom-contextmenu"
|
||||
role="tooltip"
|
||||
data-popper-placement="bottom"
|
||||
:style="`top: ${dropdowns.y + 5}px;left: ${dropdowns.x}px;`"
|
||||
:key="Math.random()"
|
||||
v-show="isShow"
|
||||
>
|
||||
<ul class="el-dropdown-menu">
|
||||
<li
|
||||
v-for="(v, k) in dropdownList"
|
||||
class="el-dropdown-menu__item"
|
||||
aria-disabled="false"
|
||||
tabindex="-1"
|
||||
:key="k"
|
||||
@click="onCurrentClick(v.contextMenuClickId)"
|
||||
>
|
||||
<SvgIcon :name="v.icon" />
|
||||
<span>{{ v.txt }}{{ item.type === 'line' ? '线' : '节点' }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="el-popper__arrow" style="left: 10px"></div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, toRefs, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflowContextmenu',
|
||||
props: {
|
||||
dropdown: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
dropdownList: [
|
||||
{ contextMenuClickId: 0, txt: '删除', icon: 'ele-Delete' },
|
||||
{ contextMenuClickId: 1, txt: '编辑', icon: 'ele-Edit' },
|
||||
],
|
||||
item: {
|
||||
type: 'node',
|
||||
},
|
||||
conn: {},
|
||||
});
|
||||
// 父级传过来的坐标 x,y 值
|
||||
const dropdowns = computed(() => {
|
||||
return <any>props.dropdown;
|
||||
});
|
||||
// 当前项菜单点击
|
||||
const onCurrentClick = (contextMenuClickId: number) => {
|
||||
emit('current', Object.assign({}, { contextMenuClickId }, state.item), state.conn);
|
||||
};
|
||||
// 打开右键菜单:判断是否固定,固定则不显示关闭按钮
|
||||
const openContextmenu = (item: any, conn = {}) => {
|
||||
state.item = item;
|
||||
state.conn = conn;
|
||||
closeContextmenu();
|
||||
setTimeout(() => {
|
||||
state.isShow = true;
|
||||
}, 10);
|
||||
};
|
||||
// 关闭右键菜单
|
||||
const closeContextmenu = () => {
|
||||
state.isShow = false;
|
||||
};
|
||||
// 监听页面监听进行右键菜单的关闭
|
||||
onMounted(() => {
|
||||
document.body.addEventListener('click', closeContextmenu);
|
||||
document.body.addEventListener('contextmenu', closeContextmenu);
|
||||
});
|
||||
// 页面卸载时,移除右键菜单监听事件
|
||||
onUnmounted(() => {
|
||||
document.body.removeEventListener('click', closeContextmenu);
|
||||
document.body.removeEventListener('contextmenu', closeContextmenu);
|
||||
});
|
||||
return {
|
||||
dropdowns,
|
||||
openContextmenu,
|
||||
closeContextmenu,
|
||||
onCurrentClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-contextmenu {
|
||||
transform-origin: center top;
|
||||
z-index: 2190;
|
||||
position: fixed;
|
||||
.el-dropdown-menu__item {
|
||||
font-size: 12px !important;
|
||||
white-space: nowrap;
|
||||
i {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
73
web/src/views/pages/workflow/component/drawer/index.vue
Normal file
73
web/src/views/pages/workflow/component/drawer/index.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer :title="`${nodeData.type === 'line' ? '线' : '节点'}操作`" v-model="isOpen" size="320px">
|
||||
<el-scrollbar>
|
||||
<Line v-if="nodeData.type === 'line'" @change="onLineChange" @close="close" ref="lineRef" />
|
||||
<Node v-else @submit="onNodeSubmit" @close="close" ref="nodeRef" />
|
||||
</el-scrollbar>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, ref, nextTick } from 'vue';
|
||||
import Line from './line.vue';
|
||||
import Node from './node.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface WorkflowDrawerState {
|
||||
isOpen: boolean;
|
||||
nodeData: {
|
||||
type: string;
|
||||
};
|
||||
jsplumbConn: any;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflowDrawer',
|
||||
components: { Line, Node },
|
||||
setup(props, { emit }) {
|
||||
const lineRef = ref();
|
||||
const nodeRef = ref();
|
||||
const state = reactive<WorkflowDrawerState>({
|
||||
isOpen: false,
|
||||
nodeData: {
|
||||
type: 'node',
|
||||
},
|
||||
jsplumbConn: {},
|
||||
});
|
||||
// 打开抽屉
|
||||
const open = (item: any, conn: any) => {
|
||||
state.isOpen = true;
|
||||
state.jsplumbConn = conn;
|
||||
state.nodeData = item;
|
||||
nextTick(() => {
|
||||
if (item.type === 'line') lineRef.value.getParentData(item);
|
||||
else nodeRef.value.getParentData(item);
|
||||
});
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
state.isOpen = false;
|
||||
};
|
||||
// 线 label 内容改变时
|
||||
const onLineChange = (label: any) => {
|
||||
state.jsplumbConn.label = label;
|
||||
emit('label', state.jsplumbConn);
|
||||
};
|
||||
// 节点内容改变时
|
||||
const onNodeSubmit = (data: object) => {
|
||||
emit('node', data);
|
||||
};
|
||||
return {
|
||||
lineRef,
|
||||
nodeRef,
|
||||
open,
|
||||
close,
|
||||
onLineChange,
|
||||
onNodeSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
62
web/src/views/pages/workflow/component/drawer/line.vue
Normal file
62
web/src/views/pages/workflow/component/drawer/line.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="pt15 pr15 pb15 pl15">
|
||||
<el-form :model="line" size="default" label-width="50px">
|
||||
<el-form-item label="来往">
|
||||
<el-input v-model="line.contact" placeholder="来往" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-input v-model="line.type" placeholder="类型" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="label">
|
||||
<el-input v-model="line.label" placeholder="请输入label内容" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="onLineTextReset">
|
||||
<SvgIcon name="ele-RefreshRight" />
|
||||
重置
|
||||
</el-button>
|
||||
<el-button @click="onLineTextChange" type="primary">
|
||||
<SvgIcon name="ele-Check" />
|
||||
保存
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface WorkflowDrawerLineState {
|
||||
line: any;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflowDrawerLine',
|
||||
setup(props, { emit }) {
|
||||
const state = reactive<WorkflowDrawerLineState>({
|
||||
line: {},
|
||||
});
|
||||
// 获取父组件数据
|
||||
const getParentData = (data: object) => {
|
||||
state.line = data;
|
||||
};
|
||||
// 重置
|
||||
const onLineTextReset = () => {
|
||||
state.line.label = '';
|
||||
};
|
||||
// 保存
|
||||
const onLineTextChange = () => {
|
||||
emit('change', state.line.label);
|
||||
emit('close');
|
||||
};
|
||||
return {
|
||||
getParentData,
|
||||
onLineTextReset,
|
||||
onLineTextChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
272
web/src/views/pages/workflow/component/drawer/node.vue
Normal file
272
web/src/views/pages/workflow/component/drawer/node.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<div class="workflow-drawer-node">
|
||||
<el-tabs type="border-card" v-model="tabsActive">
|
||||
<!-- 节点编辑 -->
|
||||
<el-tab-pane label="节点编辑" name="1">
|
||||
<el-scrollbar>
|
||||
<el-form :model="node" :rules="nodeRules" ref="nodeFormRef" size="default" label-width="80px" class="pt15 pr15 pb15 pl15">
|
||||
<el-form-item label="数据id" prop="id">
|
||||
<el-input v-model="node.id" placeholder="请输入数据id" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="节点id" prop="nodeId">
|
||||
<el-input v-model="node.nodeId" placeholder="请输入节点id" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-input v-model="node.type" placeholder="请输入类型" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="left坐标" prop="left">
|
||||
<el-input v-model="node.left" placeholder="请输入left坐标" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="top坐标" prop="top">
|
||||
<el-input v-model="node.top" placeholder="请输入top坐标" clearable disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="icon图标" prop="icon">
|
||||
<el-input v-model="node.icon" placeholder="请输入icon图标" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="node.name" placeholder="请输入名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="mb15" @click="onNodeRefresh">
|
||||
<SvgIcon name="ele-RefreshRight" />
|
||||
重置
|
||||
</el-button>
|
||||
<el-button type="primary" class="mb15" @click="onNodeSubmit">
|
||||
<SvgIcon name="ele-Check" />
|
||||
保存
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 扩展表单 -->
|
||||
<el-tab-pane label="扩展表单" name="2">
|
||||
<el-scrollbar>
|
||||
<el-form :model="form" ref="extendFormRef" size="default" label-width="80px" class="pt15 pr15 pb15 pl15">
|
||||
<el-form-item
|
||||
:label="val.label"
|
||||
:prop="val.prop"
|
||||
v-for="(val, key) in node.from"
|
||||
:key="key"
|
||||
:rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="form[val.prop]"
|
||||
:placeholder="val.placeholder"
|
||||
clearable
|
||||
v-if="val.type === 'input'"
|
||||
:disabled="val.disabled"
|
||||
></el-input>
|
||||
<el-select v-model="form[val.prop]" placeholder="请选择" v-if="val.type === 'select'" clearable :disabled="val.disabled">
|
||||
<el-option v-for="item in val.options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
<el-checkbox-group v-model="form[val.prop]" v-if="val.type === 'checkbox'" :disabled="val.disabled">
|
||||
<el-checkbox label="美食推荐" name="type"></el-checkbox>
|
||||
<el-checkbox label="统计分析" name="type"></el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="mb15" @click="onExtendRefresh">
|
||||
<SvgIcon name="ele-RefreshRight" />
|
||||
重置
|
||||
</el-button>
|
||||
<el-button type="primary" class="mb15" @click="onExtendSubmit" :loading="loading.extend">
|
||||
<SvgIcon name="ele-Check" />
|
||||
保存
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 图表可视化 -->
|
||||
<el-tab-pane label="图表可视化" name="3">
|
||||
<el-scrollbar>
|
||||
<div class="flex-content-right">
|
||||
<div style="height: 200px; width: 320px" ref="chartsMonitorRef"></div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, ref, nextTick, getCurrentInstance } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface WorkflowDrawerNodeState {
|
||||
node: { [key: string]: any };
|
||||
nodeRules: any;
|
||||
form: any;
|
||||
tabsActive: string;
|
||||
loading: {
|
||||
extend: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflowDrawerNode',
|
||||
setup(props, { emit }) {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const nodeFormRef = ref();
|
||||
const extendFormRef = ref();
|
||||
const chartsMonitorRef = ref();
|
||||
const state = reactive<WorkflowDrawerNodeState>({
|
||||
node: {},
|
||||
nodeRules: {
|
||||
id: [{ required: true, message: '请输入数据id', trigger: 'blur' }],
|
||||
nodeId: [{ required: true, message: '请输入节点id', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '请输入类型', trigger: 'blur' }],
|
||||
left: [{ required: true, message: '请输入left坐标', trigger: 'blur' }],
|
||||
top: [{ required: true, message: '请输入top坐标', trigger: 'blur' }],
|
||||
icon: [{ required: true, message: '请输入icon图标', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
},
|
||||
form: {
|
||||
module: [],
|
||||
},
|
||||
tabsActive: '1',
|
||||
loading: {
|
||||
extend: false,
|
||||
},
|
||||
});
|
||||
// 获取父组件数据
|
||||
const getParentData = (data: object) => {
|
||||
state.tabsActive = '1';
|
||||
state.node = data;
|
||||
initChartsMonitor();
|
||||
};
|
||||
// 节点编辑-重置
|
||||
const onNodeRefresh = () => {
|
||||
state.node.icon = '';
|
||||
state.node.name = '';
|
||||
};
|
||||
// 节点编辑-保存
|
||||
const onNodeSubmit = () => {
|
||||
nodeFormRef.value.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
emit('submit', state.node);
|
||||
emit('close');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
// 扩展表单-重置
|
||||
const onExtendRefresh = () => {
|
||||
extendFormRef.value.resetFields();
|
||||
};
|
||||
// 扩展表单-保存
|
||||
const onExtendSubmit = () => {
|
||||
extendFormRef.value.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
state.loading.extend = true;
|
||||
setTimeout(() => {
|
||||
state.loading.extend = false;
|
||||
ElMessage.success('保存成功');
|
||||
emit('close');
|
||||
}, 1000);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
// 图表可视化-初始化
|
||||
const initChartsMonitor = () => {
|
||||
const myChart = echarts.init(proxy.$refs.chartsMonitorRef);
|
||||
const numsOne = [];
|
||||
const numsTwo = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
numsOne.push(`${Math.floor(Math.random() * 52 + 10)}:${Math.floor(Math.random() * 52 + 1)}`);
|
||||
numsTwo.push(Math.floor(Math.random() * 52 + 1));
|
||||
}
|
||||
const option = {
|
||||
grid: {
|
||||
top: 50,
|
||||
right: 30,
|
||||
bottom: 30,
|
||||
left: 50,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: numsOne,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
itemStyle: {
|
||||
color: '#289df5',
|
||||
borderColor: '#289df5',
|
||||
areaStyle: {
|
||||
type: 'default',
|
||||
opacity: 0.1,
|
||||
},
|
||||
},
|
||||
data: numsTwo,
|
||||
type: 'line',
|
||||
areaStyle: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
nextTick(() => {
|
||||
myChart.resize();
|
||||
});
|
||||
};
|
||||
return {
|
||||
nodeFormRef,
|
||||
extendFormRef,
|
||||
chartsMonitorRef,
|
||||
getParentData,
|
||||
onNodeRefresh,
|
||||
onNodeSubmit,
|
||||
onExtendRefresh,
|
||||
onExtendSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.workflow-drawer-node {
|
||||
:deep {
|
||||
.el-tabs {
|
||||
box-shadow: unset;
|
||||
border: unset;
|
||||
.el-tabs__nav {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
.el-tabs__item {
|
||||
flex: 1;
|
||||
padding: unset;
|
||||
text-align: center;
|
||||
&:first-of-type.is-active {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
&:last-of-type.is-active {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-tabs__content {
|
||||
padding: 0;
|
||||
height: calc(100vh - 90px);
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
39
web/src/views/pages/workflow/component/tool/help.vue
Normal file
39
web/src/views/pages/workflow/component/tool/help.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="workflow-tool-help">
|
||||
<el-dialog v-model="isShow" width="769px">
|
||||
<template #header>
|
||||
<div v-drag="['.workflow-tool-help .el-dialog', '.workflow-tool-help .el-dialog__header']">使用帮助</div>
|
||||
</template>
|
||||
<div>1、拖入:鼠标移入左侧导航中,鼠标形状改变时拖动到右侧网格状的视图中。</div>
|
||||
<div class="mt10">2、移动:鼠标移入到视图中的某个节点元素,鼠标形状改变时拖动改变位置。</div>
|
||||
<div class="mt10">3、连线:鼠标移入到视图中的某个节点元素的icon(图标),鼠标形状改变(变成"+"),按下鼠标左键进行拖线连接。</div>
|
||||
<div class="mt10">4、节点:鼠标移入到视图中的某个节点元素,点击鼠标右键可进行删除、编辑节点。</div>
|
||||
<div class="mt10 mb10">5、线条:鼠标移入到视图中的某个线条,线条颜色改变时,点击鼠标右键可进行删除、编辑线条。</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflowToolHelp',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
});
|
||||
// 打开弹窗
|
||||
const open = () => {
|
||||
state.isShow = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const close = () => {
|
||||
state.isShow = false;
|
||||
};
|
||||
return {
|
||||
open,
|
||||
close,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
79
web/src/views/pages/workflow/component/tool/index.vue
Normal file
79
web/src/views/pages/workflow/component/tool/index.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="workflow-tool">
|
||||
<div class="pl15">{{ setToolTitle }}</div>
|
||||
<div class="workflow-tool-right">
|
||||
<div class="workflow-tool-icon" v-for="(v, k) in toolList" :key="k" :title="v.title" @click="onToolClick(v.fnName)">
|
||||
<SvgIcon :name="v.icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, reactive, toRefs } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflowTool',
|
||||
setup(props, { emit }) {
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const state = reactive({
|
||||
toolList: [
|
||||
{ icon: 'ele-Help', title: '帮助', fnName: 'help' },
|
||||
{ icon: 'ele-Download', title: '下载', fnName: 'download' },
|
||||
{ icon: 'ele-Check', title: '提交', fnName: 'submit' },
|
||||
{ icon: 'ele-DocumentCopy', title: '复制', fnName: 'copy' },
|
||||
{ icon: 'ele-Delete', title: '删除', fnName: 'del' },
|
||||
{ icon: 'ele-FullScreen', title: '全屏', fnName: 'fullscreen' },
|
||||
],
|
||||
});
|
||||
// 设置 tool 标题
|
||||
const setToolTitle = computed(() => {
|
||||
let { globalTitle } = themeConfig.value;
|
||||
return `${globalTitle}工作流`;
|
||||
});
|
||||
// 顶部工具栏
|
||||
const onToolClick = (fnName: string) => {
|
||||
emit('tool', fnName);
|
||||
};
|
||||
return {
|
||||
setToolTitle,
|
||||
onToolClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.workflow-tool {
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
color: var(--el-text-color-primary);
|
||||
.workflow-tool-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
&-icon {
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
color: var(--next-bg-topBarColor);
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
i {
|
||||
display: inline-block;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
693
web/src/views/pages/workflow/index.vue
Normal file
693
web/src/views/pages/workflow/index.vue
Normal file
@@ -0,0 +1,693 @@
|
||||
<template>
|
||||
<div class="workflow-container">
|
||||
<div class="workflow-mask" v-if="isShow"></div>
|
||||
<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
|
||||
<div class="workflow">
|
||||
<!-- 顶部工具栏 -->
|
||||
<Tool @tool="onToolClick" />
|
||||
|
||||
<!-- 左侧导航区 -->
|
||||
<div class="workflow-content">
|
||||
<div class="workflow-left">
|
||||
<el-scrollbar>
|
||||
<div
|
||||
ref="leftNavRefs"
|
||||
v-for="(val, key) in leftNavList"
|
||||
:key="val.id"
|
||||
:style="{ height: val.isOpen ? 'auto' : '50px', overflow: 'hidden' }"
|
||||
class="workflow-left-id"
|
||||
>
|
||||
<div class="workflow-left-title" @click="onTitleClick(val)">
|
||||
<span>{{ val.title }}</span>
|
||||
<SvgIcon :name="val.isOpen ? 'ele-ArrowDown' : 'ele-ArrowRight'" />
|
||||
</div>
|
||||
<div class="workflow-left-item" v-for="(v, k) in val.children" :key="k" :data-name="v.name" :data-icon="v.icon" :data-id="v.id">
|
||||
<div class="workflow-left-item-icon">
|
||||
<SvgIcon :name="v.icon" class="workflow-icon-drag" />
|
||||
<div class="font10 pl5 name">{{ v.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- 右侧绘画区 -->
|
||||
<div class="workflow-right" ref="workflowRightRef">
|
||||
<div
|
||||
v-for="(v, k) in jsplumbData.nodeList"
|
||||
:key="v.nodeId"
|
||||
:id="v.nodeId"
|
||||
:data-node-id="v.nodeId"
|
||||
:class="v.class"
|
||||
:style="{ left: v.left, top: v.top }"
|
||||
@click="onItemCloneClick(k)"
|
||||
@contextmenu.prevent="onContextmenu(v, k, $event)"
|
||||
>
|
||||
<div class="workflow-right-box" :class="{ 'workflow-right-active': jsPlumbNodeIndex === k }">
|
||||
<div class="workflow-left-item-icon">
|
||||
<SvgIcon :name="v.icon" class="workflow-icon-drag" />
|
||||
<div class="font10 pl5 name">{{ v.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 节点右键菜单 -->
|
||||
<Contextmenu :dropdown="dropdownNode" ref="contextmenuNodeRef" @current="onCurrentNodeClick" />
|
||||
<!-- 线右键菜单 -->
|
||||
<Contextmenu :dropdown="dropdownLine" ref="contextmenuLineRef" @current="onCurrentLineClick" />
|
||||
<!-- 抽屉表单、线 -->
|
||||
<Drawer ref="drawerRef" @label="setLineLabel" @node="setNodeContent" />
|
||||
|
||||
<!-- 顶部工具栏-帮助弹窗 -->
|
||||
<Help ref="helpRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, computed, onMounted, onUnmounted, nextTick, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { jsPlumb } from 'jsplumb';
|
||||
import Sortable from 'sortablejs';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import Tool from './component/tool/index.vue';
|
||||
import Help from './component/tool/help.vue';
|
||||
import Contextmenu from './component/contextmenu/index.vue';
|
||||
import Drawer from './component/drawer/index.vue';
|
||||
import commonFunction from '/@/utils/commonFunction';
|
||||
import { leftNavList } from './js/mock';
|
||||
import { jsplumbDefaults, jsplumbMakeSource, jsplumbMakeTarget, jsplumbConnect } from './js/config';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface NodeListState {
|
||||
id: string | number;
|
||||
nodeId: string | undefined;
|
||||
class: HTMLElement | string;
|
||||
left: number | string;
|
||||
top: number | string;
|
||||
icon: string;
|
||||
name: string;
|
||||
}
|
||||
interface LineListState {
|
||||
sourceId: string;
|
||||
targetId: string;
|
||||
label: string;
|
||||
}
|
||||
interface XyState {
|
||||
x: string | number;
|
||||
y: string | number;
|
||||
}
|
||||
interface WorkflowState {
|
||||
workflowRightRef: HTMLDivElement | null;
|
||||
leftNavRefs: any[];
|
||||
leftNavList: any[];
|
||||
dropdownNode: XyState;
|
||||
dropdownLine: XyState;
|
||||
isShow: boolean;
|
||||
jsPlumb: any;
|
||||
jsPlumbNodeIndex: null | number;
|
||||
jsplumbDefaults: any;
|
||||
jsplumbMakeSource: any;
|
||||
jsplumbMakeTarget: any;
|
||||
jsplumbConnect: any;
|
||||
jsplumbData: {
|
||||
nodeList: Array<NodeListState>;
|
||||
lineList: Array<LineListState>;
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'pagesWorkflow',
|
||||
components: { Tool, Contextmenu, Drawer, Help },
|
||||
setup() {
|
||||
const contextmenuNodeRef = ref();
|
||||
const contextmenuLineRef = ref();
|
||||
const drawerRef = ref();
|
||||
const helpRef = ref();
|
||||
const stores = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(stores);
|
||||
const { copyText } = commonFunction();
|
||||
const state = reactive<WorkflowState>({
|
||||
workflowRightRef: null as HTMLDivElement | null,
|
||||
leftNavRefs: [],
|
||||
leftNavList: [],
|
||||
dropdownNode: { x: '', y: '' },
|
||||
dropdownLine: { x: '', y: '' },
|
||||
isShow: false,
|
||||
jsPlumb: null,
|
||||
jsPlumbNodeIndex: null,
|
||||
jsplumbDefaults,
|
||||
jsplumbMakeSource,
|
||||
jsplumbMakeTarget,
|
||||
jsplumbConnect,
|
||||
jsplumbData: {
|
||||
nodeList: [],
|
||||
lineList: [],
|
||||
},
|
||||
});
|
||||
// 设置 view 的高度
|
||||
const setViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// 设置 宽度小于 768,不支持操
|
||||
const setClientWidth = () => {
|
||||
const clientWidth = document.body.clientWidth;
|
||||
clientWidth < 768 ? (state.isShow = true) : (state.isShow = false);
|
||||
};
|
||||
// 左侧导航-数据初始化
|
||||
const initLeftNavList = () => {
|
||||
state.leftNavList = leftNavList;
|
||||
state.jsplumbData = {
|
||||
nodeList: [
|
||||
{ nodeId: 'huej738hbji', left: '148px', top: '93px', class: 'workflow-right-clone', icon: 'iconfont icon-gongju', name: '引擎', id: '11' },
|
||||
{
|
||||
nodeId: '52kcszzyxrd',
|
||||
left: '458px',
|
||||
top: '203px',
|
||||
class: 'workflow-right-clone',
|
||||
icon: 'iconfont icon-shouye_dongtaihui',
|
||||
name: '模版',
|
||||
id: '12',
|
||||
},
|
||||
{
|
||||
nodeId: 'nltskl6k4me',
|
||||
left: '164px',
|
||||
top: '350px',
|
||||
class: 'workflow-right-clone',
|
||||
icon: 'iconfont icon-zhongduancanshuchaxun',
|
||||
name: '名称',
|
||||
id: '13',
|
||||
},
|
||||
],
|
||||
lineList: [
|
||||
{ sourceId: 'huej738hbji', targetId: '52kcszzyxrd', label: '传送' },
|
||||
{ sourceId: 'huej738hbji', targetId: 'nltskl6k4me', label: '' },
|
||||
],
|
||||
};
|
||||
};
|
||||
// 左侧导航-初始化拖动
|
||||
const initSortable = () => {
|
||||
state.leftNavRefs.forEach((v) => {
|
||||
Sortable.create(v as HTMLDivElement, {
|
||||
group: {
|
||||
name: 'vue-next-admin-1',
|
||||
pull: 'clone',
|
||||
put: false,
|
||||
},
|
||||
animation: 0,
|
||||
sort: false,
|
||||
draggable: '.workflow-left-item',
|
||||
forceFallback: true,
|
||||
onEnd: function (evt: any) {
|
||||
const { name, icon, id } = evt.clone.dataset;
|
||||
const { layerX, layerY, clientX, clientY } = evt.originalEvent;
|
||||
const el = state.workflowRightRef!;
|
||||
const { x, y, width, height } = el.getBoundingClientRect();
|
||||
if (clientX < x || clientX > width + x || clientY < y || y > y + height) {
|
||||
ElMessage.warning('请把节点拖入到画布中');
|
||||
} else {
|
||||
// 节点id(唯一)
|
||||
const nodeId = Math.random().toString(36).substr(2, 12);
|
||||
// 处理节点数据
|
||||
const node = {
|
||||
nodeId,
|
||||
left: `${layerX - 40}px`,
|
||||
top: `${layerY - 15}px`,
|
||||
class: 'workflow-right-clone',
|
||||
name,
|
||||
icon,
|
||||
id,
|
||||
};
|
||||
// 右侧视图内容数组
|
||||
state.jsplumbData.nodeList.push(node);
|
||||
// 元素加载完毕时
|
||||
nextTick(() => {
|
||||
// 整个节点作为source或者target
|
||||
state.jsPlumb.makeSource(nodeId, state.jsplumbMakeSource);
|
||||
// // 整个节点作为source或者target
|
||||
state.jsPlumb.makeTarget(nodeId, state.jsplumbMakeTarget, jsplumbConnect);
|
||||
// 设置节点可以拖拽(此处为id值,非class)
|
||||
state.jsPlumb.draggable(nodeId, {
|
||||
containment: 'parent',
|
||||
stop: (el: any) => {
|
||||
state.jsplumbData.nodeList.forEach((v) => {
|
||||
if (v.nodeId === el.el.id) {
|
||||
// 节点x, y重新赋值,防止再次从左侧导航中拖拽节点时,x, y恢复默认
|
||||
v.left = `${el.pos[0]}px`;
|
||||
v.top = `${el.pos[1]}px`;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
// 初始化 jsPlumb
|
||||
const initJsPlumb = () => {
|
||||
(<any>jsPlumb).ready(() => {
|
||||
state.jsPlumb = (<any>jsPlumb).getInstance({
|
||||
detachable: false,
|
||||
Container: 'workflow-right',
|
||||
});
|
||||
state.jsPlumb.fire('jsPlumbDemoLoaded', state.jsPlumb);
|
||||
// 导入默认配置
|
||||
state.jsPlumb.importDefaults(state.jsplumbDefaults);
|
||||
// 会使整个jsPlumb立即重绘。
|
||||
state.jsPlumb.setSuspendDrawing(false, true);
|
||||
// 初始化节点、线的链接
|
||||
initJsPlumbConnection();
|
||||
// 点击线弹出右键菜单
|
||||
state.jsPlumb.bind('contextmenu', (conn: any, originalEvent: MouseEvent) => {
|
||||
originalEvent.preventDefault();
|
||||
const { sourceId, targetId } = conn;
|
||||
const { clientX, clientY } = originalEvent;
|
||||
state.dropdownLine.x = clientX;
|
||||
state.dropdownLine.y = clientY;
|
||||
const v: any = state.jsplumbData.nodeList.find((v) => v.nodeId === targetId);
|
||||
const line: any = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
|
||||
v.type = 'line';
|
||||
v.label = line.label;
|
||||
contextmenuLineRef.value.openContextmenu(v, conn);
|
||||
});
|
||||
// 连线之前
|
||||
state.jsPlumb.bind('beforeDrop', (conn: any) => {
|
||||
const { sourceId, targetId } = conn;
|
||||
const item = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
|
||||
if (item) {
|
||||
ElMessage.warning('关系已存在,不可重复连接');
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// 连线时
|
||||
state.jsPlumb.bind('connection', (conn: any) => {
|
||||
const { sourceId, targetId } = conn;
|
||||
state.jsplumbData.lineList.push({
|
||||
sourceId,
|
||||
targetId,
|
||||
label: '',
|
||||
});
|
||||
});
|
||||
// 删除连线时回调函数
|
||||
state.jsPlumb.bind('connectionDetached', (conn: any) => {
|
||||
const { sourceId, targetId } = conn;
|
||||
state.jsplumbData.lineList = state.jsplumbData.lineList.filter((line) => {
|
||||
if (line.sourceId == sourceId && line.targetId == targetId) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
// 初始化节点、线的链接
|
||||
const initJsPlumbConnection = () => {
|
||||
// 节点
|
||||
state.jsplumbData.nodeList.forEach((v) => {
|
||||
// 整个节点作为source或者target
|
||||
state.jsPlumb.makeSource(v.nodeId, state.jsplumbMakeSource);
|
||||
// 整个节点作为source或者target
|
||||
state.jsPlumb.makeTarget(v.nodeId, state.jsplumbMakeTarget, jsplumbConnect);
|
||||
// 设置节点可以拖拽(此处为id值,非class)
|
||||
state.jsPlumb.draggable(v.nodeId, {
|
||||
containment: 'parent',
|
||||
stop: (el: any) => {
|
||||
state.jsplumbData.nodeList.forEach((v) => {
|
||||
if (v.nodeId === el.el.id) {
|
||||
// 节点x, y重新赋值,防止再次从左侧导航中拖拽节点时,x, y恢复默认
|
||||
v.left = `${el.pos[0]}px`;
|
||||
v.top = `${el.pos[1]}px`;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
// 线
|
||||
state.jsplumbData.lineList.forEach((v) => {
|
||||
state.jsPlumb.connect(
|
||||
{
|
||||
source: v.sourceId,
|
||||
target: v.targetId,
|
||||
label: v.label,
|
||||
},
|
||||
state.jsplumbConnect
|
||||
);
|
||||
});
|
||||
};
|
||||
// 左侧导航-菜单标题点击
|
||||
const onTitleClick = (val: any) => {
|
||||
val.isOpen = !val.isOpen;
|
||||
};
|
||||
// 右侧内容区-当前项点击
|
||||
const onItemCloneClick = (k: number) => {
|
||||
state.jsPlumbNodeIndex = k;
|
||||
};
|
||||
// 右侧内容区-当前项右键菜单点击
|
||||
const onContextmenu = (v: any, k: number, e: MouseEvent) => {
|
||||
state.jsPlumbNodeIndex = k;
|
||||
const { clientX, clientY } = e;
|
||||
state.dropdownNode.x = clientX;
|
||||
state.dropdownNode.y = clientY;
|
||||
v.type = 'node';
|
||||
v.label = '';
|
||||
let item: any = {};
|
||||
state.leftNavList.forEach((l) => {
|
||||
if (l.children) if (l.children.find((c: any) => c.id === v.id)) item = l.children.find((c: any) => c.id === v.id);
|
||||
});
|
||||
v.from = item.form;
|
||||
contextmenuNodeRef.value.openContextmenu(v);
|
||||
};
|
||||
// 右侧内容区-当前项右键菜单点击回调(节点)
|
||||
const onCurrentNodeClick = (item: any) => {
|
||||
const { contextMenuClickId, nodeId } = item;
|
||||
if (contextMenuClickId === 0) {
|
||||
const nodeIndex = state.jsplumbData.nodeList.findIndex((item) => item.nodeId === nodeId);
|
||||
state.jsplumbData.nodeList.splice(nodeIndex, 1);
|
||||
state.jsPlumb.removeAllEndpoints(nodeId);
|
||||
state.jsPlumbNodeIndex = null;
|
||||
} else if (contextMenuClickId === 1) {
|
||||
drawerRef.value.open(item);
|
||||
}
|
||||
};
|
||||
// 右侧内容区-当前项右键菜单点击回调(线)
|
||||
const onCurrentLineClick = (item: any, conn: any) => {
|
||||
const { contextMenuClickId } = item;
|
||||
const { endpoints } = conn;
|
||||
const intercourse: any = [];
|
||||
endpoints.forEach((v: any) => {
|
||||
intercourse.push({
|
||||
id: v.element.id,
|
||||
innerText: v.element.innerText,
|
||||
});
|
||||
});
|
||||
item.contact = `${intercourse[0].innerText}(${intercourse[0].id}) => ${intercourse[1].innerText}(${intercourse[1].id})`;
|
||||
if (contextMenuClickId === 0) state.jsPlumb.deleteConnection(conn);
|
||||
else if (contextMenuClickId === 1) drawerRef.value.open(item, conn);
|
||||
};
|
||||
// 设置线的 label
|
||||
const setLineLabel = (obj: any) => {
|
||||
const { sourceId, targetId, label } = obj;
|
||||
const conn = state.jsPlumb.getConnections({
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
})[0];
|
||||
conn.setLabel(label);
|
||||
if (!label || label === '') {
|
||||
conn.addClass('workflow-right-empty-label');
|
||||
} else {
|
||||
conn.removeClass('workflow-right-empty-label');
|
||||
conn.addClass('workflow-right-label');
|
||||
}
|
||||
state.jsplumbData.lineList.forEach((v) => {
|
||||
if (v.sourceId === sourceId && v.targetId === targetId) v.label = label;
|
||||
});
|
||||
};
|
||||
// 设置节点内容
|
||||
const setNodeContent = (obj: any) => {
|
||||
const { nodeId, name, icon } = obj;
|
||||
// 设置节点 name 与 icon
|
||||
state.jsplumbData.nodeList.forEach((v) => {
|
||||
if (v.nodeId === nodeId) {
|
||||
v.name = name;
|
||||
v.icon = icon;
|
||||
}
|
||||
});
|
||||
// 重绘
|
||||
nextTick(() => {
|
||||
state.jsPlumb.setSuspendDrawing(false, true);
|
||||
});
|
||||
};
|
||||
// 顶部工具栏-当前项点击
|
||||
const onToolClick = (fnName: String) => {
|
||||
switch (fnName) {
|
||||
case 'help':
|
||||
onToolHelp();
|
||||
break;
|
||||
case 'download':
|
||||
onToolDownload();
|
||||
break;
|
||||
case 'submit':
|
||||
onToolSubmit();
|
||||
break;
|
||||
case 'copy':
|
||||
onToolCopy();
|
||||
break;
|
||||
case 'del':
|
||||
onToolDel();
|
||||
break;
|
||||
case 'fullscreen':
|
||||
onToolFullscreen();
|
||||
break;
|
||||
}
|
||||
};
|
||||
// 顶部工具栏-帮助
|
||||
const onToolHelp = () => {
|
||||
nextTick(() => {
|
||||
helpRef.value.open();
|
||||
});
|
||||
};
|
||||
// 顶部工具栏-下载
|
||||
const onToolDownload = () => {
|
||||
const { globalTitle } = themeConfig.value;
|
||||
const href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(state.jsplumbData, null, '\t'));
|
||||
const aLink = document.createElement('a');
|
||||
aLink.setAttribute('href', href);
|
||||
aLink.setAttribute('download', `${globalTitle}工作流.json`);
|
||||
aLink.click();
|
||||
aLink.remove();
|
||||
ElMessage.success('下载成功');
|
||||
};
|
||||
// 顶部工具栏-提交
|
||||
const onToolSubmit = () => {
|
||||
// console.log(state.jsplumbData);
|
||||
ElMessage.success('数据提交成功');
|
||||
};
|
||||
// 顶部工具栏-复制
|
||||
const onToolCopy = () => {
|
||||
copyText(JSON.stringify(state.jsplumbData));
|
||||
};
|
||||
// 顶部工具栏-删除
|
||||
const onToolDel = () => {
|
||||
ElMessageBox.confirm('此操作将清空画布,是否继续?', '提示', {
|
||||
confirmButtonText: '清空',
|
||||
cancelButtonText: '取消',
|
||||
})
|
||||
.then(() => {
|
||||
state.jsplumbData.nodeList.forEach((v) => {
|
||||
state.jsPlumb.removeAllEndpoints(v.nodeId);
|
||||
});
|
||||
nextTick(() => {
|
||||
state.jsplumbData = {
|
||||
nodeList: [],
|
||||
lineList: [],
|
||||
};
|
||||
ElMessage.success('清空画布成功');
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 顶部工具栏-全屏
|
||||
const onToolFullscreen = () => {
|
||||
stores.setCurrenFullscreen(true);
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(async () => {
|
||||
await initLeftNavList();
|
||||
initSortable();
|
||||
initJsPlumb();
|
||||
setClientWidth();
|
||||
window.addEventListener('resize', setClientWidth);
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', setClientWidth);
|
||||
});
|
||||
return {
|
||||
setViewHeight,
|
||||
setClientWidth,
|
||||
setLineLabel,
|
||||
setNodeContent,
|
||||
onTitleClick,
|
||||
onItemCloneClick,
|
||||
onContextmenu,
|
||||
onCurrentNodeClick,
|
||||
onCurrentLineClick,
|
||||
contextmenuNodeRef,
|
||||
contextmenuLineRef,
|
||||
drawerRef,
|
||||
helpRef,
|
||||
onToolClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.workflow-container {
|
||||
position: relative;
|
||||
.workflow {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
.workflow-content {
|
||||
display: flex;
|
||||
height: calc(100% - 35px);
|
||||
.workflow-left {
|
||||
width: 220px;
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
:deep(.el-collapse-item__content) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.workflow-left-title {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
border-top: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
color: var(--el-text-color-primary);
|
||||
cursor: default;
|
||||
span {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.workflow-left-item {
|
||||
display: inline-block;
|
||||
width: calc(50% - 15px);
|
||||
position: relative;
|
||||
cursor: move;
|
||||
margin: 0 0 10px 10px;
|
||||
.workflow-left-item-icon {
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
padding: 5px 10px;
|
||||
border: 1px dashed transparent;
|
||||
background: var(--next-bg-color);
|
||||
border-radius: 3px;
|
||||
i,
|
||||
.name {
|
||||
color: var(--el-text-color-secondary);
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
&:hover {
|
||||
transition: all 0.3s ease;
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
border-radius: 5px;
|
||||
i,
|
||||
.name {
|
||||
transition: all 0.3s ease;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
& .workflow-left-id:first-of-type {
|
||||
.workflow-left-title {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.workflow-right {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(90deg, rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%),
|
||||
linear-gradient(rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%);
|
||||
background-size: 10px 10px;
|
||||
.workflow-right-clone {
|
||||
position: absolute;
|
||||
.workflow-right-box {
|
||||
height: 35px;
|
||||
align-items: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding: 0 10px;
|
||||
border-radius: 3px;
|
||||
cursor: move;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 94.5px;
|
||||
background: var(--el-color-white);
|
||||
border: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
.workflow-left-item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 35px;
|
||||
}
|
||||
&:hover {
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
transition: all 0.3s ease;
|
||||
color: var(--el-color-primary);
|
||||
i {
|
||||
cursor: Crosshair;
|
||||
}
|
||||
}
|
||||
}
|
||||
.workflow-right-active {
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
:deep(.jtk-overlay):not(.aLabel) {
|
||||
padding: 4px 10px;
|
||||
border: 1px solid var(--el-border-color-light, #ebeef5) !important;
|
||||
color: var(--el-text-color-secondary) !important;
|
||||
background: var(--el-color-white) !important;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
}
|
||||
:deep(.jtk-overlay.workflow-right-empty-label) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.workflow-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
&::after {
|
||||
content: '手机版不支持 jsPlumb 操作';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #666666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
99
web/src/views/pages/workflow/js/config.ts
Normal file
99
web/src/views/pages/workflow/js/config.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
// jsplumb 默认配置
|
||||
export const jsplumbDefaults = {
|
||||
// 多个锚点 [源锚点,目标锚点]
|
||||
Anchors: [
|
||||
'Top',
|
||||
'TopCenter',
|
||||
'TopRight',
|
||||
'TopLeft',
|
||||
'Right',
|
||||
'RightMiddle',
|
||||
'Bottom',
|
||||
'BottomCenter',
|
||||
'BottomRight',
|
||||
'BottomLeft',
|
||||
'Left',
|
||||
'LeftMiddle',
|
||||
],
|
||||
// 连线的容器id
|
||||
Container: 'workflow-right',
|
||||
// 设置链接线的形状,如直线或者曲线之类的。anchor可以去设置锚点的位置。可选值"<Bezier|Flowchart|StateMachine|Straight>"
|
||||
Connector: ['Bezier', { curviness: 100 }],
|
||||
// 节点是否可以用鼠标拖动使其断开,默认为true。即用鼠标链接上的连线,也可以使用鼠标拖动让其断开。设置成false,可以让其拖动也不会自动断开
|
||||
ConnectionsDetachable: false,
|
||||
// 删除线的时候节点不删除
|
||||
DeleteEndpointsOnDetach: false,
|
||||
// 每当添加或以其他方式创建 Endpoint 并且 jsPlumb 尚未给出任何明确的 Endpoint 定义时将使用
|
||||
Endpoint: ['Blank', { Overlays: '' }],
|
||||
// 连接中源和目标端点的默认外观
|
||||
EndpointStyle: { fill: '#1879ffa1', outlineWidth: 1 },
|
||||
// jsPlumb 的内部日志记录是否打开
|
||||
LogEnabled: true,
|
||||
// 连接器的默认外观
|
||||
PaintStyle: {
|
||||
stroke: '#E0E3E7',
|
||||
strokeWidth: 1,
|
||||
outlineStroke: 'transparent',
|
||||
outlineWidth: 10,
|
||||
},
|
||||
// 用于配置任何可拖动元素的默认选项jsPlumb.draggable
|
||||
DragOptions: { cursor: 'pointer', zIndex: 2000 },
|
||||
// 添加到连接器和端点的默认叠加层。已弃用:从 4.x 开始,将不支持此功能。并非所有叠加层都可以连接到连接器和端点。
|
||||
Overlays: [
|
||||
[
|
||||
'Arrow',
|
||||
{
|
||||
width: 10, // 箭头尾部的宽度
|
||||
length: 8, // 从箭头的尾部到头部的距离
|
||||
location: 1, // 位置,建议使用0~1之间
|
||||
direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后)
|
||||
foldback: 0.623, // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角
|
||||
},
|
||||
],
|
||||
[
|
||||
'Label',
|
||||
{
|
||||
label: '',
|
||||
location: 0.5,
|
||||
cssClass: 'aLabel',
|
||||
},
|
||||
],
|
||||
],
|
||||
// 默认渲染模式 svg、canvas
|
||||
RenderMode: 'svg',
|
||||
// 悬停状态下连接的默认外观
|
||||
HoverPaintStyle: { stroke: '#b0b2b5', strokeWidth: 1 },
|
||||
// 悬停状态下端点的默认外观
|
||||
EndpointHoverStyle: { fill: 'red' },
|
||||
// 端点和连接的默认范围。范围提供了对哪些端点可以连接到哪些其他端点的基本控制
|
||||
Scope: 'jsPlumb_DefaultScope',
|
||||
};
|
||||
|
||||
// 整个节点作为source或者target
|
||||
export const jsplumbMakeSource = {
|
||||
// 设置可以拖拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
|
||||
filter: '.workflow-icon-drag',
|
||||
filterExclude: false,
|
||||
anchor: 'Continuous',
|
||||
// 是否允许自己连接自己
|
||||
allowLoopback: true,
|
||||
maxConnections: -1,
|
||||
};
|
||||
|
||||
// 整个节点作为source或者target
|
||||
export const jsplumbMakeTarget = {
|
||||
filter: '.workflow-icon-drag',
|
||||
filterExclude: false,
|
||||
// 是否允许自己连接自己
|
||||
anchor: 'Continuous',
|
||||
allowLoopback: true,
|
||||
dropOptions: { hoverClass: 'ef-drop-hover' },
|
||||
};
|
||||
|
||||
// 连线参数
|
||||
export const jsplumbConnect = {
|
||||
isSource: true,
|
||||
isTarget: true,
|
||||
// 动态锚点、提供了4个方向 Continuous、AutoDefault
|
||||
anchor: 'Continuous',
|
||||
};
|
||||
262
web/src/views/pages/workflow/js/mock.ts
Normal file
262
web/src/views/pages/workflow/js/mock.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
// 左侧菜单导航数据
|
||||
export const leftNavList = [
|
||||
{
|
||||
title: '工作流',
|
||||
icon: 'iconfont icon-shouye',
|
||||
isOpen: true,
|
||||
id: '1',
|
||||
children: [
|
||||
{
|
||||
icon: 'iconfont icon-gongju',
|
||||
name: '引擎',
|
||||
id: '11',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '客户姓名',
|
||||
prop: 'name',
|
||||
placeholder: '请输入客户姓名',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '性别',
|
||||
prop: 'sex',
|
||||
placeholder: '请选择性别',
|
||||
required: true,
|
||||
disabled: false,
|
||||
options: [
|
||||
{
|
||||
value: '0',
|
||||
label: '女',
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '男',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
label: '员工编号',
|
||||
prop: 'number',
|
||||
placeholder: '请输入员工编号',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
label: '办公电话',
|
||||
prop: 'mobile',
|
||||
placeholder: '请输入办公电话',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '权限分配',
|
||||
prop: 'role',
|
||||
placeholder: '请选择性别',
|
||||
required: true,
|
||||
disabled: false,
|
||||
options: [
|
||||
{
|
||||
value: '0',
|
||||
label: '编辑权限',
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '删除权限',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
label: '模块选择',
|
||||
prop: 'module',
|
||||
placeholder: '请选择模块',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-shouye_dongtaihui',
|
||||
name: '模版',
|
||||
id: '12',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '等级',
|
||||
prop: 'grade',
|
||||
placeholder: '请输入等级',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
label: '登记密码',
|
||||
prop: 'password',
|
||||
placeholder: '请输入登记密码',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-zhongduancanshuchaxun',
|
||||
name: '名称',
|
||||
id: '13',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '数据表',
|
||||
prop: 'dataSheet',
|
||||
placeholder: '请输入数据表',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
label: '字段配置',
|
||||
prop: 'field',
|
||||
placeholder: '请输入字段配置',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-zhongduancanshu',
|
||||
name: '版本',
|
||||
id: '14',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '发布模板',
|
||||
prop: 'publish',
|
||||
placeholder: '请输入发布模板',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-bolangnengshiyanchang',
|
||||
name: '建模',
|
||||
id: '15',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '内容模板',
|
||||
prop: 'content',
|
||||
placeholder: '请输入内容模板',
|
||||
required: true,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-xingqiu',
|
||||
name: '节点',
|
||||
id: '16',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称6',
|
||||
prop: 'name16',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '流程',
|
||||
isOpen: true,
|
||||
icon: 'iconfont icon-caijian',
|
||||
id: '2',
|
||||
children: [
|
||||
{
|
||||
icon: 'iconfont icon-fuwenben',
|
||||
name: '实例',
|
||||
id: '21',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称7',
|
||||
prop: 'name21',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-fuwenbenkuang',
|
||||
name: '轨迹',
|
||||
id: '22',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称8',
|
||||
prop: 'name22',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-shangchuan',
|
||||
name: '数据',
|
||||
id: '23',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称9',
|
||||
prop: 'name23',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '任务',
|
||||
isOpen: true,
|
||||
icon: 'iconfont icon-shuju',
|
||||
id: '3',
|
||||
children: [
|
||||
{
|
||||
icon: 'iconfont icon-icon-',
|
||||
name: '参与人',
|
||||
id: '31',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称1',
|
||||
prop: 'name31',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-gerenzhongxin',
|
||||
name: '执行人',
|
||||
id: '32',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称2',
|
||||
prop: 'name32',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-fangkuang',
|
||||
name: '工单',
|
||||
id: '33',
|
||||
form: [
|
||||
{
|
||||
type: 'input',
|
||||
label: '活动名称3',
|
||||
prop: 'name33',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
52
web/src/views/params/common/details.vue
Normal file
52
web/src/views/params/common/details.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
|
||||
<div class="flex-margin color-primary">
|
||||
<div>paramsCommonDetails</div>
|
||||
<div class="mt10 mb10">路径:path: {{ params.path }}</div>
|
||||
<div>参数:query: {{ params.query }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'paramsCommonDetails',
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const state = reactive({
|
||||
params: {
|
||||
path: '',
|
||||
query: '',
|
||||
},
|
||||
});
|
||||
// 设置 view 的高度
|
||||
const setViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
state.params = <any>route;
|
||||
});
|
||||
return {
|
||||
setViewHeight,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
100
web/src/views/params/common/index.vue
Normal file
100
web/src/views/params/common/index.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
|
||||
<div class="flex-margin" style="width: 400px">
|
||||
<el-result icon="success" title="普通路由" subTitle="可 `开启 TagsView 共用` 进行单标签测试">
|
||||
<template #extra>
|
||||
<el-alert type="success" :closable="false" class="mb30">
|
||||
<template #default>
|
||||
<div>1、设置非国际化:格式:tagsViewName=xxx</div>
|
||||
<br />
|
||||
<div>2、设置国际化:格式:tagsViewName=JSON.stringify({"zh-cn":"测试用","en":"test+page","zh-tw":"測試用"})</div>
|
||||
<br />
|
||||
<div>3、设置国际化后,去顶栏切换语言查看演示效果</div>
|
||||
<br />
|
||||
<div>
|
||||
4、 <a href="https://gitee.com/q7but" target="_black">感谢@q7but</a>、
|
||||
<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/22/files" target="_black">!22 add 添加自定义 tagVIewName 拓展,支持国际化</a>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-input v-model="tagsViewName" placeholder="请输入tagsView 名称" clearable class="mb15" style="width: 400px"></el-input>
|
||||
<el-input v-model="value" placeholder="请输入路由参数 id 值" clearable style="width: 400px"></el-input>
|
||||
<el-button type="primary" size="default" class="mt15" @click="onGoDetailsClick">
|
||||
<SvgIcon name="iconfont icon-putong" />
|
||||
普通路由传参
|
||||
</el-button>
|
||||
<el-button type="primary" size="default" class="mt15" @click="onChangeI18n">
|
||||
<SvgIcon name="iconfont icon-fuhao-zhongwen" />
|
||||
{{ tagsViewNameIsI18n ? '普通的演示' : '国际化演示' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'paramsCommon',
|
||||
setup() {
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const state = reactive({
|
||||
value: '',
|
||||
tagsViewName: '',
|
||||
tagsViewNameIsI18n: false,
|
||||
});
|
||||
const router = useRouter();
|
||||
// 设置 view 的高度
|
||||
const setViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// 跳转到详情
|
||||
/**
|
||||
* 设置 tagsView 名称:
|
||||
* 传不同的 tagsViewName 值
|
||||
*/
|
||||
const onGoDetailsClick = () => {
|
||||
const params: any = { id: state.value };
|
||||
if (state.tagsViewName) params.tagsViewName = state.tagsViewName;
|
||||
router.push({
|
||||
path: '/params/common/details',
|
||||
query: params,
|
||||
});
|
||||
state.value = '';
|
||||
};
|
||||
const onChangeI18n = () => {
|
||||
state.tagsViewNameIsI18n = !state.tagsViewNameIsI18n;
|
||||
if (state.tagsViewNameIsI18n) {
|
||||
state.tagsViewName = JSON.stringify({
|
||||
'zh-cn': '测试用',
|
||||
en: 'test page',
|
||||
'zh-tw': '測試用',
|
||||
});
|
||||
} else {
|
||||
state.tagsViewName = '我是普通路由测试tagsViewName(非国际化)';
|
||||
}
|
||||
};
|
||||
return {
|
||||
setViewHeight,
|
||||
onGoDetailsClick,
|
||||
onChangeI18n,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
52
web/src/views/params/dynamic/details.vue
Normal file
52
web/src/views/params/dynamic/details.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
|
||||
<div class="flex-margin color-primary">
|
||||
<div>paramsDynamicDetails</div>
|
||||
<div class="mt10 mb10">路径:path: {{ params.path }}</div>
|
||||
<div>参数:params: {{ params.params }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'paramsDynamicDetails',
|
||||
setup() {
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const route = useRoute();
|
||||
const state = reactive({
|
||||
params: {
|
||||
path: '',
|
||||
params: '',
|
||||
},
|
||||
});
|
||||
// 设置 view 的高度
|
||||
const setViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
state.params = <any>route;
|
||||
});
|
||||
return {
|
||||
setViewHeight,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
102
web/src/views/params/dynamic/index.vue
Normal file
102
web/src/views/params/dynamic/index.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
|
||||
<div class="flex-margin" style="width: 400px">
|
||||
<el-result icon="warning" title="动态路由" subTitle="可 `开启 TagsView 共用` 进行单标签测试">
|
||||
<template #extra>
|
||||
<el-alert type="success" :closable="false" class="mb30">
|
||||
<template #default>
|
||||
<div>1、设置非国际化:格式:tagsViewName=xxx</div>
|
||||
<br />
|
||||
<div>2、设置国际化:格式:tagsViewName=JSON.stringify({"zh-cn":"测试用","en":"test+page","zh-tw":"測試用"})</div>
|
||||
<br />
|
||||
<div>3、设置国际化后,去顶栏切换语言查看演示效果</div>
|
||||
<br />
|
||||
<div>
|
||||
4、 <a href="https://gitee.com/q7but" target="_black">感谢@q7but</a>、
|
||||
<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/22/files" target="_black">!22 add 添加自定义 tagVIewName 拓展,支持国际化</a>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-input v-model="tagsViewName" placeholder="请输入tagsView 名称" clearable class="mb15" style="width: 400px"></el-input>
|
||||
<el-input v-model="value" placeholder="请输入路由参数id值" clearable style="width: 400px"></el-input>
|
||||
<el-button type="primary" size="default" class="mt15" @click="onGoDetailsClick">
|
||||
<SvgIcon name="iconfont icon-dongtai" />
|
||||
动态路由传参
|
||||
</el-button>
|
||||
<el-button type="primary" size="default" class="mt15" @click="onChangeI18n">
|
||||
<SvgIcon name="iconfont icon-fuhao-zhongwen" />
|
||||
{{ tagsViewNameIsI18n ? '普通的演示' : '国际化演示' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'paramsDynamic',
|
||||
setup() {
|
||||
const storesTagsViewRoutes = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
|
||||
const state = reactive({
|
||||
value: '',
|
||||
tagsViewName: '',
|
||||
tagsViewNameIsI18n: false,
|
||||
});
|
||||
const router = useRouter();
|
||||
// 设置 view 的高度
|
||||
const setViewHeight = computed(() => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsViewCurrenFull.value) {
|
||||
return `30px`;
|
||||
} else {
|
||||
if (isTagsview) return `114px`;
|
||||
else return `80px`;
|
||||
}
|
||||
});
|
||||
// 跳转到详情
|
||||
const onGoDetailsClick = () => {
|
||||
if (!state.tagsViewName) return ElMessage.warning('动态路由tagsViewName为必填,因为路由配置了');
|
||||
if (!state.value) return ElMessage.warning('路由参数id值为必填');
|
||||
// name 值为路由中的 name
|
||||
router.push({
|
||||
name: 'paramsDynamicDetails',
|
||||
params: {
|
||||
t: 'vue-next-admin',
|
||||
id: state.value,
|
||||
tagsViewName: state.tagsViewName,
|
||||
},
|
||||
});
|
||||
state.value = '';
|
||||
};
|
||||
const onChangeI18n = () => {
|
||||
state.tagsViewNameIsI18n = !state.tagsViewNameIsI18n;
|
||||
if (state.tagsViewNameIsI18n) {
|
||||
state.tagsViewName = JSON.stringify({
|
||||
'zh-cn': '我是动态路由',
|
||||
en: 'Im dynamic routing',
|
||||
'zh-tw': '我是動態路由',
|
||||
});
|
||||
} else {
|
||||
state.tagsViewName = '我是动态路由测试tagsViewName(非国际化)';
|
||||
}
|
||||
};
|
||||
return {
|
||||
setViewHeight,
|
||||
onGoDetailsClick,
|
||||
onChangeI18n,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
387
web/src/views/personal/index.vue
Normal file
387
web/src/views/personal/index.vue
Normal file
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<div class="personal">
|
||||
<el-row>
|
||||
<!-- 个人信息 -->
|
||||
<el-col :xs="24" :sm="16">
|
||||
<el-card shadow="hover" header="个人信息">
|
||||
<div class="personal-user">
|
||||
<div class="personal-user-left">
|
||||
<el-upload class="h100 personal-user-left-upload" action="https://jsonplaceholder.typicode.com/posts/" multiple :limit="1">
|
||||
<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="personal-user-right">
|
||||
<el-row>
|
||||
<el-col :span="24" class="personal-title mb18">{{ currentTime }},admin,生活变的再糟糕,也不妨碍我变得更好! </el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="8" class="personal-item mb6">
|
||||
<div class="personal-item-label">昵称:</div>
|
||||
<div class="personal-item-value">小柒</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="16" class="personal-item mb6">
|
||||
<div class="personal-item-label">身份:</div>
|
||||
<div class="personal-item-value">超级管理</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="8" class="personal-item mb6">
|
||||
<div class="personal-item-label">登录IP:</div>
|
||||
<div class="personal-item-value">192.168.1.1</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="16" class="personal-item mb6">
|
||||
<div class="personal-item-label">登录时间:</div>
|
||||
<div class="personal-item-value">2021-02-05 18:47:26</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 消息通知 -->
|
||||
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>消息通知</span>
|
||||
<span class="personal-info-more">更多</span>
|
||||
</template>
|
||||
<div class="personal-info-box">
|
||||
<ul class="personal-info-ul">
|
||||
<li v-for="(v, k) in newsInfoList" :key="k" class="personal-info-li">
|
||||
<a :href="v.link" target="_block" class="personal-info-li-title">{{ v.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 营销推荐 -->
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover" class="mt15" header="营销推荐">
|
||||
<el-row :gutter="15" class="personal-recommend-row">
|
||||
<el-col :sm="6" v-for="(v, k) in recommendList" :key="k" class="personal-recommend-col">
|
||||
<div class="personal-recommend" :style="{ 'background-color': v.bg }">
|
||||
<SvgIcon :name="v.icon" :size="70" :style="{ color: v.iconColor }" />
|
||||
<div class="personal-recommend-auto">
|
||||
<div>{{ v.title }}</div>
|
||||
<div class="personal-recommend-msg">{{ v.msg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 更新信息 -->
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
||||
<div class="personal-edit-title">基本信息</div>
|
||||
<el-form :model="personalForm" size="default" label-width="40px" class="mt35 mb35">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="personalForm.name" placeholder="请输入昵称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="personalForm.email" placeholder="请输入邮箱" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="签名">
|
||||
<el-input v-model="personalForm.autograph" placeholder="请输入签名" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="职业">
|
||||
<el-select v-model="personalForm.occupation" placeholder="请选择职业" clearable class="w100">
|
||||
<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
|
||||
<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
|
||||
<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="手机">
|
||||
<el-input v-model="personalForm.phone" placeholder="请输入手机" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="personalForm.sex" placeholder="请选择性别" clearable class="w100">
|
||||
<el-option label="男" value="1"></el-option>
|
||||
<el-option label="女" value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<el-form-item>
|
||||
<el-button type="primary">
|
||||
<el-icon>
|
||||
<ele-Position />
|
||||
</el-icon>
|
||||
更新个人信息
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="personal-edit-title mb15">账号安全</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">账户密码</div>
|
||||
<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button text type="primary">立即修改</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">密保手机</div>
|
||||
<div class="personal-edit-safe-item-left-value">已绑定手机:132****4108</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button text type="primary">立即修改</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">密保问题</div>
|
||||
<div class="personal-edit-safe-item-left-value">已设置密保问题,账号安全大幅度提升</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button text type="primary">立即设置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-box">
|
||||
<div class="personal-edit-safe-item">
|
||||
<div class="personal-edit-safe-item-left">
|
||||
<div class="personal-edit-safe-item-left-label">绑定QQ</div>
|
||||
<div class="personal-edit-safe-item-left-value">已绑定QQ:110****566</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button text type="primary">立即设置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, defineComponent } from 'vue';
|
||||
import { formatAxis } from '/@/utils/formatTime';
|
||||
import { newsInfoList, recommendList } from './mock';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface PersonalState {
|
||||
newsInfoList: any;
|
||||
recommendList: any;
|
||||
personalForm: any;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'personal',
|
||||
setup() {
|
||||
const state = reactive<PersonalState>({
|
||||
newsInfoList,
|
||||
recommendList,
|
||||
personalForm: {
|
||||
name: '',
|
||||
email: '',
|
||||
autograph: '',
|
||||
occupation: '',
|
||||
phone: '',
|
||||
sex: '',
|
||||
},
|
||||
});
|
||||
// 当前时间提示语
|
||||
const currentTime = computed(() => {
|
||||
return formatAxis(new Date());
|
||||
});
|
||||
return {
|
||||
currentTime,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../theme/mixins/index.scss';
|
||||
.personal {
|
||||
.personal-user {
|
||||
height: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.personal-user-left {
|
||||
width: 100px;
|
||||
height: 130px;
|
||||
border-radius: 3px;
|
||||
:deep(.el-upload) {
|
||||
height: 100%;
|
||||
}
|
||||
.personal-user-left-upload {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
&:hover {
|
||||
img {
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-user-right {
|
||||
flex: 1;
|
||||
padding: 0 15px;
|
||||
.personal-title {
|
||||
font-size: 18px;
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
.personal-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
.personal-item-label {
|
||||
color: var(--el-text-color-secondary);
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
.personal-item-value {
|
||||
@include text-ellipsis(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-info {
|
||||
.personal-info-more {
|
||||
float: right;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.personal-info-box {
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
.personal-info-ul {
|
||||
list-style: none;
|
||||
.personal-info-li {
|
||||
font-size: 13px;
|
||||
padding-bottom: 10px;
|
||||
.personal-info-li-title {
|
||||
display: inline-block;
|
||||
@include text-ellipsis(1);
|
||||
color: var(--el-text-color-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
& a:hover {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-recommend-row {
|
||||
.personal-recommend-col {
|
||||
.personal-recommend {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
i {
|
||||
right: 0px !important;
|
||||
bottom: 0px !important;
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: -10px;
|
||||
font-size: 70px;
|
||||
transform: rotate(-30deg);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
.personal-recommend-auto {
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5%;
|
||||
color: var(--next-color-white);
|
||||
.personal-recommend-msg {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.personal-edit {
|
||||
.personal-edit-title {
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
color: var(--el-text-color-regular);
|
||||
&::after {
|
||||
content: '';
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.personal-edit-safe-box {
|
||||
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
padding: 15px 0;
|
||||
.personal-edit-safe-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.personal-edit-safe-item-left {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.personal-edit-safe-item-left-label {
|
||||
color: var(--el-text-color-regular);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.personal-edit-safe-item-left-value {
|
||||
color: var(--el-text-color-secondary);
|
||||
@include text-ellipsis(1);
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
66
web/src/views/personal/mock.ts
Normal file
66
web/src/views/personal/mock.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 消息通知
|
||||
* @returns 返回模拟数据
|
||||
*/
|
||||
export const newsInfoList: Array<object> = [
|
||||
{
|
||||
title: '[发布] 2021年02月28日发布基于 vue3.x + vite v1.0.0 版本',
|
||||
date: '02/28',
|
||||
link: 'https://gitee.com/lyt-top/vue-next-admin',
|
||||
},
|
||||
{
|
||||
title: '[发布] 2021年04月15日发布 vue2.x + webpack 重构版本',
|
||||
date: '04/15',
|
||||
link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
|
||||
},
|
||||
{
|
||||
title: '[重构] 2021年04月10日 重构 vue2.x + webpack v1.0.0 版本',
|
||||
date: '04/10',
|
||||
link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
|
||||
},
|
||||
{
|
||||
title: '[预览] 2020年12月08日,基于 vue3.x 版本后台模板的预览',
|
||||
date: '12/08',
|
||||
link: 'http://lyt-top.gitee.io/vue-next-admin-preview/#/login',
|
||||
},
|
||||
{
|
||||
title: '[预览] 2020年11月15日,基于 vue2.x 版本后台模板的预览',
|
||||
date: '11/15',
|
||||
link: 'https://lyt-top.gitee.io/vue-prev-admin-preview/#/login',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 营销推荐
|
||||
* @returns 返回模拟数据
|
||||
*/
|
||||
export const recommendList: Array<object> = [
|
||||
{
|
||||
title: '优惠券',
|
||||
msg: '现金券、折扣券、营销必备',
|
||||
icon: 'ele-Food',
|
||||
bg: '#48D18D',
|
||||
iconColor: '#64d89d',
|
||||
},
|
||||
{
|
||||
title: '多人拼团',
|
||||
msg: '社交电商、开辟流量',
|
||||
icon: 'ele-ShoppingCart',
|
||||
bg: '#F95959',
|
||||
iconColor: '#F86C6B',
|
||||
},
|
||||
{
|
||||
title: '分销中心',
|
||||
msg: '轻松招募分销员,成功推广奖励',
|
||||
icon: 'ele-School',
|
||||
bg: '#8595F4',
|
||||
iconColor: '#92A1F4',
|
||||
},
|
||||
{
|
||||
title: '秒杀',
|
||||
msg: '超低价抢购引导更多销量',
|
||||
icon: 'ele-AlarmClock',
|
||||
bg: '#FEBB50',
|
||||
iconColor: '#FDC566',
|
||||
},
|
||||
];
|
||||
173
web/src/views/system/dept/component/addDept.vue
Normal file
173
web/src/views/system/dept/component/addDept.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div class="system-add-dept-container">
|
||||
<el-dialog title="新增部门" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="上级部门">
|
||||
<el-cascader
|
||||
:options="deptData"
|
||||
:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
|
||||
placeholder="请选择部门"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="ruleForm.deptLevel"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.deptName }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="部门名称">
|
||||
<el-input v-model="ruleForm.deptName" placeholder="请输入部门名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="负责人">
|
||||
<el-input v-model="ruleForm.person" placeholder="请输入负责人" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="部门状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="部门描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">新 增</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableDataRow {
|
||||
deptName: string;
|
||||
createTime: string;
|
||||
status: boolean;
|
||||
sort: number;
|
||||
describe: string;
|
||||
id: number;
|
||||
children?: TableDataRow[];
|
||||
}
|
||||
interface DeptSate {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: {
|
||||
deptLevel: Array<string>;
|
||||
deptName: string;
|
||||
person: string;
|
||||
phone: string | number;
|
||||
email: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
};
|
||||
deptData: Array<TableDataRow>;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemAddDept',
|
||||
setup() {
|
||||
const state = reactive<DeptSate>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
deptLevel: [], // 上级部门
|
||||
deptName: '', // 部门名称
|
||||
person: '', // 负责人
|
||||
phone: '', // 手机号
|
||||
email: '', // 邮箱
|
||||
sort: 0, // 排序
|
||||
status: true, // 部门状态
|
||||
describe: '', // 部门描述
|
||||
},
|
||||
deptData: [], // 部门数据
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 初始化部门数据
|
||||
const initTableData = () => {
|
||||
state.deptData.push({
|
||||
deptName: 'vueNextAdmin',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '顶级部门',
|
||||
id: Math.random(),
|
||||
children: [
|
||||
{
|
||||
deptName: 'IT外包服务',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '总部',
|
||||
id: Math.random(),
|
||||
},
|
||||
{
|
||||
deptName: '资本控股',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '分部',
|
||||
id: Math.random(),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
179
web/src/views/system/dept/component/editDept.vue
Normal file
179
web/src/views/system/dept/component/editDept.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="system-edit-dept-container">
|
||||
<el-dialog title="修改部门" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="上级部门">
|
||||
<el-cascader
|
||||
:options="deptData"
|
||||
:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
|
||||
placeholder="请选择部门"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="ruleForm.deptLevel"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.deptName }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="部门名称">
|
||||
<el-input v-model="ruleForm.deptName" placeholder="请输入部门名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="负责人">
|
||||
<el-input v-model="ruleForm.person" placeholder="请输入负责人" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="部门状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="部门描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">修 改</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableDataRow {
|
||||
deptName: string;
|
||||
createTime: string;
|
||||
status: boolean;
|
||||
sort: number;
|
||||
describe: string;
|
||||
id: number;
|
||||
children?: TableDataRow[];
|
||||
}
|
||||
interface RuleFormState {
|
||||
deptLevel: Array<string>;
|
||||
deptName: string;
|
||||
person: string;
|
||||
phone: string | number;
|
||||
email: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
}
|
||||
interface DeptSate {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: RuleFormState;
|
||||
deptData: Array<TableDataRow>;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemEditDept',
|
||||
setup() {
|
||||
const state = reactive<DeptSate>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
deptLevel: [], // 上级部门
|
||||
deptName: '', // 部门名称
|
||||
person: '', // 负责人
|
||||
phone: '', // 手机号
|
||||
email: '', // 邮箱
|
||||
sort: 0, // 排序
|
||||
status: true, // 部门状态
|
||||
describe: '', // 部门描述
|
||||
},
|
||||
deptData: [], // 部门数据
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (row: RuleFormState) => {
|
||||
row.deptLevel = ['vueNextAdmin'];
|
||||
row.person = 'lyt';
|
||||
row.phone = '12345678910';
|
||||
row.email = 'vueNextAdmin@123.com';
|
||||
state.ruleForm = row;
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 初始化部门数据
|
||||
const initTableData = () => {
|
||||
state.deptData.push({
|
||||
deptName: 'vueNextAdmin',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '顶级部门',
|
||||
id: Math.random(),
|
||||
children: [
|
||||
{
|
||||
deptName: 'IT外包服务',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '总部',
|
||||
id: Math.random(),
|
||||
},
|
||||
{
|
||||
deptName: '资本控股',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '分部',
|
||||
id: Math.random(),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
163
web/src/views/system/dept/index.vue
Normal file
163
web/src/views/system/dept/index.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div class="system-dept-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-dept-search mb15">
|
||||
<el-input size="default" placeholder="请输入部门名称" style="max-width: 180px"> </el-input>
|
||||
<el-button size="default" type="primary" class="ml10">
|
||||
<el-icon>
|
||||
<ele-Search />
|
||||
</el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button size="default" type="success" class="ml10" @click="onOpenAddDept">
|
||||
<el-icon>
|
||||
<ele-FolderAdd />
|
||||
</el-icon>
|
||||
新增部门
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="tableData.data"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
default-expand-all
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<el-table-column prop="deptName" label="部门名称" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column label="排序" show-overflow-tooltip width="80">
|
||||
<template #default="scope">
|
||||
{{ scope.$index }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="部门状态" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
|
||||
<el-tag type="info" v-else>禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="describe" label="部门描述" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" show-overflow-tooltip width="140">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onOpenAddDept">新增</el-button>
|
||||
<el-button size="small" text type="primary" @click="onOpenEditDept(scope.row)">修改</el-button>
|
||||
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<AddDept ref="addDeptRef" />
|
||||
<EditDept ref="editDeptRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import AddDept from '/@/views/system/dept/component/addDept.vue';
|
||||
import EditDept from '/@/views/system/dept/component/editDept.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableDataRow {
|
||||
deptName: string;
|
||||
createTime: string;
|
||||
status: boolean;
|
||||
sort: number;
|
||||
describe: string;
|
||||
id: number;
|
||||
children?: TableDataRow[];
|
||||
}
|
||||
interface TableDataState {
|
||||
tableData: {
|
||||
data: Array<TableDataRow>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemDept',
|
||||
components: { AddDept, EditDept },
|
||||
setup() {
|
||||
const addDeptRef = ref();
|
||||
const editDeptRef = ref();
|
||||
const state = reactive<TableDataState>({
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
state.tableData.data.push({
|
||||
deptName: 'vueNextAdmin',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '顶级部门',
|
||||
id: Math.random(),
|
||||
children: [
|
||||
{
|
||||
deptName: 'IT外包服务',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '总部',
|
||||
id: Math.random(),
|
||||
},
|
||||
{
|
||||
deptName: '资本控股',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '分部',
|
||||
id: Math.random(),
|
||||
},
|
||||
],
|
||||
});
|
||||
state.tableData.total = state.tableData.data.length;
|
||||
};
|
||||
// 打开新增菜单弹窗
|
||||
const onOpenAddDept = () => {
|
||||
addDeptRef.value.openDialog();
|
||||
};
|
||||
// 打开编辑菜单弹窗
|
||||
const onOpenEditDept = (row: TableDataRow) => {
|
||||
editDeptRef.value.openDialog(row);
|
||||
};
|
||||
// 删除当前行
|
||||
const onTabelRowDel = (row: TableDataRow) => {
|
||||
ElMessageBox.confirm(`此操作将永久删除部门:${row.deptName}, 是否继续?`, '提示', {
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
addDeptRef,
|
||||
editDeptRef,
|
||||
onOpenAddDept,
|
||||
onOpenEditDept,
|
||||
onTabelRowDel,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
129
web/src/views/system/dic/component/addDic.vue
Normal file
129
web/src/views/system/dic/component/addDic.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="system-add-dic-container">
|
||||
<el-dialog title="新增字典" v-model="isShowDialog" width="769px">
|
||||
<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="字典名称">
|
||||
<el-input v-model="ruleForm.dicName" placeholder="请输入字典名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="字段名">
|
||||
<el-input v-model="ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="字典状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-row :gutter="35" v-for="(v, k) in ruleForm.list" :key="k">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item :prop="`list[${k}].label`">
|
||||
<template #label>
|
||||
<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
|
||||
<el-icon>
|
||||
<ele-Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<span class="ml10">字段</span>
|
||||
</template>
|
||||
<el-input v-model="v.label" style="width: 100%" placeholder="请输入字段名"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="属性" :prop="`list[${k}].value`">
|
||||
<el-input v-model="v.value" style="width: 100%" placeholder="请输入属性值"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="字典描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入字典描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">新 增</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemAddDic',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
dicName: '', // 字典名称
|
||||
fieldName: '', // 字段名
|
||||
status: true, // 字典状态
|
||||
list: [
|
||||
// 子集字段 + 属性值
|
||||
{
|
||||
id: Math.random(),
|
||||
label: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
describe: '', // 字典描述
|
||||
fieldNameList: [], // 字段名: [{子集字段 + 属性值}]
|
||||
},
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增行
|
||||
const onAddRow = () => {
|
||||
state.ruleForm.list.push({
|
||||
id: Math.random(),
|
||||
label: '',
|
||||
value: '',
|
||||
});
|
||||
};
|
||||
// 删除行
|
||||
const onDelRow = (k: number) => {
|
||||
state.ruleForm.list.splice(k, 1);
|
||||
};
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
onAddRow,
|
||||
onDelRow,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
162
web/src/views/system/dic/component/editDic.vue
Normal file
162
web/src/views/system/dic/component/editDic.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div class="system-edit-dic-container">
|
||||
<el-dialog title="修改字典" v-model="isShowDialog" width="769px">
|
||||
<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="字典名称">
|
||||
<el-input v-model="ruleForm.dicName" placeholder="请输入字典名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="字段名">
|
||||
<el-input v-model="ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="字典状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-row :gutter="35" v-for="(v, k) in ruleForm.list" :key="k">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item :prop="`list[${k}].label`">
|
||||
<template #label>
|
||||
<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
|
||||
<el-icon>
|
||||
<ele-Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
|
||||
<el-icon>
|
||||
<ele-Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<span class="ml10">字段</span>
|
||||
</template>
|
||||
<el-input v-model="v.label" style="width: 100%" placeholder="请输入字段名"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="属性" :prop="`list[${k}].value`">
|
||||
<el-input v-model="v.value" style="width: 100%" placeholder="请输入属性值"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="字典描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入字典描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">修 改</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface RuleFormList {
|
||||
id: number;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
interface RuleFormState {
|
||||
dicName: string;
|
||||
fieldName: string;
|
||||
status: boolean;
|
||||
list: RuleFormList[];
|
||||
describe: string;
|
||||
fieldNameList: Array<any>;
|
||||
}
|
||||
interface DicState {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: RuleFormState;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemEditDic',
|
||||
setup() {
|
||||
const state = reactive<DicState>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
dicName: '', // 字典名称
|
||||
fieldName: '', // 字段名
|
||||
status: true, // 字典状态
|
||||
list: [
|
||||
// 子集字段 + 属性值
|
||||
{
|
||||
id: Math.random(),
|
||||
label: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
describe: '', // 字典描述
|
||||
fieldNameList: [], // 字段名: [{子集字段 + 属性值}]
|
||||
},
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (row: RuleFormState) => {
|
||||
if (row.fieldName === 'SYS_UERINFO') {
|
||||
row.list = [
|
||||
{ id: Math.random(), label: 'sex', value: '1' },
|
||||
{ id: Math.random(), label: 'sex', value: '0' },
|
||||
];
|
||||
} else {
|
||||
row.list = [
|
||||
{ id: Math.random(), label: 'role', value: 'admin' },
|
||||
{ id: Math.random(), label: 'role', value: 'common' },
|
||||
{ id: Math.random(), label: 'roleName', value: '超级管理员' },
|
||||
{ id: Math.random(), label: 'roleName', value: '普通用户' },
|
||||
];
|
||||
}
|
||||
state.ruleForm = row;
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增行
|
||||
const onAddRow = () => {
|
||||
state.ruleForm.list.push({
|
||||
id: Math.random(),
|
||||
label: '',
|
||||
value: '',
|
||||
});
|
||||
};
|
||||
// 删除行
|
||||
const onDelRow = (k: number) => {
|
||||
state.ruleForm.list.splice(k, 1);
|
||||
};
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
onAddRow,
|
||||
onDelRow,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
159
web/src/views/system/dic/index.vue
Normal file
159
web/src/views/system/dic/index.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="system-dic-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-user-search mb15">
|
||||
<el-input size="default" placeholder="请输入字典名称" style="max-width: 180px"> </el-input>
|
||||
<el-button size="default" type="primary" class="ml10">
|
||||
<el-icon>
|
||||
<ele-Search />
|
||||
</el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
|
||||
<el-icon>
|
||||
<ele-FolderAdd />
|
||||
</el-icon>
|
||||
新增字典
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="tableData.data" style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="50" />
|
||||
<el-table-column prop="dicName" label="字典名称" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="fieldName" label="字段名" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="status" label="字典状态" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
|
||||
<el-tag type="info" v-else>禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="describe" label="字典描述" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onOpenEditDic(scope.row)">修改</el-button>
|
||||
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@size-change="onHandleSizeChange"
|
||||
@current-change="onHandleCurrentChange"
|
||||
class="mt15"
|
||||
:pager-count="5"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
v-model:current-page="tableData.param.pageNum"
|
||||
background
|
||||
v-model:page-size="tableData.param.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
<AddDic ref="addDicRef" />
|
||||
<EditDic ref="editDicRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import AddDic from '/@/views/system/dic/component/addDic.vue';
|
||||
import EditDic from '/@/views/system/dic/component/editDic.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableDataRow {
|
||||
dicName: string;
|
||||
fieldName: string;
|
||||
describe: string;
|
||||
status: boolean;
|
||||
createTime: string;
|
||||
}
|
||||
interface TableDataState {
|
||||
tableData: {
|
||||
data: Array<TableDataRow>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemDic',
|
||||
components: { AddDic, EditDic },
|
||||
setup() {
|
||||
const addDicRef = ref();
|
||||
const editDicRef = ref();
|
||||
const state = reactive<TableDataState>({
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
const data: Array<TableDataRow> = [];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
data.push({
|
||||
dicName: i === 0 ? '角色标识' : '用户性别',
|
||||
fieldName: i === 0 ? 'SYS_ROLE' : 'SYS_UERINFO',
|
||||
describe: i === 0 ? '这是角色字典' : '这是用户性别字典',
|
||||
status: true,
|
||||
createTime: new Date().toLocaleString(),
|
||||
});
|
||||
}
|
||||
state.tableData.data = data;
|
||||
state.tableData.total = state.tableData.data.length;
|
||||
};
|
||||
// 打开新增字典弹窗
|
||||
const onOpenAddDic = () => {
|
||||
addDicRef.value.openDialog();
|
||||
};
|
||||
// 打开修改字典弹窗
|
||||
const onOpenEditDic = (row: TableDataRow) => {
|
||||
editDicRef.value.openDialog(row);
|
||||
};
|
||||
// 删除字典
|
||||
const onRowDel = (row: TableDataRow) => {
|
||||
ElMessageBox.confirm(`此操作将永久删除字典名称:“${row.dicName}”,是否继续?`, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 分页改变
|
||||
const onHandleSizeChange = (val: number) => {
|
||||
state.tableData.param.pageSize = val;
|
||||
};
|
||||
// 分页改变
|
||||
const onHandleCurrentChange = (val: number) => {
|
||||
state.tableData.param.pageNum = val;
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
addDicRef,
|
||||
editDicRef,
|
||||
onOpenAddDic,
|
||||
onOpenEditDic,
|
||||
onRowDel,
|
||||
onHandleSizeChange,
|
||||
onHandleCurrentChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
230
web/src/views/system/menu/component/addMenu.vue
Normal file
230
web/src/views/system/menu/component/addMenu.vue
Normal file
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<div class="system-add-menu-container">
|
||||
<el-dialog title="新增菜单" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="80px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="上级菜单">
|
||||
<el-cascader
|
||||
:options="menuData"
|
||||
:props="{ checkStrictly: true, value: 'path', label: 'title' }"
|
||||
placeholder="请选择上级菜单"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="ruleForm.menuSuperior"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.title }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="菜单类型">
|
||||
<el-radio-group v-model="ruleForm.menuType">
|
||||
<el-radio label="menu">菜单</el-radio>
|
||||
<el-radio label="btn">按钮</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="菜单名称">
|
||||
<el-input v-model="ruleForm.meta.title" placeholder="格式:message.router.xxx" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="ruleForm.menuType === 'menu'">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="路由名称">
|
||||
<el-input v-model="ruleForm.name" placeholder="路由中的 name 值" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="路由路径">
|
||||
<el-input v-model="ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="重定向">
|
||||
<el-input v-model="ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="菜单图标">
|
||||
<IconSelector placeholder="请输入菜单图标" v-model="ruleForm.meta.icon" type="all" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="组件路径">
|
||||
<el-input v-model="ruleForm.component" placeholder="组件路径" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="链接地址">
|
||||
<el-input v-model="ruleForm.meta.isLink" placeholder="外链/内嵌时链接地址(http:xxx.com)" clearable :disabled="!ruleForm.isLink">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="权限标识">
|
||||
<el-select v-model="ruleForm.meta.roles" multiple placeholder="取角色管理" clearable class="w100">
|
||||
<el-option label="admin" value="admin"></el-option>
|
||||
<el-option label="common" value="common"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-if="ruleForm.menuType === 'btn'">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="权限标识">
|
||||
<el-input v-model="ruleForm.btnPower" placeholder="请输入权限标识" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="菜单排序">
|
||||
<el-input-number v-model="ruleForm.menuSort" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="ruleForm.menuType === 'menu'">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否隐藏">
|
||||
<el-radio-group v-model="ruleForm.meta.isHide">
|
||||
<el-radio :label="true">隐藏</el-radio>
|
||||
<el-radio :label="false">不隐藏</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="页面缓存">
|
||||
<el-radio-group v-model="ruleForm.meta.isKeepAlive">
|
||||
<el-radio :label="true">缓存</el-radio>
|
||||
<el-radio :label="false">不缓存</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否固定">
|
||||
<el-radio-group v-model="ruleForm.meta.isAffix">
|
||||
<el-radio :label="true">固定</el-radio>
|
||||
<el-radio :label="false">不固定</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否外链">
|
||||
<el-radio-group v-model="ruleForm.isLink" :disabled="ruleForm.meta.isIframe">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否内嵌">
|
||||
<el-radio-group v-model="ruleForm.meta.isIframe" @change="onSelectIframeChange">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">新 增</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRoutesList } from '/@/stores/routesList';
|
||||
import { i18n } from '/@/i18n/index';
|
||||
import IconSelector from '/@/components/iconSelector/index.vue';
|
||||
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemAddMenu',
|
||||
components: { IconSelector },
|
||||
setup() {
|
||||
const stores = useRoutesList();
|
||||
const { routesList } = storeToRefs(stores);
|
||||
const state = reactive({
|
||||
isShowDialog: false,
|
||||
// 参数请参考 `/src/router/route.ts` 中的 `dynamicRoutes` 路由菜单格式
|
||||
ruleForm: {
|
||||
menuSuperior: [], // 上级菜单
|
||||
menuType: 'menu', // 菜单类型
|
||||
name: '', // 路由名称
|
||||
component: '', // 组件路径
|
||||
isLink: false, // 是否外链
|
||||
menuSort: 0, // 菜单排序
|
||||
path: '', // 路由路径
|
||||
redirect: '', // 路由重定向,有子集 children 时
|
||||
meta: {
|
||||
title: '', // 菜单名称
|
||||
icon: '', // 菜单图标
|
||||
isHide: false, // 是否隐藏
|
||||
isKeepAlive: true, // 是否缓存
|
||||
isAffix: false, // 是否固定
|
||||
isLink: '', // 外链/内嵌时链接地址(http:xxx.com),开启外链条件,`1、isLink: 链接地址不为空`
|
||||
isIframe: false, // 是否内嵌,开启条件,`1、isIframe:true 2、isLink:链接地址不为空`
|
||||
roles: '', // 权限标识,取角色管理
|
||||
},
|
||||
btnPower: '', // 菜单类型为按钮时,权限标识
|
||||
},
|
||||
menuData: [], // 上级菜单数据
|
||||
});
|
||||
// 获取 vuex 中的路由
|
||||
const getMenuData = (routes: any) => {
|
||||
const arr: any = [];
|
||||
routes.map((val: any) => {
|
||||
val['title'] = i18n.global.t(val.meta.title);
|
||||
val['id'] = Math.random();
|
||||
arr.push({ ...val });
|
||||
if (val.children) getMenuData(val.children);
|
||||
});
|
||||
return arr;
|
||||
};
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 是否内嵌下拉改变
|
||||
const onSelectIframeChange = () => {
|
||||
if (state.ruleForm.meta.isIframe) state.ruleForm.isLink = true;
|
||||
else state.ruleForm.isLink = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog(); // 关闭弹窗
|
||||
// setBackEndControlRefreshRoutes() // 刷新菜单,未进行后端接口测试
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
state.menuData = getMenuData(routesList.value);
|
||||
});
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onSelectIframeChange,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
238
web/src/views/system/menu/component/editMenu.vue
Normal file
238
web/src/views/system/menu/component/editMenu.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="system-edit-menu-container">
|
||||
<el-dialog title="修改菜单" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="80px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="上级菜单">
|
||||
<el-cascader
|
||||
:options="menuData"
|
||||
:props="{ checkStrictly: true, value: 'path', label: 'title' }"
|
||||
placeholder="请选择上级菜单"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="ruleForm.menuSuperior"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.title }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="菜单类型">
|
||||
<el-radio-group v-model="ruleForm.menuType">
|
||||
<el-radio label="menu">菜单</el-radio>
|
||||
<el-radio label="btn">按钮</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="菜单名称">
|
||||
<el-input v-model="ruleForm.meta.title" placeholder="格式:message.router.xxx" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="ruleForm.menuType === 'menu'">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="路由名称">
|
||||
<el-input v-model="ruleForm.name" placeholder="路由中的 name 值" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="路由路径">
|
||||
<el-input v-model="ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="重定向">
|
||||
<el-input v-model="ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="菜单图标">
|
||||
<IconSelector placeholder="请输入菜单图标" v-model="ruleForm.meta.icon" type="all" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="组件路径">
|
||||
<el-input v-model="ruleForm.component" placeholder="组件路径" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="链接地址">
|
||||
<el-input v-model="ruleForm.meta.isLink" placeholder="外链/内嵌时链接地址(http:xxx.com)" clearable :disabled="!ruleForm.isLink">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="权限标识">
|
||||
<el-select v-model="ruleForm.meta.roles" multiple placeholder="取角色管理" clearable class="w100">
|
||||
<el-option label="admin" value="admin"></el-option>
|
||||
<el-option label="common" value="common"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-if="ruleForm.menuType === 'btn'">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="权限标识">
|
||||
<el-input v-model="ruleForm.btnPower" placeholder="请输入权限标识" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="菜单排序">
|
||||
<el-input-number v-model="ruleForm.menuSort" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="ruleForm.menuType === 'menu'">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否隐藏">
|
||||
<el-radio-group v-model="ruleForm.meta.isHide">
|
||||
<el-radio :label="true">隐藏</el-radio>
|
||||
<el-radio :label="false">不隐藏</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="页面缓存">
|
||||
<el-radio-group v-model="ruleForm.meta.isKeepAlive">
|
||||
<el-radio :label="true">缓存</el-radio>
|
||||
<el-radio :label="false">不缓存</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否固定">
|
||||
<el-radio-group v-model="ruleForm.meta.isAffix">
|
||||
<el-radio :label="true">固定</el-radio>
|
||||
<el-radio :label="false">不固定</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否外链">
|
||||
<el-radio-group v-model="ruleForm.isLink" :disabled="ruleForm.meta.isIframe">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="是否内嵌">
|
||||
<el-radio-group v-model="ruleForm.meta.isIframe" @change="onSelectIframeChange">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">修 改</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRoutesList } from '/@/stores/routesList';
|
||||
import { i18n } from '/@/i18n/index';
|
||||
import IconSelector from '/@/components/iconSelector/index.vue';
|
||||
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemEditMenu',
|
||||
components: { IconSelector },
|
||||
setup() {
|
||||
const stores = useRoutesList();
|
||||
const { routesList } = storeToRefs(stores);
|
||||
const state = reactive({
|
||||
isShowDialog: false,
|
||||
// 参数请参考 `/src/router/route.ts` 中的 `dynamicRoutes` 路由菜单格式
|
||||
ruleForm: {
|
||||
menuSuperior: [], // 上级菜单
|
||||
menuType: 'menu', // 菜单类型
|
||||
name: '', // 路由名称
|
||||
component: '', // 组件路径
|
||||
isLink: false, // 是否外链
|
||||
menuSort: 0, // 菜单排序
|
||||
path: '', // 路由路径
|
||||
redirect: '', // 路由重定向,有子集 children 时
|
||||
meta: {
|
||||
title: '', // 菜单名称
|
||||
icon: '', // 菜单图标
|
||||
isHide: false, // 是否隐藏
|
||||
isKeepAlive: true, // 是否缓存
|
||||
isAffix: false, // 是否固定
|
||||
isLink: '', // 外链/内嵌时链接地址(http:xxx.com),开启外链条件,`1、isLink: 链接地址不为空`
|
||||
isIframe: false, // 是否内嵌,开启条件,`1、isIframe:true 2、isLink:链接地址不为空`
|
||||
roles: '', // 权限标识,取角色管理
|
||||
},
|
||||
btnPower: '', // 菜单类型为按钮时,权限标识
|
||||
},
|
||||
menuData: [], // 上级菜单数据
|
||||
});
|
||||
// 获取 vuex 中的路由
|
||||
const getMenuData = (routes: any) => {
|
||||
const arr: any = [];
|
||||
routes.map((val: any) => {
|
||||
val['title'] = i18n.global.t(val.meta.title);
|
||||
val['id'] = Math.random();
|
||||
arr.push({ ...val });
|
||||
if (val.children) getMenuData(val.children);
|
||||
});
|
||||
return arr;
|
||||
};
|
||||
// 打开弹窗
|
||||
const openDialog = (row: any) => {
|
||||
// 模拟数据,实际请走接口
|
||||
row.menuType = 'menu';
|
||||
row.menuSort = Math.random();
|
||||
row.component = `${row.component} `
|
||||
.match(/\'(.+)\'/g)
|
||||
?.join('')
|
||||
.replace(/\'/g, '');
|
||||
state.ruleForm = row;
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 是否内嵌下拉改变
|
||||
const onSelectIframeChange = () => {
|
||||
if (state.ruleForm.meta.isIframe) state.ruleForm.isLink = true;
|
||||
else state.ruleForm.isLink = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog(); // 关闭弹窗
|
||||
// setBackEndControlRefreshRoutes() // 刷新菜单,未进行后端接口测试
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
state.menuData = getMenuData(routesList.value);
|
||||
});
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onSelectIframeChange,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
114
web/src/views/system/menu/index.vue
Normal file
114
web/src/views/system/menu/index.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="system-menu-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-menu-search mb15">
|
||||
<el-input size="default" placeholder="请输入菜单名称" style="max-width: 180px"> </el-input>
|
||||
<el-button size="default" type="primary" class="ml10">
|
||||
<el-icon>
|
||||
<ele-Search />
|
||||
</el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button size="default" type="success" class="ml10" @click="onOpenAddMenu">
|
||||
<el-icon>
|
||||
<ele-FolderAdd />
|
||||
</el-icon>
|
||||
新增菜单
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="menuTableData" style="width: 100%" row-key="path" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
|
||||
<el-table-column label="菜单名称" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<SvgIcon :name="scope.row.meta.icon" />
|
||||
<span class="ml10">{{ $t(scope.row.meta.title) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="path" label="路由路径" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="组件路径" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.component }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="权限标识" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.meta.roles }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" show-overflow-tooltip width="80">
|
||||
<template #default="scope">
|
||||
{{ scope.$index }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" show-overflow-tooltip width="80">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" size="small">{{ scope.row.xx }}菜单</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" show-overflow-tooltip width="140">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onOpenAddMenu">新增</el-button>
|
||||
<el-button size="small" text type="primary" @click="onOpenEditMenu(scope.row)">修改</el-button>
|
||||
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<AddMenu ref="addMenuRef" />
|
||||
<EditMenu ref="editMenuRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, computed, defineComponent } from 'vue';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRoutesList } from '/@/stores/routesList';
|
||||
import AddMenu from '/@/views/system/menu/component/addMenu.vue';
|
||||
import EditMenu from '/@/views/system/menu/component/editMenu.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemMenu',
|
||||
components: { AddMenu, EditMenu },
|
||||
setup() {
|
||||
const stores = useRoutesList();
|
||||
const { routesList } = storeToRefs(stores);
|
||||
const addMenuRef = ref();
|
||||
const editMenuRef = ref();
|
||||
const state = reactive({});
|
||||
// 获取 vuex 中的路由
|
||||
const menuTableData = computed(() => {
|
||||
return routesList.value;
|
||||
});
|
||||
// 打开新增菜单弹窗
|
||||
const onOpenAddMenu = () => {
|
||||
addMenuRef.value.openDialog();
|
||||
};
|
||||
// 打开编辑菜单弹窗
|
||||
const onOpenEditMenu = (row: RouteRecordRaw) => {
|
||||
editMenuRef.value.openDialog(row);
|
||||
};
|
||||
// 删除当前行
|
||||
const onTabelRowDel = (row: RouteRecordRaw) => {
|
||||
ElMessageBox.confirm(`此操作将永久删除路由:${row.path}, 是否继续?`, '提示', {
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
return {
|
||||
addMenuRef,
|
||||
editMenuRef,
|
||||
onOpenAddMenu,
|
||||
onOpenEditMenu,
|
||||
menuTableData,
|
||||
onTabelRowDel,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
240
web/src/views/system/role/component/addRole.vue
Normal file
240
web/src/views/system/role/component/addRole.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div class="system-add-role-container">
|
||||
<el-dialog title="新增角色" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input v-model="ruleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="角色标识">
|
||||
<template #label>
|
||||
<el-tooltip effect="dark" content="用于 `router/route.ts` meta.roles" placement="top-start">
|
||||
<span>角色标识</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="ruleForm.roleSign" placeholder="请输入角色标识" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="角色状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="角色描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="菜单权限">
|
||||
<el-tree :data="menuData" :props="menuProps" show-checkbox class="menu-data-tree" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">新 增</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface MenuDataTree {
|
||||
id: number;
|
||||
label: string;
|
||||
children?: MenuDataTree[];
|
||||
}
|
||||
interface RoleState {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: {
|
||||
roleName: string;
|
||||
roleSign: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
};
|
||||
menuData: Array<MenuDataTree>;
|
||||
menuProps: {
|
||||
children: string;
|
||||
label: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemAddRole',
|
||||
setup() {
|
||||
const state = reactive<RoleState>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
roleName: '', // 角色名称
|
||||
roleSign: '', // 角色标识
|
||||
sort: 0, // 排序
|
||||
status: true, // 角色状态
|
||||
describe: '', // 角色描述
|
||||
},
|
||||
menuData: [],
|
||||
menuProps: {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
},
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
state.isShowDialog = true;
|
||||
getMenuData();
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 获取菜单结构数据
|
||||
const getMenuData = () => {
|
||||
state.menuData = [
|
||||
{
|
||||
id: 1,
|
||||
label: '系统管理',
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
label: '菜单管理',
|
||||
children: [
|
||||
{
|
||||
id: 111,
|
||||
label: '菜单新增',
|
||||
},
|
||||
{
|
||||
id: 112,
|
||||
label: '菜单修改',
|
||||
},
|
||||
{
|
||||
id: 113,
|
||||
label: '菜单删除',
|
||||
},
|
||||
{
|
||||
id: 114,
|
||||
label: '菜单查询',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
label: '角色管理',
|
||||
children: [
|
||||
{
|
||||
id: 121,
|
||||
label: '角色新增',
|
||||
},
|
||||
{
|
||||
id: 122,
|
||||
label: '角色修改',
|
||||
},
|
||||
{
|
||||
id: 123,
|
||||
label: '角色删除',
|
||||
},
|
||||
{
|
||||
id: 124,
|
||||
label: '角色查询',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
label: '用户管理',
|
||||
children: [
|
||||
{
|
||||
id: 131,
|
||||
label: '用户新增',
|
||||
},
|
||||
{
|
||||
id: 132,
|
||||
label: '用户修改',
|
||||
},
|
||||
{
|
||||
id: 133,
|
||||
label: '用户删除',
|
||||
},
|
||||
{
|
||||
id: 134,
|
||||
label: '用户查询',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '权限管理',
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
label: '前端控制',
|
||||
children: [
|
||||
{
|
||||
id: 211,
|
||||
label: '页面权限',
|
||||
},
|
||||
{
|
||||
id: 212,
|
||||
label: '页面权限',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
label: '后端控制',
|
||||
children: [
|
||||
{
|
||||
id: 221,
|
||||
label: '页面权限',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
};
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.system-add-role-container {
|
||||
.menu-data-tree {
|
||||
width: 100%;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
242
web/src/views/system/role/component/editRole.vue
Normal file
242
web/src/views/system/role/component/editRole.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div class="system-edit-role-container">
|
||||
<el-dialog title="修改角色" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input v-model="ruleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="角色标识">
|
||||
<template #label>
|
||||
<el-tooltip effect="dark" content="用于 `router/route.ts` meta.roles" placement="top-start">
|
||||
<span>角色标识</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="ruleForm.roleSign" placeholder="请输入角色标识" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="角色状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="角色描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="菜单权限">
|
||||
<el-tree :data="menuData" :props="menuProps" :default-checked-keys="[112, 113]" node-key="id" show-checkbox class="menu-data-tree" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">修 改</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface MenuDataTree {
|
||||
id: number;
|
||||
label: string;
|
||||
children?: MenuDataTree[];
|
||||
}
|
||||
interface DialogRow {
|
||||
roleName: string;
|
||||
roleSign: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
}
|
||||
interface RoleState {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: DialogRow;
|
||||
menuData: Array<MenuDataTree>;
|
||||
menuProps: {
|
||||
children: string;
|
||||
label: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemEditRole',
|
||||
setup() {
|
||||
const state = reactive<RoleState>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
roleName: '', // 角色名称
|
||||
roleSign: '', // 角色标识
|
||||
sort: 0, // 排序
|
||||
status: true, // 角色状态
|
||||
describe: '', // 角色描述
|
||||
},
|
||||
menuData: [],
|
||||
menuProps: {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
},
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (row: DialogRow) => {
|
||||
state.ruleForm = row;
|
||||
state.isShowDialog = true;
|
||||
getMenuData();
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 获取菜单结构数据
|
||||
const getMenuData = () => {
|
||||
state.menuData = [
|
||||
{
|
||||
id: 1,
|
||||
label: '系统管理',
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
label: '菜单管理',
|
||||
children: [
|
||||
{
|
||||
id: 111,
|
||||
label: '菜单新增',
|
||||
},
|
||||
{
|
||||
id: 112,
|
||||
label: '菜单修改',
|
||||
},
|
||||
{
|
||||
id: 113,
|
||||
label: '菜单删除',
|
||||
},
|
||||
{
|
||||
id: 114,
|
||||
label: '菜单查询',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
label: '角色管理',
|
||||
children: [
|
||||
{
|
||||
id: 121,
|
||||
label: '角色新增',
|
||||
},
|
||||
{
|
||||
id: 122,
|
||||
label: '角色修改',
|
||||
},
|
||||
{
|
||||
id: 123,
|
||||
label: '角色删除',
|
||||
},
|
||||
{
|
||||
id: 124,
|
||||
label: '角色查询',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
label: '用户管理',
|
||||
children: [
|
||||
{
|
||||
id: 131,
|
||||
label: '用户新增',
|
||||
},
|
||||
{
|
||||
id: 132,
|
||||
label: '用户修改',
|
||||
},
|
||||
{
|
||||
id: 133,
|
||||
label: '用户删除',
|
||||
},
|
||||
{
|
||||
id: 134,
|
||||
label: '用户查询',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '权限管理',
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
label: '前端控制',
|
||||
children: [
|
||||
{
|
||||
id: 211,
|
||||
label: '页面权限',
|
||||
},
|
||||
{
|
||||
id: 212,
|
||||
label: '页面权限',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
label: '后端控制',
|
||||
children: [
|
||||
{
|
||||
id: 221,
|
||||
label: '页面权限',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
};
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.system-edit-role-container {
|
||||
.menu-data-tree {
|
||||
width: 100%;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
164
web/src/views/system/role/index.vue
Normal file
164
web/src/views/system/role/index.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="system-role-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-user-search mb15">
|
||||
<el-input size="default" placeholder="请输入角色名称" style="max-width: 180px"> </el-input>
|
||||
<el-button size="default" type="primary" class="ml10">
|
||||
<el-icon>
|
||||
<ele-Search />
|
||||
</el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button size="default" type="success" class="ml10" @click="onOpenAddRole">
|
||||
<el-icon>
|
||||
<ele-FolderAdd />
|
||||
</el-icon>
|
||||
新增角色
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="tableData.data" style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="60" />
|
||||
<el-table-column prop="roleName" label="角色名称" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="roleSign" label="角色标识" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="sort" label="排序" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="status" label="角色状态" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
|
||||
<el-tag type="info" v-else>禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="describe" label="角色描述" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="scope">
|
||||
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onOpenEditRole(scope.row)"
|
||||
>修改</el-button
|
||||
>
|
||||
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@size-change="onHandleSizeChange"
|
||||
@current-change="onHandleCurrentChange"
|
||||
class="mt15"
|
||||
:pager-count="5"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
v-model:current-page="tableData.param.pageNum"
|
||||
background
|
||||
v-model:page-size="tableData.param.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
<AddRole ref="addRoleRef" />
|
||||
<EditRole ref="editRoleRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import AddRole from '/@/views/system/role/component/addRole.vue';
|
||||
import EditRole from '/@/views/system/role/component/editRole.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableData {
|
||||
roleName: string;
|
||||
roleSign: string;
|
||||
describe: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
createTime: string;
|
||||
}
|
||||
interface TableDataState {
|
||||
tableData: {
|
||||
data: Array<TableData>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemRole',
|
||||
components: { AddRole, EditRole },
|
||||
setup() {
|
||||
const addRoleRef = ref();
|
||||
const editRoleRef = ref();
|
||||
const state = reactive<TableDataState>({
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
const data: Array<TableData> = [];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
data.push({
|
||||
roleName: i === 0 ? '超级管理员' : '普通用户',
|
||||
roleSign: i === 0 ? 'admin' : 'common',
|
||||
describe: `测试角色${i + 1}`,
|
||||
sort: i,
|
||||
status: true,
|
||||
createTime: new Date().toLocaleString(),
|
||||
});
|
||||
}
|
||||
state.tableData.data = data;
|
||||
state.tableData.total = state.tableData.data.length;
|
||||
};
|
||||
// 打开新增角色弹窗
|
||||
const onOpenAddRole = () => {
|
||||
addRoleRef.value.openDialog();
|
||||
};
|
||||
// 打开修改角色弹窗
|
||||
const onOpenEditRole = (row: Object) => {
|
||||
editRoleRef.value.openDialog(row);
|
||||
};
|
||||
// 删除角色
|
||||
const onRowDel = (row: any) => {
|
||||
ElMessageBox.confirm(`此操作将永久删除角色名称:“${row.roleName}”,是否继续?`, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 分页改变
|
||||
const onHandleSizeChange = (val: number) => {
|
||||
state.tableData.param.pageSize = val;
|
||||
};
|
||||
// 分页改变
|
||||
const onHandleCurrentChange = (val: number) => {
|
||||
state.tableData.param.pageNum = val;
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
addRoleRef,
|
||||
editRoleRef,
|
||||
onOpenAddRole,
|
||||
onOpenEditRole,
|
||||
onRowDel,
|
||||
onHandleSizeChange,
|
||||
onHandleCurrentChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
200
web/src/views/system/user/component/addUser.vue
Normal file
200
web/src/views/system/user/component/addUser.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div class="system-add-user-container">
|
||||
<el-dialog title="新增用户" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="账户名称">
|
||||
<el-input v-model="ruleForm.userName" placeholder="请输入账户名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="ruleForm.userNickname" placeholder="请输入用户昵称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="关联角色">
|
||||
<el-select v-model="ruleForm.roleSign" placeholder="请选择" clearable class="w100">
|
||||
<el-option label="超级管理员" value="admin"></el-option>
|
||||
<el-option label="普通用户" value="common"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="部门">
|
||||
<el-cascader
|
||||
:options="deptData"
|
||||
:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
|
||||
placeholder="请选择部门"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="ruleForm.department"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.deptName }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="ruleForm.sex" placeholder="请选择" clearable class="w100">
|
||||
<el-option label="男" value="男"></el-option>
|
||||
<el-option label="女" value="女"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="账户密码">
|
||||
<el-input v-model="ruleForm.password" placeholder="请输入" type="password" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="账户过期">
|
||||
<el-date-picker v-model="ruleForm.overdueTime" type="date" placeholder="请选择" class="w100"> </el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="用户状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="用户描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入用户描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">新 增</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface DeptData {
|
||||
deptName: string;
|
||||
createTime: string;
|
||||
status: boolean;
|
||||
sort: number | string;
|
||||
describe: string;
|
||||
id: number;
|
||||
children?: DeptData[];
|
||||
}
|
||||
interface UserState {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: {
|
||||
userName: string;
|
||||
userNickname: string;
|
||||
roleSign: string;
|
||||
department: any;
|
||||
phone: string;
|
||||
email: string;
|
||||
sex: string;
|
||||
password: string;
|
||||
overdueTime: string;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
};
|
||||
deptData: Array<DeptData>;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemAddUser',
|
||||
setup() {
|
||||
const state = reactive<UserState>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
userName: '', // 账户名称
|
||||
userNickname: '', // 用户昵称
|
||||
roleSign: '', // 关联角色
|
||||
department: [], // 部门
|
||||
phone: '', // 手机号
|
||||
email: '', // 邮箱
|
||||
sex: '', // 性别
|
||||
password: '', // 账户密码
|
||||
overdueTime: '', // 账户过期
|
||||
status: true, // 用户状态
|
||||
describe: '', // 用户描述
|
||||
},
|
||||
deptData: [], // 部门数据
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 初始化部门数据
|
||||
const initTableData = () => {
|
||||
state.deptData.push({
|
||||
deptName: 'vueNextAdmin',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '顶级部门',
|
||||
id: Math.random(),
|
||||
children: [
|
||||
{
|
||||
deptName: 'IT外包服务',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '总部',
|
||||
id: Math.random(),
|
||||
},
|
||||
{
|
||||
deptName: '资本控股',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '分部',
|
||||
id: Math.random(),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
202
web/src/views/system/user/component/editUser.vue
Normal file
202
web/src/views/system/user/component/editUser.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="system-edit-user-container">
|
||||
<el-dialog title="修改用户" v-model="isShowDialog" width="769px">
|
||||
<el-form :model="ruleForm" size="default" label-width="90px">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="账户名称">
|
||||
<el-input v-model="ruleForm.userName" placeholder="请输入账户名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="ruleForm.userNickname" placeholder="请输入用户昵称" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="关联角色">
|
||||
<el-select v-model="ruleForm.roleSign" placeholder="请选择" clearable class="w100">
|
||||
<el-option label="超级管理员" value="admin"></el-option>
|
||||
<el-option label="普通用户" value="common"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="部门">
|
||||
<el-cascader
|
||||
:options="deptData"
|
||||
:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
|
||||
placeholder="请选择部门"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="ruleForm.department"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.deptName }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="ruleForm.sex" placeholder="请选择" clearable class="w100">
|
||||
<el-option label="男" value="男"></el-option>
|
||||
<el-option label="女" value="女"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="账户密码">
|
||||
<el-input v-model="ruleForm.password" placeholder="请输入" type="password" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="账户过期">
|
||||
<el-date-picker v-model="ruleForm.overdueTime" type="date" placeholder="请选择" class="w100"> </el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="用户状态">
|
||||
<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="用户描述">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入用户描述" maxlength="150"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">修 改</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface DeptData {
|
||||
deptName: string;
|
||||
createTime: string;
|
||||
status: boolean;
|
||||
sort: number | string;
|
||||
describe: string;
|
||||
id: number;
|
||||
children?: DeptData[];
|
||||
}
|
||||
interface RuleFormRow {
|
||||
userName: string;
|
||||
userNickname: string;
|
||||
roleSign: string;
|
||||
department: any;
|
||||
phone: string;
|
||||
email: string;
|
||||
sex: string;
|
||||
password: string;
|
||||
overdueTime: string;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
}
|
||||
interface UserState {
|
||||
isShowDialog: boolean;
|
||||
ruleForm: RuleFormRow;
|
||||
deptData: Array<DeptData>;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemEditUser',
|
||||
setup() {
|
||||
const state = reactive<UserState>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
userName: '', // 账户名称
|
||||
userNickname: '', // 用户昵称
|
||||
roleSign: '', // 关联角色
|
||||
department: [], // 部门
|
||||
phone: '', // 手机号
|
||||
email: '', // 邮箱
|
||||
sex: '', // 性别
|
||||
password: '', // 账户密码
|
||||
overdueTime: '', // 账户过期
|
||||
status: true, // 用户状态
|
||||
describe: '', // 用户描述
|
||||
},
|
||||
deptData: [], // 部门数据
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (row: RuleFormRow) => {
|
||||
state.ruleForm = row;
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 新增
|
||||
const onSubmit = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 初始化部门数据
|
||||
const initTableData = () => {
|
||||
state.deptData.push({
|
||||
deptName: 'vueNextAdmin',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '顶级部门',
|
||||
id: Math.random(),
|
||||
children: [
|
||||
{
|
||||
deptName: 'IT外包服务',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '总部',
|
||||
id: Math.random(),
|
||||
},
|
||||
{
|
||||
deptName: '资本控股',
|
||||
createTime: new Date().toLocaleString(),
|
||||
status: true,
|
||||
sort: Math.random(),
|
||||
describe: '分部',
|
||||
id: Math.random(),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
177
web/src/views/system/user/index.vue
Normal file
177
web/src/views/system/user/index.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="system-user-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-user-search mb15">
|
||||
<el-input size="default" placeholder="请输入用户名称" style="max-width: 180px"> </el-input>
|
||||
<el-button size="default" type="primary" class="ml10">
|
||||
<el-icon>
|
||||
<ele-Search />
|
||||
</el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button size="default" type="success" class="ml10" @click="onOpenAddUser">
|
||||
<el-icon>
|
||||
<ele-FolderAdd />
|
||||
</el-icon>
|
||||
新增用户
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="tableData.data" style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="60" />
|
||||
<el-table-column prop="userName" label="账户名称" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="userNickname" label="用户昵称" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="roleSign" label="关联角色" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="department" label="部门" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="phone" label="手机号" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="email" label="邮箱" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="status" label="用户状态" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
|
||||
<el-tag type="info" v-else>禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="describe" label="用户描述" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="scope">
|
||||
<el-button :disabled="scope.row.userName === 'admin'" size="small" text type="primary" @click="onOpenEditUser(scope.row)">修改</el-button>
|
||||
<el-button :disabled="scope.row.userName === 'admin'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@size-change="onHandleSizeChange"
|
||||
@current-change="onHandleCurrentChange"
|
||||
class="mt15"
|
||||
:pager-count="5"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
v-model:current-page="tableData.param.pageNum"
|
||||
background
|
||||
v-model:page-size="tableData.param.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
<AddUer ref="addUserRef" />
|
||||
<EditUser ref="editUserRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import AddUer from '/@/views/system/user/component/addUser.vue';
|
||||
import EditUser from '/@/views/system/user/component/editUser.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface TableDataRow {
|
||||
userName: string;
|
||||
userNickname: string;
|
||||
roleSign: string;
|
||||
department: string[];
|
||||
phone: string;
|
||||
email: string;
|
||||
sex: string;
|
||||
password: string;
|
||||
overdueTime: Date;
|
||||
status: boolean;
|
||||
describe: string;
|
||||
createTime: string;
|
||||
}
|
||||
interface TableDataState {
|
||||
tableData: {
|
||||
data: Array<TableDataRow>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemUser',
|
||||
components: { AddUer, EditUser },
|
||||
setup() {
|
||||
const addUserRef = ref();
|
||||
const editUserRef = ref();
|
||||
const state = reactive<TableDataState>({
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
const data: Array<TableDataRow> = [];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
data.push({
|
||||
userName: i === 0 ? 'admin' : 'test',
|
||||
userNickname: i === 0 ? '我是管理员' : '我是普通用户',
|
||||
roleSign: i === 0 ? 'admin' : 'common',
|
||||
department: i === 0 ? ['vueNextAdmin', 'IT外包服务'] : ['vueNextAdmin', '资本控股'],
|
||||
phone: '12345678910',
|
||||
email: 'vueNextAdmin@123.com',
|
||||
sex: '女',
|
||||
password: '123456',
|
||||
overdueTime: new Date(),
|
||||
status: true,
|
||||
describe: i === 0 ? '不可删除' : '测试用户',
|
||||
createTime: new Date().toLocaleString(),
|
||||
});
|
||||
}
|
||||
state.tableData.data = data;
|
||||
state.tableData.total = state.tableData.data.length;
|
||||
};
|
||||
// 打开新增用户弹窗
|
||||
const onOpenAddUser = () => {
|
||||
addUserRef.value.openDialog();
|
||||
};
|
||||
// 打开修改用户弹窗
|
||||
const onOpenEditUser = (row: TableDataRow) => {
|
||||
editUserRef.value.openDialog(row);
|
||||
};
|
||||
// 删除用户
|
||||
const onRowDel = (row: TableDataRow) => {
|
||||
ElMessageBox.confirm(`此操作将永久删除账户名称:“${row.userName}”,是否继续?`, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 分页改变
|
||||
const onHandleSizeChange = (val: number) => {
|
||||
state.tableData.param.pageSize = val;
|
||||
};
|
||||
// 分页改变
|
||||
const onHandleCurrentChange = (val: number) => {
|
||||
state.tableData.param.pageNum = val;
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
return {
|
||||
addUserRef,
|
||||
editUserRef,
|
||||
onOpenAddUser,
|
||||
onOpenEditUser,
|
||||
onRowDel,
|
||||
onHandleSizeChange,
|
||||
onHandleCurrentChange,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
499
web/src/views/tools/index.vue
Normal file
499
web/src/views/tools/index.vue
Normal file
@@ -0,0 +1,499 @@
|
||||
<template>
|
||||
<el-card shadow="hover" header="正则验证(一些项目中常用的正则)">
|
||||
<el-form :model="ruleForm" :rules="rules" class="tools-warp-form" size="default" label-position="top">
|
||||
<el-form-item label="验证百分比(不可以小数):" prop="a22">
|
||||
<div class="tools-warp-form-msg">验证可以输入大于0小于100的数字</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a22" @input="onVerifyNumberPercentage($event)" placeholder="请输入数字进行测试">
|
||||
<template #append> % </template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="验证百分比(可以小数):" prop="a23" class="mt20">
|
||||
<div class="tools-warp-form-msg">验证可以输入大于0小于100的数字</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a23" @input="onVerifyNumberPercentageFloat($event)" placeholder="请输入数字进行测试">
|
||||
<template #append> % </template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="小数或整数:" prop="a1" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证可以输入小数或整数,0 开始, . 只能出现一次,保留小数点后保留2位小数。(负数时,模拟拼接负号给后台)。
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a1" @input="onVerifyNumberIntegerAndFloat($event)" placeholder="请输入小数或整数进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="正整数:" prop="a2" class="mt20">
|
||||
<div class="tools-warp-form-msg">验证只可以输入正整数,0 开始后面将不可以输入。</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a2" @input="onVerifiyNumberInteger($event)" placeholder="请输入整数进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="去掉中文及空格:" prop="a3" class="mt20">
|
||||
<div class="tools-warp-form-msg">验证不可以输入空格与中文。</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a3" @input="onVerifyCnAndSpace($event)" placeholder="请输入内容进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="去掉英文及空格:" prop="a4" class="mt20">
|
||||
<div class="tools-warp-form-msg">验证不可以输入空格与英文。</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a4" @input="onVerifyEnAndSpace($event)" placeholder="请输入内容进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="禁止输入空格:" prop="a5" class="mt20">
|
||||
<div class="tools-warp-form-msg">验证不可以输入空格。</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a5" @input="onVerifyAndSpace($event)" placeholder="请输入内容进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="金额用 `,` 区分开:" prop="a6" class="mt20">
|
||||
<div class="tools-warp-form-msg">金额添加 `,` 进行区分,便于阅读。{{ ruleForm.a6 }}</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a6" @input="onVerifyNumberComma($event)" placeholder="请输入金额进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="匹配文字变色(搜索时):" prop="a7" class="mt20">
|
||||
<div class="tools-warp-form-msg">示例:<span v-html="text"></span></div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a7" @input="onVerifyTextColor($event)" placeholder="请输入示例中的部分文字"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="数字转中文大写:" prop="a8" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证数字转成中文的大写。<span class="tools-warp-form-msg-red">{{ cnText }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a8" @input="onVerifyNumberCnUppercase($event)" placeholder="请输入金额进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码:" prop="a9" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证手机号码 (true: 正确,false: 不正确)。<span class="tools-warp-form-msg-red">{{ phone }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a9" @input="onVerifyPhone($event)" placeholder="请输入手机号进行测试" maxlength="11"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="国内电话号码:" prop="a10" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证国内电话号码 (true: 正确,false: 不正确)。<span class="tools-warp-form-msg-red">{{ telePhone }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a10" @input="onVerifyTelPhone($event)" placeholder="请输入国内电话号码进行测试" maxlength="12"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录账号:" prop="a11" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证登录账号是否正确。字母开头,允许5-16字节,允许字母数字下划线 (true: 正确,false: 不正确)。<span class="tools-warp-form-msg-red">{{
|
||||
account
|
||||
}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a11" @input="onVerifyAccount($event)" placeholder="请输入账号进行测试" maxlength="16"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码:" prop="a12" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证密码是否正确。以字母开头,长度在6~16之间,只能包含字母、数字和下划线 (true: 正确,false: 不正确)。<span
|
||||
class="tools-warp-form-msg-red"
|
||||
>{{ password }}</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a12" @input="onVerifyPassword($event)" placeholder="请输入密码进行测试" maxlength="16"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="强密码:" prop="a13" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证强密码是否正确。字母+数字+特殊字符,长度在6-16之间 (true: 正确,false: 不正确)。<span class="tools-warp-form-msg-red">{{
|
||||
passwordPowerful
|
||||
}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a13" @input="onVerifyPasswordPowerful($event)" placeholder="请输入密码进行测试" maxlength="16"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码强度:" prop="a14" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证密码强度。返回 强、中、弱。(弱:纯数字,纯字母,纯特殊字符,中:字母+数字,字母+特殊字符,数字+特殊字符,强:字母+数字+特殊字符)<span
|
||||
class="tools-warp-form-msg-red"
|
||||
>{{ passwordStrength }}</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a14" @input="onVerifyPasswordStrength($event)" placeholder="请输入密码进行测试" maxlength="16"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="IP地址:" prop="a15" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证IP地址是否正确。(true: 正确,false: 不正确)。<span class="tools-warp-form-msg-red">{{ iPAddress }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a15" @input="onVerifyIPAddress($event)" placeholder="请输入IP地址进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱:" prop="a16" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证邮箱是否正确。(true: 正确,false:不正确)。<span class="tools-warp-form-msg-red">{{ email }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a16" @input="onVerifyEmail($event)" placeholder="请输入邮箱进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证:" prop="a17" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证身份证是否正确。(true: 正确,false:不正确)。<span class="tools-warp-form-msg-red">{{ idCard }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a17" @input="onVerifyIDCard($event)" placeholder="请输入身份证进行测试" maxlength="18"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名:" prop="a18" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证姓名是否正确,包括少数民族名字。(true: 正确,false:不正确)。<span class="tools-warp-form-msg-red">{{ fullName }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a18" @input="onVerifyFullName($event)" placeholder="请输入姓名进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮政编码:" prop="a19" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证邮政编码是否正确,不能以 0 开始。(true: 正确,false:不正确)。<span class="tools-warp-form-msg-red">{{ postalCode }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a19" @input="onVerifyPostalCode($event)" placeholder="请输入邮政编码进行测试" maxlength="6"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="url:" prop="a20" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证url是否正确。(true: 正确,false:不正确)。<span class="tools-warp-form-msg-red">{{ url }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a20" @input="onVerifyUrl($event)" placeholder="请输入内容进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="车牌号:" prop="a21" class="mt20">
|
||||
<div class="tools-warp-form-msg">
|
||||
验证车牌号是否正确。(true: 正确,false:不正确)。<span class="tools-warp-form-msg-red">{{ carNum }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input v-model="ruleForm.a21" @input="onVerifyCarNum($event)" placeholder="请输入车牌号进行测试"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
import {
|
||||
verifyNumberPercentage,
|
||||
verifyNumberPercentageFloat,
|
||||
verifyNumberIntegerAndFloat,
|
||||
verifiyNumberInteger,
|
||||
verifyCnAndSpace,
|
||||
verifyEnAndSpace,
|
||||
verifyAndSpace,
|
||||
verifyNumberComma,
|
||||
verifyTextColor,
|
||||
verifyNumberCnUppercase,
|
||||
verifyPhone,
|
||||
verifyTelPhone,
|
||||
verifyAccount,
|
||||
verifyPassword,
|
||||
verifyPasswordPowerful,
|
||||
verifyPasswordStrength,
|
||||
verifyIPAddress,
|
||||
verifyEmail,
|
||||
verifyIdCard,
|
||||
verifyFullName,
|
||||
verifyPostalCode,
|
||||
verifyUrl,
|
||||
verifyCarNum,
|
||||
} from '/@/utils/toolsValidate';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'tools',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
text: '世间美好,与你环环相扣,祝你开心每一天!',
|
||||
phone: false,
|
||||
cnText: '',
|
||||
telePhone: false,
|
||||
account: false,
|
||||
password: false,
|
||||
passwordPowerful: false,
|
||||
passwordStrength: '',
|
||||
iPAddress: false,
|
||||
email: false,
|
||||
idCard: false,
|
||||
fullName: false,
|
||||
postalCode: false,
|
||||
url: false,
|
||||
carNum: false,
|
||||
/**
|
||||
* 变量名为了方便,随便取了,
|
||||
* 实际中,按正常程序进行命名
|
||||
*/
|
||||
ruleForm: {
|
||||
a1: '',
|
||||
a2: '',
|
||||
a3: '',
|
||||
a4: '',
|
||||
a5: '',
|
||||
a6: '',
|
||||
a7: '',
|
||||
a8: '',
|
||||
a9: '',
|
||||
a10: '',
|
||||
a11: '',
|
||||
a12: '',
|
||||
a13: '',
|
||||
a14: '',
|
||||
a15: '',
|
||||
a16: '',
|
||||
a17: '',
|
||||
a18: '',
|
||||
a19: '',
|
||||
a20: '',
|
||||
a21: '',
|
||||
a22: '',
|
||||
a23: '',
|
||||
},
|
||||
rules: {
|
||||
a1: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入小数或整数进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a2: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入正整数进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a3: [{ required: true, message: '请输入内容进行测试', trigger: 'change' }],
|
||||
a4: [{ required: true, message: '请输入内容进行测试', trigger: 'change' }],
|
||||
a5: [{ required: true, message: '请输入内容进行测试', trigger: 'change' }],
|
||||
a6: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入小数或整数进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a7: [{ required: true, message: '请输入内容进行测试', trigger: 'change' }],
|
||||
a8: [{ required: true, message: '请输入金额进行测试', trigger: 'change' }],
|
||||
a9: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入手机号进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a10: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入国内电话号码进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a11: [{ required: true, message: '请输入账号进行测试', trigger: 'change' }],
|
||||
a12: [{ required: true, message: '请输入密码进行测试', trigger: 'change' }],
|
||||
a13: [{ required: true, message: '请输入密码进行测试', trigger: 'change' }],
|
||||
a14: [{ required: true, message: '请输入密码进行测试', trigger: 'change' }],
|
||||
a15: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入IP地址进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a16: [{ required: true, message: '请输入邮箱进行测试', trigger: 'change' }],
|
||||
a17: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入身份证进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a18: [{ required: true, message: '请输入姓名进行测试', trigger: 'change' }],
|
||||
a19: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入邮政编码进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a20: [{ required: true, message: '请输入内容进行测试', trigger: 'change' }],
|
||||
a21: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入车牌号进行测试',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
a22: [{ required: true, message: '请输入数字进行测试', trigger: 'change' }],
|
||||
a23: [{ required: true, message: '请输入数字进行测试', trigger: 'change' }],
|
||||
},
|
||||
});
|
||||
// 验证百分比(不可以小数)
|
||||
const onVerifyNumberPercentage = (val: string) => {
|
||||
state.ruleForm.a22 = verifyNumberPercentage(val);
|
||||
};
|
||||
// 验证百分比(可以小数)
|
||||
const onVerifyNumberPercentageFloat = (val: string) => {
|
||||
state.ruleForm.a23 = verifyNumberPercentageFloat(val);
|
||||
};
|
||||
// 小数或整数
|
||||
const onVerifyNumberIntegerAndFloat = (val: string) => {
|
||||
state.ruleForm.a1 = verifyNumberIntegerAndFloat(val);
|
||||
};
|
||||
// 正整数
|
||||
const onVerifiyNumberInteger = (val: string) => {
|
||||
state.ruleForm.a2 = verifiyNumberInteger(val);
|
||||
};
|
||||
// 去掉中文及空格
|
||||
const onVerifyCnAndSpace = (val: string) => {
|
||||
state.ruleForm.a3 = verifyCnAndSpace(val);
|
||||
};
|
||||
// 去掉英文及空格
|
||||
const onVerifyEnAndSpace = (val: string) => {
|
||||
state.ruleForm.a4 = verifyEnAndSpace(val);
|
||||
};
|
||||
// 禁止输入空格
|
||||
const onVerifyAndSpace = (val: string) => {
|
||||
state.ruleForm.a5 = verifyAndSpace(val);
|
||||
};
|
||||
// 金额用 `,` 区分开
|
||||
const onVerifyNumberComma = (val: string) => {
|
||||
state.ruleForm.a6 = verifyNumberComma(val);
|
||||
};
|
||||
// 匹配文字变色(搜索时)
|
||||
const onVerifyTextColor = (val: string) => {
|
||||
state.ruleForm.a7 = verifyAndSpace(val);
|
||||
if (state.ruleForm.a7 === '') state.text = `世间美好,与你环环相扣,祝你开心每一天!`;
|
||||
else state.text = verifyTextColor(state.ruleForm.a7, state.text);
|
||||
};
|
||||
// 数字转中文大写
|
||||
const onVerifyNumberCnUppercase = (val: string) => {
|
||||
state.ruleForm.a8 = verifyNumberIntegerAndFloat(val);
|
||||
if (state.ruleForm.a8 === '') state.cnText = '';
|
||||
else state.cnText = verifyNumberCnUppercase(state.ruleForm.a8);
|
||||
};
|
||||
// 手机号码
|
||||
const onVerifyPhone = (val: string) => {
|
||||
state.phone = verifyPhone(val);
|
||||
};
|
||||
// 国内电话号码
|
||||
const onVerifyTelPhone = (val: string) => {
|
||||
state.telePhone = verifyTelPhone(val);
|
||||
};
|
||||
// 登录账号
|
||||
const onVerifyAccount = (val: string) => {
|
||||
state.ruleForm.a11 = verifyCnAndSpace(val);
|
||||
state.account = verifyAccount(state.ruleForm.a11);
|
||||
};
|
||||
// 密码
|
||||
const onVerifyPassword = (val: string) => {
|
||||
state.ruleForm.a12 = verifyCnAndSpace(val);
|
||||
state.password = verifyPassword(state.ruleForm.a12);
|
||||
};
|
||||
// 强密码
|
||||
const onVerifyPasswordPowerful = (val: string) => {
|
||||
state.ruleForm.a13 = verifyCnAndSpace(val);
|
||||
state.passwordPowerful = verifyPasswordPowerful(state.ruleForm.a13);
|
||||
};
|
||||
// 密码强度
|
||||
const onVerifyPasswordStrength = (val: string) => {
|
||||
state.ruleForm.a14 = verifyCnAndSpace(val);
|
||||
state.passwordStrength = verifyPasswordStrength(state.ruleForm.a14);
|
||||
};
|
||||
// IP地址
|
||||
const onVerifyIPAddress = (val: string) => {
|
||||
state.iPAddress = verifyIPAddress(val);
|
||||
};
|
||||
// 邮箱
|
||||
const onVerifyEmail = (val: string) => {
|
||||
state.ruleForm.a16 = verifyCnAndSpace(val);
|
||||
state.email = verifyEmail(state.ruleForm.a16);
|
||||
};
|
||||
// 身份证
|
||||
const onVerifyIDCard = (val: string) => {
|
||||
state.ruleForm.a17 = verifyCnAndSpace(val);
|
||||
state.idCard = verifyIdCard(state.ruleForm.a17);
|
||||
};
|
||||
// 姓名
|
||||
const onVerifyFullName = (val: string) => {
|
||||
state.ruleForm.a18 = verifyAndSpace(val);
|
||||
state.fullName = verifyFullName(state.ruleForm.a18);
|
||||
};
|
||||
// 邮政编码
|
||||
const onVerifyPostalCode = (val: string) => {
|
||||
state.ruleForm.a19 = verifiyNumberInteger(val);
|
||||
state.postalCode = verifyPostalCode(state.ruleForm.a19);
|
||||
};
|
||||
// url
|
||||
const onVerifyUrl = (val: string) => {
|
||||
state.ruleForm.a20 = verifyAndSpace(val);
|
||||
state.url = verifyUrl(state.ruleForm.a20);
|
||||
};
|
||||
// 车牌号
|
||||
const onVerifyCarNum = (val: string) => {
|
||||
state.ruleForm.a21 = verifyAndSpace(val);
|
||||
state.carNum = verifyCarNum(state.ruleForm.a21);
|
||||
};
|
||||
return {
|
||||
onVerifyNumberPercentage,
|
||||
onVerifyNumberPercentageFloat,
|
||||
onVerifyNumberIntegerAndFloat,
|
||||
onVerifiyNumberInteger,
|
||||
onVerifyCnAndSpace,
|
||||
onVerifyEnAndSpace,
|
||||
onVerifyAndSpace,
|
||||
onVerifyNumberComma,
|
||||
onVerifyTextColor,
|
||||
onVerifyNumberCnUppercase,
|
||||
onVerifyPhone,
|
||||
onVerifyTelPhone,
|
||||
onVerifyAccount,
|
||||
onVerifyPassword,
|
||||
onVerifyPasswordPowerful,
|
||||
onVerifyPasswordStrength,
|
||||
onVerifyIPAddress,
|
||||
onVerifyEmail,
|
||||
onVerifyIDCard,
|
||||
onVerifyFullName,
|
||||
onVerifyPostalCode,
|
||||
onVerifyUrl,
|
||||
onVerifyCarNum,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tools-warp-form {
|
||||
:deep(.el-form-item--small.el-form-item) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.tools-warp-form-msg {
|
||||
color: #666666;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
.tools-warp-form-msg-red {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
.tools-warp-form-msg + div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1278
web/src/views/visualizing/demo1.vue
Normal file
1278
web/src/views/visualizing/demo1.vue
Normal file
File diff suppressed because it is too large
Load Diff
1344
web/src/views/visualizing/demo2.vue
Normal file
1344
web/src/views/visualizing/demo2.vue
Normal file
File diff suppressed because it is too large
Load Diff
BIN
web/src/views/visualizing/images/bathymetry.jpg
Normal file
BIN
web/src/views/visualizing/images/bathymetry.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 607 KiB |
BIN
web/src/views/visualizing/images/world.jpg
Normal file
BIN
web/src/views/visualizing/images/world.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 783 KiB |
51
web/src/views/visualizing/mock/demo1.ts
Normal file
51
web/src/views/visualizing/mock/demo1.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// 地图模拟数据
|
||||
export const echartsMapList: Array<object> = [
|
||||
{ name: '深圳市人民政府', value: '100' },
|
||||
{ name: '莲花山公园', value: '100' },
|
||||
{ name: '世界之窗', value: '100' },
|
||||
{ name: '华侨城欢乐谷', value: '100' },
|
||||
{ name: '宝安区西乡', value: '100' },
|
||||
];
|
||||
|
||||
// 地图经纬度数据
|
||||
export const echartsMapData: object = {
|
||||
深圳市人民政府: [114.064524, 22.549225],
|
||||
莲花山公园: [114.0658, 22.560072],
|
||||
世界之窗: [113.979419, 22.540579],
|
||||
华侨城欢乐谷: [113.986066, 22.548056],
|
||||
宝安区西乡: [113.869053, 22.581714],
|
||||
};
|
||||
|
||||
// 地图图片显示
|
||||
export const echartsMapImgs: Array<object> = [
|
||||
{
|
||||
url: 'https://img1.baidu.com/it/u=4244861097,3561366422&fm=11&fmt=auto&gp=0.jpg',
|
||||
name: '深圳市人民政府',
|
||||
add: '深圳市福田区福中三路市民中心C区',
|
||||
dec: '深圳市人民政府是根据《中华人民共和国地方各级人民代表大会和地方各级人民政府组织法》设立的,是深圳市人民代表大会的执行机关,是深圳市的国家行政机关。',
|
||||
},
|
||||
{
|
||||
url: 'https://img1.baidu.com/it/u=3793608028,4006842751&fm=26&fmt=auto&gp=0.jpg',
|
||||
name: '莲花山公园',
|
||||
add: '广东省深圳市福田区莲花街道莲花北社区红荔路6030号',
|
||||
dec: '莲花山公园筹建于1992年10月10日 ,1997年6月23日正式对外局部开放。',
|
||||
},
|
||||
{
|
||||
url: 'https://img0.baidu.com/it/u=1406340112,1927292660&fm=26&fmt=auto&gp=0.jpg',
|
||||
name: '世界之窗',
|
||||
add: '深圳市南山区深南大道9037号',
|
||||
dec: '这里,世界首座实景拍摄悬空式球幕影院“飞跃美利坚””,为游客提供集休闲放松于一体的都市时尚生活空间。',
|
||||
},
|
||||
{
|
||||
url: 'https://img0.baidu.com/it/u=3042342330,902556630&fm=26&fmt=auto&gp=0.jpg',
|
||||
name: '华侨城欢乐谷',
|
||||
add: '广东省深圳市南山区沙河街道星河街社区侨城西街1号',
|
||||
dec: '深圳欢乐谷注重满足人们参与、体验的新型诱游需求,营造出自然、清新、活泼、惊奇、热烈、刺激的休闲旅游氛围。',
|
||||
},
|
||||
{
|
||||
url: 'https://img2.baidu.com/it/u=1075072079,1229283519&fm=11&fmt=auto&gp=0.jpg',
|
||||
name: '宝安区西乡',
|
||||
add: '西乡街道下辖25个社区',
|
||||
dec: '西乡街道,隶属于广东省深圳市宝安区,位于宝安区西南部,东接石岩街道,南接新安街道,西至珠江口岸边,北接航城街道。',
|
||||
},
|
||||
];
|
||||
131
web/src/views/visualizing/mock/demo2.ts
Normal file
131
web/src/views/visualizing/mock/demo2.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
// 顶部下来菜单
|
||||
export const dropdownList: Array<object> = [
|
||||
{
|
||||
label: '广东省农业农村厅',
|
||||
},
|
||||
{
|
||||
label: '广西省农业农村厅',
|
||||
},
|
||||
{
|
||||
label: '四川省农业农村厅',
|
||||
},
|
||||
{
|
||||
label: '湖北省农业农村厅',
|
||||
},
|
||||
{
|
||||
label: '福建省农业农村厅',
|
||||
},
|
||||
{
|
||||
label: '山东省农业农村厅',
|
||||
},
|
||||
{
|
||||
label: '江西省农业农村厅',
|
||||
},
|
||||
];
|
||||
|
||||
// sky 天气
|
||||
export const skyList: Array<object> = [
|
||||
{
|
||||
v1: '时间',
|
||||
v2: '天气',
|
||||
v3: '温度',
|
||||
v4: '湿度',
|
||||
v5: '降水概率',
|
||||
v6: '风向',
|
||||
v7: '风力',
|
||||
type: 'title',
|
||||
},
|
||||
{
|
||||
v1: '今天',
|
||||
v2: 'ele-Sunny',
|
||||
v3: '20°/26°',
|
||||
v4: '80%',
|
||||
v5: '50%',
|
||||
v6: '东南风',
|
||||
v7: '13m/s',
|
||||
},
|
||||
{
|
||||
v1: '明天',
|
||||
v2: 'ele-Lightning',
|
||||
v3: '20°/26°',
|
||||
v4: '80%',
|
||||
v5: '50%',
|
||||
v6: '东南风',
|
||||
v7: '13m/s',
|
||||
},
|
||||
{
|
||||
v1: '后天',
|
||||
v2: 'ele-Sunny',
|
||||
v3: '20°/26°',
|
||||
v4: '80%',
|
||||
v5: '50%',
|
||||
v6: '东南风',
|
||||
v7: '13m/s',
|
||||
},
|
||||
];
|
||||
|
||||
// 当前设置状态
|
||||
export const dBtnList: Array<object> = [
|
||||
{
|
||||
v1: '地块A-灌溉',
|
||||
v2: '阳光玫瑰种植',
|
||||
v3: '126天',
|
||||
v4: '设备在线',
|
||||
},
|
||||
{
|
||||
v1: '地块B-收割',
|
||||
v2: '阳光玫瑰种植',
|
||||
v3: '360天',
|
||||
v4: '设备预警',
|
||||
},
|
||||
];
|
||||
|
||||
// 当前设备监测
|
||||
export const chartData4List: Array<object> = [
|
||||
{
|
||||
label: '温度',
|
||||
},
|
||||
{
|
||||
label: '光照',
|
||||
},
|
||||
{
|
||||
label: '湿度',
|
||||
},
|
||||
{
|
||||
label: '风力',
|
||||
},
|
||||
{
|
||||
label: '张力',
|
||||
},
|
||||
{
|
||||
label: '气压',
|
||||
},
|
||||
];
|
||||
|
||||
// 3DEarth 地图周围按钮组
|
||||
export const earth3DBtnList: Array<object> = [
|
||||
{
|
||||
topLevelClass: 'fixed-top',
|
||||
icon: 'ele-MagicStick',
|
||||
label: '环境监测',
|
||||
type: 0,
|
||||
},
|
||||
{
|
||||
topLevelClass: 'fixed-right',
|
||||
icon: 'ele-MoonNight',
|
||||
label: '精准管理',
|
||||
type: 1,
|
||||
},
|
||||
{
|
||||
topLevelClass: 'fixed-bottom',
|
||||
icon: 'ele-TrendCharts',
|
||||
label: '数据报表',
|
||||
type: 2,
|
||||
},
|
||||
{
|
||||
topLevelClass: 'fixed-left',
|
||||
icon: 'ele-Van',
|
||||
label: '产品追溯',
|
||||
type: 3,
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user