generated from Leo_Ding/web-template
293 lines
8.7 KiB
Vue
293 lines
8.7 KiB
Vue
<template>
|
||
<a-layout-header class="basic-header" :class="cpClassNames" :style="cpStyles">
|
||
<!-- 左侧 -->
|
||
<div v-if="cpShowLeftSlot" class="basic-header__left">
|
||
<slot name="left"></slot>
|
||
</div>
|
||
<!-- 中间 -->
|
||
<div v-if="cpShowDefaultSlot" class="basic-header__center">
|
||
<slot></slot>
|
||
</div>
|
||
<!-- 右侧 -->
|
||
<div class="basic-header__right">
|
||
<a-space :size="16">
|
||
<action-button @click="handleConfig">
|
||
<setting-outlined></setting-outlined>
|
||
</action-button>
|
||
<a-dropdown :trigger="['hover']">
|
||
<action-button :style="{ height: '44px' }">
|
||
<translation-outlined />
|
||
</action-button>
|
||
<a-spin />
|
||
<template #overlay>
|
||
<a-menu v-model:selectedKeys="current">
|
||
<a-menu-item v-for="(item, key) in langData" :key="key" @click="handleLang(key)">
|
||
{{ item.icon }} {{ item.label }}
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</template>
|
||
</a-dropdown>
|
||
|
||
<a-dropdown :trigger="['click']">
|
||
<action-button :style="{ height: '44px' }">
|
||
<a-avatar class="mr-8-1 display-inline-flex justify-content-center" :size="24"
|
||
:src="userInfo?.avatar">
|
||
</a-avatar>
|
||
<span>{{ userInfo?.name }}</span>
|
||
</action-button>
|
||
<a-spin />
|
||
<template #overlay>
|
||
<a-menu>
|
||
<a-menu-item key="edit" @click="handleOpen">
|
||
<edit-outlined />
|
||
修改密码
|
||
</a-menu-item>
|
||
<a-menu-item key="logout" @click="handleLogout">
|
||
<login-outlined></login-outlined>
|
||
{{ $t('component.RightContent.logout') }}
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</template>
|
||
</a-dropdown>
|
||
</a-space>
|
||
</div>
|
||
</a-layout-header>
|
||
<a-modal v-model:open="open" title="修改密码" @ok="handleOk">
|
||
<a-form ref="formRef" :model="pwdFormData" :rules="pwdFormRules">
|
||
<a-card class="mb-8-2">
|
||
<a-row :gutter="12">
|
||
<a-col :span="24">
|
||
<a-form-item :label="'原密码'" name="oldPwd">
|
||
<a-input-password :placeholder="'请输入原密码'"
|
||
v-model:value="pwdFormData.oldPwd"></a-input-password>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="24">
|
||
<a-form-item :label="'新密码'" name="newPwd">
|
||
<a-input-password :placeholder="'请输入新密码'"
|
||
v-model:value="pwdFormData.newPwd"></a-input-password>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="24">
|
||
<a-form-item :label="'确认密码'" name="confirmPwd">
|
||
<a-input-password :placeholder="'确认密码'"
|
||
v-model:value="pwdFormData.confirmPwd"></a-input-password>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
</a-form>
|
||
</a-modal>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { message, Modal } from 'ant-design-vue'
|
||
import { storeToRefs } from 'pinia'
|
||
import { computed, useSlots, ref } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { LoginOutlined, SettingOutlined, EditOutlined, TranslationOutlined } from '@ant-design/icons-vue'
|
||
import { useAppStore, useUserStore } from '@/store'
|
||
import ActionButton from './ActionButton.vue'
|
||
import { theme as antTheme } from 'ant-design-vue'
|
||
import { config as conf } from '@/config'
|
||
import { useI18n } from 'vue-i18n'
|
||
import storage from '@/utils/storage'
|
||
import apis from '@/apis'
|
||
|
||
import { useForm, useModal, useSpining } from '@/hooks'
|
||
const { modal, showModal, hideModal, showLoading, hideLoading } = useModal()
|
||
const { locale, t } = useI18n()
|
||
defineOptions({
|
||
name: 'BasicHeader',
|
||
})
|
||
|
||
/**
|
||
* @property {string} theme 主题【light=亮色,dark=暗色】
|
||
*/
|
||
const props = defineProps({
|
||
theme: {
|
||
type: String,
|
||
},
|
||
})
|
||
const emit = defineEmits(['config'])
|
||
|
||
const slots = useSlots(['default', 'left', 'right'])
|
||
const formRef = ref(null)
|
||
const userStore = useUserStore()
|
||
const appStore = useAppStore()
|
||
|
||
const router = useRouter()
|
||
const { config } = storeToRefs(appStore)
|
||
const { userInfo } = storeToRefs(userStore)
|
||
const { token } = antTheme.useToken()
|
||
const pwdFormData = ref({})
|
||
const pwdFormRules = {
|
||
oldPwd: [{ required: true, message: '请输原密码' }],
|
||
newPwd: [{ required: true, message: '请输入新密码' }],
|
||
confirmPwd: [{ required: true, message: '请确认新密码' }],
|
||
}
|
||
const open = ref(false)
|
||
const cpClassNames = computed(() => ({
|
||
[`basic-header--${props.theme}`]: true,
|
||
}))
|
||
const cpStyles = computed(() => {
|
||
const styles = {
|
||
zIndex: config.value.layout === 'topBottom' ? 120 : 110,
|
||
}
|
||
|
||
if (config.value.headerTheme === 'light') {
|
||
styles.boxShadow = `0 0 0 1px ${token.value.colorSplit}`
|
||
}
|
||
|
||
return styles
|
||
})
|
||
const cpShowLeftSlot = computed(() => !!slots.left)
|
||
const cpShowDefaultSlot = computed(() => !!slots.default)
|
||
const defaultLang = storage.local.getItem(conf('storage.lang')) || 'zh-ch'
|
||
const current = ref(defaultLang)
|
||
const langData = ref({
|
||
'zh-ch': {
|
||
lang: 'zh-ch',
|
||
label: '简体中文',
|
||
icon: '🇨🇳',
|
||
title: '语言',
|
||
},
|
||
'en-us': {
|
||
lang: 'en-us',
|
||
label: 'English',
|
||
icon: '🇺🇸',
|
||
title: 'Language',
|
||
},
|
||
})
|
||
|
||
/**
|
||
* 退出登录
|
||
*/
|
||
function handleLogout() {
|
||
Modal.confirm({
|
||
title: t('component.RightContent.logout'),
|
||
okText: t('button.confirm'),
|
||
cancelText: t('button.cancel'),
|
||
onOk: () => {
|
||
userStore.logout().then(() => {
|
||
router.push({
|
||
name: 'login',
|
||
})
|
||
})
|
||
},
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 修改资料
|
||
*/
|
||
|
||
function handleOpen() {
|
||
open.value = true
|
||
}
|
||
|
||
/**
|
||
* 切换语言
|
||
*/
|
||
|
||
function handleLang(lang) {
|
||
storage.local.setItem(conf('storage.lang'), lang)
|
||
locale.value = lang
|
||
current.value = lang
|
||
location.reload()
|
||
}
|
||
/**
|
||
* 配置
|
||
*/
|
||
function handleConfig() {
|
||
emit('config')
|
||
}
|
||
function handleOk() {
|
||
formRef.value.validateFields().then(async (values) => {
|
||
console.log(pwdFormData.value)
|
||
if (pwdFormData.value.newPwd !== pwdFormData.value.confirmPwd) {
|
||
return message.error('两次密码不一致')
|
||
}
|
||
try {
|
||
console.log(pwdFormData.value)
|
||
showLoading()
|
||
const params = {
|
||
new_password: pwdFormData.value.newPwd,
|
||
old_password: pwdFormData.value.oldPwd
|
||
}
|
||
const result = await apis.user.updatePassword(params)
|
||
if (conf('http.code.success') === result?.success) {
|
||
hideModal()
|
||
hideLoading()
|
||
userStore.logout().then(() => {
|
||
router.push({
|
||
name: 'login',
|
||
})
|
||
})
|
||
}
|
||
} catch (error) {
|
||
message.error({ content: error.message })
|
||
hideLoading()
|
||
}
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.basic-header {
|
||
height: v-bind('config.headerHeight + "px"');
|
||
line-height: 1;
|
||
position: sticky;
|
||
top: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
padding-inline: 16px;
|
||
// box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||
|
||
&__left {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
&__center {
|
||
flex: auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
&__right {
|
||
flex-shrink: 0;
|
||
margin: 0 0 0 auto;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
:deep(.ant-menu-horizontal) {
|
||
border: none;
|
||
line-height: v-bind('config.headerHeight + "px"');
|
||
}
|
||
|
||
&--light {
|
||
background: #fff;
|
||
}
|
||
|
||
&--dark {
|
||
color: #fff;
|
||
|
||
:deep(.action-btn) {
|
||
&:hover {
|
||
background: #252a3d;
|
||
}
|
||
}
|
||
}
|
||
|
||
:deep(.basic-menu) {
|
||
.basic-menu__title {
|
||
.ant-badge {
|
||
margin-top: -2px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|