diff options
Diffstat (limited to 'src/components/layout')
| -rw-r--r-- | src/components/layout/avatar.js | 3 | ||||
| -rw-r--r-- | src/components/layout/index.vue | 38 | ||||
| -rw-r--r-- | src/components/layout/layoutHeader.vue | 333 | ||||
| -rw-r--r-- | src/components/layout/leftMenu.vue | 160 |
4 files changed, 534 insertions, 0 deletions
diff --git a/src/components/layout/avatar.js b/src/components/layout/avatar.js new file mode 100644 index 0000000..490950c --- /dev/null +++ b/src/components/layout/avatar.js @@ -0,0 +1,3 @@ +const avatarUrl = "data:image/png;base64,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" + +export default avatarUrl
\ No newline at end of file diff --git a/src/components/layout/index.vue b/src/components/layout/index.vue new file mode 100644 index 0000000..62858ac --- /dev/null +++ b/src/components/layout/index.vue @@ -0,0 +1,38 @@ +<template> + <div id="layout"> + <layout-header></layout-header> + <div id="layout-container"> + <left-menu></left-menu> + <div class="pageContent"> + <router-view></router-view> + </div> + </div> + </div> +</template> + +<script setup> +import layoutHeader from '@/components/layout/layoutHeader.vue'; +import leftMenu from '@/components/layout/leftMenu.vue'; +</script> + +<style lang="scss" scoped> +#layout { + width: 100%; + height: 100%; + #layout-container { + height: calc(100vh - 70px); + position: relative; + .pageContent { + padding: 18px 45px 32px 107px; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 18px 0; + overflow-x: hidden; + overflow-y: auto; + position: relative; + } + } +} +</style> diff --git a/src/components/layout/layoutHeader.vue b/src/components/layout/layoutHeader.vue new file mode 100644 index 0000000..b2bf673 --- /dev/null +++ b/src/components/layout/layoutHeader.vue @@ -0,0 +1,333 @@ +<template> + <div id="layout-header"> + <!-- left --> + <div class="header-left"> + <div class="header-logo"> + <el-icon :size="45"> + <Loading /> + </el-icon> + <p> + <span>Appsketch</span> + <span>Works</span> + </p> + </div> + <el-dropdown class="workspace-dropdown" placement="bottom-start"> + <div class="workspace-dropdown-button"> + Community + <el-icon class="el-icon--right"><arrow-down /></el-icon> + </div> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item>Community</el-dropdown-item> + <el-dropdown-item> + <el-icon class="primary-color" :size="16"> + <CirclePlus /> + </el-icon> + <span>Add a Private Workspace</span> + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + <!-- search --> + <div id="header-search"> + <el-input v-model="keyword" size="large" placeholder="Search"> + <template #prefix> + <el-icon><Search /></el-icon> + </template> + </el-input> + </div> + </div> + <!-- right --> + <div class="header-right"> + <el-switch + class="theme-switch" + :width="56" + size="large" + v-model="isLight" + inline-prompt + :active-icon="Sunny" + :inactive-icon="Moon" + @change="themeChange" + > + </el-switch> + <el-dropdown + class="avatar-dropdown" + placement="bottom-end" + popper-class="avatar-popper" + > + <div class="avatar-dropdown-img"> + <img :src="avatarUrl" alt="" /> + <el-icon class="el-icon--right"><arrow-down /></el-icon> + </div> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item> + <div> + <p class="profile-text">Profile</p> + <p class="profile-info">[email protected]</p> + </div> + </el-dropdown-item> + <el-dropdown-item> + <el-icon :size="16"> + <CircleClose /> + </el-icon> + <span class="profile-text">Sign Out</span> + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + + <el-dropdown + class="headerMore-dropdown" + trigger="click" + placement="bottom-end" + popper-class="headerMore-popper" + > + <button class="headerMore-dropdown-button"> + <el-icon><Grid /></el-icon> + </button> + <template #dropdown> + <el-dropdown-menu> + <ul class="headerMore-list"> + <li class="headerMore-item active"> + <el-icon><Setting /></el-icon> + <span>PCAPs</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>About</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>company</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>FAQ</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>Docs</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>Resources</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>Contact</span> + </li> + <li class="headerMore-item"> + <el-icon><Setting /></el-icon> + <span>Pricing</span> + </li> + </ul> + </el-dropdown-menu> + </template> + </el-dropdown> + </div> + </div> +</template> + +<script setup> +import avatarUrl from './avatar.js'; +import { Sunny, Moon } from '@element-plus/icons-vue'; +import { toRefs, ref } from 'vue'; +import { useMainStore } from '@/store/index'; +import { useTheme } from '@/hooks/useTheme'; + +const props = defineProps({ + test: { + type: String, + }, +}); + +const mainStore = useMainStore(); + +const keyword = ref(''); + +const { themeSet } = useTheme(); +const isLight = ref(mainStore.theme === 'light'); +const themeChange = (val) => { + themeSet(val ? 'light' : 'dark'); +}; +</script> + +<style lang="scss" scoped> +#layout-header { + width: 100%; + height: 70px; + background-color: var(--background_secondary); + padding: 14px 32px; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); + display: flex; + align-items: center; + justify-content: space-between; + .header-left { + align-items: center; + display: flex; + .header-logo { + display: flex; + align-items: center; + gap: 15px; + .el-icon { + color: var(--primary); + } + p { + span { + font-size: 22px; + font-weight: 500; + } + span:last-of-type { + margin-left: 10px; + color: var(--primary); + } + } + } + .workspace-dropdown { + margin-left: 20px; + .workspace-dropdown-button { + outline: none; + cursor: pointer; + display: inline-flex; + justify-content: center; + align-items: center; + gap: 10px; + background: #e0eaff; + border: 1px solid var(--primary); + border-radius: 4px; + color: var(--primary); + height: 27px; + padding: 0 8px; + } + } + #header-search { + margin-left: 60px; + .el-input { + width: 320px; + } + } + } + + .header-right { + display: flex; + align-items: center; + gap: 20px; + :deep(.theme-switch) { + .el-icon { + font-size: 20px !important; + color: #fac302 !important; + } + .el-switch__core { + border: none; + background: #636363; + } + .el-icon { + margin-left: 4px; + } + &.is-checked { + .el-switch__core { + background: #e8e8e8; + } + .el-icon { + margin-left: 0px; + } + } + } + .avatar-dropdown { + .avatar-dropdown-img { + outline: none; + cursor: default; + align-items: center; + display: flex; + gap: 0 5px; + img { + width: 36px; + height: 36px; + } + } + } + .headerMore-dropdown { + .headerMore-dropdown-button { + background: transparent; + border: none; + border-radius: 4px; + box-shadow: none; + cursor: pointer; + outline: none; + color: var(--text); + font-size: 22px; + width: 28px; + height: 28px; + display: flex; + justify-content: center; + align-items: center; + &:hover { + color: var(--primary); + } + &[aria-expanded='true'] { + background: #f0f0f0; + color: var(--primary); + } + } + } + } +} +</style> +<style lang="scss"> +.avatar-popper { + .profile-text, .el-icon { + color: var(--text); + } + .profile-info{ + color: var(--text_secondary); + + } + .el-dropdown-menu__item { + padding: 10px 16px; + &:last-of-type { + border-top: 1px solid #ceced2; + } + } +} + +.headerMore-popper { + .el-dropdown-menu { + padding: 0; + .headerMore-list { + padding: 8px; + display: flex; + gap: 0 8px; + justify-content: center; + max-width: 224px; + flex-wrap: wrap; + .headerMore-item { + width: 64px; + height: 64px; + align-items: center; + border-radius: 4px; + color: var(--text); + display: flex; + flex-direction: column; + gap: 4px 0; + justify-content: center; + transition: all 0.2s ease-in; + cursor: pointer; + .el-icon { + font-size: 20px; + } + span { + font-size: 14px; + } + &:hover { + color: var(--primary); + } + &.active { + background: #f0f0f0; + color: var(--primary); + } + } + } + } +} +</style> diff --git a/src/components/layout/leftMenu.vue b/src/components/layout/leftMenu.vue new file mode 100644 index 0000000..8360919 --- /dev/null +++ b/src/components/layout/leftMenu.vue @@ -0,0 +1,160 @@ +<template> + <div id="leftMenu"> + <ul class="leftMenu-list"> + <li + class="leftMenu-item" + :class="{ + active: `${currentRoute}` == item.route, + }" + v-for="item in menuList" + :key="item.route" + @click="jummp(item.route)" + > + <div class="leftMenu-icon"> + <el-icon><Notebook /></el-icon> + </div> + <div class="leftMenu-text">{{ item.name }}</div> + </li> + </ul> + </div> +</template> + +<script setup> +import { useRouter } from 'vue-router'; +import { toRefs, ref, watch } from 'vue'; +import { useI18n } from "vue-i18n" + +const props = defineProps({ + test: { + type: String, + }, +}); + +const { t } = useI18n() + +const router = useRouter(); + +const currentRoute = ref(''); +watch( + () => router.currentRoute.value, + (value) => { + currentRoute.value = value.path; + }, + { immediate: true } +); + +const jummp = (path) => { + router.push({ + path: path, + }); +}; + +const menuList = ref([ + { + name: 'Workbooks', + route: '/workbooks', + }, + { + name: 'Applications', + route: '/applications', + }, + { + name: 'Signatures', + route: '/signatures', + }, + { + name: 'Pcaps', + route: '/pcaps', + }, + { + name: 'Packages', + route: '/packages', + }, + { + name: 'Jobs', + route: '/jobs', + }, + { + name: 'Playbooks', + route: '/playbooks', + }, + { + name: 'Runners', + route: '/runners', + }, +]); +</script> + +<style lang="scss" scoped> +#leftMenu { + z-index: 10; + position: absolute; + left: 0; + top: 0; + width: 64px; + height: calc(100vh - 70px); + padding: 24px 0; + transition: width 0.2s ease-in; + background-color: var(--background_secondary); + &:hover { + width: 240px; + .leftMenu-list .leftMenu-item .leftMenu-text { + display: block; + width: 100%; + } + } + .leftMenu-list { + display: flex; + flex-direction: column; + gap: 12px 0; + width: 100%; + .leftMenu-item { + width: 100%; + height: 56px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + &:hover { + .leftMenu-icon { + color: var(--primary); + } + .leftMenu-text { + color: var(--primary); + } + } + &.active { + background-color: var(--primary_inactive); + .leftMenu-icon { + color: var(--primary); + } + .leftMenu-text { + color: var(--primary); + } + } + .leftMenu-icon { + flex-shrink: 0; + width: 64px; + height: 56px; + display: flex; + justify-content: center; + align-items: center; + font-size: 32px; + color: var(--text_secondary); + } + .leftMenu-text { + display: none; + width: 0; + transition: width 0.2s ease-in; + padding: 0 5px; + font-size: 18px; + font-weight: 600; + max-height: 56px; + overflow: hidden; + white-space: nowrap; + color: var(--text_secondary); + } + } + } +} +</style> |
