mirror of https://gitee.com/xiaonuobase/snowy
【更新】优化前端搜索组源码及个人中心优化
parent
1a828f8378
commit
cad24eb2a8
|
@ -1,58 +0,0 @@
|
|||
import { mapState, mapActions } from 'pinia'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import { searchStore } from '@/store'
|
||||
|
||||
export default {
|
||||
mounted() {
|
||||
// 绑定搜索功能快捷键 [ 打开 ]
|
||||
hotkeys(this.searchHotkey.open, (event) => {
|
||||
event.preventDefault()
|
||||
this.searchPanelOpen()
|
||||
})
|
||||
// 绑定搜索功能快捷键 [ 关闭 ]
|
||||
hotkeys(this.searchHotkey.close, (event) => {
|
||||
event.preventDefault()
|
||||
this.searchPanelClose()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
hotkeys.unbind(this.searchHotkey.open)
|
||||
hotkeys.unbind(this.searchHotkey.close)
|
||||
},
|
||||
computed: {
|
||||
...mapState(searchStore, {
|
||||
searchActive: (state) => state.active,
|
||||
searchHotkey: (state) => state.hotkey
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapActions(searchStore, ['toggleActive', 'setActive']),
|
||||
// 接收点击搜索按钮
|
||||
handleSearchClick() {
|
||||
this.toggleActive()
|
||||
if (this.searchActive) {
|
||||
setTimeout(() => {
|
||||
if (this.$refs.panelSearch) {
|
||||
this.$refs.panelSearch.focus()
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
},
|
||||
searchPanelOpen() {
|
||||
if (!this.searchActive) {
|
||||
this.setActive(true)
|
||||
setTimeout(() => {
|
||||
if (this.$refs.panelSearch) {
|
||||
this.$refs.panelSearch.focus()
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
},
|
||||
// 关闭搜索面板
|
||||
searchPanelClose() {
|
||||
if (this.searchActive) {
|
||||
this.setActive(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,210 +1,262 @@
|
|||
<template>
|
||||
<div @keyup.up="handleKeyUp" @keyup.down="handleKeyDown" @keyup.enter="handleKeyEnter" @click.self="handlePanelClick">
|
||||
<a-input
|
||||
ref="input"
|
||||
v-model="searchText"
|
||||
class="search-box"
|
||||
style="width: 100%"
|
||||
allowClear
|
||||
placeholder="搜索页面(支持拼音检索)"
|
||||
@change="querySearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-card
|
||||
:body-style="{ padding: '0 0' }"
|
||||
hoverable
|
||||
@mouseenter="onCardIn"
|
||||
@mouseleave="onCardOut"
|
||||
@keypress.up="handleKeyUp"
|
||||
@keypress.down="handleKeyDown"
|
||||
style="margin: 10px 0"
|
||||
>
|
||||
<div ref="cardList" class="search-card beauty-scroll">
|
||||
<a-list size="small" :data-source="resultsList">
|
||||
<template #renderItem="{ item, index }">
|
||||
<a-list-item
|
||||
@click="handleSelect(item.fullPath)"
|
||||
@mouseover="onCardItemHover(index)"
|
||||
:class="{ active: index === cardIndex }"
|
||||
style="padding-right: 10px"
|
||||
>
|
||||
<template #actions>
|
||||
<a>
|
||||
<enter-outlined />
|
||||
</a>
|
||||
</template>
|
||||
<a-list-item-meta :description="item.fullName">
|
||||
<template #title>
|
||||
<a>{{ item.name }}</a>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<a-avatar style="color: var(--text-color); background-color: transparent" :type="item.icon">
|
||||
<template #icon>
|
||||
<component :is="item.icon" />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-card>
|
||||
<div class="search-tips">
|
||||
<span class="key">S</span>
|
||||
<span class="tips">打开搜索面板</span>
|
||||
|
||||
<span class="key">
|
||||
<arrow-up-outlined />
|
||||
</span>
|
||||
<span class="key">
|
||||
<arrow-down-outlined />
|
||||
</span>
|
||||
<span class="tips">选择</span>
|
||||
|
||||
<span class="key">
|
||||
<enter-outlined />
|
||||
</span>
|
||||
<span class="tips">确认</span>
|
||||
|
||||
<span class="key left">Esc</span>
|
||||
<span class="tips">关闭</span>
|
||||
</div>
|
||||
<div class="search panel-item" @click="searchPanelOpen">
|
||||
<search-outlined />
|
||||
</div>
|
||||
<xn-form-container
|
||||
title="搜索"
|
||||
:visible="searchActive"
|
||||
:closable="false"
|
||||
:footer="null"
|
||||
:width="600"
|
||||
destroyOnClose
|
||||
dialogClass="searchModal"
|
||||
:bodyStyle="{ maxHeight: '520px', overflow: 'auto', padding: '14px' }"
|
||||
@close="searchPanelClose"
|
||||
>
|
||||
<div
|
||||
@keyup.up="handleKeyUp"
|
||||
@keyup.down="handleKeyDown"
|
||||
@keyup.enter="handleKeyEnter"
|
||||
@click.self="handlePanelClick"
|
||||
>
|
||||
<a-input
|
||||
ref="inputRef"
|
||||
v-model="searchText"
|
||||
class="search-box"
|
||||
style="width: 100%"
|
||||
allowClear
|
||||
placeholder="搜索页面(支持拼音检索)"
|
||||
@change="querySearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<search-outlined />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-card
|
||||
:body-style="{ padding: '0 0' }"
|
||||
hoverable
|
||||
@mouseenter="onCardIn"
|
||||
@mouseleave="onCardOut"
|
||||
@keypress.up="handleKeyUp"
|
||||
@keypress.down="handleKeyDown"
|
||||
style="margin: 10px 0"
|
||||
>
|
||||
<div ref="cardListRef" class="search-card beauty-scroll">
|
||||
<a-list size="small" :data-source="resultsList">
|
||||
<template #renderItem="{ item, index }">
|
||||
<a-list-item
|
||||
@click="handleSelect(item.fullPath)"
|
||||
@mouseover="onCardItemHover(index)"
|
||||
:class="{ active: index === cardIndex }"
|
||||
style="padding-right: 10px"
|
||||
>
|
||||
<template #actions>
|
||||
<a>
|
||||
<enter-outlined />
|
||||
</a>
|
||||
</template>
|
||||
<a-list-item-meta :description="item.fullName">
|
||||
<template #title>
|
||||
<a>{{ item.name }}</a>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<a-avatar style="color: var(--text-color); background-color: transparent" :type="item.icon">
|
||||
<template #icon>
|
||||
<component :is="item.icon" />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-card>
|
||||
<div class="search-tips">
|
||||
<span class="key">S</span>
|
||||
<span class="tips">打开搜索面板</span>
|
||||
|
||||
<span class="key">
|
||||
<arrow-up-outlined />
|
||||
</span>
|
||||
<span class="key">
|
||||
<arrow-down-outlined />
|
||||
</span>
|
||||
<span class="tips">选择</span>
|
||||
|
||||
<span class="key">
|
||||
<enter-outlined />
|
||||
</span>
|
||||
<span class="tips">确认</span>
|
||||
|
||||
<span class="key left">Esc</span>
|
||||
<span class="tips">关闭</span>
|
||||
</div>
|
||||
</div>
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import Fuse from 'fuse.js'
|
||||
import { mapState } from 'pinia'
|
||||
import { searchStore } from '@/store'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
searchText: '',
|
||||
cardIndex: 0,
|
||||
results: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(searchStore, ['pool']),
|
||||
// 这份数据是展示在搜索面板下面的
|
||||
resultsList() {
|
||||
return this.results.length === 0 || this.searchText === '' ? this.pool : this.results
|
||||
},
|
||||
// 根据 pool 更新 fuse 实例
|
||||
fuse() {
|
||||
return new Fuse(this.pool, {
|
||||
shouldSort: true, // 按分数对结果列表进行排序
|
||||
threshold: 0.6, // 什么时候放弃
|
||||
location: 0, // 大致位置
|
||||
distance: 100, // 接近程度
|
||||
minMatchCharLength: 1, // 匹配长度
|
||||
keys: ['name', 'namePinyin', 'namePinyinFirst']
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 过滤选项 这个方法在每次输入框的值发生变化时会触发
|
||||
querySearch(e) {
|
||||
let queryString = e.target.value || ''
|
||||
const results = queryString && this.fuse.search(queryString).map((e) => e.item)
|
||||
this.searchText = queryString
|
||||
this.results = results
|
||||
},
|
||||
// 聚焦输入框
|
||||
focus() {
|
||||
this.searchText = ''
|
||||
setTimeout(() => {
|
||||
if (this.$refs.input) {
|
||||
this.$refs.input.focus()
|
||||
}
|
||||
// 还原
|
||||
this.searchText = ''
|
||||
this.results = []
|
||||
}, 300)
|
||||
},
|
||||
handleKeyEnter() {
|
||||
let idx = this.cardIndex
|
||||
if (this.resultsList[idx]) {
|
||||
this.handleSelect(this.resultsList[idx].fullPath)
|
||||
}
|
||||
},
|
||||
handleKeyUp() {
|
||||
this.handleKeyUpOrDown(true)
|
||||
},
|
||||
handleKeyDown() {
|
||||
this.handleKeyUpOrDown(false)
|
||||
},
|
||||
handleKeyUpOrDown(up) {
|
||||
let len = this.resultsList.length - 1
|
||||
let idx = this.cardIndex
|
||||
if (up) {
|
||||
// 上
|
||||
if (idx > 0) {
|
||||
idx--
|
||||
} else {
|
||||
idx = len
|
||||
}
|
||||
} else {
|
||||
// 下
|
||||
if (idx < len) {
|
||||
idx++
|
||||
} else {
|
||||
idx = 0
|
||||
}
|
||||
}
|
||||
this.cardIndex = idx
|
||||
if (this.$refs.cardList.getElementsByClassName('ant-list-item')[idx]) {
|
||||
this.$refs.cardList.scrollTop = this.$refs.cardList.getElementsByClassName('ant-list-item')[idx].offsetTop
|
||||
} else {
|
||||
this.$refs.cardList.scrollTop = 0
|
||||
}
|
||||
},
|
||||
onCardIn() {
|
||||
this.$refs.input.activated = false
|
||||
this.$refs.input.blur()
|
||||
},
|
||||
onCardOut() {
|
||||
this.cardIndex = -1
|
||||
},
|
||||
onCardItemHover(index) {
|
||||
this.cardIndex = index
|
||||
},
|
||||
// 接收用户在下拉菜单中选中事件
|
||||
handleSelect(path) {
|
||||
// 如果用户选择的就是当前页面 就直接关闭搜索面板
|
||||
if (path === this.$route.path) {
|
||||
this.handleEsc()
|
||||
return
|
||||
}
|
||||
this.$router.push({ path })
|
||||
this.handleEsc()
|
||||
},
|
||||
// 关闭输入框的下拉菜单
|
||||
closeSuggestion() {
|
||||
if (this.$refs.input.activated) {
|
||||
this.results = []
|
||||
this.$refs.input.activated = false
|
||||
}
|
||||
},
|
||||
// 接收用户点击空白区域的关闭
|
||||
handlePanelClick(e) {
|
||||
if ('INPUT' !== e.target.tagName) {
|
||||
this.handleEsc()
|
||||
}
|
||||
},
|
||||
// 接收用户触发的关闭
|
||||
async handleEsc() {
|
||||
this.closeSuggestion()
|
||||
await this.$nextTick()
|
||||
this.$emit('close')
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const searchText = ref('')
|
||||
const cardIndex = ref(0)
|
||||
const results = ref([])
|
||||
const search = searchStore()
|
||||
const inputRef = ref()
|
||||
const cardListRef = ref()
|
||||
const setActive = search.setActive
|
||||
const toggleActive = search.toggleActive
|
||||
const pool = computed(() => {
|
||||
return search.pool
|
||||
})
|
||||
const searchActive = computed(() => {
|
||||
return search.active
|
||||
})
|
||||
const searchHotkey = computed(() => {
|
||||
return search.hotkey
|
||||
})
|
||||
const mixinSearch = computed(() => {
|
||||
return mixinSearch
|
||||
})
|
||||
// 这份数据是展示在搜索面板下面的
|
||||
const resultsList = computed(() => {
|
||||
return results.value.length === 0 || searchText.value === '' ? pool.value : results.value
|
||||
})
|
||||
// 根据 pool 更新 fuse 实例
|
||||
const fuse = computed(() => {
|
||||
return new Fuse(pool.value, {
|
||||
shouldSort: true, // 按分数对结果列表进行排序
|
||||
threshold: 0.6, // 什么时候放弃
|
||||
location: 0, // 大致位置
|
||||
distance: 100, // 接近程度
|
||||
minMatchCharLength: 1, // 匹配长度
|
||||
keys: ['name', 'namePinyin', 'namePinyinFirst']
|
||||
})
|
||||
})
|
||||
// 过滤选项 这个方法在每次输入框的值发生变化时会触发
|
||||
const querySearch = (e) => {
|
||||
let queryString = e.target.value || ''
|
||||
const result = queryString && fuse.value.search(queryString).map((e) => e.item)
|
||||
searchText.value = queryString
|
||||
results.value = result
|
||||
}
|
||||
// 聚焦输入框
|
||||
const focus = () => {
|
||||
searchText.value = ''
|
||||
setTimeout(() => {
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus()
|
||||
}
|
||||
// 还原
|
||||
searchText.value = ''
|
||||
results.value = []
|
||||
}, 300)
|
||||
}
|
||||
const handleKeyEnter = () => {
|
||||
let idx = cardIndex.value
|
||||
if (resultsList.value[idx]) {
|
||||
handleSelect(resultsList.value[idx].fullPath)
|
||||
}
|
||||
}
|
||||
const handleKeyUp = () => {
|
||||
handleKeyUpOrDown(true)
|
||||
}
|
||||
const handleKeyDown = () => {
|
||||
handleKeyUpOrDown(false)
|
||||
}
|
||||
const handleKeyUpOrDown = (up) => {
|
||||
let len = resultsList.value.length - 1
|
||||
let idx = cardIndex.value
|
||||
if (up) {
|
||||
// 上
|
||||
if (idx > 0) {
|
||||
idx--
|
||||
} else {
|
||||
idx = len
|
||||
}
|
||||
} else {
|
||||
// 下
|
||||
if (idx < len) {
|
||||
idx++
|
||||
} else {
|
||||
idx = 0
|
||||
}
|
||||
}
|
||||
cardIndex.value = idx
|
||||
if (cardListRef.value.getElementsByClassName('ant-list-item')[idx]) {
|
||||
cardListRef.value.scrollTop = cardListRef.value.getElementsByClassName('ant-list-item')[idx].offsetTop
|
||||
} else {
|
||||
cardListRef.value.scrollTop = 0
|
||||
}
|
||||
}
|
||||
const onCardIn = () => {
|
||||
inputRef.value.activated = false
|
||||
inputRef.value.blur()
|
||||
}
|
||||
const onCardOut = () => {
|
||||
cardIndex.value = -1
|
||||
}
|
||||
const onCardItemHover = (index) => {
|
||||
cardIndex.value = index
|
||||
}
|
||||
// 接收用户在下拉菜单中选中事件
|
||||
const handleSelect = (path) => {
|
||||
// 如果用户选择的就是当前页面 就直接关闭搜索面板
|
||||
if (path === route.path) {
|
||||
searchPanelClose()
|
||||
return
|
||||
}
|
||||
router.push({ path })
|
||||
searchPanelClose()
|
||||
}
|
||||
// 接收用户点击空白区域的关闭
|
||||
const handlePanelClick = (e) => {
|
||||
if ('INPUT' !== e.target.tagName) {
|
||||
searchPanelClose()
|
||||
}
|
||||
}
|
||||
// 打开搜索面板
|
||||
const searchPanelOpen = () => {
|
||||
if (!searchActive.value) {
|
||||
setActive(true)
|
||||
setTimeout(() => {
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus()
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
// 关闭搜索面板
|
||||
const searchPanelClose = () => {
|
||||
if (searchActive.value) {
|
||||
setActive(false)
|
||||
}
|
||||
results.value = []
|
||||
if (inputRef.value.activated) {
|
||||
inputRef.value.activated = false
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
// 绑定搜索功能快捷键 [ 打开 ]
|
||||
hotkeys(searchHotkey.value.open, (event) => {
|
||||
event.preventDefault()
|
||||
searchPanelOpen()
|
||||
})
|
||||
// 绑定搜索功能快捷键 [ 关闭 ]
|
||||
hotkeys(searchHotkey.value.close, (event) => {
|
||||
event.preventDefault()
|
||||
searchPanelClose()
|
||||
})
|
||||
})
|
||||
// 挂载
|
||||
hotkeys.unbind(searchHotkey.value.open)
|
||||
hotkeys.unbind(searchHotkey.value.close)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -249,7 +301,7 @@
|
|||
}
|
||||
}
|
||||
.search-card {
|
||||
height: 220px;
|
||||
height: 380px;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,34 @@
|
|||
<template>
|
||||
<div class="d2-panel-search-item" :class="hoverMode ? 'can-hover' : ''" flex>
|
||||
<div class="d2-panel-search-item" :class="props.hoverMode ? 'can-hover' : ''" flex>
|
||||
<div class="d2-panel-search-item__icon" flex-box="0">
|
||||
<div class="d2-panel-search-item__icon-box" flex="main:center cross:center">
|
||||
<a-icon v-if="item.icon" :type="item.icon" />
|
||||
<a-icon v-if="props.item.icon" :type="props.item.icon" />
|
||||
<a-icon v-else type="menu" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="d2-panel-search-item__info" flex-box="1" flex="dir:top">
|
||||
<div class="d2-panel-search-item__info-title" flex-box="1" flex="cross:center">
|
||||
<span>{{ item.title }}</span>
|
||||
<span>{{ props.item.title }}</span>
|
||||
</div>
|
||||
<div class="d2-panel-search-item__info-fullTitle" flex-box="0">
|
||||
<span>{{ item.fullTitle }}</span>
|
||||
<span>{{ props.item.fullTitle }}</span>
|
||||
</div>
|
||||
<div class="d2-panel-search-item__info-path" flex-box="0">
|
||||
<span>{{ item.path }}</span>
|
||||
<span>{{ props.item.path }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
default: () => ({})
|
||||
},
|
||||
hoverMode: {
|
||||
default: false
|
||||
}
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
item: {
|
||||
default: () => ({})
|
||||
},
|
||||
hoverMode: {
|
||||
default: false
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="修改密码"
|
||||
:mask-closable="false"
|
||||
:width="800"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 打开
|
||||
showUpdPwdModal(value) {
|
||||
this.visible = true
|
||||
},
|
||||
// 切换icon风格
|
||||
radioGroupChange(e) {
|
||||
this.iconItemDefault = e.target.value
|
||||
},
|
||||
// 选择图标后关闭并返回
|
||||
handleOk() {
|
||||
this.visible = false
|
||||
this.$emit('updPwdCallBack')
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
this.$emit('updPwdCallBack')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,12 +1,11 @@
|
|||
<template>
|
||||
<div class="user-bar">
|
||||
<div v-if="!ismobile" class="search panel-item hidden-sm-and-down" @click="handleSearchClick">
|
||||
<search-outlined />
|
||||
</div>
|
||||
<div v-if="!ismobile" class="screen panel-item hidden-sm-and-down" @click="fullscreen">
|
||||
<!-- 搜索面板 -->
|
||||
<panel-search v-if="!isMobile" />
|
||||
<div v-if="!isMobile" class="screen panel-item hidden-sm-and-down" @click="fullscreen">
|
||||
<fullscreen-outlined />
|
||||
</div>
|
||||
<devUserMessage />
|
||||
<dev-user-message />
|
||||
<a-dropdown class="user panel-item">
|
||||
<div class="user-avatar">
|
||||
<a-avatar :src="userInfo.avatar" />
|
||||
|
@ -30,7 +29,7 @@
|
|||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-dropdown v-if="!ismobile" class="panel-item">
|
||||
<a-dropdown v-if="!isMobile" class="panel-item">
|
||||
<global-outlined />
|
||||
<template #overlay>
|
||||
<a-menu :selected-keys="lang">
|
||||
|
@ -43,7 +42,7 @@
|
|||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<div v-if="setDeawer === 'true'" class="setting panel-item" @click="openSetting">
|
||||
<div v-if="setDrawer === 'true'" class="setting panel-item" @click="openSetting">
|
||||
<layout-outlined />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -52,141 +51,109 @@
|
|||
<a-drawer v-model:visible="settingDialog" :closable="false" width="300">
|
||||
<setting />
|
||||
</a-drawer>
|
||||
<!-- 搜索面板 -->
|
||||
<xn-form-container
|
||||
title="搜索"
|
||||
:visible="searchActive"
|
||||
:closable="false"
|
||||
:footer="null"
|
||||
:width="600"
|
||||
destroyOnClose
|
||||
dialogClass="searchModal"
|
||||
:bodyStyle="{ maxHeight: '520px', overflow: 'auto', padding: '14px' }"
|
||||
@close="searchPanelClose"
|
||||
>
|
||||
<panel-search ref="panelSearch" @close="searchPanelClose" />
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup name="layoutUserBar">
|
||||
import { createVNode } from 'vue'
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
|
||||
import screenfull from 'screenfull'
|
||||
import { Modal } from 'ant-design-vue'
|
||||
import i18n from '@/locales/index'
|
||||
import screenFull from 'screenfull'
|
||||
import { message } from 'ant-design-vue'
|
||||
import setting from './setting.vue'
|
||||
import Setting from './setting.vue'
|
||||
import router from '@/router'
|
||||
import tool from '@/utils/tool'
|
||||
import config from '@/config/index'
|
||||
import loginApi from '@/api/auth/loginApi'
|
||||
import devUserMessage from './message.vue'
|
||||
import panelSearch from './panel-search/index.vue'
|
||||
import mixinSearch from './mixins/search'
|
||||
import { mapState } from 'pinia'
|
||||
import DevUserMessage from './message.vue'
|
||||
import PanelSearch from './panel-search/index.vue'
|
||||
import { globalStore } from '@/store'
|
||||
export default {
|
||||
components: {
|
||||
setting,
|
||||
devUserMessage,
|
||||
panelSearch
|
||||
},
|
||||
mixins: [mixinSearch],
|
||||
data() {
|
||||
return {
|
||||
lang: [],
|
||||
settingDialog: false,
|
||||
userName: '',
|
||||
userNameF: '',
|
||||
setDeawer: import.meta.env.VITE_SET_DRAWER
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(globalStore, ['ismobile', 'userInfo'])
|
||||
},
|
||||
|
||||
created() {
|
||||
// 获取默认语言
|
||||
this.lang = new Array(this.$TOOL.data.get('APP_LANG') || this.$CONFIG.LANG)
|
||||
this.userName = this.userInfo?.userName || ''
|
||||
this.userNameF = this.userName.substring(0, 1)
|
||||
},
|
||||
methods: {
|
||||
// 个人信息
|
||||
handleUser(key) {
|
||||
if (key === 'uc') {
|
||||
router.push({ path: '/usercenter' })
|
||||
}
|
||||
if (key === 'clearCache') {
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确认清理所有缓存?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: false,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
message.loading('正在清理中...', 1)
|
||||
const lang = ref(new Array(tool.data.get('APP_LANG') || config.LANG))
|
||||
const settingDialog = ref(false)
|
||||
const setDrawer = ref(import.meta.env.VITE_SET_DRAWER)
|
||||
const store = globalStore()
|
||||
const isMobile = computed(() => {
|
||||
return store.ismobile
|
||||
})
|
||||
const userInfo = computed(() => {
|
||||
return store.userInfo
|
||||
})
|
||||
const userName = ref(userInfo.value?.userName || '')
|
||||
|
||||
// 个人信息
|
||||
const handleUser = (key) => {
|
||||
if (key === 'uc') {
|
||||
router.push({ path: '/usercenter' })
|
||||
}
|
||||
if (key === 'clearCache') {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确认清理所有缓存?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: false,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
message.loading('正在清理中...', 1)
|
||||
tool.data.clear()
|
||||
setTimeout(() => {
|
||||
router.replace({ path: '/login' })
|
||||
location.reload()
|
||||
}, 100)
|
||||
},
|
||||
onCancel() {}
|
||||
})
|
||||
}
|
||||
if (key === 'outLogin') {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确认退出当前用户?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: false,
|
||||
onOk() {
|
||||
// 取得缓存中的token
|
||||
const token = tool.data.get('TOKEN')
|
||||
const param = {
|
||||
token: token
|
||||
}
|
||||
message.loading('退出中...', 1)
|
||||
loginApi
|
||||
.logout(param)
|
||||
.then(() => {
|
||||
// 清理掉个人的一些信息
|
||||
tool.data.remove('TOKEN')
|
||||
tool.data.remove('USER_INFO')
|
||||
tool.data.remove('MENU')
|
||||
tool.data.remove('PERMISSIONS')
|
||||
router.replace({ path: '/login' })
|
||||
})
|
||||
.catch(() => {
|
||||
tool.data.clear()
|
||||
setTimeout(() => {
|
||||
router.replace({ path: '/login' })
|
||||
location.reload()
|
||||
}, 100)
|
||||
},
|
||||
onCancel() {}
|
||||
})
|
||||
}
|
||||
if (key === 'outLogin') {
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确认退出当前用户?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: false,
|
||||
onOk() {
|
||||
// 取得缓存中的token
|
||||
const token = tool.data.get('TOKEN')
|
||||
const param = {
|
||||
token: token
|
||||
}
|
||||
message.loading('退出中...', 1)
|
||||
loginApi
|
||||
.logout(param)
|
||||
.then(() => {
|
||||
// message.c
|
||||
// 清理掉个人的一些信息
|
||||
tool.data.remove('TOKEN')
|
||||
tool.data.remove('USER_INFO')
|
||||
tool.data.remove('MENU')
|
||||
tool.data.remove('PERMISSIONS')
|
||||
router.replace({ path: '/login' })
|
||||
})
|
||||
.catch(() => {
|
||||
tool.data.clear()
|
||||
router.replace({ path: '/login' })
|
||||
location.reload()
|
||||
})
|
||||
},
|
||||
onCancel() {}
|
||||
})
|
||||
}
|
||||
},
|
||||
// 设置多语言语种
|
||||
handleIn18(key) {
|
||||
this.lang = []
|
||||
this.lang.push(key)
|
||||
this.$i18n.locale = key
|
||||
this.$TOOL.data.set('APP_LANG', key)
|
||||
},
|
||||
// 设置抽屉
|
||||
openSetting() {
|
||||
this.settingDialog = true
|
||||
},
|
||||
// 全屏
|
||||
fullscreen() {
|
||||
const element = document.documentElement
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.toggle(element)
|
||||
}
|
||||
},
|
||||
// 搜索
|
||||
fullSearch() {}
|
||||
router.replace({ path: '/login' })
|
||||
location.reload()
|
||||
})
|
||||
},
|
||||
onCancel() {}
|
||||
})
|
||||
}
|
||||
}
|
||||
// 设置多语言语种
|
||||
const handleIn18 = (key) => {
|
||||
lang.value = []
|
||||
lang.value.push(key)
|
||||
i18n.locale = key
|
||||
tool.data.set('APP_LANG', key)
|
||||
}
|
||||
// 设置抽屉
|
||||
const openSetting = () => {
|
||||
settingDialog.value = true
|
||||
}
|
||||
// 全屏
|
||||
const fullscreen = () => {
|
||||
const element = document.documentElement
|
||||
if (screenFull.isEnabled) {
|
||||
screenFull.toggle(element)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -77,7 +77,9 @@
|
|||
|
||||
const global_store = globalStore()
|
||||
|
||||
const userInfo = ref(tool.data.get('USER_INFO'))
|
||||
const userInfo = computed(() => {
|
||||
return global_store.userInfo
|
||||
})
|
||||
const cropUpload = ref()
|
||||
const avatarLoading = ref(false)
|
||||
const uploadLogo = () => {
|
||||
|
|
|
@ -39,15 +39,14 @@
|
|||
import { required } from '@/utils/formRules'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import tool from '@/utils/tool'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { globalStore } from '@/store'
|
||||
|
||||
const store = globalStore()
|
||||
|
||||
const formRef = ref()
|
||||
// 获取用户信息
|
||||
const userInfo = tool.data.get('USER_INFO')
|
||||
let formData = ref({})
|
||||
formData.value = userInfo
|
||||
formData.value = cloneDeep(store.userInfo)
|
||||
const submitLoading = ref(false)
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
|
@ -64,7 +63,7 @@
|
|||
userCenterApi.userUpdateUserInfo(formData.value).then(() => {
|
||||
submitLoading.value = false
|
||||
// 更新前端缓存
|
||||
store.setUserInfo(formData.value)
|
||||
store.setUserInfo(cloneDeep(formData.value))
|
||||
tool.data.set('USER_INFO', formData.value)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue