'admin-20.12.31:完成主体布局、添加切换专场动画等'

This commit is contained in:
lyt-Top 2020-12-31 11:47:52 +08:00
parent 1ccc74ec2b
commit c482f693fe
60 changed files with 1732 additions and 135 deletions

View File

@ -1,5 +1,5 @@
# port 端口号
VITE_PORT = 8080
VITE_PORT = 9000
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN = false

View File

@ -1,22 +1,23 @@
import dotenv from 'dotenv';
// vite 打包相关
import dotenv from 'dotenv'
export interface ViteEnv {
VITE_PORT: number;
VITE_OPEN: boolean;
VITE_PUBLIC_PATH: string;
VITE_PORT: number
VITE_OPEN: boolean
VITE_PUBLIC_PATH: string
}
export function loadEnv(): ViteEnv {
const env = process.env.NODE_ENV;
const ret: any = {};
const env = process.env.NODE_ENV
const ret: any = {}
const envList = [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env', ,]
envList.forEach((e) => { dotenv.config({ path: e }) });
envList.forEach((e) => { dotenv.config({ path: e }) })
for (const envName of Object.keys(process.env)) {
let realName = (process.env as any)[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') realName = Number(realName);
if (envName === 'VITE_OPEN') realName = Boolean(realName);
ret[envName] = realName;
process.env[envName] = realName;
let realName = (process.env as any)[envName].replace(/\\n/g, '\n')
realName = realName === 'true' ? true : realName === 'false' ? false : realName
if (envName === 'VITE_PORT') realName = Number(realName)
if (envName === 'VITE_OPEN') realName = Boolean(realName)
ret[envName] = realName
process.env[envName] = realName
}
return ret;
return ret
}

View File

@ -6,11 +6,13 @@
"build": "vite build"
},
"dependencies": {
"element-plus": "^v1.0.1-beta.14",
"element-plus": "^v1.0.1-beta.17",
"sortablejs": "^1.10.2",
"vue": "^3.0.4",
"vue-router": "^4.0.1"
},
"devDependencies": {
"@types/sortablejs": "^1.10.6",
"@vue/compiler-sfc": "^3.0.4",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",

View File

@ -1,3 +1,16 @@
<template>
<router-view />
</template>
<script lang="ts">
import { onBeforeMount } from "vue";
import { setIconfont } from "/@/utils/setIconfont.ts";
export default {
name: "app",
setup() {
onBeforeMount(() => {
setIconfont(["//at.alicdn.com/t/font_2298093_8wsrw2zw3rg.css"]);
});
},
};
</script>

View File

@ -1,3 +0,0 @@
<template>
dd
</template>

View File

@ -7,14 +7,169 @@ const staticRoutes: Array<RouteRecordRaw> = [
component: () => import('/@/views/layout/index.vue'),
redirect: '/home',
meta: {
title: '首页'
title: '首页',
index: 0
},
children: [{
path: '/home',
name: 'home',
component: () => import('/@/views/home/index.vue'),
meta: {
title: '首页'
title: '首页',
index: 0
}
},
{
path: '/docs',
name: 'docs',
component: () => import('/@/views/docs/index.vue'),
meta: {
title: '文档',
index: 1
}
},
{
path: '/docs1',
name: 'docs1',
component: () => import('/@/views/docs copy 1/index.vue'),
meta: {
title: '文档1',
index: 2
}
},
{
path: '/docs2',
name: 'docs2',
component: () => import('/@/views/docs copy 2/index.vue'),
meta: {
title: '文档2',
index: 3
}
},
{
path: '/docs3',
name: 'docs3',
component: () => import('/@/views/docs copy 3/index.vue'),
meta: {
title: '文档3',
index: 4
}
},
{
path: '/docs4',
name: 'docs4',
component: () => import('/@/views/docs copy 4/index.vue'),
meta: {
title: '文档4',
index: 5
}
},
{
path: '/docs5',
name: 'docs5',
component: () => import('/@/views/docs copy 5/index.vue'),
meta: {
title: '文档5',
index: 6
}
},
{
path: '/docs6',
name: 'docs6',
component: () => import('/@/views/docs copy 6/index.vue'),
meta: {
title: '文档6',
index: 7
}
},
{
path: '/docs7',
name: 'docs7',
component: () => import('/@/views/docs copy 7/index.vue'),
meta: {
title: '文档7',
index: 8
}
},
{
path: '/docs8',
name: 'docs8',
component: () => import('/@/views/docs copy 8/index.vue'),
meta: {
title: '文档8',
index: 9
}
},
{
path: '/docs9',
name: 'docs9',
component: () => import('/@/views/docs copy 9/index.vue'),
meta: {
title: '文档9',
index: 10
}
},
{
path: '/docs10',
name: 'docs10',
component: () => import('/@/views/docs copy 10/index.vue'),
meta: {
title: '文档10',
index: 11
}
},
{
path: '/docs11',
name: 'docs11',
component: () => import('/@/views/docs copy 11/index.vue'),
meta: {
title: '文档11',
index: 12
}
},
{
path: '/docs12',
name: 'docs12',
component: () => import('/@/views/docs copy 12/index.vue'),
meta: {
title: '文档12',
index: 13
}
},
{
path: '/docs13',
name: 'docs13',
component: () => import('/@/views/docs copy 13/index.vue'),
meta: {
title: '文档13',
index: 14
}
},
{
path: '/docs14',
name: 'docs14',
component: () => import('/@/views/docs copy 14/index.vue'),
meta: {
title: '文档14',
index: 15
}
},
{
path: '/docs15',
name: 'docs15',
component: () => import('/@/views/docs copy 15/index.vue'),
meta: {
title: '文档15',
index: 16
}
},
{
path: '/docs16',
name: 'docs16',
component: () => import('/@/views/docs copy 16/index.vue'),
meta: {
title: '文档16',
index: 17
}
}]
},
@ -26,9 +181,25 @@ const staticRoutes: Array<RouteRecordRaw> = [
title: '登陆'
}
},
{
path: '/404',
name: '404',
component: () => import('/@/views/error/404.vue'),
meta: {
title: '找不到此页面'
}
},
{
path: '/401',
name: '401',
component: () => import('/@/views/error/401.vue'),
meta: {
title: '没有权限'
}
},
{
path: '/:pathMatch(.*)',
redirect: '/'
redirect: '/404'
}
]
@ -37,4 +208,8 @@ const router = createRouter({
routes: staticRoutes
})
// router.afterEach((to, from) => {
// })
export default router

View File

@ -1,3 +1,5 @@
/* 初始化样式
------------------------------- */
* {
margin: 0;
padding: 0;
@ -9,20 +11,68 @@ body,
#app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, SimSun, sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
background-color: #f8f8f8;
font-size: 14px;
}
/* 主布局样式
------------------------------- */
.layout-container {
width: 100%;
height: 100%;
.el-aside {
background: #29384d;
border-right: 1px solid rgb(230, 230, 230);
height: inherit;
}
.el-header {
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
padding: 0 !important;
}
.el-main {
padding: 0 !important;
overflow: unset !important;
}
.el-scrollbar {
width: 100%;
}
.layout-scrollbar {
@extend .el-scrollbar;
padding: 15px;
}
.el-scrollbar__view {
height: 100%;
display: flex;
flex-direction: column;
}
}
/* 宽高 100%
------------------------------- */
.w100 {
width: 100% !important;
}
.h100 {
height: 100% !important;
}
/* 字体大小全局样式
------------------------------- */
@for $i from 10 through 32 {
.font#{$i} {
font-size: #{$i}px !important;
}
}
/* 外边距内边距全局样式
------------------------------- */
@for $i from 5 through 20 {
.mt#{$i} {
margin-top: #{$i}px !important;

View File

@ -1,41 +1,37 @@
/* 定义重用混入指令
------------------------------- */
@mixin transition($second: 0.3) {
transition: all #{$second}s;
}
@mixin translateX($opacity: 0, $offsetX: 0) {
opacity: $opacity;
transform: translateX(#{$offsetX}px);
}
/* 页面切换动画
------------------------------- */
.fade-transform-enter-active,
.fade-transform-leave-active {
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
will-change: transform;
@include transition();
transition: all 0.3s ease;
position: absolute;
}
.fade-transform-enter {
@include translateX(-30);
.slide-right-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-active {
@include translateX(30);
.slide-right-leave-to {
opacity: 0;
transform: translateX(30px);
}
.slide-left-enter-from {
@extend .slide-right-leave-to;
}
.slide-left-leave-to {
@extend .slide-right-enter-from;
}
/* Breadcrumb 面包屑过渡动画
------------------------------- */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
@include transition();
}
.breadcrumb-enter,
.breadcrumb-leave-active {
@include translateX(20);
}
.breadcrumb-move {
@include transition();
}
.breadcrumb-leave-active {
transition: all 0.3s ease;
position: absolute;
}
.breadcrumb-enter-from,
.breadcrumb-leave-to {
opacity: 0;
transform: translateX(20px);
}

View File

@ -666,23 +666,31 @@
/* NavMenu 导航菜单
------------------------------- */
// horizontal
.el-menu--horizontal > .el-menu-item.is-active,
.el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
.el-menu {
border-right: none !important;
}
.el-menu-item.is-active,
.el-submenu.is-active .el-submenu__title {
border-bottom-color: set-color(primary);
color: set-color(primary);
}
.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,
.el-menu--horizontal .el-menu-item:not(.is-disabled):hover,
.el-menu--horizontal > .el-submenu:focus .el-submenu__title,
.el-menu--horizontal > .el-submenu:hover .el-submenu__title,
.el-menu--horizontal .el-menu .el-menu-item.is-active,
.el-menu--horizontal .el-menu .el-submenu.is-active > .el-submenu__title {
.el-menu-item:not(.is-disabled):focus,
.el-menu-item:not(.is-disabled):hover,
.el-submenu:focus .el-submenu__title,
.el-submenu:hover .el-submenu__title,
.el-menu .el-menu-item.is-active,
.el-menu .el-submenu.is-active > .el-submenu__title {
color: set-color(primary);
}
.el-menu-item a,
.el-menu-item a:hover {
color: inherit;
text-decoration: none;
}
// default
.el-menu-item.is-active {
color: set-color(primary);
background-color: set-color(primary-light-9);
}
.el-menu-item:focus,
.el-menu-item:hover,
@ -711,6 +719,11 @@
.el-breadcrumb__inner.is-link:hover {
color: set-color(primary);
}
.el-breadcrumb__inner a,
.el-breadcrumb__inner.is-link {
color: #303133;
font-weight: normal;
}
/* Dropdown 下拉菜单
------------------------------- */
@ -797,3 +810,13 @@
background-color: set-color(primary-light-9);
}
}
/* scrollbar
------------------------------- */
.el-scrollbar__wrap {
overflow-x: hidden !important;
max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
}
.el-select-dropdown .el-scrollbar__wrap {
overflow-x: scroll !important;
}

View File

@ -1,4 +1,4 @@
@import './app.scss';
@import './base.scss';
@import './element.scss';
@import './media.scss';
@import './media/media.scss';

View File

@ -1 +0,0 @@
// 540/720/960/1140

View File

@ -0,0 +1,35 @@
@import './index.scss';
/* 页面宽度小于768px
------------------------------- */
@media screen and (max-width: $sm) {
.error {
.error-flex {
flex-direction: column-reverse !important;
height: auto !important;
width: 100% !important;
}
.right,
.left {
flex: unset !important;
display: flex !important;
}
.left-item {
margin: auto !important;
}
.right img {
max-width: 450px !important;
@extend .left-item;
}
}
}
/* 页面宽度大于768px小于992px
------------------------------- */
@media screen and (min-width: $sm) and (max-width: $md) {
.error {
.error-flex {
padding-left: 30px !important;
}
}
}

View File

@ -0,0 +1,12 @@
/* 栅格布局媒体查询变量
* $xs <768px 响应式栅格
* $sm 768px 响应式栅格
* $md 992px 响应式栅格
* $lg 1200px 响应式栅格
* $xl 1920px 响应式栅格
------------------------------- */
$xs: 576px;
$sm: 768px;
$md: 992px;
$lg: 1200px;
$xl: 1920px;

View File

@ -0,0 +1,21 @@
@import './index.scss';
/* 页面宽度小于576px
------------------------------- */
@media screen and (max-width: $xs) {
.login-container {
.login-content {
width: 90% !important;
padding: 20px 0 !important;
}
.login-content-form-btn {
width: 100% !important;
padding: 12px 0 !important;
}
.login-copyright {
.login-copyright-msg {
white-space: unset !important;
}
}
}
}

View File

@ -0,0 +1,2 @@
@import './login.scss';
@import './error.scss';

View File

@ -0,0 +1,26 @@
@import './index.scss';
/* 页面宽度小于576px
------------------------------- */
@media screen and (max-width: $xs) {
}
/* 页面宽度小于768px
------------------------------- */
@media screen and (max-width: $sm) {
}
/* 页面宽度大于768px小于992px
------------------------------- */
@media screen and (min-width: $sm) and (max-width: $md) {
}
/* 页面宽度大于992px小于1200px
------------------------------- */
@media screen and (min-width: $md) and (max-width: $lg) {
}
/* 页面宽度大于1920px
------------------------------- */
@media screen and (min-width: $xl) {
}

View File

@ -78,26 +78,26 @@ export function formatPast(param: any, format: string = "YYYY-mm-dd") {
time = Number.parseInt(`${time - t}`)
if (time < 10000) {
// 10秒内
return "刚刚";
return "刚刚"
} else if (time < 60000 && time >= 10000) {
// 超过10秒少于1分钟内
s = Math.floor(time / 1000);
return `${s}秒前`;
s = Math.floor(time / 1000)
return `${s}秒前`
} else if (time < 3600000 && time >= 60000) {
// 超过1分钟少于1小时
s = Math.floor(time / 60000);
return `${s}分钟前`;
s = Math.floor(time / 60000)
return `${s}分钟前`
} else if (time < 86400000 && time >= 3600000) {
// 超过1小时少于24小时
s = Math.floor(time / 3600000);
return `${s}小时前`;
s = Math.floor(time / 3600000)
return `${s}小时前`
} else if (time < 259200000 && time >= 86400000) {
// 超过1天少于3天内
s = Math.floor(time / 86400000);
return `${s}天前`;
s = Math.floor(time / 86400000)
return `${s}天前`
} else {
// 超过3天
let date = typeof param === "string" || "object" ? new Date(param) : param;
let date = typeof param === "string" || "object" ? new Date(param) : param
return formatDate(date, format);
}
}

View File

@ -0,0 +1,10 @@
// 动态设置字体图标
export function setIconfont(url: Array<string> = []) {
if (url.length <= 0) return false
url.map(v => {
let link = document.createElement('link')
link.rel = 'stylesheet'
link.href = v
document.getElementsByTagName('head')[0].appendChild(link)
})
}

View File

@ -0,0 +1,37 @@
// 1. localStorage
// 设置永久缓存
export function setLocal(key: string, val: any) {
window.localStorage.setItem(key, JSON.stringify(val))
}
// 获取永久缓存
export function getLocal(key: string) {
let json: any = window.localStorage.getItem(key)
return JSON.parse(json)
}
// 移除永久缓存
export function removeLocal(key: string) {
window.localStorage.removeItem(key)
}
// 移除全部永久缓存
export function clearLocal() {
window.localStorage.clear()
}
// 2. sessionStorage
// 设置临时缓存
export function setSession(key: string, val: any) {
window.sessionStorage.setItem(key, JSON.stringify(val))
}
// 获取临时缓存
export function getSession(key: string) {
let json: any = window.sessionStorage.getItem(key)
return JSON.parse(json)
}
// 移除临时缓存
export function removeSession(key: string) {
window.sessionStorage.removeItem(key)
}
// 移除全部临时缓存
export function clearSession() {
window.sessionStorage.clear()
}

View File

@ -1,39 +1,39 @@
import { ElMessage } from 'element-plus';
import { ElMessage } from 'element-plus'
// hex颜色转rgb颜色
export function hexToRgb(str: any) {
let hexs: any = '';
let reg = /^\#?[0-9A-F]{6}$/;
if (!reg.test(str)) return ElMessage({ type: 'warning', message: "输入错误的hex" });
str = str.replace("#", "");
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
let hexs: any = ''
let reg = /^\#?[0-9A-F]{6}$/
if (!reg.test(str)) return ElMessage({ type: 'warning', message: "输入错误的hex" })
str = str.replace("#", "")
hexs = str.match(/../g)
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16)
return hexs
}
// rgb颜色转Hex颜色
export function rgbToHex(r: any, g: any, b: any) {
let reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage({ type: 'warning', message: "输入错误的rgb颜色值" });
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join("")}`;
let reg = /^\d{1,3}$/
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage({ type: 'warning', message: "输入错误的rgb颜色值" })
let hexs = [r.toString(16), g.toString(16), b.toString(16)]
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`
return `#${hexs.join("")}`
}
// 加深颜色值level为加深的程度限0-1之间
export function getDarkColor(color: any, level: number) {
let reg = /^\#?[0-9A-F]{6}$/;
if (!reg.test(color)) return ElMessage({ type: 'warning', message: "输入错误的hex颜色值" });
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
let reg = /^\#?[0-9A-F]{6}$/
if (!reg.test(color)) return ElMessage({ type: 'warning', message: "输入错误的hex颜色值" })
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level))
return rgbToHex(rgb[0], rgb[1], rgb[2])
}
// 变浅颜色值level为加深的程度限0-1之间
export function getLightColor(color: any, level: number) {
let reg = /^\#?[0-9A-F]{6}$/;
if (!reg.test(color)) return ElMessage({ type: 'warning', message: "输入错误的hex颜色值" });
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
return rgbToHex(rgb[0], rgb[1], rgb[2]);
let reg = /^\#?[0-9A-F]{6}$/
if (!reg.test(color)) return ElMessage({ type: 'warning', message: "输入错误的hex颜色值" })
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
return rgbToHex(rgb[0], rgb[1], rgb[2])
}

View File

@ -1,43 +1,43 @@
// 页面添加水印效果
const setWatermark = (str: any) => {
const id = "1.23452384164.123412416";
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
const can = document.createElement("canvas");
can.width = 250;
can.height = 180;
const cans: any = can.getContext("2d");
cans.rotate((-20 * Math.PI) / 180);
cans.font = "12px Vedana";
cans.fillStyle = "rgba(200, 200, 200, 0.30)";
cans.textAlign = "center";
cans.textBaseline = "Middle";
cans.fillText(str, can.width / 10, can.height / 2);
const div = document.createElement("div");
div.id = id;
div.style.pointerEvents = "none";
div.style.top = "35px";
div.style.left = "0px";
div.style.position = "fixed";
div.style.zIndex = "100000";
div.style.width = document.documentElement.clientWidth + "px";
div.style.height = document.documentElement.clientHeight + "px";
div.style.background = `url(${can.toDataURL("image/png")}) left top repeat`;
document.body.appendChild(div);
return id;
const id = "1.23452384164.123412416"
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any)
const can = document.createElement("canvas")
can.width = 250
can.height = 180
const cans: any = can.getContext("2d")
cans.rotate((-20 * Math.PI) / 180)
cans.font = "12px Vedana"
cans.fillStyle = "rgba(200, 200, 200, 0.30)"
cans.textAlign = "center"
cans.textBaseline = "Middle"
cans.fillText(str, can.width / 10, can.height / 2)
const div = document.createElement("div")
div.id = id
div.style.pointerEvents = "none"
div.style.top = "35px"
div.style.left = "0px"
div.style.position = "fixed"
div.style.zIndex = "100000"
div.style.width = document.documentElement.clientWidth + "px"
div.style.height = document.documentElement.clientHeight + "px"
div.style.background = `url(${can.toDataURL("image/png")}) left top repeat`
document.body.appendChild(div)
return id
};
const watermark = {
// 设置水印
set: (str: any) => {
let id = setWatermark(str);
if (document.getElementById(id) === null) id = setWatermark(str);
window.onresize = () => { setWatermark(str) };
let id = setWatermark(str)
if (document.getElementById(id) === null) id = setWatermark(str)
window.onresize = () => { setWatermark(str) }
},
// 删除水印
del: () => {
let id = '1.23452384164.123412416';
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
let id = '1.23452384164.123412416'
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any)
}
}
export default watermark;
export default watermark

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,3 @@
<template>
docs
</template>

View File

@ -0,0 +1,113 @@
<template>
<div class="error">
<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">您未被授权没有操作权限~</div>
<div class="left-item-animation left-item-msg">联系方式加QQ群探讨 665452019</div>
<div class="left-item-animation left-item-btn">
<el-button type="primary" round @click="onSetAuth">重新授权</el-button>
</div>
</div>
</div>
<div class="right">
<img src="https://gitee.com/lyt-top/vue-admin-wonderful-images/raw/master/images/error/401.png" />
</div>
</div>
</div>
</template>
<script lang="ts">
import { useRouter } from "vue-router";
import { clearSession } from "/@/utils/storage.ts";
export default {
name: "401",
setup() {
const router = useRouter();
const onSetAuth = () => {
clearSession();
router.push("/login");
};
return {
onSetAuth,
};
},
};
</script>
<style scoped lang="scss">
.error {
height: 100%;
background-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: #d6e0f6;
font-size: 55px;
}
.left-item-title {
font-size: 20px;
color: #333333;
margin: 15px 0 5px 0;
animation-delay: 0.1s;
}
.left-item-msg {
color: #c0bebe;
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%;
}
}
}
@keyframes error-num {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes error-img {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
</style>

View File

@ -0,0 +1,111 @@
<template>
<div class="error">
<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">地址输入错误请重新输入地址~</div>
<div class="left-item-animation left-item-msg">您可以先检查网址然后重新输入或给我们反馈问题</div>
<div class="left-item-animation left-item-btn">
<el-button type="primary" round @click="onGoHome">返回首页</el-button>
</div>
</div>
</div>
<div class="right">
<img src="https://gitee.com/lyt-top/vue-admin-wonderful-images/raw/master/images/error/404.png" />
</div>
</div>
</div>
</template>
<script lang="ts">
import { useRouter } from "vue-router";
export default {
name: "404",
setup() {
const router = useRouter();
const onGoHome = () => {
router.push("/");
};
return {
onGoHome,
};
},
};
</script>
<style scoped lang="scss">
.error {
height: 100%;
background-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: #d6e0f6;
font-size: 55px;
}
.left-item-title {
font-size: 20px;
color: #333333;
margin: 15px 0 5px 0;
animation-delay: 0.1s;
}
.left-item-msg {
color: #c0bebe;
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%;
}
}
}
@keyframes error-num {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes error-img {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
</style>

View File

@ -1,5 +1,85 @@
<template>
home
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home</p>
<p>home123123</p>
</template>
<script lang="ts">

View File

@ -0,0 +1,154 @@
<template>
<el-aside width="240px">
<el-scrollbar>
<Vertical :menuList="menuList" />
</el-scrollbar>
</el-aside>
</template>
<script lang="ts">
import Vertical from "/@/views/layout/navMenu/vertical.vue";
import { toRefs, reactive } from "vue";
export default {
name: "layoutAside",
components: { Vertical },
setup() {
const state = reactive({
menuList: [
{
path: "/home",
meta: {
title: "首页",
icon: "el-icon-s-home",
},
children: [
{
path: "/docs3",
meta: {
title: "文档3",
icon: "el-icon-s-flag",
},
},
{
path: "/docs1",
meta: {
title: "文档1",
icon: "el-icon-s-flag",
},
},
{
path: "/docs2",
meta: {
title: "文档2",
icon: "el-icon-s-flag",
},
},
],
},
{
path: "/docs",
meta: {
title: "文档",
icon: "el-icon-s-management",
isLink: "https://www.ele.me",
},
},
{
path: "/docs4",
meta: {
title: "文档4",
icon: "el-icon-s-management",
},
},
{
path: "/docs5",
meta: {
title: "文档5",
icon: "el-icon-s-management",
},
},
{
path: "/docs6",
meta: {
title: "文档6",
icon: "el-icon-s-management",
},
},
{
path: "/docs7",
meta: {
title: "文档7",
icon: "el-icon-s-management",
},
},
{
path: "/docs8",
meta: {
title: "文档8",
icon: "el-icon-s-management",
},
},
{
path: "/docs9",
meta: {
title: "文档9",
icon: "el-icon-s-management",
},
},
{
path: "/docs10",
meta: {
title: "文档10",
icon: "el-icon-s-management",
},
},
{
path: "/docs11",
meta: {
title: "文档11",
icon: "el-icon-s-management",
},
},
{
path: "/docs12",
meta: {
title: "文档12",
icon: "el-icon-s-management",
},
},
{
path: "/docs13",
meta: {
title: "文档13",
icon: "el-icon-s-management",
},
},
{
path: "/docs14",
meta: {
title: "文档14",
icon: "el-icon-s-management",
},
},
{
path: "/docs15",
meta: {
title: "文档15",
icon: "el-icon-s-management",
},
},
{
path: "/docs16",
meta: {
title: "文档16",
icon: "el-icon-s-management",
},
},
],
});
return {
...toRefs(state),
};
},
};
</script>

View File

@ -0,0 +1,13 @@
<template>
<el-header height="84px">
<NavBarsIndex />
</el-header>
</template>
<script lang="ts">
import NavBarsIndex from "/@/views/layout/navBars/index.vue";
export default {
name: "layoutHeader",
components: { NavBarsIndex },
};
</script>

View File

@ -0,0 +1,35 @@
<template>
<el-main>
<el-scrollbar class="layout-scrollbar">
<router-view v-slot="{ Component }">
<transition appear :name="transitionName" mode="out-in">
<div :key="key">
<keep-alive>
<component :is="Component" />
</keep-alive>
</div>
</transition>
</router-view>
</el-scrollbar>
</el-main>
</template>
<script lang="ts">
import { computed, defineComponent, toRefs, reactive } from "vue";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
export default defineComponent({
name: "layoutMain",
setup() {
const route = useRoute();
const state = reactive({
transitionName: "slide-right",
});
const key = computed(() => route.path);
onBeforeRouteUpdate((to, from) => {
state.transitionName =
to.meta.index > from.meta.index ? "slide-right" : "slide-left";
});
return { key, ...toRefs(state) };
},
});
</script>

View File

@ -0,0 +1,21 @@
<template>
<el-container class="layout-container">
<Aside />
<el-container>
<el-scrollbar>
<Header />
<Main />
</el-scrollbar>
</el-container>
</el-container>
</template>
<script>
import Aside from '/@/views/layout/component/aside.vue';
import Header from '/@/views/layout/component/header.vue';
import Main from '/@/views/layout/component/main.vue';
export default {
name: 'layoutFashion',
components: { Aside, Header, Main }
}
</script>

View File

@ -1,10 +1,11 @@
<template>
layout
<router-view></router-view>
<Fashion />
</template>
<script lang="ts">
import Fashion from "/@/views/layout/fashion.vue";
export default {
name: "layout",
components: { Fashion },
};
</script>

View File

@ -0,0 +1,9 @@
<template>
layoutLogo
</template>
<script lang="ts">
export default {
name: "layoutLogo",
};
</script>

View File

@ -0,0 +1,63 @@
<template>
<div class="layout-navbars-breadcrumb">
<i class="el-icon-s-fold layout-navbars-breadcrumb-icon"></i>
<el-breadcrumb>
<transition-group name="breadcrumb" mode="out-in">
<el-breadcrumb-item v-for="(v,k) in breadcrumbList" :key="v.meta.title">
<span v-if="k === breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">{{v.meta.title}}</span>
<a v-else @click.prevent="onBreadcrumbClick(v)">{{v.meta.title}}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onBeforeMount } from "vue";
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
export default {
name: "layoutBreadcrumb",
setup() {
const route = useRoute();
const router = useRouter();
const state = reactive({
breadcrumbList: [{ meta: { title: "" } }], // v-for
});
const getBreadcrumbList = (matched: any) => {
state.breadcrumbList = matched;
};
const onBreadcrumbClick = (v: object) => {
const { redirect, path } = v;
if (redirect) router.push(redirect);
else router.push(path);
};
onBeforeMount(() => {
state.breadcrumbList = route.matched;
});
onBeforeRouteUpdate((to) => {
getBreadcrumbList(to.matched);
});
return {
onBreadcrumbClick,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb {
flex: 1;
height: inherit;
display: flex;
align-items: center;
.layout-navbars-breadcrumb-icon {
cursor: pointer;
font-size: 18px;
margin-right: 15px;
}
.layout-navbars-breadcrumb-span {
opacity: 0.8;
}
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<div class="layout-navbars-breadcrumb-index">
<Breadcrumb />
<User />
</div>
</template>
<script lang="ts">
import Breadcrumb from "/@/views/layout/navBars/breadcrumb/breadcrumb.vue";
import User from "/@/views/layout/navBars/breadcrumb/user.vue";
export default {
name: "layoutBreadcrumbIndex",
components: { Breadcrumb, User },
};
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-index {
height: 50px;
display: flex;
align-items: center;
border-bottom: 1px solid rgb(230, 230, 230);
padding: 0 15px;
}
</style>

View File

@ -0,0 +1,9 @@
<template>
layoutBreadcrumbSeting
</template>
<script lang="ts">
export default {
name: "layoutBreadcrumbSeting",
};
</script>

View File

@ -0,0 +1,60 @@
<template>
<div class="layout-navbars-breadcrumb-user">
<i class="el-icon-search layout-navbars-breadcrumb-user-icon" title="菜单搜索"></i>
<i class="el-icon-setting layout-navbars-breadcrumb-user-icon" title="布局配置"></i>
<i class="el-icon-bell layout-navbars-breadcrumb-user-icon" title="消息"></i>
<i class="icon-fullscreen iconfont layout-navbars-breadcrumb-user-icon mr10" title="开全屏"></i>
<el-dropdown :show-timeout="70" :hide-timeout="50">
<span class="layout-navbars-breadcrumb-user-link">
<img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg"
class="layout-navbars-breadcrumb-user-link-photo mr5" /> small@小柒
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>首页</el-dropdown-item>
<el-dropdown-item>文档</el-dropdown-item>
<el-dropdown-item>404</el-dropdown-item>
<el-dropdown-item>401</el-dropdown-item>
<el-dropdown-item divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script lang="ts">
export default {
name: "layoutBreadcrumbUser",
};
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-user {
display: flex;
align-items: center;
&-link {
height: 100%;
display: flex;
align-items: center;
&-photo {
width: 25px;
height: 25px;
border-radius: 100%;
}
}
&-icon {
display: inline-block;
padding: 0 10px;
cursor: pointer;
transition: all 0.3s;
color: #000000;
height: 50px;
line-height: 50px;
&:hover {
background: rgba(0, 0, 0, 0.04);
}
}
}
</style>

View File

@ -0,0 +1,24 @@
<template>
<div class="layout-navbars-container">
<BreadcrumbIndex />
<TagsView />
</div>
</template>
<script lang="ts">
import BreadcrumbIndex from "/@/views/layout/navBars/breadcrumb/index.vue";
import TagsView from "/@/views/layout/navBars/tagsView/tagsView.vue";
export default {
name: "layoutNavBars",
components: { BreadcrumbIndex, TagsView },
};
</script>
<style scoped lang="scss">
.layout-navbars-container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,79 @@
<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: ${dropdown.y + 5}px;left: ${dropdown.x}px;`" v-show="isShow">
<ul class="el-dropdown-menu">
<li class="el-dropdown-menu__item" aria-disabled="false" tabindex="-1" v-for="(v,k) in dropdownList" :key="k">
<i :class="v.icon"></i>
<span>{{v.txt}}</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: "layoutTagsViewContextmenu",
props: {
dropdown: {
type: Object,
},
},
setup(props) {
const state = reactive({
isShow: false,
dropdownList: [
{ id: 0, txt: "刷新", affix: false, icon: "el-icon-refresh-right" },
{ id: 1, txt: "关闭", affix: true, icon: "el-icon-close" },
{ id: 2, txt: "关闭其它", affix: false, icon: "el-icon-circle-close" },
{ id: 3, txt: "全部关闭", affix: false, icon: "el-icon-folder-delete" },
],
});
const dropdown = computed(() => {
return props.dropdown;
});
const openContextmenu = () => {
closeContextmenu();
setTimeout(() => {
state.isShow = true;
}, 10);
};
const closeContextmenu = () => {
state.isShow = false;
};
onMounted(() => {
document.body.addEventListener("click", closeContextmenu);
});
onUnmounted(() => {
document.body.removeEventListener("click", closeContextmenu);
});
return {
dropdown,
openContextmenu,
closeContextmenu,
...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;
}
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<el-scrollbar ref="elScrollbarRef" @wheel.native.prevent="onHandleScroll">
<slot />
</el-scrollbar>
</template>
<script lang="ts">
import { getCurrentInstance, reactive, nextTick, toRefs } from "vue";
export default {
setup() {
const { proxy } = getCurrentInstance();
const state = reactive({
tagsArr: [],
});
const onHandleScroll = (e: object) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft =
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft + eventDelta / 4;
};
const setScrollLeft = (tags: Array<object> = []) => {
nextTick(() => {
state.tagsArr = tags.value;
});
};
const moveToTarget = (currentTag: any) => {
const tagList = state.tagsArr;
let firstTag = null;
let lastTag = null;
if (tagList.length > 0) {
firstTag = tagList[0];
lastTag = tagList[tagList.length - 1];
}
if (firstTag === currentTag) {
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft = 0;
} else if (lastTag === currentTag) {
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft =
proxy.$refs.elScrollbarRef.$refs.wrap.scrollWidth -
proxy.$refs.elScrollbarRef.$refs.wrap.offsetWidth;
} else {
const currentIndex = tagList.findIndex((item) => item === currentTag);
const prevTag = tagList[currentIndex - 1];
const nextTag = tagList[currentIndex + 1];
const afterNextTagOffsetLeft =
nextTag.offsetLeft + nextTag.offsetWidth + 4;
const beforePrevTagOffsetLeft = prevTag.offsetLeft - 4;
if (
afterNextTagOffsetLeft >
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft +
proxy.$refs.elScrollbarRef.$refs.wrap.offsetWidth
) {
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft =
afterNextTagOffsetLeft -
proxy.$refs.elScrollbarRef.$refs.wrap.offsetWidth;
} else if (
beforePrevTagOffsetLeft <
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft
) {
proxy.$refs.elScrollbarRef.$refs.wrap.scrollLeft = beforePrevTagOffsetLeft;
}
}
};
return {
setScrollLeft,
onHandleScroll,
moveToTarget,
...toRefs(state),
};
},
};
</script>

View File

@ -0,0 +1,180 @@
<template>
<div class="layout-navbars-tagsview">
<Scroll ref="scrollRef">
<ul class="layout-navbars-tagsview-ul">
<li v-for="(v,k) in arr2" :key="k" class="layout-navbars-tagsview-ul-li" :class="{'is-active':isActive(v.path)}"
@contextmenu.prevent="onContextmenu(v,$event)" @click="onTagsClick(v,k)"
:ref="el => { if (el) tagsRefs[k] = el }">
<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont" v-if="isActive(v.path)"></i>
<span>{{v.name}}</span>
<template v-if="isActive(v.path)">
<i class="el-icon-refresh-right ml5"></i>
<i class="el-icon-close layout-navbars-tagsview-ul-li-icon"></i>
</template>
</li>
</ul>
</Scroll>
</div>
<Contextmenu :dropdown="dropdown" ref="contextmenuRef" />
</template>
<script lang="ts">
import {
toRefs,
reactive,
onMounted,
computed,
ref,
nextTick,
onBeforeUpdate,
} from "vue";
import { useRoute, useRouter, onBeforeRouteUpdate } from "vue-router";
import Sortable from "sortablejs";
import Contextmenu from "/@/views/layout/navBars/tagsView/contextmenu.vue";
import Scroll from "/@/views/layout/navBars/tagsView/scroll.vue";
export default {
name: "layoutTagsView",
components: { Contextmenu, Scroll },
setup() {
const tagsRefs = ref([]);
const scrollRef = ref();
const contextmenuRef = ref();
const route = useRoute();
const router = useRouter();
const state = reactive({
routePath: route.path,
dropdown: {
x: "",
y: "",
},
tagsRefsIndex: 0,
arr2: [
{ id: 11, name: "微软", path: "/home" },
{ id: 12, name: "亚马逊", path: "/docs" },
{ id: 13, name: "京东1", path: "/docs1" },
{ id: 15, name: "谷歌2", path: "/docs2" },
{ id: 1, name: "苹果3", path: "/docs3" },
{ id: 14, name: "苹果4", path: "/docs4" },
{ id: 14, name: "苹果5", path: "/docs5" },
{ id: 14, name: "苹果6", path: "/docs6" },
{ id: 14, name: "苹果7", path: "/docs7" },
{ id: 14, name: "苹果8", path: "/docs8" },
{ id: 14, name: "苹果9", path: "/docs9" },
{ id: 14, name: "苹果10", path: "/docs10" },
{ id: 14, name: "苹果11", path: "/docs11" },
{ id: 14, name: "苹果12", path: "/docs12" },
{ id: 14, name: "苹果13", path: "/docs13" },
{ id: 14, name: "苹果14", path: "/docs14" },
{ id: 14, name: "苹果15", path: "/docs15" },
{ id: 14, name: "苹果16", path: "/docs16" },
],
});
const initSortable = () => {
const el = document.querySelector(".layout-navbars-tagsview-ul");
const sortable = Sortable.create(el, { animation: 300 });
};
const isActive = (path: string) => {
return path === state.routePath;
};
const onContextmenu = (v: object, e: object) => {
const { clientX, clientY } = e;
state.dropdown.x = clientX;
state.dropdown.y = clientY;
contextmenuRef.value.openContextmenu();
};
const onTagsClick = (v: object, k: number) => {
state.tagsRefsIndex = k;
router.push(v.path);
};
const moveToCurrentTag = () => {
nextTick(() => {
scrollRef.value.moveToTarget(tagsRefs.value[state.tagsRefsIndex]);
});
};
const getTagsRefsIndex = (path: string) => {
if (state.arr2.length > 0) {
state.tagsRefsIndex = state.arr2.findIndex(
(item) => item.path === path
);
}
};
onBeforeUpdate(() => {
tagsRefs.value = [];
});
onMounted(() => {
initSortable();
scrollRef.value.setScrollLeft(tagsRefs);
});
onBeforeRouteUpdate((to) => {
state.routePath = to.path;
getTagsRefsIndex(to.path);
moveToCurrentTag();
});
return {
isActive,
onContextmenu,
onTagsClick,
tagsRefs,
contextmenuRef,
scrollRef,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.layout-navbars-tagsview {
flex: 1;
&-ul {
list-style: none;
margin: 0;
padding: 0;
height: 100%;
display: flex;
align-items: center;
color: #606266;
font-size: 12px;
white-space: nowrap;
padding: 0 15px;
&-li {
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
border: 1px solid #e6e6e6;
padding: 0 15px;
margin-right: 5px;
border-radius: 2px;
cursor: pointer;
justify-content: space-between;
&:hover {
background: #f6f6f6;
color: var(--color-primary);
}
&-iconfont {
position: relative;
left: -5px;
}
&-icon {
border-radius: 100%;
position: relative;
height: 14px;
width: 14px;
text-align: center;
line-height: 14px;
right: -5px;
&:hover {
color: #fff;
background-color: var(--color-primary-light-3);
}
}
}
.is-active {
color: #ffffff;
background: var(--color-primary);
border-color: var(--color-primary);
}
}
}
</style>

View File

@ -0,0 +1,41 @@
<template>
<template v-for="val in chil">
<el-submenu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0">
<template #title>
<i :class="val.meta.icon"></i>
{{ val.meta.title }}
</template>
<sub-item :chil="val.children" />
</el-submenu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<template v-if="!val.meta.isLink">{{ val.meta.title }}</template>
<template v-else><a :href="val.meta.isLink" target="_blank">{{ val.meta.title }}</a></template>
</el-menu-item>
</template>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue";
import { useRoute } from "vue-router";
export default defineComponent({
name: "navMenuSubItem",
props: {
chil: {
type: Array,
default() {
return [];
},
},
},
setup(props) {
const route = useRoute();
const chil = computed(() => {
return props.chil;
});
return {
chil,
};
},
});
</script>

View File

@ -0,0 +1,52 @@
<template>
<el-menu router :default-active="defaultActive" background-color="#29384D" text-color="#e6e6e6">
<template v-for="val in menuList">
<el-submenu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
<template #title>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<span>{{ val.meta.title }}</span>
</template>
<SubItem :chil="val.children" />
</el-submenu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<template #title v-if="!val.meta.isLink">{{ val.meta.title }}</template>
<template #title v-else><a :href="val.meta.isLink" target="_blank">{{ val.meta.title }}</a></template>
</el-menu-item>
</template>
</el-menu>
</template>
<script lang="ts">
import { toRefs, reactive, computed, defineComponent } from "vue";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
import SubItem from "/@/views/layout/navMenu/subItem.vue";
export default defineComponent({
name: "navMenuVertical",
components: { SubItem },
props: {
menuList: {
type: Array,
default() {
return [];
},
},
},
setup(props) {
const route = useRoute();
const state = reactive({
defaultActive: route.path,
});
const menuList = computed(() => {
return props.menuList;
});
onBeforeRouteUpdate((to) => {
state.defaultActive = to.path;
});
return {
menuList,
...toRefs(state),
};
},
});
</script>

View File

@ -12,7 +12,7 @@
v-model="ruleForm.code" clearable autocomplete="off"></el-input>
</el-col>
<el-col :span="8">
<el-button>获取验证码</el-button>
<el-button class="login-content-form-btn">获取验证码</el-button>
</el-col>
</el-row>
</el-form-item>

View File

@ -23,8 +23,8 @@
</div>
</div>
<div class="login-copyright">
<div class="mb5">版权所有深圳市xxx软件科技有限公司</div>
<div>Copyright: Shenzhen XXX Software Technology Co. Ltd 粤ICP备05010000号</div>
<div class="mb5 login-copyright-company">版权所有深圳市xxx软件科技有限公司</div>
<div class="login-copyright-msg">Copyright: Shenzhen XXX Software Technology 粤ICP备05010000号</div>
</div>
</div>
</template>
@ -32,12 +32,11 @@
<script lang="ts">
import Account from "/@/views/login/component/account.vue";
import Mobile from "/@/views/login/component/mobile.vue";
import { toRefs, reactive, getCurrentInstance } from "vue";
import { toRefs, reactive } from "vue";
export default {
name: "login",
components: { Account, Mobile },
setup() {
const { proxy } = getCurrentInstance();
const state = reactive({
tabsActiveName: "account",
isTabPaneShow: false,
@ -46,8 +45,8 @@ export default {
state.isTabPaneShow = !state.isTabPaneShow;
};
return {
...toRefs(state),
onTabsClick,
...toRefs(state),
};
},
};
@ -57,7 +56,7 @@ export default {
.login-container {
width: 100%;
height: 100%;
background: url("/@/assets/bg-login.png") no-repeat;
background: url("/src/assets/bg-login.png") no-repeat;
background-size: 100% 100%;
.login-logo {
position: absolute;
@ -73,13 +72,14 @@ export default {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transform: translate(-50%, -50%) translate3d(0, 0, 0);
background-color: rgba(255, 255, 255, 0.95);
box-shadow: 0 2px 12px 0 var(--color-primary-light-1);
border-radius: 4px;
transition: height 0.2s linear;
height: 480px;
overflow: hidden;
z-index: 1;
.login-content-main {
margin: 0 auto;
width: 80%;
@ -90,6 +90,7 @@ export default {
text-align: center;
letter-spacing: 4px;
margin: 15px 0 30px;
white-space: nowrap;
}
}
}
@ -105,6 +106,12 @@ export default {
color: white;
font-size: 12px;
opacity: 0.8;
.login-copyright-company {
white-space: nowrap;
}
.login-copyright-msg {
@extend .login-copyright-company;
}
}
}
</style>