init
This commit is contained in:
6
.env.development
Normal file
6
.env.development
Normal file
@@ -0,0 +1,6 @@
|
||||
# 开发环境基地址
|
||||
|
||||
VITE_BASE_URL='/api'
|
||||
# VITE_BASE_URL='http://192.168.101.7:8000'
|
||||
|
||||
VITE_BASE_WSURL='ws://web-tunnel.feashow.com/api'
|
||||
7
.env.production
Normal file
7
.env.production
Normal file
@@ -0,0 +1,7 @@
|
||||
# 生产环境基地址
|
||||
|
||||
VITE_TITLE='fateverse'
|
||||
|
||||
VITE_BASE_URL='/api'
|
||||
|
||||
VITE_BASE_WSURL='ws://web-tunnel.feashow.com/api'
|
||||
33
.gitignore
vendored
33
.gitignore
vendored
@@ -1,11 +1,28 @@
|
||||
# ---> Vue
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
docs/_book
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
13
index.html
Normal file
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tunnel Cloud</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
9202
package-lock.json
generated
Normal file
9202
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
package.json
Normal file
31
package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "tunnel-cloud-web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.4.0",
|
||||
"echarts": "^5.4.2",
|
||||
"element-plus": "^2.3.5",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.35",
|
||||
"sass": "^1.62.1",
|
||||
"scss": "^0.2.4",
|
||||
"js-cookie": "^3.0.5",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"vite-plugin-inspect": "^0.7.26",
|
||||
"vue": "^3.2.47",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.1",
|
||||
"unplugin-auto-import": "^0.15.3",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.3.4"
|
||||
}
|
||||
}
|
||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
7
src/App.vue
Normal file
7
src/App.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
227
src/assets/styles/index.scss
Normal file
227
src/assets/styles/index.scss
Normal file
@@ -0,0 +1,227 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
html, body, #app, .el-container, .el-aside, .el-main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 106px;
|
||||
color: #EDC49A;
|
||||
font-weight: bold;
|
||||
font-size: 23px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
line-height: 65px;
|
||||
padding: 0 15px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #79bbff;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.el-main {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-aside {
|
||||
box-shadow: 4px 0 2px 1px rgb(171, 167, 167);
|
||||
transition: width 0.3s;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
background-color: #211F31;
|
||||
}
|
||||
|
||||
.el-popover.el-popper {
|
||||
padding: 10px !important;
|
||||
min-width: 60px !important;
|
||||
ul{
|
||||
li{
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
height: 65px;
|
||||
border-bottom: 1px solid #999999;
|
||||
padding: 0 15px 0 0;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.right-bar {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
border-radius: 12px !important;
|
||||
border: none;
|
||||
|
||||
.el-dialog__header {
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
// background-color: #262626;
|
||||
margin: 0;
|
||||
// .el-dialog__title{
|
||||
// color: white;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.table-header-btn {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.query-form {
|
||||
.el-form-item__label {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.query-btn {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-button {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.table {
|
||||
thead .el-table-column--selection .cell {
|
||||
display: none;
|
||||
}
|
||||
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dialog-form {
|
||||
.el-form-item {
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-form-item__content {
|
||||
.el-select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-dialog {
|
||||
:deep .el-dialog__header {
|
||||
padding: 10px 20px;
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
top: 15px;
|
||||
|
||||
.i {
|
||||
font-size: large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep .el-dialog__footer {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.border {
|
||||
:deep .el-dialog__header {
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
:deep .el-dialog__footer {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
.layout-left {
|
||||
width: 30%;
|
||||
border: 1px solid #ebeef5;
|
||||
padding: 10px;
|
||||
|
||||
|
||||
.dict-tree {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
//:deep .el-button{
|
||||
// //border: 1px solid;
|
||||
// background-color: #fff;
|
||||
//}
|
||||
.left-type {
|
||||
margin-right: 20px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-right {
|
||||
width: 70%;
|
||||
border: 1px solid #ebeef5;
|
||||
margin-left: 10px;
|
||||
padding: 10px
|
||||
}
|
||||
}
|
||||
0
src/assets/styles/sidebar.scss
Normal file
0
src/assets/styles/sidebar.scss
Normal file
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
40
src/components/HelloWorld.vue
Normal file
40
src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
msg: String,
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
|
||||
in your IDE for a better DX
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
44
src/layout/appmain/AppMain.vue
Normal file
44
src/layout/appmain/AppMain.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="app-main-container">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="fade-transform" type="transition" appear mode="out-in">
|
||||
<div>
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.fullPath"
|
||||
></component>
|
||||
</div>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.app-main-container {
|
||||
padding: 15px;
|
||||
max-height: calc(100vh - 96px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
/* 可能为enter失效,拆分为 enter-from和enter-to */
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-enter-to {
|
||||
opacity: 1;
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
</style>
|
||||
42
src/layout/index.vue
Normal file
42
src/layout/index.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-aside
|
||||
:width="!siderbarStore.getSiderBarStatus() ? '200px' : 'fit-content'"
|
||||
:class="!siderbarStore.getSiderBarStatus() ? 'expand' : ''"
|
||||
>
|
||||
<SiderBar></SiderBar>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<NavBar></NavBar>
|
||||
<TagsView></TagsView>
|
||||
<AppMain></AppMain>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SiderBar from './siderbar/index.vue'
|
||||
import NavBar from './navbar/index.vue'
|
||||
import TagsView from './tagsview/index.vue'
|
||||
import AppMain from './appmain/AppMain.vue';
|
||||
import {useSiderBar} from '../store/siderbar';
|
||||
|
||||
const siderbarStore = useSiderBar()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.expand {
|
||||
animation: Expand 0.3s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes Expand {
|
||||
from {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
to {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
47
src/layout/navbar/Breadcrumb.vue
Normal file
47
src/layout/navbar/Breadcrumb.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<el-breadcrumb separator=">">
|
||||
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path">
|
||||
<span v-if="item.meta.noRedirect || index === breadcrumbList.length-1" class="no-redirect">
|
||||
{{ item.meta.title }}
|
||||
</span>
|
||||
<router-link v-else :to="item.redirect || item.path">
|
||||
{{ item.meta.title }}
|
||||
</router-link>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
watch(route, () => {
|
||||
getBreadcrumb()
|
||||
})
|
||||
|
||||
//面包屑导航数据
|
||||
const breadcrumbList = ref([])
|
||||
|
||||
//获取面包屑导航数据
|
||||
const getBreadcrumb = () => {
|
||||
let matched = route.matched.filter(item => item.meta && item.meta.title)
|
||||
const first = matched[0]
|
||||
if (!isDashboard(first)) {
|
||||
matched = [{path: '/home', meta: {title: '首页'}}].concat(matched)
|
||||
}
|
||||
breadcrumbList.value.length = 0;
|
||||
const reBreadcrumbList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
breadcrumbList.value.push(...reBreadcrumbList)
|
||||
}
|
||||
|
||||
const isDashboard = (meta) => {
|
||||
const name = meta && meta.name
|
||||
if (!name) {
|
||||
return
|
||||
}
|
||||
return name.trim().toLocaleLowerCase() === 'Home'.toLocaleLowerCase()
|
||||
}
|
||||
|
||||
getBreadcrumb()
|
||||
|
||||
</script>
|
||||
14
src/layout/navbar/Hamburger.vue
Normal file
14
src/layout/navbar/Hamburger.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="toggle" @click="toggleClick">
|
||||
<component :is="siderbarStore.getSiderBarStatus() ? 'Fold' : 'Expand'" class="icon"></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useSiderBar} from '@/store/siderbar.js'
|
||||
|
||||
const siderbarStore = useSiderBar()
|
||||
const toggleClick = () => {
|
||||
siderbarStore.setSiderBarStatus()
|
||||
}
|
||||
</script>
|
||||
37
src/layout/navbar/index.vue
Normal file
37
src/layout/navbar/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<Hamburger></Hamburger>
|
||||
<Breadcrumb></Breadcrumb>
|
||||
<div class="right-bar">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
:width="80"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<img src="@/assets/vue.svg" alt="">
|
||||
</template>
|
||||
<template #default>
|
||||
<ul>
|
||||
<li @click="handleToAuth">个人中心</li>
|
||||
<li @click="handleLogout">退出登录</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Breadcrumb from './Breadcrumb.vue';
|
||||
import Hamburger from './Hamburger.vue';
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const handleToAuth = () => {
|
||||
router.push('/auth')
|
||||
}
|
||||
const handleLogout = () => {
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
25
src/layout/siderbar/index.vue
Normal file
25
src/layout/siderbar/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="logo" ref="logo">
|
||||
<span v-if="!siderbarStore.getSiderBarStatus()">TUNNEL-CLOUD</span>
|
||||
</div>
|
||||
<el-menu
|
||||
router
|
||||
:default-active="activeMenu"
|
||||
:unique-opened="true"
|
||||
:collapse="siderbarStore.getSiderBarStatus()"
|
||||
active-text-color="#EDC49A"
|
||||
background-color='#211F31'
|
||||
text-color="#fff"
|
||||
>
|
||||
<el-menu-item index="/">
|
||||
<el-icon><User /></el-icon>
|
||||
<template #title>首页</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useSiderBar} from '@/store/siderbar.js'
|
||||
const siderbarStore = useSiderBar()
|
||||
</script>
|
||||
|
||||
92
src/layout/tagsview/index.vue
Normal file
92
src/layout/tagsview/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="link-box">
|
||||
<el-scrollbar noresize>
|
||||
<div>
|
||||
<router-link
|
||||
v-for="item in tagsViewStore.visitedViews"
|
||||
:key="item.path" :to="{ path: item.path }" class="tag"
|
||||
:class="isActive(item) ? 'active' : ''"
|
||||
@click.prevent
|
||||
@contextmenu.prevent.native="openMenu(item, $event)">
|
||||
<span>{{ item.meta.title }}</span>
|
||||
<component is="CircleClose" class="close" @click.prevent="closeTagView(item.path)"></component>
|
||||
</router-link>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTagsView } from '@/store/tagsview.js'
|
||||
|
||||
const route = useRoute()
|
||||
const tagsViewStore = useTagsView()
|
||||
const visible = ref(false)
|
||||
const left = ref()
|
||||
const top = ref()
|
||||
|
||||
watch(route, () => {
|
||||
init()
|
||||
})
|
||||
const init = () => {
|
||||
tagsViewStore.addVisitedViews(route)
|
||||
}
|
||||
const closeTagView = (path) => {
|
||||
tagsViewStore.delVisitedViews(path)
|
||||
}
|
||||
const isActive = (tag) => {
|
||||
return tag.path === route.path
|
||||
}
|
||||
const openMenu = (tag, e) => {
|
||||
left.value = e.x + 10
|
||||
top.value = e.y + 10
|
||||
visible.value = true
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.close {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.link-box {
|
||||
padding: 0 15px;
|
||||
border-bottom: 1px solid #222;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 3px 6px;
|
||||
border: 1px solid #666;
|
||||
font-size: 14px;
|
||||
margin-right: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #211F31;
|
||||
color: #EDC49A;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.contextmenu {
|
||||
position: absolute;
|
||||
z-index: 3000;
|
||||
background: #fff;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #eee;
|
||||
color: #EDC49A;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
24
src/main.js
Normal file
24
src/main.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import ElementPlus from 'element-plus'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
//导入图标组件
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
import '@/assets/styles/index.scss'
|
||||
import '@/assets/styles/sidebar.scss'
|
||||
|
||||
const app = createApp(App)
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
app.use(ElementPlus,{locale: zhCn})
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
78
src/router/index.js
Normal file
78
src/router/index.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import NProgress from 'nprogress'//进度条
|
||||
import 'nprogress/nprogress.css'
|
||||
import Layout from '@/layout/index.vue'
|
||||
// import { getToken } from '../utils/auth'
|
||||
// import { usePermissionStore } from '../store/permisstion.js'
|
||||
// import { useAuthStore } from '../store/userstore.js'
|
||||
NProgress.configure({ showSpinner: false })
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: ()=>import('@/views/login/index.vue'),
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '登录'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'layout',
|
||||
component: Layout,
|
||||
redirect: '/home',
|
||||
meta: {
|
||||
hidden: false
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
meta: {
|
||||
title: '首页',
|
||||
breadcrumb: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
});
|
||||
// router.beforeEach(async (to,form,next)=>{
|
||||
// const permissionStore = usePermissionStore()
|
||||
// const authStore = useAuthStore()
|
||||
// NProgress.start()
|
||||
// if(!getToken()) {
|
||||
// if(to.path === '/login') {
|
||||
// next()
|
||||
// NProgress.done()
|
||||
// }else {
|
||||
// next({path: '/login'})
|
||||
// }
|
||||
// }else {
|
||||
// if(to.path === '/login') {
|
||||
// next('/')
|
||||
// NProgress.done()
|
||||
// }else {
|
||||
// permissionStore.setIsLoadRoutes(true)
|
||||
// if(permissionStore.isLoadRoutes && permissionStore.asyncRouters.length==0){
|
||||
// // await authStore.setUserInfo()
|
||||
// next({...to, replace: true})
|
||||
// } else {
|
||||
// next()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// })
|
||||
router.afterEach(()=>{
|
||||
NProgress.done()
|
||||
})
|
||||
|
||||
export default router;
|
||||
|
||||
19
src/store/siderbar.js
Normal file
19
src/store/siderbar.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { defineStore } from "pinia";
|
||||
export const useSiderBar = defineStore('siderbar',()=>{
|
||||
//定义控制侧边栏是否展开变量默认展开
|
||||
const isCollapse = ref(false)
|
||||
//获取侧边栏状态
|
||||
const getSiderBarStatus = () => {
|
||||
return isCollapse.value
|
||||
}
|
||||
//设置侧边栏状态
|
||||
const setSiderBarStatus = () => {
|
||||
const status = getSiderBarStatus()
|
||||
return isCollapse.value = !status
|
||||
}
|
||||
|
||||
return {
|
||||
getSiderBarStatus,
|
||||
setSiderBarStatus,
|
||||
}
|
||||
})
|
||||
56
src/store/tagsview.js
Normal file
56
src/store/tagsview.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
export const useTagsView = defineStore('tagsView',()=>{
|
||||
const router = useRouter()
|
||||
//已显示的标签页list
|
||||
const visitedViews = ref([])
|
||||
|
||||
//添加标签页面
|
||||
const addVisitedViews = ({path,meta}) => {
|
||||
if(visitedViews.value.length == 0) {
|
||||
visitedViews.value.push({path,meta})
|
||||
}else {
|
||||
const paths = visitedViews.value.map(item => item.path)
|
||||
if(paths.includes(path) == false) {
|
||||
visitedViews.value.push({path,meta})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//删除标签页
|
||||
const delVisitedViews = (path) => {
|
||||
console.log('进入删除');
|
||||
if(visitedViews.value.length - 1 == 0) {
|
||||
return
|
||||
}
|
||||
visitedViews.value.forEach((item,index)=>{
|
||||
if(item.path == path) {
|
||||
visitedViews.value.splice(index,1)
|
||||
}
|
||||
})
|
||||
toLastTagView(visitedViews)
|
||||
}
|
||||
|
||||
//删除其他标签页
|
||||
const delOtherVisitedViews = ({path,meta}) => {
|
||||
visitedViews.value = []
|
||||
visitedViews.value.push({path,meta})
|
||||
toLastTagView(visitedViews)
|
||||
}
|
||||
|
||||
//路由到末尾标签页
|
||||
const toLastTagView = (view) => {
|
||||
console.log(view,'进入跳转末页');
|
||||
const lastTagView = view.value.slice(-1)[0]
|
||||
router.push(lastTagView.path)
|
||||
}
|
||||
|
||||
return {
|
||||
visitedViews,
|
||||
addVisitedViews,
|
||||
delVisitedViews,
|
||||
delOtherVisitedViews,
|
||||
}
|
||||
})
|
||||
14
src/views/home/index.vue
Normal file
14
src/views/home/index.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
home
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import { useCacheStore } from '@/store/cache.js'
|
||||
// const cacheStore = useCacheStore()
|
||||
// cacheStore.setCacheKey(['normal_disable','show_hide'])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
92
src/views/login/index.vue
Normal file
92
src/views/login/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="login-box">
|
||||
<el-form
|
||||
:model="loginForm"
|
||||
ref="formInstance"
|
||||
:rules="rules"
|
||||
label-width="65px"
|
||||
>
|
||||
<h3>Rib-Account-ADMIN</h3>
|
||||
<el-form-item prop="username" label="账号">
|
||||
<el-input v-model="loginForm.username" :prefix-icon="User"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password" label="密码">
|
||||
<el-input v-model="loginForm.password" type="password" :prefix-icon="Lock"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleLogin(formInstance)" type="primary">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onBeforeUnmount, reactive,} from 'vue'
|
||||
// import {useAuthStore} from '@/store/userstore'
|
||||
import {ElLoading} from 'element-plus'
|
||||
import {User, Lock, Key} from '@element-plus/icons-vue'
|
||||
|
||||
const router = useRouter()
|
||||
// const authStore = useAuthStore()
|
||||
const loginForm = reactive({
|
||||
username: 'admin',
|
||||
password: '123456',
|
||||
uuid: ''
|
||||
})
|
||||
const formInstance = ref()
|
||||
const rules = reactive({
|
||||
username: [
|
||||
{required: true, message: '请输入账户名', trigger: 'blur'},
|
||||
],
|
||||
password: [
|
||||
{required: true, message: '请输入密码', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
const handleLogin = (instance) => {
|
||||
if (!instance) return
|
||||
instance.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
// 发送请求
|
||||
// await authStore.userLogin(loginForm).then(res=>{
|
||||
// if(res) {
|
||||
ElLoading.service({text: '正在加载系统资源', background: '#409eff', lock: true})
|
||||
await router.push('/')
|
||||
// }
|
||||
// })
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
ElLoading.service({text: '正在加载系统资源', background: '#409eff', lock: true}).close()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-box {
|
||||
height: 100%;
|
||||
background-color: #4158D0;
|
||||
background-image: linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.el-form {
|
||||
padding: 12px 15px;
|
||||
border-radius: 12px;
|
||||
width: 25%;
|
||||
background-color: #fff;
|
||||
|
||||
h3 {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
102
vite.config.js
Normal file
102
vite.config.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
import { defineConfig } from 'vite'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
import Icons from 'unplugin-icons/vite'
|
||||
import IconsResolver from 'unplugin-icons/resolver'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import Inspect from 'vite-plugin-inspect'
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
//自动导入vue相关函数
|
||||
imports: ['vue','vue-router'],
|
||||
|
||||
resolvers: [
|
||||
ElementPlusResolver(),
|
||||
//自动导入图标组件
|
||||
IconsResolver({
|
||||
prefix: 'Icon',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [
|
||||
// 自动注册图标组件
|
||||
IconsResolver({
|
||||
enabledCollections: ['ep'],
|
||||
}),
|
||||
//自动导入组件
|
||||
ElementPlusResolver()
|
||||
],
|
||||
}),
|
||||
Icons({
|
||||
autoInstall: true,
|
||||
}),
|
||||
Inspect(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
build: {
|
||||
minify: 'esbuild',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: false, // 生产环境移除log
|
||||
drop_debugger: false // 生产环境禁用debugger
|
||||
}
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 8888,
|
||||
strictPort: false,
|
||||
open: true,
|
||||
proxy: {
|
||||
// '/api/custom': {
|
||||
// // target: 'http://web-tunnel.feashow.com/api',
|
||||
// target: 'http://192.168.31.175:8000',
|
||||
// changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
// '/api/admin': {
|
||||
// target: 'http://web-tunnel.feashow.com/api',
|
||||
// // target: 'http://192.168.31.175:8000',
|
||||
// changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
// '/api/auth': {
|
||||
// target: 'http://web-tunnel.feashow.com/api',
|
||||
// // target: 'http://192.168.31.175:8000',
|
||||
// changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
// '/api/log': {
|
||||
// // target: 'http://web-tunnel.feashow.com/api',
|
||||
// target: 'http://192.168.31.175:8000',
|
||||
// changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
// '/api/code-gen': {
|
||||
// // target: 'http://web-tunnel.feashow.com/api',
|
||||
// target: 'http://192.168.31.175:8000',
|
||||
// changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
'/api': {
|
||||
target: 'http://web-tunnel.feashow.com/api',
|
||||
// target: 'http://192.168.31.175:8000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
},
|
||||
'/socket': {
|
||||
target: 'ws://web-tunnel.feashow.com/api/notice-ws/notice',
|
||||
ws: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user