更新Navbar

This commit is contained in:
洛洛希雅Lolosia 2023-08-30 10:36:17 +08:00
parent 6ff36a9436
commit 6741099695
7 changed files with 208 additions and 203 deletions

View File

@ -44,7 +44,7 @@ export default [
{
order_no: '@guid()',
timestamp: +Mock.Random.date('T'),
username: '@name()',
userName: '@name()',
price: '@float(1000, 15000, 0, 2)',
'status|1': ['success', 'pending']
}

View File

@ -0,0 +1,29 @@
import { Fragment } from '@vue/runtime-core'
import type { Prop, VNode } from 'vue'
import { Comment, defineComponent } from 'vue'
export default defineComponent({
name: 'NavBarAppTools',
props: {
position: {
type: String,
require: true
} as Prop<'left' | 'right' | 'center'>
},
render() {
const navbar = useBasicStore().navbar
const sorts = navbar[this.position!] as Map<string, () => VNode[]>
if (!sorts) return h(Comment, 'nav bar app tools')
let out = [] as VNode[]
for (const value of sorts.values()) {
try {
out.push(...value())
} catch (e) {
console.error(e)
}
}
if (this.$props.position === 'right') out = out.reverse()
return h(Fragment, out)
}
})

View File

@ -12,131 +12,39 @@
<!-- 面包屑导航 -->
<breadcrumb class="breadcrumb-container" />
</div>
<component :is="item" v-for="(item, i) of navbar.left" :key="i" />
<NavBarAppTools position="left" />
</div>
<div class="navbar-center">
<!--导航标题-->
<div v-if="settings.showNavbarTitle" class="heardCenterTitle">{{ settings.title }}</div>
<component :is="item" v-for="(item, i) of navbar.center" :key="i" />
<NavBarAppTools position="center" />
</div>
<div class="navbar-right">
<component :is="item" v-for="(item, i) of navbar.right" :key="i" />
<NavBarAppTools position="right" />
<!-- 下拉操作菜单 -->
<debugger />
<div v-if="settings.ShowDropDown" class="right-menu rowSC">
<!-- <ScreenFull />-->
<!-- <ScreenLock />-->
<!-- <ThemeSelect />-->
<!-- <SizeSelect />-->
<!-- <LangSelect />-->
<el-dropdown trigger="click" size="default">
<div class="avatar-wrapper" :title="(userInfo?.realName || userInfo?.userName) + ' - 在线'">
<img :src="userInfo.avatar ?? userImage" alt="用户头像" class="user-avatar" />
<div class="user-avatar-status" :style="{backgroundColor: !online ? 'red' : null}">
<el-popover :visible="!online" :effect="'dark'" :width="170">
<template #reference>
<div />
</template>
<div class="online-status">
<menu-icon icon="exclamation-triangle-fill" />
<span>与服务器连接中断</span>
</div>
</el-popover>
</div>
</div>
<template #dropdown>
<el-dropdown-menu class="drop-down">
<el-dropdown-item class="welcome-user">
{{ time }}<b>{{ userInfo.realName || userImage.userName || "平台用户" }}</b>
</el-dropdown-item>
<router-link to="/">
<el-dropdown-item divided>{{ langTitle("Home") }}</el-dropdown-item>
</router-link>
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click="loginOut">{{ langTitle("login out") }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<AvatarFrame />
</div>
</div>
</template>
<script setup lang="ts">
import type { VNode } from "vue";
import { nextTick } from "vue";
import { useRouter } from "vue-router";
import Breadcrumb from "./Breadcrumb.vue";
import Hamburger from "./Hamburger.vue";
import LangSelect from "./component/LangSelect.vue";
import ScreenFull from "./component/ScreenFull.vue";
import SizeSelect from "./component/SizeSelect.vue";
import ThemeSelect from "./component/ThemeSelect.vue";
import ScreenLock from "./component/ScreenLock.vue";
import { resetState } from "@/hooks/use-permission";
import { elMessage } from "@/hooks/use-element";
import { useBasicStore } from "@/store/basic";
import { langTitle } from "@/hooks/use-common";
import userImage from "@/assets/layout/user.png";
import Debugger from "@/layout/default/app-main/component/Debugger.vue";
import MenuIcon from "@/components/MenuIcon.vue";
import { useDebuggerStore } from "@/store/debuger";
import AvatarFrame from '@/layout/default/app-main/component/AvatarFrame.vue'
import Breadcrumb from './Breadcrumb.vue'
import Hamburger from './Hamburger.vue'
import { useBasicStore } from '@/store/basic'
import Debugger from '@/layout/default/app-main/component/Debugger.vue'
import NavBarAppTools from '@/layout/default/app-main/NavBarAppTools'
const basicStore = useBasicStore();
const { settings, sidebar, setToggleSideBar, userInfo } = basicStore;
const online = toRef(basicStore, "online");
const basicStore = useBasicStore()
const { settings, sidebar, setToggleSideBar } = basicStore
const toggleSideBar = () => {
setToggleSideBar();
};
//退
const router = useRouter();
const loginOut = () => {
elMessage("退出登录成功");
router.push(`/login?redirect=/`);
nextTick(() => {
resetState();
});
};
const time = computed(() => {
const hover = new Date().getHours();
if (hover > 20) return "晚上";
if (hover > 17) return "傍晚";
if (hover > 13) return "下午";
if (hover > 11) return "中午";
if (hover > 8) return "上午";
if (hover > 5) return "早上";
return "凌晨";
});
const navbar = reactive({
left: [] as VNode[],
center: [] as VNode[],
right: [] as VNode[]
});
watch(() => basicStore.navbar.cursor, () => {
const left = navbar.left = [] as VNode[];
for (const value of basicStore.navbar.left.values()) {
left.push(...value);
setToggleSideBar()
}
const center = navbar.center = [] as VNode[];
for (const value of basicStore.navbar.center.values()) {
center.push(...value);
}
const right = navbar.right = [] as VNode[];
for (const value of basicStore.navbar.right.values()) {
right.push(...value);
}
});
</script>
<style lang="scss" scoped>
.online-status {
display: flex;
align-items: center;
@ -210,46 +118,6 @@ watch(() => basicStore.navbar.cursor, () => {
font-size: 20px;
transform: translate(-50%, -50%);
}
//drop-down
.right-menu {
cursor: pointer;
margin-right: 16px;
background-color: var(--nav-bar-right-menu-background);
//logo
.avatar-wrapper {
margin-top: 5px;
position: relative;
cursor: pointer;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
}
.user-avatar-status {
position: absolute;
right: -2px;
bottom: 0;
height: 10px;
width: 10px;
border-radius: 50%;
background-color: #55e802;
box-shadow: 0 0 3px black;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
.drop-down {

View File

@ -0,0 +1,28 @@
<template>
<div class="avatar-wrapper" :title="(userInfo?.realName || userInfo?.userName) + ' - 在线'">
<img :src="userInfo.avatar ?? userImage" alt="用户头像" class="user-avatar" />
<div class="user-avatar-status" :style="{ backgroundColor: !online ? 'red' : null }">
<el-popover :visible="!online" :effect="'dark'" :width="170">
<template #reference>
<div />
</template>
<div class="online-status">
<menu-icon icon="exclamation-triangle-fill" />
<span>与服务器连接中断</span>
</div>
</el-popover>
</div>
</div>
</template>
<script setup lang="ts">
import userImage from '@/assets/layout/user.png'
import { useBasicStore } from '@/store/basic'
const basicStore = useBasicStore()
const { settings, sidebar, userInfo } = basicStore
const online = toRef(basicStore, 'online')
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,81 @@
<template>
<div v-if="settings.ShowDropDown" class="right-menu rowSC">
<!-- <ScreenFull />-->
<!-- <ScreenLock />-->
<!-- <ThemeSelect />-->
<!-- <SizeSelect />-->
<!-- <LangSelect />-->
<el-dropdown trigger="click" size="default">
<Avatar />
<template #dropdown>
<el-dropdown-menu class="drop-down">
<el-dropdown-item class="welcome-user">
{{ time }}
<b>{{ userInfo.realName || userInfo.userName || '平台用户' }}</b>
</el-dropdown-item>
<router-link to="/">
<el-dropdown-item divided>{{ langTitle('Home') }}</el-dropdown-item>
</router-link>
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click="loginOut">{{ langTitle('login out') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script setup lang="ts">
import Avatar from '@/layout/default/app-main/component/Avatar.vue'
import { nextTick } from 'vue'
import { useRouter } from 'vue-router'
import LangSelect from './component/LangSelect.vue'
import ScreenFull from './component/ScreenFull.vue'
import SizeSelect from './component/SizeSelect.vue'
import ThemeSelect from './component/ThemeSelect.vue'
import ScreenLock from './component/ScreenLock.vue'
import { resetState } from '@/hooks/use-permission'
import { elMessage } from '@/hooks/use-element'
import { useBasicStore } from '@/store/basic'
import { langTitle } from '@/hooks/use-common'
const basicStore = useBasicStore()
const { settings, sidebar, userInfo } = basicStore
//退
const router = useRouter()
const loginOut = () => {
elMessage('退出登录成功')
router.push(`/login?redirect=/`)
nextTick(() => {
resetState()
})
}
const time = computed(() => {
const hover = new Date().getHours()
if (hover > 20) return '晚上'
if (hover > 17) return '傍晚'
if (hover > 13) return '下午'
if (hover > 11) return '中午'
if (hover > 8) return '上午'
if (hover > 5) return '早上'
return '凌晨'
})
</script>
<style scoped lang="scss">
//drop-down
.right-menu {
cursor: pointer;
margin-right: 16px !important;
position: relative;
background-color: var(--nav-bar-right-menu-background);
.switch-platform-btn {
margin: 0 0.5em 0 1em;
padding: 0 0.5em;
font-size: 1em;
}
}
</style>

View File

@ -13,7 +13,7 @@ export const useBasicStore = defineStore('basic', {
getUserInfo: false,
online: true,
userInfo: {
username: '',
userName: '',
realName: '',
avatar: '',
phone: '',
@ -75,7 +75,7 @@ export const useBasicStore = defineStore('basic', {
state.filterAsyncRoutes = []
//reset userInfo
state.userInfo = {
username: '',
userName: '',
realName: '',
avatar: '',
phone: '',

View File

@ -7,11 +7,11 @@
<div class="title-container">
<h3 class="title text-center">{{ settings.title }}</h3>
</div>
<el-form-item prop="username" :rules="formRules.isNotNull('用户名')">
<el-form-item prop="userName" :rules="formRules.isNotNull('用户名')">
<span class="svg-container">
<ElSvgIcon name="User" :size="14" />
</span>
<el-input v-model="subForm.username" placeholder="用户名" />
<el-input v-model="subForm.userName" placeholder="用户名" />
<!--占位-->
</el-form-item>
<el-form-item prop="password" :rules="formRules.isNotNull('密码')">
@ -40,117 +40,116 @@
</template>
<script setup lang="ts">
import { reactive, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useBasicStore } from "@/store/basic";
import { elMessage, useElement } from "@/hooks/use-element";
import { getMyRole, login } from "@/api/user";
import type { FormInstance, InputInstance } from "element-plus";
import { settings as viteSettings } from "@/settings";
import { ElMessage } from "element-plus";
import { reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useBasicStore } from '@/store/basic'
import { elMessage, useElement } from '@/hooks/use-element'
import { getMyRole, login } from '@/api/user'
import type { FormInstance, InputInstance } from 'element-plus'
import { settings as viteSettings } from '@/settings'
import { ElMessage } from 'element-plus'
/* listen router change and set the query */
const { settings } = useBasicStore();
const { settings } = useBasicStore()
//element valid
const formRules = useElement().formRules;
const formRules = useElement().formRules
//form
const subForm = reactive({
username: "",
password: ""
});
userName: '',
password: ''
})
const state: any = reactive({
otherQuery: {},
redirect: undefined
});
const route = useRoute();
})
const route = useRoute()
function getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc;
}, {});
return acc
}, {})
}
watch(
() => route.query,
(query) => {
if (query) {
state.redirect = query.redirect;
state.otherQuery = getOtherQuery(query);
state.redirect = query.redirect
state.otherQuery = getOtherQuery(query)
}
},
{ immediate: true }
);
)
/*
* login relative
* */
let subLoading = $ref(false);
let subLoading = $ref(false)
//tip message
let tipMessage = $ref("");
let tipMessage = $ref('')
//sub form
const refLoginForm: FormInstance = $ref(null);
const refLoginForm: FormInstance = $ref(null)
function handleLogin() {
refLoginForm.validate((valid) => {
if (valid) {
subLoading = true;
loginFunc();
subLoading = true
loginFunc()
} else {
ElMessage.error('请检查输入是否正确')
}
else {
ElMessage.error("请检查输入是否正确")
}
});
})
}
const router = useRouter();
const basicStore = useBasicStore();
const router = useRouter()
const basicStore = useBasicStore()
async function loginFunc() {
try {
const data = await login(subForm);
elMessage("登录成功");
basicStore.setToken(data?.Authorization);
const role = await getMyRole();
const data = await login(subForm)
elMessage('登录成功')
basicStore.setToken(data?.Authorization)
const role = await getMyRole()
basicStore.setUserInfo({
userInfo: data,
roles: [role.roleType],
codes: [role.roleId]
});
})
// router.push("/").catch(e => console.error(e));
if (route.query?.redirect) {
let url = route.query.redirect as string;
if (viteSettings.viteBasePath.endsWith("/") && url.startsWith("/")) {
url = url.replace(/^\//, "");
let url = route.query.redirect as string
if (viteSettings.viteBasePath.endsWith('/') && url.startsWith('/')) {
url = url.replace(/^\//, '')
}
window.location.href = viteSettings.viteBasePath + url;
window.location.href = viteSettings.viteBasePath + url
} else {
window.location.href = viteSettings.viteBasePath;
window.location.href = viteSettings.viteBasePath
}
} catch (e: Error | any) {
tipMessage = e?.message;
tipMessage = e?.message
} finally {
subLoading = false;
subLoading = false
}
}
/*
* password show or hidden
* */
const passwordType = ref("password");
const refPassword = ref<InputInstance>();
const passwordType = ref('password')
const refPassword = ref<InputInstance>()
function showPwd() {
if (passwordType.value === "password") {
passwordType.value = "";
if (passwordType.value === 'password') {
passwordType.value = ''
} else {
passwordType.value = "password";
passwordType.value = 'password'
}
nextTick(() => {
refPassword.value!.focus();
});
refPassword.value!.focus()
})
}
</script>
<style lang="scss" scoped>