✨ feat: 默认改为暗色模式
This commit is contained in:
@@ -13,10 +13,8 @@ const isDark = useDark()
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 初始化主题样式
|
|
||||||
|
|
||||||
console.log(isDark.value)
|
console.log(isDark.value)
|
||||||
if (isDark.value) {
|
if (!isDark.value) {
|
||||||
useSettingsStore().toggleTheme()
|
useSettingsStore().toggleTheme()
|
||||||
}
|
}
|
||||||
handleThemeStyle(useSettingsStore().theme)
|
handleThemeStyle(useSettingsStore().theme)
|
||||||
|
|||||||
BIN
gear-ui3/src/assets/images/back.png
Normal file
BIN
gear-ui3/src/assets/images/back.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 978 KiB |
@@ -1,70 +1,151 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="all-applications-container">
|
<div class="applications-wrapper">
|
||||||
<h3 class="title">全部应用</h3>
|
<!-- 一、业务功能区:常用应用 -->
|
||||||
<el-tabs v-model="activeTabName" class="app-tabs">
|
<div class="business-area">
|
||||||
<el-tab-pane
|
<!-- 常用应用标题栏 -->
|
||||||
v-for="menu in filteredMenus"
|
<div class="business-area-header">
|
||||||
:key="menu.path"
|
<h2 class="business-area-title">常用应用</h2>
|
||||||
:label="menu.meta?.title"
|
<button class="edit-btn" @click="showSettingDialog = true">
|
||||||
:name="menu.path"
|
<svg-icon icon-class="edit" /> 编辑常用
|
||||||
>
|
</button>
|
||||||
<div class="app-grid">
|
</div>
|
||||||
<div
|
|
||||||
v-for="child in menu.children"
|
|
||||||
:key="child.path"
|
|
||||||
class="app-item"
|
|
||||||
@click="handleAppClick(menu, child)"
|
|
||||||
>
|
|
||||||
<!-- 图标区域 -->
|
|
||||||
<div class="app-icon-wrapper">
|
|
||||||
<svg-icon :icon-class="child.meta.icon || 'documentation'" class="app-icon" />
|
|
||||||
</div>
|
|
||||||
<!-- 文字区域 -->
|
|
||||||
<span class="app-name">{{ child.meta?.title }}</span>
|
|
||||||
|
|
||||||
<!-- 三点菜单区域 -->
|
<!-- 常用应用列表 -->
|
||||||
<div class="app-actions">
|
<div class="business-modules">
|
||||||
<el-dropdown
|
<div
|
||||||
trigger="hover"
|
v-for="(module, index) in businessModules"
|
||||||
@click.native.stop
|
:key="index"
|
||||||
@command="(command) => handleCommand(command, menu, child)"
|
class="business-module"
|
||||||
>
|
@click="handleLink(module.link)"
|
||||||
<span class="actions-icon">
|
>
|
||||||
<More />
|
<div class="business-module-icon">
|
||||||
</span>
|
<svg-icon :icon-class="module.icon" />
|
||||||
<template #dropdown>
|
</div>
|
||||||
<el-dropdown-menu>
|
<h3 class="business-module-title">{{ module.title }}</h3>
|
||||||
<el-dropdown-item command="addFavorites">
|
</div>
|
||||||
添加到常用
|
</div>
|
||||||
</el-dropdown-item>
|
</div>
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
<!-- 二、全部应用区域(原功能保留) -->
|
||||||
</el-dropdown>
|
<div class="all-applications-container">
|
||||||
|
<h3 class="title">全部应用</h3>
|
||||||
|
<el-tabs v-model="activeTabName" class="app-tabs">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="menu in filteredMenus"
|
||||||
|
:key="menu.path"
|
||||||
|
:label="menu.meta?.title"
|
||||||
|
:name="menu.path"
|
||||||
|
>
|
||||||
|
<div class="app-grid">
|
||||||
|
<div
|
||||||
|
v-for="child in menu.children"
|
||||||
|
:key="child.path"
|
||||||
|
class="app-item"
|
||||||
|
@click="handleAppClick(menu, child)"
|
||||||
|
>
|
||||||
|
<!-- 图标区域 -->
|
||||||
|
<div class="app-icon-wrapper">
|
||||||
|
<svg-icon :icon-class="child.meta.icon || 'documentation'" class="app-icon" />
|
||||||
|
</div>
|
||||||
|
<!-- 文字区域 -->
|
||||||
|
<span class="app-name">{{ child.meta?.title }}</span>
|
||||||
|
|
||||||
|
<!-- 三点菜单区域 -->
|
||||||
|
<div class="app-actions">
|
||||||
|
<el-dropdown
|
||||||
|
trigger="hover"
|
||||||
|
@click.native.stop
|
||||||
|
@command="(command) => handleCommand(command, menu, child)"
|
||||||
|
>
|
||||||
|
<span class="actions-icon">
|
||||||
|
<More />
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="addFavorites">
|
||||||
|
添加到常用
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 三、设置弹窗:编辑常用应用 -->
|
||||||
|
<div class="dialog-mask" v-if="showSettingDialog">
|
||||||
|
<div class="setting-dialog">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<h3>编辑常用应用</h3>
|
||||||
|
<button class="close-btn" @click="showSettingDialog = false">
|
||||||
|
<svg-icon icon-class="close" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
<div class="dialog-content">
|
||||||
</el-tabs>
|
<div class="empty-tip" v-if="businessModules.length === 0">
|
||||||
|
暂无常用应用,可从下方"全部应用"中添加
|
||||||
|
</div>
|
||||||
|
<ul class="module-list" v-else>
|
||||||
|
<li v-for="(module, index) in businessModules" :key="index" class="module-item">
|
||||||
|
<div class="module-info">
|
||||||
|
<span class="module-index">{{ index + 1 }}</span>
|
||||||
|
<span class="module-name">{{ module.title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="module-actions">
|
||||||
|
<el-button
|
||||||
|
@click="moveModule(index, 'up')"
|
||||||
|
:disabled="index === 0"
|
||||||
|
icon="ArrowUp"
|
||||||
|
type="primary"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
@click="moveModule(index, 'down')"
|
||||||
|
:disabled="index === businessModules.length - 1"
|
||||||
|
icon="ArrowDown"
|
||||||
|
type="primary"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
@click="deleteModule(index)"
|
||||||
|
icon="Delete"
|
||||||
|
danger
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<button class="cancel-btn" @click="showSettingDialog = false">取消</button>
|
||||||
|
<button class="confirm-btn" @click="showSettingDialog = false">完成</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getRouters } from '@/api/menu'
|
import { getRouters } from '@/api/menu'
|
||||||
import { More } from '@element-plus/icons-vue'
|
import { More } from '@element-plus/icons-vue'
|
||||||
|
import { useStorage } from '@vueuse/core'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AllApplications',
|
name: 'AllApplications',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
allMenus: [],
|
allMenus: [],
|
||||||
activeTabName: ''
|
activeTabName: '',
|
||||||
|
showSettingDialog: false, // 控制弹窗显示
|
||||||
|
businessModules: [] // 常用应用列表(从localStorage同步)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// 过滤隐藏菜单和无children的菜单
|
||||||
filteredMenus() {
|
filteredMenus() {
|
||||||
const filterHidden = (menus) => {
|
const filterHidden = (menus) => {
|
||||||
return menus
|
return menus
|
||||||
.filter(menu => menu.hidden !== true && menu?.meta?.title != '综合大屏')
|
.filter(menu => menu.hidden !== true && menu?.meta?.title !== '综合大屏')
|
||||||
.map(menu => {
|
.map(menu => {
|
||||||
if (menu.children) {
|
if (menu.children) {
|
||||||
menu.children = filterHidden(menu.children)
|
menu.children = filterHidden(menu.children)
|
||||||
@@ -72,66 +153,214 @@ export default {
|
|||||||
return menu
|
return menu
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const topLevelMenus = filterHidden(this.allMenus).filter(
|
// 只保留有子菜单的顶层菜单
|
||||||
|
return filterHidden(this.allMenus).filter(
|
||||||
menu => menu.children && menu.children.length > 0
|
menu => menu.children && menu.children.length > 0
|
||||||
)
|
)
|
||||||
return topLevelMenus
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
// 1. 初始化菜单数据
|
||||||
this.fetchMenus()
|
this.fetchMenus()
|
||||||
|
|
||||||
|
// 2. 初始化常用应用(持久化到localStorage)
|
||||||
|
this.$businessModulesRef = useStorage('businessModules', [
|
||||||
|
{ title: '考勤日历', icon: 'code', link: '/people/calendar' },
|
||||||
|
{ title: '系统设置', icon: 'system', link: '/system/menu' }
|
||||||
|
])
|
||||||
|
// 同步ref值到data(确保Options API正常操作)
|
||||||
|
this.businessModules = this.$businessModulesRef.value
|
||||||
|
// 监听常用应用变化,同步到localStorage
|
||||||
|
this.$watch('businessModules', (newVal) => {
|
||||||
|
this.$businessModulesRef.value = newVal
|
||||||
|
}, { deep: true })
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 1. 获取菜单数据
|
||||||
fetchMenus() {
|
fetchMenus() {
|
||||||
getRouters().then(response => {
|
getRouters().then(response => {
|
||||||
this.allMenus = response.data
|
this.allMenus = response.data
|
||||||
|
// 默认激活第一个tab
|
||||||
if (this.filteredMenus.length > 0) {
|
if (this.filteredMenus.length > 0) {
|
||||||
this.activeTabName = this.filteredMenus[0].path
|
this.activeTabName = this.filteredMenus[0].path
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleAppClick(parentMenu, childMenu) {
|
|
||||||
const basePath = parentMenu.path
|
// 2. 应用跳转(常用应用/全部应用通用)
|
||||||
const fullPath = `${basePath}/${childMenu.path}`
|
handleLink(link) {
|
||||||
this.$router.push(fullPath)
|
if (link.startsWith('http') || link.endsWith('.html')) {
|
||||||
},
|
window.open(link, '_blank')
|
||||||
// 处理添加到常用功能
|
} else {
|
||||||
handleAddToFavorites(parentMenu, childMenu) {
|
this.$router.push(link)
|
||||||
// 构建完整的应用信息
|
|
||||||
const appInfo = {
|
|
||||||
parent: parentMenu,
|
|
||||||
child: childMenu,
|
|
||||||
fullPath: `${parentMenu.path}/${childMenu.path}`,
|
|
||||||
timestamp: new Date().getTime()
|
|
||||||
}
|
}
|
||||||
console.log(appInfo)
|
|
||||||
// 向父组件发送事件
|
|
||||||
this.$emit('addFavorites', appInfo)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 3. 全部应用卡片点击跳转
|
||||||
|
handleAppClick(parentMenu, childMenu) {
|
||||||
|
const fullPath = `${parentMenu.path}/${childMenu.path}`
|
||||||
|
this.handleLink(fullPath)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4. 处理三点菜单命令(添加到常用)
|
||||||
handleCommand(command, parentMenu, childMenu) {
|
handleCommand(command, parentMenu, childMenu) {
|
||||||
if (command === 'addFavorites') {
|
if (command === 'addFavorites') {
|
||||||
this.handleAddToFavorites(parentMenu, childMenu)
|
this.handleAddToFavorites(parentMenu, childMenu)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5. 添加/移除常用应用
|
||||||
|
handleAddToFavorites(parentMenu, childMenu) {
|
||||||
|
const fullPath = `${parentMenu.path}/${childMenu.path}`
|
||||||
|
const existIndex = this.businessModules.findIndex(
|
||||||
|
module => module.link === fullPath
|
||||||
|
)
|
||||||
|
|
||||||
|
// 已存在则移除,不存在则添加
|
||||||
|
if (existIndex !== -1) {
|
||||||
|
this.businessModules.splice(existIndex, 1)
|
||||||
|
} else {
|
||||||
|
this.businessModules.push({
|
||||||
|
title: childMenu.meta.title,
|
||||||
|
icon: childMenu.meta.icon,
|
||||||
|
link: fullPath
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 6. 调整常用应用排序
|
||||||
|
moveModule(index, direction) {
|
||||||
|
const newModules = [...this.businessModules]
|
||||||
|
// 上移:与前一个交换
|
||||||
|
if (direction === 'up' && index > 0) {
|
||||||
|
[newModules[index], newModules[index - 1]] = [newModules[index - 1], newModules[index]]
|
||||||
|
}
|
||||||
|
// 下移:与后一个交换
|
||||||
|
if (direction === 'down' && index < newModules.length - 1) {
|
||||||
|
[newModules[index], newModules[index + 1]] = [newModules[index + 1], newModules[index]]
|
||||||
|
}
|
||||||
|
// 触发watch同步到localStorage
|
||||||
|
this.businessModules = newModules
|
||||||
|
},
|
||||||
|
|
||||||
|
// 7. 删除常用应用
|
||||||
|
deleteModule(index) {
|
||||||
|
if (confirm('确定要移除该常用应用吗?')) {
|
||||||
|
this.businessModules.splice(index, 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
/* 外层容器统一样式:深色背景 */
|
||||||
|
.applications-wrapper {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 一、常用应用区域样式 */
|
||||||
|
.business-area {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 常用应用标题栏 */
|
||||||
|
.business-area-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.business-area-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #E5E7EB;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: #2C2C3C;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #E5E7EB;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #3A3A4A;
|
||||||
|
color: #F9FAFB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 常用应用列表 */
|
||||||
|
.business-modules {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.business-module {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #2C2C3C;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
transition: all 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
background: #3A3A4A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.business-module-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 12px;
|
||||||
|
background: #374151; /* 可根据图标色调整背景 */
|
||||||
|
|
||||||
|
:deep(svg) {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.business-module-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #E5E7EB;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 二、全部应用区域样式 */
|
||||||
.all-applications-container {
|
.all-applications-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-top: 20px;
|
background: #1E1E2E;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
color: #303133;
|
color: #E5E7EB;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 应用列表容器改为 flex 横向布局 */
|
/* 应用列表网格 */
|
||||||
.app-grid {
|
.app-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -140,27 +369,28 @@ export default {
|
|||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 每个应用 item 样式 */
|
/* 应用卡片 */
|
||||||
.app-item {
|
.app-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 横向排列 */
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
width: 160px;
|
width: 160px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
background-color: #ffffff;
|
background-color: #2C2C3C;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08); /* 初始阴影更浅 */
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
position: relative; /* 为三点菜单定位做准备 */
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); /* 悬停加深阴影 */
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
transform: translateY(-2px); /* 轻微上浮增强交互感 */
|
transform: translateY(-2px);
|
||||||
|
background-color: #3A3A4A;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 图标区域 */
|
/* 应用图标容器 */
|
||||||
.app-icon-wrapper {
|
.app-icon-wrapper {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -168,39 +398,36 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 12px; /* 与文字保持间距 */
|
margin-right: 12px;
|
||||||
background-color: #f5f7fa; /* 浅背景突出图标 */
|
background-color: #374151;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
/* hover 时图标区域背景变化 */
|
|
||||||
.app-item:hover & {
|
.app-item:hover & {
|
||||||
background-color: #e6f0ff;
|
background-color: #4B5563;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 图标样式 */
|
|
||||||
.app-icon {
|
.app-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #409eff;
|
color: #F3F4F6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 应用名称样式 */
|
/* 应用名称 */
|
||||||
.app-name {
|
.app-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #606266;
|
color: #E5E7EB;
|
||||||
flex: 1; /* 占满剩余空间,让三点菜单靠右 */
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 三点菜单样式 */
|
/* 三点菜单 */
|
||||||
.app-actions {
|
.app-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
opacity: 0; /* 默认隐藏 */
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 鼠标悬停在卡片上时显示三点菜单 */
|
|
||||||
.app-item:hover .app-actions {
|
.app-item:hover .app-actions {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@@ -213,29 +440,201 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
color: #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 三、设置弹窗样式 */
|
||||||
|
.dialog-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-dialog {
|
||||||
|
width: 500px;
|
||||||
|
background: #2C2C3C;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-bottom: 1px solid #3A3A4A;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #F9FAFB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
padding: 24px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #2C2C3C;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态提示 */
|
||||||
|
.empty-tip {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #374151;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 常用应用编辑列表 */
|
||||||
|
.module-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #3A3A4A;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-index {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #374151;
|
||||||
|
color: #E5E7EB;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-name {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗底部按钮 */
|
||||||
|
.dialog-footer {
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-top: 1px solid #3A3A4A;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
background: #2C2C3C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn, .confirm-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.2s;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tabs 样式优化 */
|
.cancel-btn {
|
||||||
|
background: #374151;
|
||||||
|
color: #E5E7EB;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #4B5563;
|
||||||
|
color: #F9FAFB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn {
|
||||||
|
background: #3B82F6;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #2563eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 四、Element组件样式穿透调整 */
|
||||||
:deep(.el-tabs__header) {
|
:deep(.el-tabs__header) {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-tabs__nav-wrap::after) {
|
:deep(.el-tabs__nav-wrap::after) {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
|
background-color: #3A3A4A;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-tabs__item) {
|
:deep(.el-tabs__item) {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__item.is-active) {
|
||||||
|
color: #E5E7EB;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 下拉菜单样式调整 */
|
|
||||||
:deep(.el-dropdown-menu) {
|
:deep(.el-dropdown-menu) {
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
|
background-color: #2C2C3C;
|
||||||
|
border: 1px solid #3A3A4A;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-dropdown-item) {
|
:deep(.el-dropdown-item) {
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
color: #E5E7EB;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #3A3A4A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button) {
|
||||||
|
padding: 6px 12px;
|
||||||
|
color: #E5E7EB;
|
||||||
|
background-color: #374151;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #4B5563;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -16,22 +16,21 @@
|
|||||||
|
|
||||||
<!-- 配置内容区 -->
|
<!-- 配置内容区 -->
|
||||||
<div class="settings-content">
|
<div class="settings-content">
|
||||||
|
<!-- 原有图表配置表格 -->
|
||||||
<div class="charts-config">
|
<div class="charts-config">
|
||||||
<h3 class="section-title">
|
<h3 class="section-title">
|
||||||
|
<el-icon><Grid /></el-icon>
|
||||||
图表配置
|
图表配置
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<!-- 直接保留表格,移除draggable包裹 -->
|
|
||||||
<el-table
|
<el-table
|
||||||
border
|
border
|
||||||
size="small"
|
size="small"
|
||||||
:height="tableHeight"
|
:height="tableHeight"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:data="tempChartConfigs">
|
:data="tempChartConfigs">
|
||||||
|
<!-- 表格列内容保持不变 -->
|
||||||
<el-table-column prop="title" label="图表名称" min-width="120" />
|
<el-table-column prop="title" label="图表名称" min-width="120" />
|
||||||
<el-table-column prop="id" label="标识" min-width="100" />
|
<el-table-column prop="id" label="标识" min-width="100" />
|
||||||
|
|
||||||
<!-- 图表高度配置列 -->
|
|
||||||
<el-table-column label="图表高度" min-width="120">
|
<el-table-column label="图表高度" min-width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -48,8 +47,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<!-- 布局配置列 -->
|
|
||||||
<el-table-column label="超小屏 (≤768px)" min-width="120">
|
<el-table-column label="超小屏 (≤768px)" min-width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -57,14 +54,13 @@
|
|||||||
size="small"
|
size="small"
|
||||||
append-to="#full-dashboard-container"
|
append-to="#full-dashboard-container"
|
||||||
:disabled="saving">
|
:disabled="saving">
|
||||||
<el-option :value="24">独占一行</el-option>
|
<el-option value="24">独占一行</el-option>
|
||||||
<el-option :value="12">半行宽度</el-option>
|
<el-option value="12">半行宽度</el-option>
|
||||||
<el-option :value="8">三分之一</el-option>
|
<el-option value="8">三分之一</el-option>
|
||||||
<el-option :value="6">四分之一</el-option>
|
<el-option value="6">四分之一</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="小屏 (≥768px)" min-width="120">
|
<el-table-column label="小屏 (≥768px)" min-width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -72,14 +68,13 @@
|
|||||||
size="small"
|
size="small"
|
||||||
append-to="#full-dashboard-container"
|
append-to="#full-dashboard-container"
|
||||||
:disabled="saving">
|
:disabled="saving">
|
||||||
<el-option :value="24">独占一行</el-option>
|
<el-option value="24">独占一行</el-option>
|
||||||
<el-option :value="12">半行宽度</el-option>
|
<el-option value="12">半行宽度</el-option>
|
||||||
<el-option :value="8">三分之一</el-option>
|
<el-option value="8">三分之一</el-option>
|
||||||
<el-option :value="6">四分之一</el-option>
|
<el-option value="6">四分之一</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="中屏 (≥992px)" min-width="120">
|
<el-table-column label="中屏 (≥992px)" min-width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -87,14 +82,13 @@
|
|||||||
size="small"
|
size="small"
|
||||||
append-to="#full-dashboard-container"
|
append-to="#full-dashboard-container"
|
||||||
:disabled="saving">
|
:disabled="saving">
|
||||||
<el-option :value="24">独占一行</el-option>
|
<el-option value="24">独占一行</el-option>
|
||||||
<el-option :value="12">半行宽度</el-option>
|
<el-option value="12">半行宽度</el-option>
|
||||||
<el-option :value="8">三分之一</el-option>
|
<el-option value="8">三分之一</el-option>
|
||||||
<el-option :value="6">四分之一</el-option>
|
<el-option value="6">四分之一</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="大屏 (≥1200px)" min-width="120">
|
<el-table-column label="大屏 (≥1200px)" min-width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -109,7 +103,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="超大屏 (≥1920px)" min-width="120">
|
<el-table-column label="超大屏 (≥1920px)" min-width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -124,7 +117,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" width="120" align="center">
|
<el-table-column label="操作" width="120" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -147,6 +139,61 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增:布局预览区域 -->
|
||||||
|
<div class="layout-preview">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<el-icon><Eye /></el-icon>
|
||||||
|
布局预览(实时同步配置)
|
||||||
|
</h3>
|
||||||
|
<!-- 预览容器:模拟仪表盘布局 -->
|
||||||
|
<div class="preview-container">
|
||||||
|
<!-- 无配置时显示提示 -->
|
||||||
|
<div v-if="tempChartConfigs.length === 0" class="empty-preview">
|
||||||
|
<el-empty description="暂无图表配置,无法预览" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 有配置时,用el-row+el-col渲染预览 -->
|
||||||
|
<el-row
|
||||||
|
v-else
|
||||||
|
:gutter="16"
|
||||||
|
class="preview-row"
|
||||||
|
:style="{ marginBottom: '16px' }">
|
||||||
|
<el-col
|
||||||
|
v-for="(chart, index) in tempChartConfigs"
|
||||||
|
:key="chart.id || index"
|
||||||
|
:xs="chart.layout.xs"
|
||||||
|
:sm="chart.layout.sm"
|
||||||
|
:md="chart.layout.md"
|
||||||
|
:lg="chart.layout.lg"
|
||||||
|
:xl="chart.layout.xl"
|
||||||
|
class="preview-col">
|
||||||
|
<!-- 用Card模拟图表容器,高度同步配置 -->
|
||||||
|
<el-card
|
||||||
|
class="preview-card"
|
||||||
|
:style="{
|
||||||
|
height: `${chart.height}px`,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
transition: 'all 0.3s ease'
|
||||||
|
}">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="chart-title">{{ chart.title }}</span>
|
||||||
|
<span class="chart-id">[{{ chart.id }}]</span>
|
||||||
|
</div>
|
||||||
|
<!-- 模拟图表内容区域 -->
|
||||||
|
<div class="chart-placeholder" :style="{ flex: 1, marginTop: '8px' }">
|
||||||
|
<div class="placeholder-bg"></div>
|
||||||
|
<div class="placeholder-info">
|
||||||
|
<p>高度:{{ chart.height }}px</p>
|
||||||
|
<p>布局:xs={{ chart.layout.xs }} / sm={{ chart.layout.sm }} / md={{ chart.layout.md }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息 -->
|
||||||
@@ -164,11 +211,11 @@
|
|||||||
import { ref, onMounted, reactive } from 'vue';
|
import { ref, onMounted, reactive } from 'vue';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import { useEventListener } from '@vueuse/core';
|
import { useEventListener } from '@vueuse/core';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox, ElEmpty, ElRow, ElCol, ElCard } from 'element-plus';
|
||||||
import { Loading, ArrowUp, ArrowDown, Grid } from '@element-plus/icons-vue';
|
// 新增导入预览用图标
|
||||||
// 移除draggable组件导入
|
import { Loading, Grid, Eye } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
// 接收存储键名props
|
// 原有逻辑保持不变,仅新增预览相关变量(如有需要)
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
storageKey: {
|
storageKey: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -177,7 +224,6 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 从storage读取配置
|
|
||||||
const persistedChartConfigs = useStorage(
|
const persistedChartConfigs = useStorage(
|
||||||
props.storageKey,
|
props.storageKey,
|
||||||
[],
|
[],
|
||||||
@@ -186,12 +232,8 @@ const persistedChartConfigs = useStorage(
|
|||||||
serializer: {
|
serializer: {
|
||||||
read: (v) => {
|
read: (v) => {
|
||||||
try {
|
try {
|
||||||
if (typeof v === 'string') {
|
if (typeof v === 'string') return JSON.parse(v);
|
||||||
return JSON.parse(v);
|
if (Array.isArray(v)) return v;
|
||||||
}
|
|
||||||
if (Array.isArray(v)) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
return [];
|
return [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('解析存储的图表配置失败,使用空配置:', e);
|
console.error('解析存储的图表配置失败,使用空配置:', e);
|
||||||
@@ -204,41 +246,25 @@ const persistedChartConfigs = useStorage(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 临时配置
|
|
||||||
const tempChartConfigs = ref([]);
|
const tempChartConfigs = ref([]);
|
||||||
// 初始配置快照
|
|
||||||
const initialChartConfigs = ref([]);
|
const initialChartConfigs = ref([]);
|
||||||
// 保存状态
|
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
// 移除拖拽状态变量:isDragging
|
|
||||||
|
|
||||||
// 窗口尺寸响应
|
|
||||||
const windowWidth = ref(window.innerWidth);
|
const windowWidth = ref(window.innerWidth);
|
||||||
const tableHeight = ref(300);
|
const tableHeight = ref(300);
|
||||||
// 高度输入错误标记
|
|
||||||
const heightErrorIndex = ref(-1);
|
const heightErrorIndex = ref(-1);
|
||||||
// 批量设置弹窗
|
|
||||||
const showBatchSettings = ref(false);
|
const showBatchSettings = ref(false);
|
||||||
// 批量设置数据
|
|
||||||
const batchSettings = reactive({
|
const batchSettings = reactive({
|
||||||
applyRange: 'all', // 'all' 或 'visible'
|
applyRange: 'all',
|
||||||
height: null,
|
height: null,
|
||||||
layout: {
|
layout: { xs: null, sm: null, md: null, lg: null, xl: null }
|
||||||
xs: null,
|
|
||||||
sm: null,
|
|
||||||
md: null,
|
|
||||||
lg: null,
|
|
||||||
xl: null
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听窗口尺寸变化
|
// 原有方法(onMounted、calculateTableHeight、resetToSession等)保持不变
|
||||||
useEventListener('resize', () => {
|
useEventListener('resize', () => {
|
||||||
windowWidth.value = window.innerWidth;
|
windowWidth.value = window.innerWidth;
|
||||||
calculateTableHeight();
|
calculateTableHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始化:补充height默认值
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!Array.isArray(persistedChartConfigs.value)) {
|
if (!Array.isArray(persistedChartConfigs.value)) {
|
||||||
persistedChartConfigs.value = [];
|
persistedChartConfigs.value = [];
|
||||||
@@ -247,21 +273,14 @@ onMounted(() => {
|
|||||||
calculateTableHeight();
|
calculateTableHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算表格高度
|
|
||||||
const calculateTableHeight = () => {
|
const calculateTableHeight = () => {
|
||||||
// 根据屏幕高度动态计算表格高度,确保整体布局合理
|
const panelHeight = window.innerHeight - 400; // 预留预览区域高度
|
||||||
const panelHeight = window.innerHeight - 220;
|
tableHeight.value = Math.max(300, Math.min(500, panelHeight));
|
||||||
tableHeight.value = Math.max(300, Math.min(600, panelHeight));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置临时配置:补充height默认值
|
|
||||||
const resetToSession = () => {
|
const resetToSession = () => {
|
||||||
try {
|
try {
|
||||||
const source = Array.isArray(persistedChartConfigs.value)
|
const source = Array.isArray(persistedChartConfigs.value) ? persistedChartConfigs.value : [];
|
||||||
? persistedChartConfigs.value
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// 深拷贝 + 补充height默认值和visible属性
|
|
||||||
const configCopy = JSON.parse(JSON.stringify(source)).map(config => ({
|
const configCopy = JSON.parse(JSON.stringify(source)).map(config => ({
|
||||||
...config,
|
...config,
|
||||||
height: config.height || 400,
|
height: config.height || 400,
|
||||||
@@ -274,7 +293,6 @@ const resetToSession = () => {
|
|||||||
xl: config.layout?.xl || 6
|
xl: config.layout?.xl || 6
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
tempChartConfigs.value = configCopy;
|
tempChartConfigs.value = configCopy;
|
||||||
initialChartConfigs.value = JSON.parse(JSON.stringify(configCopy));
|
initialChartConfigs.value = JSON.parse(JSON.stringify(configCopy));
|
||||||
ElMessage.info('已恢复到打开设置时的状态');
|
ElMessage.info('已恢复到打开设置时的状态');
|
||||||
@@ -284,7 +302,6 @@ const resetToSession = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 高度输入验证
|
|
||||||
const validateHeight = (row, index) => {
|
const validateHeight = (row, index) => {
|
||||||
heightErrorIndex.value = -1;
|
heightErrorIndex.value = -1;
|
||||||
if (isNaN(row.height) || row.height < 100 || row.height > 1000) {
|
if (isNaN(row.height) || row.height < 100 || row.height > 1000) {
|
||||||
@@ -294,46 +311,32 @@ const validateHeight = (row, index) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 移动图表位置(按钮控制)- 保留按钮功能,移除拖拽相关逻辑
|
|
||||||
const moveChart = (index, direction) => {
|
const moveChart = (index, direction) => {
|
||||||
let newIndex;
|
let newIndex;
|
||||||
if (direction === 'up' && index > 0) {
|
if (direction === 'up' && index > 0) {
|
||||||
newIndex = index - 1;
|
newIndex = index - 1;
|
||||||
// 交换位置
|
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] = [tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
||||||
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] =
|
|
||||||
[tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
|
||||||
// 触发重绘
|
|
||||||
tempChartConfigs.value = [...tempChartConfigs.value];
|
tempChartConfigs.value = [...tempChartConfigs.value];
|
||||||
// 添加动画效果
|
|
||||||
highlightRow(index);
|
highlightRow(index);
|
||||||
highlightRow(newIndex);
|
highlightRow(newIndex);
|
||||||
} else if (direction === 'down' && index < tempChartConfigs.value.length - 1) {
|
} else if (direction === 'down' && index < tempChartConfigs.value.length - 1) {
|
||||||
newIndex = index + 1;
|
newIndex = index + 1;
|
||||||
// 交换位置
|
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] = [tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
||||||
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] =
|
|
||||||
[tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
|
||||||
// 触发重绘
|
|
||||||
tempChartConfigs.value = [...tempChartConfigs.value];
|
tempChartConfigs.value = [...tempChartConfigs.value];
|
||||||
// 添加动画效果
|
|
||||||
highlightRow(index);
|
highlightRow(index);
|
||||||
highlightRow(newIndex);
|
highlightRow(newIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 高亮行(动画效果)- 保留按钮移动后的高亮效果
|
|
||||||
const highlightRow = (index) => {
|
const highlightRow = (index) => {
|
||||||
const rowEl = document.querySelector(`.el-table__row:nth-child(${index + 1})`);
|
const rowEl = document.querySelector(`.el-table__row:nth-child(${index + 1})`);
|
||||||
if (rowEl) {
|
if (rowEl) {
|
||||||
rowEl.classList.add('row-highlight');
|
rowEl.classList.add('row-highlight');
|
||||||
setTimeout(() => {
|
setTimeout(() => rowEl.classList.remove('row-highlight'), 600);
|
||||||
rowEl.classList.remove('row-highlight');
|
|
||||||
}, 600);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 应用批量设置
|
|
||||||
const applyBatchSettings = () => {
|
const applyBatchSettings = () => {
|
||||||
// 过滤需要应用设置的图表
|
|
||||||
const targetCharts = batchSettings.applyRange === 'visible'
|
const targetCharts = batchSettings.applyRange === 'visible'
|
||||||
? tempChartConfigs.value.filter(chart => chart.visible)
|
? tempChartConfigs.value.filter(chart => chart.visible)
|
||||||
: tempChartConfigs.value;
|
: tempChartConfigs.value;
|
||||||
@@ -343,15 +346,10 @@ const applyBatchSettings = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用设置
|
|
||||||
targetCharts.forEach(chart => {
|
targetCharts.forEach(chart => {
|
||||||
// 应用高度设置
|
if (batchSettings.height !== null && !isNaN(batchSettings.height) && batchSettings.height >= 100 && batchSettings.height <= 1000) {
|
||||||
if (batchSettings.height !== null && !isNaN(batchSettings.height) &&
|
|
||||||
batchSettings.height >= 100 && batchSettings.height <= 1000) {
|
|
||||||
chart.height = batchSettings.height;
|
chart.height = batchSettings.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用布局设置
|
|
||||||
Object.keys(batchSettings.layout).forEach(key => {
|
Object.keys(batchSettings.layout).forEach(key => {
|
||||||
if (batchSettings.layout[key] !== null) {
|
if (batchSettings.layout[key] !== null) {
|
||||||
chart.layout[key] = batchSettings.layout[key];
|
chart.layout[key] = batchSettings.layout[key];
|
||||||
@@ -359,35 +357,25 @@ const applyBatchSettings = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 刷新表格
|
|
||||||
tempChartConfigs.value = [...tempChartConfigs.value];
|
tempChartConfigs.value = [...tempChartConfigs.value];
|
||||||
showBatchSettings.value = false;
|
showBatchSettings.value = false;
|
||||||
ElMessage.success(`已对 ${targetCharts.length} 个图表应用批量设置`);
|
ElMessage.success(`已对 ${targetCharts.length} 个图表应用批量设置`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 应用设置
|
|
||||||
const applySettings = async () => {
|
const applySettings = async () => {
|
||||||
try {
|
try {
|
||||||
// 验证所有高度输入
|
const hasInvalidHeight = tempChartConfigs.value.some((row, index) => !validateHeight(row, index));
|
||||||
const hasInvalidHeight = tempChartConfigs.value.some((row, index) =>
|
|
||||||
!validateHeight(row, index)
|
|
||||||
);
|
|
||||||
if (hasInvalidHeight) {
|
if (hasInvalidHeight) {
|
||||||
ElMessage.error('存在无效的高度配置,请修正后重试');
|
ElMessage.error('存在无效的高度配置,请修正后重试');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有可见图表
|
|
||||||
const visibleCharts = tempChartConfigs.value.filter(chart => chart.visible);
|
const visibleCharts = tempChartConfigs.value.filter(chart => chart.visible);
|
||||||
if (visibleCharts.length === 0) {
|
if (visibleCharts.length === 0) {
|
||||||
await ElMessageBox.confirm(
|
await ElMessageBox.confirm(
|
||||||
'所有图表都处于隐藏状态,确定要应用设置吗?',
|
'所有图表都处于隐藏状态,确定要应用设置吗?',
|
||||||
'确认提示',
|
'确认提示',
|
||||||
{
|
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +389,7 @@ const applySettings = async () => {
|
|||||||
throw new Error('配置数据格式错误,必须为数组');
|
throw new Error('配置数据格式错误,必须为数组');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error !== 'cancel') { // 忽略取消操作的错误
|
if (error !== 'cancel') {
|
||||||
console.error('保存配置失败:', error);
|
console.error('保存配置失败:', error);
|
||||||
ElMessage.error('保存配置失败,请重试');
|
ElMessage.error('保存配置失败,请重试');
|
||||||
}
|
}
|
||||||
@@ -410,18 +398,12 @@ const applySettings = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 定义事件
|
|
||||||
const emits = defineEmits(['config-updated', 'close']);
|
const emits = defineEmits(['config-updated', 'close']);
|
||||||
|
defineExpose({ resetToSession, applySettings });
|
||||||
// 暴露方法给父组件
|
|
||||||
defineExpose({
|
|
||||||
resetToSession,
|
|
||||||
applySettings
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 基础样式 */
|
/* 原有样式保持不变,新增预览区域样式 */
|
||||||
.chart-settings-panel {
|
.chart-settings-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -430,14 +412,11 @@ defineExpose({
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 移除所有拖拽相关样式 */
|
|
||||||
|
|
||||||
/* 表格行动画效果 */
|
/* 表格行动画效果 */
|
||||||
.el-table__body .el-table-row {
|
.el-table__body .el-table-row {
|
||||||
transition: transform 0.3s ease, opacity 0.3s ease, background-color 0.3s ease;
|
transition: transform 0.3s ease, opacity 0.3s ease, background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 行高亮动画 - 保留按钮移动后的高亮效果 */
|
|
||||||
.row-highlight {
|
.row-highlight {
|
||||||
animation: rowHighlight 0.6s ease;
|
animation: rowHighlight 0.6s ease;
|
||||||
}
|
}
|
||||||
@@ -447,7 +426,6 @@ defineExpose({
|
|||||||
100% { background-color: transparent; }
|
100% { background-color: transparent; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 高度错误提示 */
|
|
||||||
.height-error {
|
.height-error {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
@@ -461,7 +439,6 @@ defineExpose({
|
|||||||
20%, 40%, 60%, 80% { transform: translateX(2px); }
|
20%, 40%, 60%, 80% { transform: translateX(2px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 其他样式 */
|
|
||||||
.panel-header {
|
.panel-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -512,6 +489,92 @@ defineExpose({
|
|||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 新增:预览区域样式 */
|
||||||
|
.layout-preview {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-preview {
|
||||||
|
height: 200px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-row {
|
||||||
|
margin: 0 !important; /* 清除el-row默认margin */
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-col {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-card {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e5e7eb !important;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: #f9fafb;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-id {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-placeholder {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-bg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #f0f2f5 0%, #e5e6eb 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-info {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-info p {
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 响应式调整 */
|
/* 响应式调整 */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.chart-settings-panel {
|
.chart-settings-panel {
|
||||||
@@ -544,5 +607,9 @@ defineExpose({
|
|||||||
.section-title {
|
.section-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-preview {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<el-dialog v-model="settingVisible" title="图表设置" width="70%">
|
<el-dialog v-model="settingVisible" title="图表设置" width="80%">
|
||||||
<ChartSetting />
|
<ChartSetting />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,143 +3,14 @@
|
|||||||
<!-- 第一行:头像+欢迎语 -->
|
<!-- 第一行:头像+欢迎语 -->
|
||||||
<UserGreeting />
|
<UserGreeting />
|
||||||
|
|
||||||
<!-- 业务功能区 -->
|
|
||||||
<div class="business-area-header">
|
|
||||||
<h2 class="business-area-title">常用应用</h2>
|
|
||||||
<button class="edit-btn" @click="showSettingDialog = true">
|
|
||||||
<svg-icon icon-class="edit" /> 编辑常用
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="business-modules">
|
|
||||||
<div v-for="(module, index) in businessModules" :key="index"
|
|
||||||
class="business-module" @click="handleLink(module.link)">
|
|
||||||
<div class="business-module-icon">
|
|
||||||
<svg-icon :icon-class="module.icon" />
|
|
||||||
</div>
|
|
||||||
<h3 class="business-module-title">{{ module.title }}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 全部应用 -->
|
<!-- 全部应用 -->
|
||||||
<AllApplications @addFavorites="handleAddFavorites" />
|
<AllApplications @addFavorites="handleAddFavorites" />
|
||||||
|
|
||||||
<!-- 设置弹窗 -->
|
|
||||||
<div class="dialog-mask" v-if="showSettingDialog">
|
|
||||||
<div class="setting-dialog">
|
|
||||||
<div class="dialog-header">
|
|
||||||
<h3>编辑常用应用</h3>
|
|
||||||
<button class="close-btn" @click="showSettingDialog = false">
|
|
||||||
<svg-icon icon-class="close" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="empty-tip" v-if="businessModules.length === 0">
|
|
||||||
暂无常用应用,可从下方"全部应用"中添加
|
|
||||||
</div>
|
|
||||||
<ul class="module-list" v-else>
|
|
||||||
<li v-for="(module, index) in businessModules" :key="index" class="module-item">
|
|
||||||
<div class="module-info">
|
|
||||||
<span class="module-index">{{ index + 1 }}</span>
|
|
||||||
<span class="module-name">{{ module.title }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="module-actions">
|
|
||||||
<el-button
|
|
||||||
@click="moveModule(index, 'up')"
|
|
||||||
:disabled="index === 0"
|
|
||||||
icon="ArrowUp" type="primary"
|
|
||||||
>
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
@click="moveModule(index, 'down')"
|
|
||||||
:disabled="index === businessModules.length - 1"
|
|
||||||
icon="ArrowDown" type="primary"
|
|
||||||
>
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="deleteModule(index)" icon="Delete" danger>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<button class="cancel-btn" @click="showSettingDialog = false">取消</button>
|
|
||||||
<button class="confirm-btn" @click="showSettingDialog = false">完成</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
|
||||||
import AllApplications from '@/components/AllApplications.vue';
|
import AllApplications from '@/components/AllApplications.vue';
|
||||||
import UserGreeting from '@/views/components/Hello.vue';
|
import UserGreeting from '@/views/components/Hello.vue';
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { useStorage } from '@vueuse/core'
|
|
||||||
|
|
||||||
// 响应式数据
|
|
||||||
const businessModules = useStorage('businessModules', [
|
|
||||||
{
|
|
||||||
title: '考勤日历',
|
|
||||||
icon: 'code',
|
|
||||||
link: '/people/calendar'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '系统设置',
|
|
||||||
icon: 'system',
|
|
||||||
link: '/system/menu'
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const showSettingDialog = ref(false); // 控制弹窗显示
|
|
||||||
|
|
||||||
// 路由实例
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
// 方法定义
|
|
||||||
const handleLink = (link) => {
|
|
||||||
if (link.startsWith('http') || link.endsWith('.html')) {
|
|
||||||
window.open(link, '_blank');
|
|
||||||
} else {
|
|
||||||
router.push(link);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddFavorites = (appInfo) => {
|
|
||||||
// 检查是否已经存在,如果已经存在就移除,否则添加
|
|
||||||
const index = businessModules.value.findIndex(module => module.link === appInfo.fullPath)
|
|
||||||
if (index !== -1) {
|
|
||||||
businessModules.value.splice(index, 1)
|
|
||||||
} else {
|
|
||||||
businessModules.value.push({
|
|
||||||
title: appInfo.child.meta.title,
|
|
||||||
icon: appInfo.child.meta.icon,
|
|
||||||
link: appInfo.fullPath
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 移动模块位置(排序功能)
|
|
||||||
const moveModule = (index, direction) => {
|
|
||||||
// 创建副本避免直接修改源数组导致的响应式问题
|
|
||||||
const newModules = [...businessModules.value];
|
|
||||||
// 上移:与前一个元素交换位置
|
|
||||||
if (direction === 'up' && index > 0) {
|
|
||||||
[newModules[index], newModules[index - 1]] = [newModules[index - 1], newModules[index]];
|
|
||||||
}
|
|
||||||
// 下移:与后一个元素交换位置
|
|
||||||
if (direction === 'down' && index < newModules.length - 1) {
|
|
||||||
[newModules[index], newModules[index + 1]] = [newModules[index + 1], newModules[index]];
|
|
||||||
}
|
|
||||||
// 更新响应式数据
|
|
||||||
businessModules.value = newModules;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除模块
|
|
||||||
const deleteModule = (index) => {
|
|
||||||
if (confirm('确定要移除该常用应用吗?')) {
|
|
||||||
businessModules.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,70 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
|
<div class="login-right">
|
||||||
|
<img :src="RightImage" alt="" class="right-img" />
|
||||||
|
</div>
|
||||||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
||||||
<h3 class="title">
|
<h3 class="title">
|
||||||
<img :src="LogoImage" alt="公司 Logo" class="logo-img"/>
|
{{ title }}
|
||||||
{{ title }}
|
</h3>
|
||||||
</h3>
|
<el-form-item prop="username">
|
||||||
<el-form-item prop="username">
|
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
|
||||||
<el-input
|
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
|
||||||
v-model="loginForm.username"
|
</el-input>
|
||||||
type="text"
|
</el-form-item>
|
||||||
size="large"
|
<el-form-item prop="password">
|
||||||
auto-complete="off"
|
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码"
|
||||||
placeholder="账号"
|
@keyup.enter="handleLogin">
|
||||||
>
|
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
||||||
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
|
</el-input>
|
||||||
</el-input>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item prop="code" v-if="captchaEnabled">
|
||||||
<el-form-item prop="password">
|
<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%"
|
||||||
<el-input
|
@keyup.enter="handleLogin">
|
||||||
v-model="loginForm.password"
|
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
||||||
type="password"
|
</el-input>
|
||||||
size="large"
|
<div class="login-code">
|
||||||
auto-complete="off"
|
<img :src="codeUrl" @click="getCode" class="login-code-img" />
|
||||||
placeholder="密码"
|
</div>
|
||||||
@keyup.enter="handleLogin"
|
</el-form-item>
|
||||||
>
|
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
||||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
<el-form-item style="width:100%;">
|
||||||
</el-input>
|
<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin">
|
||||||
</el-form-item>
|
<span v-if="!loading">登 录</span>
|
||||||
<el-form-item prop="code" v-if="captchaEnabled">
|
<span v-else>登 录 中...</span>
|
||||||
<el-input
|
</el-button>
|
||||||
v-model="loginForm.code"
|
<div style="float: right;" v-if="register">
|
||||||
size="large"
|
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
||||||
auto-complete="off"
|
</div>
|
||||||
placeholder="验证码"
|
</el-form-item>
|
||||||
style="width: 63%"
|
</el-form>
|
||||||
@keyup.enter="handleLogin"
|
|
||||||
>
|
|
||||||
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
|
||||||
</el-input>
|
|
||||||
<div class="login-code">
|
|
||||||
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
|
||||||
<el-form-item style="width:100%;">
|
|
||||||
<el-button
|
|
||||||
:loading="loading"
|
|
||||||
size="large"
|
|
||||||
type="primary"
|
|
||||||
style="width:100%;"
|
|
||||||
@click.prevent="handleLogin"
|
|
||||||
>
|
|
||||||
<span v-if="!loading">登 录</span>
|
|
||||||
<span v-else>登 录 中...</span>
|
|
||||||
</el-button>
|
|
||||||
<div style="float: right;" v-if="register">
|
|
||||||
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<div class="login-right">
|
|
||||||
<img :src="RightImage" alt="公司 Logo" class="right-img"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 底部 -->
|
<!-- 底部 -->
|
||||||
@@ -80,8 +54,8 @@ import Cookies from "js-cookie"
|
|||||||
import { encrypt, decrypt } from "@/utils/jsencrypt"
|
import { encrypt, decrypt } from "@/utils/jsencrypt"
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useProductStore from '@/store/modules/product'
|
import useProductStore from '@/store/modules/product'
|
||||||
import RightImage from '@/assets/images/right.jpg'
|
import RightImage from '@/assets/images/right.png'
|
||||||
import LogoImage from '@/assets/logo/logo-full.jpg'
|
import LogoImage from '@/assets/logo/logo.png'
|
||||||
|
|
||||||
const title = import.meta.env.VITE_APP_TITLE
|
const title = import.meta.env.VITE_APP_TITLE
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@@ -112,7 +86,7 @@ const register = ref(false)
|
|||||||
const redirect = ref(undefined)
|
const redirect = ref(undefined)
|
||||||
|
|
||||||
watch(route, (newRoute) => {
|
watch(route, (newRoute) => {
|
||||||
redirect.value = newRoute.query && newRoute.query.redirect
|
redirect.value = newRoute.query && newRoute.query.redirect
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
function handleLogin() {
|
function handleLogin() {
|
||||||
@@ -183,13 +157,14 @@ getCookie()
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-image: url("../assets/images/back.jpg");
|
background-color: #1a1a1a;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin: 0px auto 30px auto;
|
margin: 0px auto 30px auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #707070;
|
color: #ebebeb;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -201,8 +176,8 @@ getCookie()
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logo-img {
|
.logo-img {
|
||||||
width: 100px;
|
width: 50px;
|
||||||
height: 100px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-container {
|
.login-container {
|
||||||
@@ -211,21 +186,35 @@ getCookie()
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
// height: 60%;
|
// height: 60%;
|
||||||
border-radius: 6px;
|
|
||||||
background: #ffffff;
|
|
||||||
width: 60%;
|
width: 60%;
|
||||||
padding: 25px 25px 5px 25px;
|
padding: 25px 25px 5px 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: #333333;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
color: #ebebeb;
|
||||||
|
}
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
flex: 3;
|
flex: 3;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #292929;
|
||||||
|
padding: 25px;
|
||||||
|
|
||||||
.el-input {
|
.el-input {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-icon {
|
.input-icon {
|
||||||
height: 39px;
|
height: 39px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
@@ -234,6 +223,9 @@ getCookie()
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-right {
|
.login-right {
|
||||||
|
display: flex;
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
flex: 4;
|
flex: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,15 +234,18 @@ getCookie()
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: #bfbfbf;
|
color: #bfbfbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-code {
|
.login-code {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-login-footer {
|
.el-login-footer {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
@@ -263,6 +258,7 @@ getCookie()
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-code-img {
|
.login-code-img {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
|||||||
Reference in New Issue
Block a user