فهرست منبع

使用 gemini 优化 Background.vue, Login.vue 和 router/index.js

reghao 1 روز پیش
والد
کامیت
431830ffa3

+ 0 - 49
src/components/layout/FooterBar.vue

@@ -1,49 +0,0 @@
-<template>
-  <div id="footer-bar">
-    <img class="logo" src="@/assets/img/logo.png" alt="">
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'FooterBar'
-}
-</script>
-
-<style scoped>
-#footer-bar {
-  position: relative;
-  bottom: 0;
-  left: 0;
-  right: 0;
-
-  padding-left: 6%;
-  padding-right: 6%;
-  margin-top: 30px;
-  height: 60px;
-  border-top: 2px solid rgba(34, 36, 38, 0.15);
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15);
-  text-align: center;
-}
-
-.content {
-  padding-top: 10px;
-  font-size: 14px;
-}
-.logo {
-  width: 30px;
-  cursor: pointer;
-}
-.author,
-.statement {
-  padding-top: 5px;
-}
-
-.weixin {
-  width: 30px;
-  cursor: pointer;
-}
-.er {
-  width: 150px;
-}
-</style>

+ 0 - 38
src/components/layout/LoginBar.vue

@@ -1,38 +0,0 @@
-<template>
-  <el-row class="el-menu-demo">
-    <el-col :md="2">
-      <ul role="menubar" class="el-menu--horizontal el-menu">
-        <li role="menuitem" class="el-menu-item">
-          <a href="/" style="color: #007bff;text-decoration-line: none">
-            <img src="@/assets/img/logo.png" class="logo" alt="img">
-            devopsapp
-          </a>
-        </li>
-      </ul>
-    </el-col>
-  </el-row>
-</template>
-
-<script>
-export default {
-  name: 'LoginBar',
-  data() {
-    return {
-    }
-  },
-  created() {
-  },
-  methods: {
-  }
-}
-</script>
-
-<style scoped>
-@media screen and (max-width: 768px) {
-}
-
-.logo {
-  width: 30px;
-  position: relative;
-}
-</style>

+ 0 - 350
src/components/layout/NavBar.vue

@@ -1,350 +0,0 @@
-<template>
-  <el-row class="el-menu-demo">
-    <el-col :md="2">
-      <ul role="menubar" class="el-menu--horizontal el-menu">
-        <li role="menuitem" class="el-menu-item">
-          <a href="/vod" style="color: #007bff;text-decoration-line: none">
-            <img src="@/assets/img/logo.png" class="logo" alt="img">
-            devopsapp
-          </a>
-        </li>
-      </ul>
-    </el-col>
-    <el-col :md="8">
-      <el-menu
-        mode="horizontal"
-      >
-        <el-menu-item index="1">
-          <a href="/vod/video" style="text-decoration-line: none">
-            <span style="color: #007bff">分区</span>
-          </a>
-        </el-menu-item>
-        <el-menu-item index="2">
-          <a href="/vod/shortvideo" style="text-decoration-line: none">
-            <span style="color: #007bff">短视频</span>
-          </a>
-        </el-menu-item>
-        <el-menu-item index="3">
-          <a href="/vod/playlist" style="text-decoration-line: none">
-            <span style="color: #007bff">播放列表</span>
-          </a>
-        </el-menu-item>
-      </el-menu>
-    </el-col>
-    <el-col :md="6">
-      <ul class="el-menu--horizontal el-menu">
-        <li class="el-menu-item">
-          <el-autocomplete
-            v-model="keyword"
-            :fetch-suggestions="querySearchAsync"
-            :placeholder="searchHint"
-            clearable
-            suffix-icon="el-icon-search"
-            size="medium"
-            :debounce="1000"
-            @keyup.enter.native="onSearch"
-            @select="onSearch"
-          />
-        </li>
-      </ul>
-    </el-col>
-    <el-col :md="8">
-      <ul class="el-menu--horizontal el-menu">
-        <li class="el-menu-item">
-          <el-dropdown v-if="user">
-            <img
-              :src="user.avatarUrl"
-              class="el-avatar--circle el-avatar--medium"
-              alt=""
-            >
-            <el-dropdown-menu v-if="user" slot="dropdown" class="iconsize">
-              <el-dropdown-item
-                icon="el-icon-s-home"
-                class="size"
-                @click.native="goToUserHome"
-              >我的主页</el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-collection"
-                class="size"
-                @click.native="goToPlaylist"
-              >播放列表</el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-s-data"
-                class="size"
-                @click.native="goToHistory"
-              >历史记录
-              </el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-menu"
-                class="size"
-                @click.native="goToBackground"
-              >进入后台</el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-error"
-                class="size"
-                @click.native="goToLogout"
-              >登出</el-dropdown-item>
-            </el-dropdown-menu>
-          </el-dropdown>
-          <span
-            v-else
-            style="color: #007bff"
-            @click="login"
-          >登入</span>
-        </li>
-        <li class="el-menu-item" @click="goToTimeline">
-          <el-badge class="item" :value="statusCount" :max="99">
-            <span class="el-icon-view" style="color: #007bff">状态</span>
-          </el-badge>
-        </li>
-        <li class="el-menu-item" @click="goToMessage">
-          <el-badge class="item" :value="msgCount" :max="99">
-            <span class="el-icon-bell" style="color: #007bff">消息</span>
-          </el-badge>
-        </li>
-        <li class="el-menu-item">
-          <el-button size="mini" type="upload" icon="el-icon-upload" @click="goToPublish">投稿</el-button>
-        </li>
-      </ul>
-    </el-col>
-
-    <!--登入弹窗-->
-    <el-dialog
-      title="用户登入"
-      append-to-body
-      :visible.sync="dialogVisible"
-      width="30%"
-      center
-    >
-      <el-form ref="form" :model="userLogin">
-        <el-form-item label="帐号">
-          <el-input
-            v-model="userLogin.principal"
-            placeholder="请输入手机号或邮箱"
-            style="width: 70%; padding-right: 2px"
-            clearable
-          />
-        </el-form-item>
-        <el-form-item label="密码">
-          <el-input
-            v-model="userLogin.credential"
-            placeholder="请输入验证码"
-            style="width: 45%; padding-right: 2px"
-          />
-          <el-button :disabled="isBtn" @click="loginVerifyCode">{{ code }}</el-button>
-        </el-form-item>
-        <el-form-item label="图形验证码" label-width="90px">
-          <el-image :src="captchaCode" @click="getCaptcha" />
-          <el-input
-            v-model="userLogin.captchaCode"
-            placeholder="请输入图形验证码"
-            style="width: 45%; padding-right: 2px"
-          />
-        </el-form-item>
-        <el-form-item>
-          <el-button
-            type="primary"
-            :loading="isLoading"
-            @click.native="loginBtn"
-          >登 入</el-button>
-          <el-button type="plain" @click="register">注册/忘记密码?</el-button>
-        </el-form-item>
-      </el-form>
-    </el-dialog>
-  </el-row>
-</template>
-
-<script>
-import { userMixin } from 'assets/js/mixin'
-import { keywordSuggest } from '@/api/search'
-import { getAuthedUser } from '@/utils/auth'
-import { getUnreadCount } from '@/api/user'
-
-export default {
-  name: 'NavBar',
-  mixins: [userMixin],
-  data() {
-    return {
-      user: null,
-      restaurants: [],
-      searchHint: '想要搜点神马呢',
-      searchType: '1',
-      keyword: '',
-      statusCount: 0,
-      msgCount: 0
-    }
-  },
-  created() {
-    /* const userdata = Vue.$cookies.get('USERDATA')
-    const userId = userdata.split(':')[0]*/
-    const userInfo = getAuthedUser()
-    if (userInfo !== null) {
-      this.user = userInfo
-      getUnreadCount().then(resp => {
-        if (resp.code === 0) {
-          this.msgCount = resp.data.total
-        }
-      })
-    }
-  },
-  methods: {
-    handleSelect(key, keyPath) {
-      console.log(key, keyPath)
-    },
-    // ****************************************************************************************************************
-    // 重点:当框中的改变时触发该方法,elementui自动设置了防抖,参见debounce属性
-    // queryString 为输入框中的值。cb为返回显示列表的回调函数
-    querySearchAsync(queryString, cb) {
-      if (queryString === '') {
-        return
-      }
-
-      setTimeout(() => {
-        keywordSuggest(queryString).then(res => {
-          if (res.code === 0) {
-            this.restaurants = res.data.map((item) => {
-              return {
-                value: item.keyword,
-                rank: 1
-              }
-            })
-
-            // 如果 cb 返回一个空数组, 那么模糊搜索输入建议的下拉选项会因为 length 为 0 而消失
-            // cb([])
-            cb(this.restaurants)
-            // eslint-disable-next-line no-empty
-          } else {
-          }
-        })
-      }, 500)
-    },
-    // select 事件或 enter 键事件
-    onSearch() {
-      console.log('回车事件')
-      // 正则去空格
-      if (this.keyword.replace(/\s*/g, '')) {
-        this.toSearchPage()
-      } else {
-        this.$message({
-          showClose: true,
-          message: '不能为空!',
-          type: 'warning'
-        })
-      }
-    },
-    // 跳转搜索页面,传递搜索框的参数
-    toSearchPage() {
-      const currentPath = this.$route.path
-      if (currentPath === '/search') {
-        this.$router.push({
-          path: '/search',
-          query: {
-            searchType: 1,
-            keyword: this.keyword,
-            pn: 1
-          }
-        })
-        this.$router.go(0)
-      } else {
-        const routeUrl = this.$router.resolve({
-          path: '/search',
-          query: {
-            searchType: 1,
-            keyword: this.keyword,
-            pn: 1
-          }
-        })
-        window.open(routeUrl.href, '_blank')
-      }
-    },
-    // ****************************************************************************************************************
-    login() {
-      const path = '/login'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    register() {
-      console.log('帐号注册')
-    },
-    // ****************************************************************************************************************
-    goToUserHome() {
-      const path = '/vod/user/' + this.user.userIdStr
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToPlaylist() {
-      const path = '/bg/my/album'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToHistory() {
-      const path = '/bg/my/history'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToBackground() {
-      const path = '/bg'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToTimeline() {
-      const path = '/vod/timeline'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToMessage() {
-      const path = '/bg/account/message'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToPublish() {
-      const path = '/bg/post/video'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    }
-  }
-}
-</script>
-
-<style scoped>
-@media screen and (max-width: 768px) {
-}
-
-.logo {
-  width: 30px;
-  position: relative;
-}
-
-.size {
-  font-size: 16px;
-}
-
-.item {
-  margin-top: 10px;
-  margin-right: 10px;
-}
-</style>

+ 0 - 194
src/components/layout/NavBarNoSearch.vue

@@ -1,194 +0,0 @@
-<template>
-  <el-row class="el-menu-demo">
-    <el-col :md="2">
-      <ul role="menubar" class="el-menu--horizontal el-menu">
-        <li role="menuitem" class="el-menu-item">
-          <a href="/" style="color: #007bff;text-decoration-line: none">
-            <img src="@/assets/img/logo.png" class="logo" alt="img">
-            devopsapp
-          </a>
-        </li>
-      </ul>
-    </el-col>
-    <el-col :md="8">
-      <ul class="el-menu--horizontal el-menu">
-        <li class="el-menu-item">
-          <el-dropdown v-if="user">
-            <img
-              :src="user.avatarUrl"
-              class="el-avatar--circle el-avatar--medium"
-              alt=""
-            >
-            <el-dropdown-menu v-if="user" slot="dropdown" class="iconsize">
-              <el-dropdown-item
-                icon="el-icon-s-home"
-                class="size"
-                @click.native="goToUserHome"
-              >我的主页</el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-collection"
-                class="size"
-                @click.native="goToPlaylist"
-              >播放列表</el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-s-data"
-                class="size"
-                @click.native="goToHistory"
-              >历史记录
-              </el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-menu"
-                class="size"
-                @click.native="goToBackground"
-              >进入后台</el-dropdown-item>
-              <el-dropdown-item
-                icon="el-icon-error"
-                class="size"
-                @click.native="goToLogout"
-              >登出</el-dropdown-item>
-            </el-dropdown-menu>
-          </el-dropdown>
-          <span
-            v-else
-            style="color: #007bff"
-            @click="login"
-          >登入</span>
-        </li>
-        <li class="el-menu-item" @click="goToTimeline">
-          <el-badge class="item" :value="statusCount" :max="99">
-            <span class="el-icon-view" style="color: #007bff">状态</span>
-          </el-badge>
-        </li>
-        <li class="el-menu-item" @click="goToMessage">
-          <el-badge class="item" :value="msgCount" :max="99">
-            <span class="el-icon-bell" style="color: #007bff">消息</span>
-          </el-badge>
-        </li>
-      </ul>
-    </el-col>
-  </el-row>
-</template>
-
-<script>
-import { userMixin } from 'assets/js/mixin'
-import { getAuthedUser } from '@/utils/auth'
-import { getUnreadCount } from '@/api/user'
-
-export default {
-  name: 'NavBar',
-  mixins: [userMixin],
-  data() {
-    return {
-      user: null,
-      activeIndex: '1',
-      restaurants: [],
-      searchType: '1',
-      keyword: '',
-      statusCount: 0,
-      msgCount: 0
-    }
-  },
-  created() {
-    /* const userdata = Vue.$cookies.get('USERDATA')
-    const userId = userdata.split(':')[0]*/
-    const userInfo = getAuthedUser()
-    if (userInfo !== null) {
-      this.user = userInfo
-      getUnreadCount().then(resp => {
-        if (resp.code === 0) {
-          this.msgCount = resp.data.total
-        }
-      })
-    }
-  },
-  methods: {
-    login() {
-      const path = '/login'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    register() {
-      console.log('帐号注册')
-    },
-    // ****************************************************************************************************************
-    goToUserHome() {
-      const path = '/vod/user/' + this.user.userIdStr
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToPlaylist() {
-      const path = '/bg/my/album'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToHistory() {
-      const path = '/bg/my/history'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToBackground() {
-      const path = '/bg'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToTimeline() {
-      const path = '/vod/timeline'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToMessage() {
-      const path = '/bg/account/message'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    },
-    goToPublish() {
-      const path = '/bg/post/video'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    }
-  }
-}
-</script>
-
-<style scoped>
-@media screen and (max-width: 768px) {
-}
-
-.logo {
-  width: 30px;
-  position: relative;
-}
-
-.size {
-  font-size: 16px;
-}
-
-.item {
-  margin-top: 10px;
-  margin-right: 10px;
-}
-</style>

+ 1 - 0
src/router/background_devops.js

@@ -39,6 +39,7 @@ const Webhook = () => import('views/devops/sys/Webhook')
 export default {
   path: '/bg',
   name: 'Background',
+  title: 'bg',
   component: Background,
   meta: { needAuth: true, roles: ['devops_admin', 'devops_java', 'devops_dotnet', 'devops_npm'] },
   children: [

+ 30 - 41
src/router/index.js

@@ -1,59 +1,46 @@
-import VueRouter from 'vue-router'
 import Vue from 'vue'
-
+import VueRouter from 'vue-router'
 import BackgroundDevopsRouter from './background_devops'
 
-// 懒加载引入页面组件,es6语法
-// ********************************************************************************************************************
-const Login = () => import('views/Login')
-const Register = () => import('views/Register')
-const Forgot = () => import('views/Forgot')
-const Index = () => import('views/Index')
-const Background = () => import('views/admin/Background')
-const Dashboard = () => import('views/admin/Dashboard')
+// 懒加载引入页面组件
+const Login = () => import('@/views/Login')
+const Background = () => import('@/views/admin/Background')
+const Dashboard = () => import('@/views/admin/Dashboard')
+const NotFound = () => import('@/views/404.vue')
 
-// ********************************************************************************************************************
-// 使用安装路由插件
 Vue.use(VueRouter)
+
 export const constantRoutes = [
+  {
+    path: '/login',
+    name: 'Login',
+    component: Login,
+    meta: { title: '登录', needAuth: false }
+  },
   {
     path: '/',
-    redirect: '/bg',
-    name: 'Index',
-    component: Index,
-    meta: { needAuth: false }
+    redirect: '/bg', // 根路径直接引导至 /bg
+    meta: { needAuth: true }
   },
   {
     path: '/bg',
-    name: 'BackgroundIndex',
     component: Background,
     meta: { needAuth: true },
     children: [
       {
-        path: '',
+        path: '', // 访问 /bg 时,默认渲染此组件
+        name: 'DashboardIndex',
+        component: Dashboard,
+        // 注意:这里 title 设为“控制台”,方便 TagsView 显示
+        meta: { title: '控制台', icon: 'el-icon-odometer', needAuth: true, roles: ['admin'] }
+      },
+      {
+        path: 'dashboard', // 同时也保留 /bg/dashboard 的访问能力
         name: 'Dashboard',
         component: Dashboard,
-        meta: { needAuth: true, roles: ['admin'] }
+        meta: { title: '控制台', icon: 'el-icon-odometer', needAuth: true, roles: ['admin'] }
       }
     ]
-  },
-  {
-    path: '/login',
-    name: 'Login',
-    component: Login,
-    meta: { needAuth: false }
-  },
-  {
-    path: '/register',
-    name: 'Register',
-    component: Register,
-    meta: { needAuth: false }
-  },
-  {
-    path: '/forgot',
-    name: 'Forgot',
-    component: Forgot,
-    meta: { needAuth: false }
   }
 ]
 
@@ -62,16 +49,18 @@ export const asyncRoutes = [
   {
     path: '*',
     name: '404',
-    component: () => import('@/views/404.vue'),
-    meta: { needAuth: false }
+    component: NotFound,
+    meta: { title: '404', needAuth: false }
   }
 ]
 
-// 创建路由对象并导出
-export default new VueRouter({
+const router = new VueRouter({
   mode: 'history',
+  base: process.env.BASE_URL,
   routes: constantRoutes,
   scrollBehavior(to, from, savedPosition) {
     return { x: 0, y: 0 }
   }
 })
+
+export default router

+ 101 - 20
src/views/404.vue

@@ -1,32 +1,113 @@
 <template>
-  <el-container>
-    <el-main>
-      <nav-bar />
-      <el-row>
-        <div style="text-align: center;">
-          <h1>Not Found!</h1>
-          <el-button type="text" @click="goIndex">回到首页</el-button>
-        </div>
-      </el-row>
-    </el-main>
-  </el-container>
+  <div class="error-container">
+    <div class="error-content">
+      <div class="error-code">404</div>
+      <div class="error-tag">Oops! 页面走丢了</div>
+      <p class="error-desc">
+        抱歉,您访问的页面不存在或已被删除。请检查输入的地址是否正确,或者点击下方按钮返回系统首页。
+      </p>
+      <div class="error-actions">
+        <el-button type="primary" round icon="el-icon-back" @click="goHome">
+          返回首页
+        </el-button>
+        <el-button plain round icon="el-icon-refresh" @click="refresh">
+          重试一下
+        </el-button>
+      </div>
+    </div>
+
+    <div class="decoration">
+      <i class="el-icon-monitor"></i>
+      <i class="el-icon-connection"></i>
+      <i class="el-icon-set-up"></i>
+    </div>
+  </div>
 </template>
 
 <script>
-import NavBar from 'components/layout/LoginBar'
-
 export default {
-  name: 'NotFound',
-  components: {
-    NavBar
-  },
+  name: 'Page404',
   methods: {
-    goIndex() {
-      this.$router.push('/')
+    goHome() {
+      // 运维系统首页通常是 /bg
+      this.$router.push('/bg')
+    },
+    refresh() {
+      location.reload()
     }
   }
 }
 </script>
 
-<style>
+<style scoped>
+.error-container {
+  height: 100vh;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #f0f2f5;
+  position: relative;
+  overflow: hidden;
+}
+
+.error-content {
+  text-align: center;
+  z-index: 2;
+  max-width: 500px;
+  padding: 0 20px;
+}
+
+.error-code {
+  font-size: 120px;
+  font-weight: 900;
+  color: #334157;
+  line-height: 1;
+  margin-bottom: 20px;
+  letter-spacing: -5px;
+  /* 渐变文字效果 */
+  background: linear-gradient(to bottom, #334157, #409EFF);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.error-tag {
+  font-size: 24px;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 15px;
+}
+
+.error-desc {
+  font-size: 14px;
+  color: #909399;
+  line-height: 1.6;
+  margin-bottom: 30px;
+}
+
+.error-actions .el-button {
+  padding: 12px 25px;
+  font-weight: 500;
+  transition: transform 0.2s;
+}
+
+.error-actions .el-button:hover {
+  transform: translateY(-2px);
+}
+
+/* 后景装饰图标 */
+.decoration i {
+  position: absolute;
+  color: rgba(64, 158, 255, 0.05);
+  z-index: 1;
+}
+
+.decoration i:nth-child(1) { font-size: 200px; top: -50px; left: -50px; transform: rotate(-15deg); }
+.decoration i:nth-child(2) { font-size: 150px; bottom: -20px; right: 5%; transform: rotate(10deg); }
+.decoration i:nth-child(3) { font-size: 100px; top: 20%; right: -30px; transform: rotate(45deg); }
+
+/* 响应式适配 */
+@media screen and (max-width: 480px) {
+  .error-code { font-size: 80px; }
+  .error-tag { font-size: 20px; }
+}
 </style>

+ 0 - 112
src/views/Forgot.vue

@@ -1,112 +0,0 @@
-<template>
-  <el-container>
-    <el-main>
-      <login-bar />
-      <router-view />
-      <el-row class="movie-list">
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px" />
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px" />
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-          <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px; text-align: center">
-            <el-card class="box-card">
-              <div slot="header" class="clearfix">
-                <span>找回密码</span>
-              </div>
-              <div class="text item">
-                <el-form ref="form" :model="userForgot" label-width="100px">
-                  <el-form-item label="邮箱/手机号" label-width="100px">
-                    <el-input
-                      v-model="userForgot.principal"
-                      placeholder="请输入邮箱或手机号"
-                      style="width: 45%; padding-right: 10px"
-                    />
-                    <el-button :disabled="isBtn" @click="forgotVerifyCode">{{ code }}</el-button>
-                  </el-form-item>
-                  <el-form-item label="验证码" label-width="90px">
-                    <el-input
-                      v-model="userForgot.verifyCode"
-                      placeholder="请输入短信验证码"
-                      style="padding-right: 1px"
-                    />
-                  </el-form-item>
-                  <el-form-item label="新密码">
-                    <el-input
-                      v-model="userForgot.newCredential"
-                      type="password"
-                      placeholder="请输入新密码"
-                      style="padding-right: 1px"
-                    />
-                  </el-form-item>
-                  <el-form-item label="图形验证码" label-width="90px">
-                    <el-image :src="captchaCode" @click="getCaptcha" />
-                    <el-input
-                      v-model="userForgot.captchaCode"
-                      placeholder="请输入图形验证码"
-                      style="width: 50%; padding-right: 10px"
-                    />
-                  </el-form-item>
-                  <el-form-item>
-                    <el-button
-                      type="primary"
-                      :loading="isLoading"
-                      @click.native="forgotBtn"
-                    >找回</el-button>
-                    <el-button type="plain" @click="login">返回登入</el-button>
-                  </el-form-item>
-                </el-form>
-              </div>
-            </el-card>
-          </el-row>
-        </el-col>
-      </el-row>
-    </el-main>
-  </el-container>
-</template>
-
-<script>
-import { userMixin } from 'assets/js/mixin'
-import LoginBar from 'components/layout/LoginBar'
-
-export default {
-  name: 'Forgot',
-  components: {
-    LoginBar
-  },
-  mixins: [userMixin],
-  watch: {
-    $route() {
-      this.$router.go()
-    }
-  },
-  created() {
-    document.title = '找回密码'
-    this.fetchPubkey(3)
-  },
-  data() {
-    return {
-    }
-  },
-  methods: {
-    login() {
-      this.$router.push('/login')
-    }
-  }
-}
-</script>
-
-<style>
-/*处于手机屏幕时*/
-@media screen and (max-width: 768px){
-  .movie-list {
-    padding-top: 8px;
-    padding-left: 0.5%;
-    padding-right: 0.5%;
-  }
-}
-
-.movie-list {
-  padding-top: 15px;
-  padding-left: 6%;
-  padding-right: 6%;
-}
-</style>

+ 0 - 90
src/views/Index.vue

@@ -1,90 +0,0 @@
-<template>
-  <el-container>
-    <el-main>
-      <el-col
-        v-for="(more, index) in moreList"
-        :key="index"
-        :md="6"
-        :sm="12"
-        :xs="12"
-        style="padding: 5px;"
-      >
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <el-row>
-              <el-col :md="4">
-                <router-link target="_blank" :to="more.link">
-                  <el-avatar>
-                    <el-image :src="more.coverUrl" />
-                  </el-avatar>
-                </router-link>
-              </el-col>
-              <el-col :md="16">
-                <el-row style="padding: 5px;">
-                  <span v-html="more.title" />
-                </el-row>
-                <el-row style="padding: 5px;">
-                  <span v-html="more.desc" />
-                </el-row>
-              </el-col>
-            </el-row>
-          </div>
-        </el-card>
-      </el-col>
-    </el-main>
-  </el-container>
-</template>
-
-<script>
-export default {
-  name: 'Index',
-  data() {
-    return {
-      moreList: []
-    }
-  },
-  watch: {
-    // 地址栏 url 发生变化时重新加载本页面
-    $route() {
-      this.$router.go()
-    }
-  },
-  created() {
-    document.title = 'devopsapp'
-    this.getData()
-  },
-  methods: {
-    getData() {
-      this.moreList.push(
-        {
-          title: 'vod',
-          desc: 'VOD',
-          link: '/vod',
-          coverUrl: ''
-        },
-        {
-          title: 'blog',
-          desc: 'Blog',
-          link: '/blog',
-          coverUrl: ''
-        },
-        {
-          title: 'disk',
-          desc: 'Disk',
-          link: '/disk',
-          coverUrl: ''
-        },
-        {
-          title: 'devops',
-          desc: 'DevOps',
-          link: '/bg',
-          coverUrl: ''
-        }
-      )
-    }
-  }
-}
-</script>
-
-<style>
-</style>

+ 197 - 60
src/views/Login.vue

@@ -1,61 +1,75 @@
 <template>
-  <el-container>
-    <el-main>
-      <login-bar />
-      <router-view />
-      <el-row class="movie-list">
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px" />
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-          <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px; text-align: center">
-            <el-card class="box-card">
-              <el-tabs v-model="activeName" @tab-click="tabClick">
-                <el-tab-pane name="login1">
-                  <span slot="label">密码登入</span>
-                  <div class="text item">
-                    <el-form ref="form" :model="userLogin" label-width="80px">
-                      <el-form-item label="帐号">
-                        <el-input
-                          v-model="userLogin.principal"
-                          placeholder="请输入帐号"
-                          style="padding-right: 1px"
-                          clearable
-                        />
-                      </el-form-item>
-                      <el-form-item label="密码">
-                        <el-input
-                          v-model="userLogin.credential"
-                          type="password"
-                          placeholder="请输入密码"
-                          style="padding-right: 1px"
-                          clearable
-                        />
-                      </el-form-item>
-                      <el-form-item>
-                        <el-button type="primary" size="mini" :loading="isLoading" @click.native="loginBtn">登 入</el-button>
-                        <el-button type="plain" size="mini" @click="forgot">忘记密码</el-button>
-                      </el-form-item>
-                    </el-form>
+  <div class="login-container">
+    <div class="login-background"></div>
+
+    <el-main class="login-main">
+      <div class="login-box">
+        <el-card class="login-card" shadow="always">
+          <div class="login-header">
+            <img src="@/assets/img/logo.png" alt="logo" class="login-logo" v-if="false"> <h2 class="login-title">DevOps 管理平台</h2>
+            <p class="login-subtitle">请登录您的账号以继续</p>
+          </div>
+
+          <el-tabs v-model="activeName" @tab-click="tabClick" stretch>
+            <el-tab-pane name="login1">
+              <span slot="label"><i class="el-icon-lock"></i> 密码登录</span>
+
+              <div class="login-form-wrapper">
+                <el-form ref="form" :model="userLogin" label-position="top">
+                  <el-form-item label="账号">
+                    <el-input
+                        v-model="userLogin.principal"
+                        placeholder="请输入账号"
+                        prefix-icon="el-icon-user"
+                        clearable
+                    />
+                  </el-form-item>
+
+                  <el-form-item label="密码">
+                    <el-input
+                        v-model="userLogin.credential"
+                        type="password"
+                        placeholder="请输入密码"
+                        prefix-icon="el-icon-key"
+                        show-password
+                        clearable
+                        @keyup.enter.native="loginBtn"
+                    />
+                  </el-form-item>
+
+                  <div class="login-actions">
+                    <el-button
+                        type="primary"
+                        class="submit-btn"
+                        :loading="isLoading"
+                        @click.native="loginBtn"
+                    >
+                      立即登录
+                    </el-button>
+
+                    <div class="extra-options">
+                      <el-link type="info" :underline="false" @click="forgot">忘记密码?</el-link>
+                    </div>
                   </div>
-                </el-tab-pane>
-              </el-tabs>
-            </el-card>
-          </el-row>
-        </el-col>
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px" />
-      </el-row>
+                </el-form>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </el-card>
+
+        <div class="login-footer">
+          <p>© 2026 DevOps Automation Team. All rights reserved.</p>
+        </div>
+      </div>
     </el-main>
-  </el-container>
+  </div>
 </template>
 
 <script>
 import { userMixin } from 'assets/js/mixin'
-import LoginBar from 'components/layout/LoginBar'
 
 export default {
   name: 'Login',
-  components: {
-    LoginBar
-  },
   mixins: [userMixin],
   data() {
     return {
@@ -68,11 +82,12 @@ export default {
     }
   },
   created() {
-    document.title = '用户登'
+    document.title = '用户登'
     this.fetchPubkey(1)
   },
   methods: {
     tabClick(tab) {
+      // 保持原逻辑
     },
     forgot() {
       this.$message.info('联系管理员')
@@ -81,19 +96,141 @@ export default {
 }
 </script>
 
-<style>
-/*处于手机屏幕时*/
-@media screen and (max-width: 768px){
-  .movie-list {
-    padding-top: 8px;
-    padding-left: 0.5%;
-    padding-right: 0.5%;
+<style scoped>
+/* 容器全屏并居中 */
+.login-container {
+  height: 100vh;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: #f0f2f5;
+  position: relative;
+  overflow: hidden;
+}
+
+/* 渐变背景装饰 */
+.login-background {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(135deg, #334157 0%, #1e2a3a 100%);
+  z-index: 0;
+}
+
+/* 增加一点装饰性圆影 */
+.login-background::after {
+  content: '';
+  position: absolute;
+  top: -10%;
+  right: -10%;
+  width: 500px;
+  height: 500px;
+  background: rgba(64, 158, 255, 0.05);
+  border-radius: 50%;
+}
+
+.login-main {
+  z-index: 1;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+.login-box {
+  width: 100%;
+  max-width: 420px;
+  padding: 20px;
+}
+
+.login-card {
+  border: none;
+  border-radius: 12px;
+  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2) !important;
+}
+
+/* 头部样式 */
+.login-header {
+  text-align: center;
+  padding: 10px 0 25px;
+}
+
+.login-title {
+  margin: 0;
+  font-size: 24px;
+  color: #303133;
+  letter-spacing: 1px;
+}
+
+.login-subtitle {
+  margin-top: 8px;
+  font-size: 14px;
+  color: #909399;
+}
+
+/* 表单内部样式 */
+.login-form-wrapper {
+  padding: 10px 5px;
+}
+
+.submit-btn {
+  width: 100%;
+  padding: 12px;
+  font-size: 16px;
+  margin-top: 10px;
+  border-radius: 6px;
+  transition: transform 0.2s;
+}
+
+.submit-btn:active {
+  transform: scale(0.98);
+}
+
+.login-actions {
+  margin-top: 20px;
+}
+
+.extra-options {
+  text-align: center;
+  margin-top: 15px;
+}
+
+.login-footer {
+  text-align: center;
+  margin-top: 30px;
+  color: rgba(255, 255, 255, 0.6);
+  font-size: 12px;
+}
+
+/* 适配移动端 */
+@media screen and (max-width: 480px) {
+  .login-box {
+    max-width: 100%;
+  }
+  .login-card {
+    box-shadow: none !important;
   }
 }
 
-.movie-list {
-  padding-top: 15px;
-  padding-left: 6%;
-  padding-right: 6%;
+/* 覆盖 Element UI 默认样式使之更现代 */
+::v-deep .el-tabs__nav-wrap::after {
+  height: 1px;
+}
+
+::v-deep .el-input__inner {
+  height: 45px;
+  line-height: 45px;
+  background-color: #f7f9fc;
+  border-color: #e4e7ed;
+}
+
+::v-deep .el-input__inner:focus {
+  background-color: #fff;
+}
+
+::v-deep .el-form-item__label {
+  padding-bottom: 4px;
+  font-weight: 500;
+  color: #606266;
 }
 </style>

+ 0 - 127
src/views/Register.vue

@@ -1,127 +0,0 @@
-<template>
-  <el-container>
-    <el-main>
-      <login-bar />
-      <router-view />
-      <el-row class="movie-list">
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px" />
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px" />
-        <el-col :md="8" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-          <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px; text-align: center">
-            <el-card class="box-card">
-              <div slot="header" class="clearfix">
-                <span>注册帐号</span>
-              </div>
-              <div class="text item">
-                <el-form v-if="registryEnable" ref="form" :model="userRegistry" label-width="100px">
-                  <el-form-item label="帐号" label-width="90px">
-                    <el-input
-                      v-model="userRegistry.principal"
-                      placeholder="请输入邮箱或手机号"
-                      style="width: 45%; padding-right: 5px"
-                      clearable
-                    />
-                    <el-button :disabled="isBtn" @click="registerVerifyCode">{{ code }}</el-button>
-                  </el-form-item>
-                  <el-form-item label="验证码" label-width="90px">
-                    <el-input
-                      v-model="userRegistry.verifyCode"
-                      placeholder="请输入短信验证码"
-                      style="padding-right: 1px"
-                      clearable
-                    />
-                  </el-form-item>
-                  <el-form-item label="密码" label-width="90px">
-                    <el-input
-                      v-model="userRegistry.credential"
-                      type="password"
-                      placeholder="请输入密码"
-                      style="padding-right: 1px"
-                      clearable
-                    />
-                  </el-form-item>
-                  <el-form-item label="图形验证码" label-width="90px">
-                    <el-image :src="captchaCode" @click="getCaptcha" />
-                    <el-input
-                      v-model="userRegistry.captchaCode"
-                      placeholder="请输入图形验证码"
-                      style="width: 50%; padding-right: 10px"
-                      clearable
-                    />
-                  </el-form-item>
-                </el-form>
-                <div v-else class="text item">
-                  <span>系统当前未开放注册</span>
-                </div>
-                <el-button v-if="registryEnable" type="primary" :loading="isLoading" @click.native="registerBtn">注册</el-button>
-                <el-button type="plain" @click="login">返回登入</el-button>
-              </div>
-            </el-card>
-          </el-row>
-        </el-col>
-      </el-row>
-    </el-main>
-  </el-container>
-</template>
-
-<script>
-import { userMixin } from 'assets/js/mixin'
-import LoginBar from 'components/layout/LoginBar'
-import { getRegistryStatus } from '@/api/account'
-
-export default {
-  name: 'Register',
-  components: {
-    LoginBar
-  },
-  mixins: [userMixin],
-  data() {
-    return {
-      registryEnable: false
-    }
-  },
-  watch: {
-    $route() {
-      this.$router.go()
-    }
-  },
-  mounted() {
-    this.getData()
-  },
-  created() {
-    document.title = '注册帐号'
-  },
-  methods: {
-    getData() {
-      getRegistryStatus().then(resp => {
-        if (resp.code === 0) {
-          this.registryEnable = resp.data
-          if (this.registryEnable) {
-            this.fetchPubkey(2)
-          }
-        }
-      })
-    },
-    login() {
-      this.$router.push('/login')
-    }
-  }
-}
-</script>
-
-<style>
-/*处于手机屏幕时*/
-@media screen and (max-width: 768px){
-  .movie-list {
-    padding-top: 8px;
-    padding-left: 0.5%;
-    padding-right: 0.5%;
-  }
-}
-
-.movie-list {
-  padding-top: 15px;
-  padding-left: 6%;
-  padding-right: 6%;
-}
-</style>

+ 257 - 40
src/views/admin/Background.vue

@@ -1,75 +1,292 @@
 <template>
-  <el-container class-name="main-container">
-    <el-aside :class="asideClass">
-      <LeftAside />
+  <el-container class="admin-wrapper">
+    <el-aside :width="collapsed ? '64px' : '240px'" class="admin-aside">
+      <div class="logo-wrapper">
+        <router-link to="/bg" class="logo-link">
+          <img src="@/assets/img/logo.png" alt="Logo" class="logo-img">
+          <span v-if="!collapsed" class="logo-title">DevOps</span>
+        </router-link>
+      </div>
+      <el-menu
+          :default-active="$route.path"
+          router
+          class="admin-menu"
+          background-color="#334157"
+          text-color="#b5bcc7"
+          active-text-color="#fff"
+          :unique-opened="true"
+          :collapse="collapsed"
+      >
+        <el-submenu v-for="item in menuList" :key="item.path" :index="item.path">
+          <template slot="title">
+            <i :class="item.icon || 'el-icon-menu'" />
+            <span slot="title">{{ item.title }}</span>
+          </template>
+          <el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
+            <i :class="child.icon || 'el-icon-document'" />
+            <span slot="title">{{ child.title }}</span>
+          </el-menu-item>
+        </el-submenu>
+      </el-menu>
     </el-aside>
-    <el-container>
-      <el-header class-name="main-header">
-        <TopNav />
+
+    <el-container class="main-layout">
+      <el-header class="admin-header" height="50px">
+        <div class="header-left">
+          <i :class="['collapse-btn', collapsed ? 'el-icon-s-unfold' : 'el-icon-s-fold']" @click="toggleSidebar" />
+        </div>
+        <div class="header-right">
+          <el-dropdown trigger="click">
+            <div class="user-info">
+              <el-avatar size="small" icon="el-icon-user-solid" class="user-avatar" />
+              <span class="username">{{ user ? user.username : '管理员' }}</span>
+            </div>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item @click.native="backToHome">回到前台</el-dropdown-item>
+              <el-dropdown-item divided @click.native="goToLogout">安全登出</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </div>
       </el-header>
-      <el-main class-name="main-center">
-        <router-view />
+
+      <div class="tags-view-container">
+        <div class="tags-view-wrapper">
+          <router-link
+              v-for="tag in visitedViews"
+              :key="tag.path"
+              :to="{ path: tag.path, query: tag.query }"
+              tag="span"
+              class="tags-view-item"
+              :class="isActive(tag) ? 'active' : ''"
+              @contextmenu.prevent.native="openMenu(tag, $event)"
+          >
+            {{ tag.title }}
+            <span v-if="visitedViews.length > 1" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
+          </router-link>
+        </div>
+
+        <ul v-show="menuVisible" :style="{left: left + 'px', top: top + 'px'}" class="contextmenu">
+          <li @click="refreshSelectedTag(selectedTag)">刷新页面</li>
+          <li @click="closeSelectedTag(selectedTag)" v-if="visitedViews.length > 1">关闭当前</li>
+          <li @click="closeOthersTags">关闭其他</li>
+          <li @click="closeAllTags" v-if="visitedViews.length > 1">关闭所有</li>
+        </ul>
+      </div>
+
+      <el-main class="admin-main">
+        <transition name="fade-transform" mode="out-in">
+          <keep-alive>
+            <router-view :key="$route.path" />
+          </keep-alive>
+        </transition>
       </el-main>
     </el-container>
   </el-container>
 </template>
 
 <script>
-import TopNav from '@/views/admin/TopNav.vue'
-import LeftAside from '@/views/admin/LeftAside.vue'
+import store from '@/store'
+import { userMixin } from 'assets/js/mixin'
+import { getAuthedUser } from '@/utils/auth'
 
 export default {
   name: 'Background',
-  components: {
-    TopNav,
-    LeftAside
-  },
+  mixins: [userMixin],
   data() {
     return {
-      collapsed: false
+      collapsed: false,
+      user: null,
+      menuList: [],
+      visitedViews: [],
+      // 右键菜单相关
+      menuVisible: false,
+      top: 0,
+      left: 0,
+      selectedTag: {}
     }
   },
-  computed: { // 计算属性
-    asideClass() { // 如果collapsed属性为true就展开不样式 反之就展开样式
-      return this.collapsed ? 'main-aside-collapsed' : 'main-aside'
+  watch: {
+    $route() {
+      this.addTags()
+    },
+    // 监听菜单显示,开启全局点击关闭事件
+    menuVisible(value) {
+      if (value) {
+        document.body.addEventListener('click', this.closeMenu)
+      } else {
+        document.body.removeEventListener('click', this.closeMenu)
+      }
     }
   },
-  created() { // 钩子函数
+  created() {
+    this.initMenuList()
+    this.user = getAuthedUser()
+    this.addTags()
     this.$root.Bus.$on('HandleSideMenu', value => {
       this.collapsed = value
     })
   },
   methods: {
+    initMenuList() {
+      const routes = store.getters.addRoutes
+      routes.forEach(route => {
+        if (route.path === '/bg' && route.children) {
+          this.menuList.push(...route.children)
+        } else if (route.path.startsWith('/bg/')) {
+          this.menuList.push(route)
+        }
+      })
+    },
+    isActive(route) {
+      return route.path === this.$route.path
+    },
+    // 增加标签逻辑(含 10 个限制)
+    addTags() {
+      const { name, path, meta, query } = this.$route
+      if (!path || path.includes('login')) return
+
+      const title = (meta && meta.title) ? meta.title : path.split('/').pop()
+      const isExist = this.visitedViews.some(v => v.path === path)
+
+      if (!isExist) {
+        // 限制最多 10 个,如果满了,删除第一个(最早的)
+        if (this.visitedViews.length >= 10) {
+          this.visitedViews.shift()
+        }
+        this.visitedViews.push({ name, path, title, query })
+      }
+    },
+    // 刷新当前标签
+    refreshSelectedTag(view) {
+      this.$router.replace({ path: '/redirect' + view.path }) // 需要配置 redirect 路由,或简单使用 go(0)
+      if (!view.path) this.$router.go(0)
+    },
+    // 关闭单个标签
+    closeSelectedTag(view) {
+      const index = this.visitedViews.indexOf(view)
+      if (this.visitedViews.length <= 1) return // 限制最少 1 个
+
+      this.visitedViews.splice(index, 1)
+      if (this.isActive(view)) {
+        const lastView = this.visitedViews.slice(-1)[0]
+        this.$router.push(lastView ? lastView.path : '/bg')
+      }
+    },
+    // 右键:关闭其他
+    closeOthersTags() {
+      this.visitedViews = [this.selectedTag]
+      if (!this.isActive(this.selectedTag)) {
+        this.$router.push(this.selectedTag.path)
+      }
+    },
+    // 右键:关闭所有(保留最后一个)
+    closeAllTags() {
+      const lastTag = this.visitedViews[this.visitedViews.length - 1]
+      this.visitedViews = [lastTag]
+      this.$router.push(lastTag.path)
+    },
+    // 右键菜单控制
+    openMenu(tag, e) {
+      const menuMinWidth = 105
+      const offsetLeft = this.$el.getBoundingClientRect().left
+      const offsetWidth = this.$el.offsetWidth
+      const maxLeft = offsetWidth - menuMinWidth
+      const left = e.clientX - offsetLeft + 15
+
+      if (left > maxLeft) {
+        this.left = maxLeft
+      } else {
+        this.left = left
+      }
+      this.top = e.clientY - 10 // 稍微偏移避开鼠标
+      this.menuVisible = true
+      this.selectedTag = tag
+    },
+    closeMenu() {
+      this.menuVisible = false
+    },
+    toggleSidebar() {
+      this.collapsed = !this.collapsed
+      this.$root.Bus.$emit('HandleSideMenu', this.collapsed)
+    },
+    backToHome() {
+      // this.$router.push('/vod')
+      this.$message.info('get user info')
+    }
   }
 }
 </script>
 
 <style scoped>
-.main-container {
-  height: 100%;
+/* 基础布局样式 */
+.admin-wrapper { height: 100vh; width: 100%; overflow: hidden; }
+.admin-aside { background-color: #334157; transition: width 0.3s; display: flex; flex-direction: column; z-index: 10; }
+.logo-wrapper { height: 50px; line-height: 50px; background-color: #2b3648; display: flex; justify-content: center; align-items: center; }
+.logo-img { height: 28px; }
+.logo-title { color: #fff; font-weight: 600; font-size: 14px; margin-left: 10px; }
+.admin-menu { border: none !important; flex: 1; }
+
+.admin-header { background: #fff; border-bottom: 1px solid #e6e6e6; display: flex; justify-content: space-between; align-items: center; padding: 0 15px; }
+.collapse-btn { font-size: 20px; cursor: pointer; }
+.user-info { display: flex; align-items: center; cursor: pointer; }
+.username { font-size: 14px; color: #606266; margin-left: 8px; }
+
+/* TagsView 样式 */
+.tags-view-container {
+  height: 34px;
   width: 100%;
-  box-sizing: border-box;
+  background: #fff;
+  border-bottom: 1px solid #d8dce5;
+  box-shadow: 0 1px 3px 0 rgba(0,0,0,.12);
+  position: relative;
 }
-
-/* 不展开样式*/
-.main-aside-collapsed {
-  /* 在CSS中,通过对某一样式声明! important ,可以更改默认的CSS样式优先级规则,使该条样式属性声明具有最高优先级 */
-  width: 64px !important;
-  height: 100%;
-  background-color: #334157;
-  margin: 0px;
+.tags-view-wrapper { padding: 4px 10px; display: flex; }
+.tags-view-item {
+  display: inline-block;
+  cursor: pointer;
+  height: 25px;
+  line-height: 25px;
+  border: 1px solid #d8dce5;
+  color: #495060;
+  background: #fff;
+  padding: 0 8px;
+  font-size: 12px;
+  margin-left: 5px;
+  border-radius: 2px;
+  position: relative;
 }
-
-/* 展开样式*/
-.main-aside {
-  width: 240px !important;
-  height: 100%;
-  background-color: #334157;
-  margin: 0px;
+.tags-view-item.active { background-color: #409EFF; color: #fff; border-color: #409EFF; }
+.tags-view-item.active::before {
+  content: ''; background: #fff; display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 5px;
 }
+.el-icon-close { margin-left: 5px; border-radius: 50%; padding: 1px; transition: all .2s; }
+.el-icon-close:hover { background-color: #b4bccc; color: #fff; }
 
-.main-header, .main-center {
-  padding: 0px;
-  border-left: 2px solid #333;
+/* 右键菜单样式 */
+.contextmenu {
+  margin: 0;
+  background: #fff;
+  z-index: 3000;
+  position: absolute;
+  list-style-type: none;
+  padding: 5px 0;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 400;
+  color: #333;
+  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
+}
+.contextmenu li {
+  margin: 0;
+  padding: 7px 16px;
+  cursor: pointer;
 }
+.contextmenu li:hover { background: #eee; color: #409EFF; }
+
+.admin-main { background-color: #f0f2f5; padding: 15px; }
+
+/* 动画 */
+.fade-transform-enter-active, .fade-transform-leave-active { transition: all .3s; }
+.fade-transform-enter { opacity: 0; transform: translateX(-20px); }
+.fade-transform-leave-to { opacity: 0; transform: translateX(20px); }
 </style>

+ 0 - 91
src/views/admin/LeftAside.vue

@@ -1,91 +0,0 @@
-<template>
-  <el-menu
-    :default-active="this.$route.path"
-    router
-    class="el-menu-vertical-demo"
-    background-color="#334157"
-    text-color="#fff"
-    active-text-color="#ffd04b"
-    :unique-opened="true"
-    :collapse="collapsed"
-    :collapse-transition="collapseTransition"
-  >
-    <div class="logobox">
-      <a href="/bg" style="text-decoration-line: none">
-        <img class="logoimg" src="@/assets/img/logo.png" alt="">
-      </a>
-    </div>
-    <el-submenu v-for="item in menuList" :key="item.path" :index="item.path">
-      <template slot="title">
-        <i :class="item.icon" />
-        <span>{{ item.title }}</span>
-      </template>
-      <el-menu-item-group>
-        <el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
-          <i :class="child.icon" />
-          <span slot="title">{{ child.title }}</span>
-        </el-menu-item>
-      </el-menu-item-group>
-    </el-submenu>
-  </el-menu>
-</template>
-
-<script>
-import store from '@/store'
-
-export default {
-  name: 'LeftAside',
-  data() {
-    return {
-      collapsed: false,
-      collapseTransition: false,
-      menuList: []
-    }
-  },
-  created() {
-    for (const route of store.getters.addRoutes) {
-      if (route.path === '/bg') {
-        // devops 系统
-        for (const route1 of route.children) {
-          this.menuList.push(route1)
-        }
-      } else if (route.path.startsWith('/bg/')) {
-        // tnb 系统
-        this.menuList.push(route)
-      }
-    }
-
-    // 钩子函数
-    this.$root.Bus.$on('HandleSideMenu', value => {
-      this.collapsed = value
-    })
-  },
-  methods: {
-  }
-}
-</script>
-
-<style>
-.el-menu-vertical-demo:not(.el-menu--collapse) {
-  width: 180px;
-  min-height: 720px;
-}
-
-.el-menu-vertical-demo:not(.el-menu--collapse) {
-  border: none;
-  text-align: left;
-}
-
-.logobox {
-  height: 40px;
-  line-height: 40px;
-  color: #9d9d9d;
-  font-size: 20px;
-  text-align: center;
-  padding: 20px 0px;
-}
-
-.logoimg {
-  height: 40px;
-}
-</style>

+ 0 - 78
src/views/admin/TopNav.vue

@@ -1,78 +0,0 @@
-<template>
-  <el-menu
-    class="el-menu-demo"
-    mode="horizontal"
-    background-color="#334157"
-    text-color="#fff"
-    active-text-color="#fff"
-  >
-    <el-button
-      class="button_icon"
-      :icon="collapsed ? 'el-icon-s-fold' : 'el-icon-s-unfold'"
-      @click="doToggle()"
-    />
-    <el-submenu index="2" class="submenu">
-      <template slot="title">{{ user.username }}</template>
-      <el-menu-item index="2-1" @click="backToHome">
-        <i class="el-icon-s-home" />
-        <span slot="title">回到前台</span>
-      </el-menu-item>
-      <el-menu-item index="2-3" @click.native="goToLogout">
-        <i class="el-icon-close" />
-        <span slot="title">登出</span>
-      </el-menu-item>
-    </el-submenu>
-  </el-menu>
-</template>
-
-<script>
-import { userMixin } from 'assets/js/mixin'
-import { getAuthedUser } from '@/utils/auth'
-
-export default {
-  name: 'TopNav',
-  mixins: [userMixin],
-  data() {
-    return {
-      user: null,
-      collapsed: false,
-      imgshow: require('@/assets/img/logo.png'),
-      imgsq: require('@/assets/img/logo.png')
-    }
-  },
-  created() {
-    this.user = getAuthedUser()
-  },
-  methods: {
-    doToggle() {
-      // 主要控制 collapsed 为 true 或 false
-      this.collapsed = !this.collapsed
-      this.$root.Bus.$emit('HandleSideMenu', this.collapsed)
-    },
-    backToHome() {
-      const path = '/vod'
-      if (this.$route.path === path) {
-        this.$router.go(0)
-        return
-      }
-      this.$router.push(path)
-    }
-  }
-}
-</script>
-
-<style scoped>
-.el-menu-vertical-demo:not(.el-menu--collapse) {
-  border: none;
-}
-
-.submenu {
-  float: right;
-}
-
-.button_icon {
-  height: 60px;
-  background-color: transparent;
-  border: none;
-}
-</style>