Commit 0fd0cb01 by Sweet Zhang

分页对接,菜单可折叠

parent 79fddc42
...@@ -6,7 +6,12 @@ ...@@ -6,7 +6,12 @@
<!-- 主应用布局 --> <!-- 主应用布局 -->
<div v-else class="flex flex-1 h-screen"> <div v-else class="flex flex-1 h-screen">
<!-- 侧边导航 --> <!-- 侧边导航 -->
<Sidebar :current-page="currentPage" @logout="handleLogout" /> <Sidebar
:current-page="currentPage"
:collapsed="sidebarCollapsed"
@logout="handleLogout"
@toggle-collapse="toggleSidebar"
/>
<!-- 移动端菜单按钮 --> <!-- 移动端菜单按钮 -->
<button <button
...@@ -25,7 +30,10 @@ ...@@ -25,7 +30,10 @@
/> />
<!-- 主内容区域 --> <!-- 主内容区域 -->
<div class="flex-1 flex flex-col"> <div
class="flex-1 flex flex-col transition-all duration-300"
:class="sidebarCollapsed ? 'ml-16' : 'ml-64'"
>
<main class="flex-1 overflow-y-auto bg-gray-50 p-4 md:p-6"> <main class="flex-1 overflow-y-auto bg-gray-50 p-4 md:p-6">
<header class="mb-6"> <header class="mb-6">
<h2 class="text-2xl font-bold text-gray-800"> <h2 class="text-2xl font-bold text-gray-800">
...@@ -47,6 +55,7 @@ import { useRoute, useRouter } from 'vue-router' ...@@ -47,6 +55,7 @@ import { useRoute, useRouter } from 'vue-router'
import LoginPage from './views/LoginPage.vue' import LoginPage from './views/LoginPage.vue'
import Sidebar from './views/Sidebar.vue' import Sidebar from './views/Sidebar.vue'
import MobileSidebar from './views/MobileSidebar.vue' import MobileSidebar from './views/MobileSidebar.vue'
import { pageTitles } from '@/utils/menuConfig'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
...@@ -56,6 +65,7 @@ const isLoginPage = ref(true) ...@@ -56,6 +65,7 @@ const isLoginPage = ref(true)
const isAuthenticated = ref(false) const isAuthenticated = ref(false)
const currentPage = ref('compose') const currentPage = ref('compose')
const showMobileMenu = ref(false) const showMobileMenu = ref(false)
const sidebarCollapsed = ref(false) // 新增:侧边栏折叠状态
// 监听路由变化,更新当前页面状态 // 监听路由变化,更新当前页面状态
watch( watch(
...@@ -67,13 +77,9 @@ watch( ...@@ -67,13 +77,9 @@ watch(
}, },
) )
// 页面标题映射(保持不变) // 切换侧边栏折叠状态
const pageTitles = { const toggleSidebar = () => {
compose: '写邮件', sidebarCollapsed.value = !sidebarCollapsed.value
contacts: '联系人管理',
senders: '发件人管理',
variables: '变量管理',
emails: '邮件记录',
} }
// 方法(移除页面切换相关方法) // 方法(移除页面切换相关方法)
......
// 菜单项接口定义
export interface MenuItem {
name: string
path: string
icon: string
title: string
}
// 菜单配置
export const menuConfig: MenuItem[] = [
{
name: 'compose',
path: '/compose',
icon: 'fas fa-pen',
title: '写邮件',
},
{
name: 'contacts',
path: '/contacts',
icon: 'fas fa-address-book',
title: '联系人管理',
},
{
name: 'senders',
path: '/senders',
icon: 'fas fa-user-circle',
title: '发件人管理',
},
{
name: 'variables',
path: '/variables',
icon: 'fas fa-file-excel',
title: '变量管理',
},
{
name: 'emails',
path: '/emails',
icon: 'fas fa-history',
title: '邮件记录',
},
]
// 页面标题映射
export const pageTitles: Record<string, string> = {
compose: '写邮件',
contacts: '联系人管理',
senders: '发件人管理',
variables: '变量管理',
emails: '邮件记录',
}
...@@ -196,7 +196,7 @@ ...@@ -196,7 +196,7 @@
:records="importRecords" :records="importRecords"
@update-record="updateImportRecord" @update-record="updateImportRecord"
@delete-record="deleteImportRecord" @delete-record="deleteImportRecord"
@close="((showImportRecordManager = false), getImportedContacts())" @close="showImportRecordManager = false"
/> />
<!-- 导入数据弹窗 --> <!-- 导入数据弹窗 -->
...@@ -397,7 +397,7 @@ const updateImportRecord = (updatedRecord: ImportRecord) => { ...@@ -397,7 +397,7 @@ const updateImportRecord = (updatedRecord: ImportRecord) => {
type: 'success', type: 'success',
}) })
// 更新成功之后,刷新导入记录列表 // 更新成功之后,刷新导入记录列表
getImportedContacts(emailForm.value.sessionId || '') getImportedContacts(emailForm.value.sessionId || '', 'update')
} else { } else {
ElMessage({ ElMessage({
message: '导入记录更新失败', message: '导入记录更新失败',
...@@ -468,7 +468,7 @@ const sendEmail = () => { ...@@ -468,7 +468,7 @@ const sendEmail = () => {
} }
// 通过sessionId获取导入的联系人 // 通过sessionId获取导入的联系人
const getImportedContacts = (sessionId?: string) => { const getImportedContacts = (sessionId?: string, type?: string) => {
const params = { const params = {
sessionId: sessionId || emailForm.value.sessionId || '', sessionId: sessionId || emailForm.value.sessionId || '',
source: importSource.value, source: importSource.value,
...@@ -477,9 +477,14 @@ const getImportedContacts = (sessionId?: string) => { ...@@ -477,9 +477,14 @@ const getImportedContacts = (sessionId?: string) => {
if (res.code === 200) { if (res.code === 200) {
console.log('导入的联系人:', res.data) console.log('导入的联系人:', res.data)
importRecords.value = res.data.records || [] importRecords.value = res.data.records || []
// 更新页面展示的抄送人和收件人 // 从导入记录中提取收件人和抄送人
// emailForm.value.receiveEmail = res.data?.receiveEmails || '' if (type == 'update') {
// emailForm.value.ccEmails = res.data?.ccEmails || '' debugger
emailForm.value.receiveEmail = importRecords.value
.map((item) => item.receiveEmail)
.join(',')
emailForm.value.ccEmails = importRecords.value.map((item) => item.ccEmail).join(',')
}
} }
}) })
} }
......
...@@ -9,34 +9,14 @@ ...@@ -9,34 +9,14 @@
</div> </div>
<nav> <nav>
<ul> <ul>
<li class="mb-2"> <li v-for="menu in menuItems" :key="menu.name" class="mb-2">
<router-link <router-link
to="/compose" :to="menu.path"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block" class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'compose' }" :class="{ 'bg-blue-500': currentPage === menu.name }"
@click="$emit('close-menu')" @click="$emit('close-menu')"
> >
<i class="fas fa-pen mr-2"></i>写邮件 <i :class="menu.icon" class="mr-2"></i>{{ menu.title }}
</router-link>
</li>
<li class="mb-2">
<router-link
to="/variables"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'variables' }"
@click="$emit('close-menu')"
>
<i class="fas fa-file-excel mr-2"></i>变量管理
</router-link>
</li>
<li class="mb-2">
<router-link
to="/emails"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'emails' }"
@click="$emit('close-menu')"
>
<i class="fas fa-history mr-2"></i>邮件记录
</router-link> </router-link>
</li> </li>
</ul> </ul>
...@@ -53,16 +33,22 @@ ...@@ -53,16 +33,22 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { menuConfig } from '@/utils/menuConfig'
const router = useRouter() const router = useRouter()
const currentPage = ref(router.currentRoute.value.name as string) const currentPage = ref(router.currentRoute.value.name as string)
// 使用统一的菜单配置
const menuItems = computed(() => menuConfig)
watch( watch(
() => router.currentRoute.value.name, () => router.currentRoute.value.name,
(name) => { (name) => {
if (name) {
currentPage.value = name as string currentPage.value = name as string
}
}, },
) )
</script> </script>
<template> <template>
<aside <aside
class="bg-sky-700 text-white w-64 flex-shrink-0 hidden md:block transition-all duration-300 ease-in-out" class="bg-sky-700 text-white flex-shrink-0 hidden md:block transition-all duration-300 ease-in-out fixed h-full z-40"
:class="collapsed ? 'w-16' : 'w-64'"
> >
<div class="p-4 border-b border-blue-500"> <!-- 顶部区域 -->
<h1 class="text-xl font-bold">邮件系统</h1> <div class="p-4 border-b border-blue-500 flex items-center justify-between">
<h1
class="text-xl font-bold transition-all duration-300"
:class="collapsed ? 'opacity-0 w-0' : 'opacity-100'"
>
邮件系统
</h1>
<button
@click="$emit('toggle-collapse')"
class="p-1 rounded hover:bg-blue-500 transition-colors flex items-center justify-center"
:title="collapsed ? '展开菜单' : '折叠菜单'"
>
<i class="fas" :class="collapsed ? 'fa-chevron-right' : 'fa-chevron-left'"></i>
</button>
</div> </div>
<!-- 导航菜单 -->
<nav class="p-4"> <nav class="p-4">
<ul> <ul>
<li class="mb-2"> <li v-for="menu in menuItems" :key="menu.name" class="mb-2">
<router-link
to="/compose"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'compose' }"
>
<i class="fas fa-pen mr-2"></i>写邮件
</router-link>
</li>
<li class="mb-2">
<router-link
to="/contacts"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'contacts' }"
>
<i class="fas fa-address-book mr-2"></i>联系人管理
</router-link>
</li>
<li class="mb-2">
<router-link <router-link
to="/senders" :to="menu.path"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block" class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center"
:class="{ 'bg-blue-500': currentPage === 'senders' }" :class="{
'bg-blue-500': currentPage === menu.name,
'justify-center': collapsed,
'justify-start': !collapsed,
}"
:title="collapsed ? menu.title : ''"
> >
<i class="fas fa-user-circle mr-2"></i>发件人管理 <i :class="[menu.icon, collapsed ? '' : 'mr-2']" class="flex-shrink-0"></i>
</router-link> <span
</li> class="transition-all duration-300"
<li class="mb-2"> :class="collapsed ? 'opacity-0 w-0 ml-0' : 'opacity-100 ml-2'"
<router-link
to="/variables"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'variables' }"
> >
<i class="fas fa-file-excel mr-2"></i>变量管理 {{ menu.title }}
</router-link> </span>
</li>
<li class="mb-2">
<router-link
to="/emails"
class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center block"
:class="{ 'bg-blue-500': currentPage === 'emails' }"
>
<i class="fas fa-history mr-2"></i>邮件记录
</router-link> </router-link>
</li> </li>
</ul> </ul>
</nav> </nav>
<!-- 底部退出按钮 -->
<div class="absolute bottom-4 left-0 right-0 px-4"> <div class="absolute bottom-4 left-0 right-0 px-4">
<button <button
@click="$emit('logout')" @click="$emit('logout')"
class="text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center text-sm" class="w-full text-left px-4 py-2 rounded hover:bg-blue-500 transition-colors flex items-center justify-center text-sm"
:class="collapsed ? 'justify-center' : 'justify-start'"
:title="collapsed ? '退出登录' : ''"
>
<i class="fas fa-sign-out-alt flex-shrink-0" :class="collapsed ? '' : 'mr-2'"></i>
<span
class="transition-all duration-300"
:class="collapsed ? 'opacity-0 w-0' : 'opacity-100'"
> >
<i class="fas fa-sign-out-alt mr-2"></i>退出登录 退出登录
</span>
</button> </button>
</div> </div>
</aside> </aside>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, defineEmits } from 'vue' import { defineProps, defineEmits, computed } from 'vue'
import { menuConfig } from '@/utils/menuConfig'
const props = defineProps({ const props = defineProps({
currentPage: { currentPage: {
type: String, type: String,
required: true, required: true,
}, },
collapsed: {
type: Boolean,
default: false,
},
}) })
const emits = defineEmits(['logout']) const emits = defineEmits(['logout', 'toggle-collapse'])
// 使用统一的菜单配置
const menuItems = computed(() => menuConfig)
</script> </script>
<style scoped>
/* 确保折叠时图标居中 */
.router-link-active {
@apply bg-blue-500;
}
/* 优化折叠状态下的样式 */
aside {
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
}
/* 防止文字在折叠时显示 */
span {
white-space: nowrap;
overflow: hidden;
}
/* 增加菜单项的可点击区域 */
li a {
min-height: 44px; /* 确保触摸友好的最小高度 */
cursor: pointer;
}
/* 优化折叠状态下的交互体验 */
li a:hover {
background-color: rgba(59, 130, 246, 0.8);
}
/* 确保图标和文字垂直对齐 */
li a {
align-items: center;
}
/* 优化图标和文字的间距 */
i {
width: 1.25rem; /* 固定图标宽度 */
text-align: center;
}
</style>
...@@ -189,6 +189,16 @@ ...@@ -189,6 +189,16 @@
<i class="fas fa-variable text-4xl mb-3 opacity-30"></i> <i class="fas fa-variable text-4xl mb-3 opacity-30"></i>
<p>暂无变量,请添加变量</p> <p>暂无变量,请添加变量</p>
</div> </div>
<!-- 分页组件 -->
<Pagination
:total="total"
:current="currentPage"
:page-size="pageSize"
@change="handlePageChange"
@update:current="handleCurrentUpdate"
@update:page-size="handlePageSizeUpdate"
/>
</div> </div>
<!-- 变量模板弹窗 --> <!-- 变量模板弹窗 -->
...@@ -292,6 +302,31 @@ import type { Variable, VariableTemplate } from '../types' ...@@ -292,6 +302,31 @@ import type { Variable, VariableTemplate } from '../types'
import { variableApi, variableGroupApi } from '@/api/api' import { variableApi, variableGroupApi } from '@/api/api'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
// 引入分页组件
import Pagination from '@/components/Pagination.vue'
// 初始数据
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10)
// 处理分页变化
const handlePageChange = (page: number, size: number) => {
console.log('分页变化:', page, size)
fetchVariables()
// 这里可以发起API请求获取新数据
}
const handleCurrentUpdate = (page: number) => {
currentPage.value = page
}
const handlePageSizeUpdate = (size: number) => {
pageSize.value = size
currentPage.value = 1 // 重置到第一页
}
// 引入弹窗组件 // 引入弹窗组件
import CommonModal from '@/components/CommonModal.vue' import CommonModal from '@/components/CommonModal.vue'
// 弹窗提示信息对象 // 弹窗提示信息对象
...@@ -474,13 +509,14 @@ const deleteVariable = (id: string, type?: string) => { ...@@ -474,13 +509,14 @@ const deleteVariable = (id: string, type?: string) => {
// 查询变量列表 // 查询变量列表
const fetchVariables = () => { const fetchVariables = () => {
const params = { const params = {
pageNo: 1, pageNo: currentPage.value,
pageSize: 10, pageSize: pageSize.value,
} }
variableApi variableApi
.getEmailVariableList(params) .getEmailVariableList(params)
.then((res) => { .then((res) => {
variables.value = res.data.records || [] variables.value = res.data.records || []
total.value = res.data.total || 0
}) })
.catch((error) => { .catch((error) => {
console.error('查询变量列表失败:', error) console.error('查询变量列表失败:', error)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment