<template>
|
<div class="system-select-container">
|
<div class="user-info-bar">
|
<div class="user-left"></div>
|
<h3 class="user-details">欢迎访问多体系建设信息化系统</h3>
|
<div class="avatar-container">
|
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
<div class="avatar-wrapper" style="display: flex;align-items: center">
|
<img src="../assets/images/avator.png" class="user-avatar" />
|
<span style="font-size: 16px">{{userName}}({{userTypeName}})</span>
|
<el-icon><caret-bottom /></el-icon>
|
</div>
|
<template #dropdown>
|
<el-dropdown-menu>
|
<el-dropdown-item command="password">
|
<span>修改密码</span>
|
</el-dropdown-item>
|
<el-dropdown-item divided command="logout">
|
<span>退出登录</span>
|
</el-dropdown-item>
|
</el-dropdown-menu>
|
</template>
|
</el-dropdown>
|
</div>
|
</div>
|
|
<!-- 重新布局的系统选择区域 -->
|
<div class="systems-container">
|
<div class="layout-container">
|
<!-- 左侧列 -->
|
<div class="left-column">
|
<!-- 通知公告 -->
|
<div class="module-card notice-module">
|
<div class="module-header">
|
<h3>通知公告</h3>
|
<span class="more-link" @click="toNoticeMng">更多 ></span>
|
</div>
|
<div class="notice-list">
|
<div class="notice-item" v-for="item in noticeList" :key="item">
|
<span class="notice-title" @click="openNoticeFile(item.filePath)">{{item.content}}</span>
|
<span class="notice-date">{{item.publishDate}}</span>
|
</div>
|
</div>
|
</div>
|
|
<!-- 流程中心 -->
|
<div class="module-card process-module">
|
<div class="module-header">
|
<h3>流程中心</h3>
|
<!-- <span class="more-link">更多 ></span>-->
|
</div>
|
<div class="process-list" v-if="flowList && flowList.length>0">
|
<div class="process-item" v-for="process in flowList" :key="process.id">
|
<div class="process-info" @click="openDetail(process)">
|
<span class="process-status" :class="{processing: process.type == 1,pending: process.type == 2,success: process.type == 3,normal: process.type == 4,seal: process.type == 5}">
|
{{process.type == 1? '内审实施计划':process.type == 2? '培训计划':process.type == 3? '项目评审':process.type == 4?'年度检定计划':process.type == 5?'用章审批(申请)': '用章审批(待审批)'}}
|
</span>
|
<span class="process-name">{{process.title}}</span>
|
</div>
|
</div>
|
</div>
|
<div class="process-list" v-else>
|
<span style="color: #999;font-size: 16px">暂无流程</span>
|
</div>
|
</div>
|
|
<!-- 备忘录 -->
|
<div class="module-card memo-module">
|
<div class="module-header">
|
<h3>备忘录</h3>
|
<el-button @click="addMemo" type="primary">保存</el-button>
|
</div>
|
<div class="memo-content">
|
<div class="memo-input">
|
<el-input
|
v-model="memo.content"
|
placeholder="添加新的备忘录..."
|
type="textarea"
|
:autosize="{ minRows: 6, maxRows: 10}"
|
>
|
</el-input>
|
<div class="memo-time" v-if="memo.updateTime">{{memo.updateTime}}</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 右侧列 -->
|
<div class="right-column">
|
<!-- 快捷入口 -->
|
<div class="module-card quick-access-module">
|
<div class="module-header">
|
<h3>快捷入口</h3>
|
<div class="pagination-controls" v-if="platformList.length > 9">
|
<el-button
|
:icon="ArrowLeft"
|
@click="prevPage"
|
size="small"
|
:disabled="currentPage === 0"
|
circle
|
/>
|
<span class="page-info">{{ currentPage + 1 }}/{{ totalPages }}</span>
|
<el-button
|
:icon="ArrowRight"
|
@click="nextPage"
|
size="small"
|
:disabled="currentPage === totalPages - 1"
|
circle
|
/>
|
</div>
|
</div>
|
<div class="systems-grid-container">
|
<div class="systems-grid">
|
<div
|
v-for="(system, index) in displayedSystems"
|
:key="system.id"
|
class="system-card"
|
@mouseenter="handleCardEnter($event, system.id)"
|
@mousemove="handleCardMove($event, system.id)"
|
@mouseleave="handleCardLeave(system.id)"
|
@click="enterSystem(system.platformAddress, getActualIndex(index))"
|
:style="getCardStyle(system.id)"
|
>
|
<div class="card-content">
|
<div class="system-icon">
|
<el-image v-if="getActualIndex(index) == 0" :src="system.platformPic"/>
|
<el-image v-else :src="picUrl + system.platformPic"/>
|
</div>
|
<h3>{{ system.platformName }}</h3>
|
</div>
|
</div>
|
|
<!-- 填充空白格子 -->
|
<div
|
v-if="showEmptyCard && displayedSystems.length < 9"
|
class="system-card empty-card"
|
@click="openAdd('add',{})"
|
>
|
<div class="card-content">
|
<div class="system-icon">
|
<el-icon><Plus /></el-icon>
|
</div>
|
<h3>新增平台</h3>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 日历 -->
|
<div class="module-card calendar-module">
|
<div class="module-header">
|
<h3>日历</h3>
|
</div>
|
<div class="calendar-header">
|
<div class="lunar-info">
|
<span class="lunar-date-full">{{ currentLunarDate }}</span>
|
<span class="lunar-year">{{ currentLunarYear }}</span>
|
</div>
|
<div class="current-date-info">
|
<div class="solar-date-large">{{ currentSolarDate }}</div>
|
<div class="week-day">{{ currentWeekDay }}</div>
|
</div>
|
<div class="calendar-actions">
|
<el-button-group>
|
<el-button :icon="ArrowLeft" @click="prevMonth" size="small" />
|
<el-button @click="goToday" size="small">今天</el-button>
|
<el-button :icon="ArrowRight" @click="nextMonth" size="small" />
|
</el-button-group>
|
</div>
|
</div>
|
<div class="calendar-content">
|
<el-calendar v-model="currentDate" ref="calendarRef">
|
<template #header>
|
<!-- 隐藏默认header -->
|
<div style="display: none;"></div>
|
</template>
|
<template #date-cell="{ data }">
|
<div class="calendar-date" :class="{ 'is-today': isToday(data.day), 'is-current-month': isCurrentMonth(data.day) }">
|
<div class="solar-date">{{ getSolarDate(data.day) }}</div>
|
<div class="lunar-date">{{ getLunarDate(data.day) }}</div>
|
<div v-if="hasEvent(data.day)" class="calendar-event-dot"></div>
|
</div>
|
</template>
|
</el-calendar>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<user-dialog ref="reviewRef"></user-dialog>
|
<edit-dialog ref="dialogRef" @getList=getPlatformList></edit-dialog>
|
<sealDialog ref="noticeRef"></sealDialog>
|
</div>
|
</template>
|
|
<script setup>
|
import {ref, onMounted, computed, reactive, toRefs, watch} from 'vue'
|
import { useRouter, useRoute } from 'vue-router'
|
import { getToken, removeToken } from "@/utils/auth";
|
import Cookies from "js-cookie";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import { Plus, ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
|
import useUserStore from '@/store/modules/user'
|
import userDialog from '@/views/build/conpanyFunctionConsult/staffManage/staffRegister/components/staffDialog.vue'
|
import editDialog from '@/views/build/conpanyFunctionConsult/infoPlatform/components/editDialog.vue'
|
import sealDialog from "@/views/work/sealManagement/apply/components/editDialog"
|
import menu1 from '@/assets/icons/menu1.png'
|
import menu2 from '@/assets/icons/menu2.png'
|
import menu3 from '@/assets/icons/menu3.png'
|
import menu4 from '@/assets/icons/menu4.png'
|
import menu5 from '@/assets/icons/menu5.png'
|
import menu6 from '@/assets/icons/menu6.png'
|
|
// 引入农历库
|
import * as lunarCalendar from 'lunar-calendar'
|
import {getIndexTitle, getMemoList, listNotice, updateMemo} from "@/api/system/notice";
|
import {renderAsync} from "docx-preview";
|
import {getInfoPlatforms} from "@/api/staffManage/staff";
|
import {getCompany} from "@/api/onlineEducation/company";
|
import {getSealApply} from "@/api/sealManage/apply";
|
|
const router = useRouter()
|
const route = useRoute();
|
const reviewRef = ref();
|
const userInfo = ref();
|
const userName = ref('')
|
const userTypeName = ref('')
|
const cardStates = ref({})
|
const currentDate = ref(new Date())
|
const newMemo = ref('')
|
const calendarRef = ref()
|
const dialogRef = ref()
|
const noticeRef = ref();
|
const userStore = useUserStore()
|
const state = reactive({
|
noticeParams: {
|
pageNum: 1,
|
pageSize: 6,
|
companyId: null
|
},
|
platformParams: {
|
pageNum: 1,
|
pageSize: 99,
|
companyId: null
|
},
|
noticeList: [],
|
platformList: [],
|
picUrl: import.meta.env.VITE_APP_BASE_API + '/',
|
isAdmin: false,
|
companyList: [],
|
flowList: [],
|
memo: {}
|
})
|
const { noticeParams,platformParams, noticeList,platformList,picUrl,isAdmin,companyList,flowList,memo } = toRefs(state)
|
// 组件挂载时获取用户信息和系统列表
|
onMounted(async () => {
|
if(getToken()){
|
userInfo.value = JSON.parse(Cookies.get('userInfo'))
|
userName.value = userInfo.value.username
|
userTypeName.value = userInfo.value.userType == 0 ? '系统管理员' : (userInfo.value.userType == 1 || userInfo.value.userType == 2 || userInfo.value.userType == 3) ? '企业用户' :userInfo.value.userType == 6 ? '企业管理员' :userInfo.value.userType == 4 ? '其他' : '学员'
|
}
|
if(userStore.roles.includes('admin')){
|
state.noticeParams.companyId = null
|
state.platformParams.companyId = null
|
state.isAdmin = true
|
}else{
|
state.noticeParams.companyId = userStore.companyId
|
state.platformParams.companyId = userStore.companyId
|
state.isAdmin = false
|
}
|
await getNoticeList()
|
await getPlatformList()
|
await getFlowList()
|
await getMemo()
|
state.platformList.forEach(system => {
|
cardStates.value[system.id] = {
|
mouseX: 0,
|
mouseY: 0,
|
width: 0,
|
height: 0,
|
hover: false
|
}
|
})
|
})
|
|
const getSealDetail = async (type,sealId) => {
|
let param = {}
|
if(type == 5){
|
param = {
|
pageNum: 1,
|
pageSize: 999,
|
companyId: state.noticeParams.companyId,
|
applyUserId: userStore.id
|
}
|
}else{
|
param = {
|
pageNum: 1,
|
pageSize: 999,
|
companyId: state.noticeParams.companyId,
|
nextCheck: userStore.id
|
}
|
}
|
const res = await getSealApply(param);
|
if(res.code === 200){
|
return res.data.list.find(i=>i.id == sealId)
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}
|
|
const toNoticeMng = ()=>{
|
router.push({ path: "/work/noticeMng" });
|
}
|
const openDetail = async (value) => {
|
if(value.type == '5' || value.type == '6'){
|
const data = await getSealDetail(Number(value.type),Number(value.dataId))
|
noticeRef.value.openDialog('review', data,state.companyList)
|
}
|
}
|
function getNoticeList() {
|
listNotice(state.noticeParams).then(res => {
|
state.noticeList = res.data.list
|
})
|
}
|
const getCompanyList = async ()=>{
|
const queryParams = {
|
pageNum: 1,
|
pageSize: 999
|
}
|
const res = await getCompany(queryParams)
|
if (res.code == 200) {
|
state.companyList = res.data.list?res.data.list:[]
|
} else {
|
ElMessage.warning(res.message)
|
}
|
}
|
|
const getPlatformList = async () => {
|
const res = await getInfoPlatforms(state.platformParams)
|
if(res.code == 200){
|
const originPlatform = {
|
id: 0,
|
platformName: '国军标9001C质量管理体系',
|
platformPic: menu1
|
}
|
state.platformList = [originPlatform, ...(Array.isArray(res.data) ? res.data : [])]
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}
|
const openAdd = async (type, value) => {
|
await getCompanyList()
|
dialogRef.value.openDialog(type, value, state.platformParams.companyId, state.isAdmin, state.companyList );
|
}
|
|
const getFlowList = async () => {
|
const res = await getIndexTitle({pageNum: 1,pageSize: 99})
|
if(res.code == 200){
|
state.flowList = Array.isArray(res.data.list) ? res.data.list : []
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}
|
|
const getMemo = async () => {
|
const res = await getMemoList()
|
if(res.code == 200){
|
state.memo = res.data ? res.data : {}
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}
|
|
// 分页相关
|
const currentPage = ref(0)
|
const pageSize = 9 // 九宫格,每页9个
|
|
// 计算总页数
|
const totalPages = computed(() => {
|
return Math.ceil(state.platformList.length / pageSize)
|
})
|
|
// 获取当前页显示的系统列表
|
const displayedSystems = computed(() => {
|
const start = currentPage.value * pageSize
|
const end = start + pageSize
|
return state.platformList.slice(start, end)
|
})
|
|
// 是否显示敬请期待的卡片(只在最后一页且系统总数不是9的倍数时显示)
|
const showEmptyCard = computed(() => {
|
// 如果是最后一页,并且系统总数不是9的倍数,且当前页显示的系统数量小于9
|
const isLastPage = currentPage.value === totalPages.value - 1
|
const totalCount = state.platformList.length
|
const currentCount = displayedSystems.value.length
|
|
return isLastPage && (totalCount % pageSize !== 0) && currentCount < 9
|
})
|
|
// 获取实际在原始数组中的索引
|
const getActualIndex = (displayIndex) => {
|
return currentPage.value * pageSize + displayIndex
|
}
|
|
// 分页方法
|
const prevPage = () => {
|
if (currentPage.value > 0) {
|
currentPage.value--
|
}
|
}
|
|
const nextPage = () => {
|
if (currentPage.value < totalPages.value - 1) {
|
currentPage.value++
|
}
|
}
|
|
// 重置分页(当系统列表变化时)
|
watch(state.platformList, () => {
|
currentPage.value = 0
|
})
|
|
const openNoticeFile = async(path)=>{
|
const ext = path.split('.').pop().toLowerCase();
|
if (ext === 'doc' || ext === 'xls' || ext === 'xlsx') {
|
ElMessageBox.confirm('暂不支持线上预览文件,是否下载查看?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
|
}).catch(() => {
|
console.log('取消预览')
|
});
|
return
|
}
|
if(ext === 'pdf'){
|
window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank')
|
return
|
}
|
try {
|
// 1. 获取文件
|
const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
|
const arrayBuffer = await response.arrayBuffer();
|
// 2. 创建新窗口
|
const win = window.open('', '_blank')
|
win.document.write(`
|
<!DOCTYPE html>
|
<html>
|
<head>
|
<title>预览</title>
|
<style>
|
body { margin: 20px; font-family: Arial; }
|
.docx-container { width: 100%; height: 100%; }
|
</style>
|
</head>
|
<body>
|
<div id="container" class="docx-container"></div>
|
</body>
|
</html>
|
`);
|
// 3. 渲染 DOCX
|
await renderAsync(arrayBuffer, win.document.getElementById('container'));
|
} catch (error) {
|
console.error('预览失败:', error);
|
alert(`预览失败: ${error.message}`);
|
}
|
}
|
// 十二生肖数组
|
const zodiacAnimals = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
|
|
// 获取生肖年份
|
const getZodiacYear = (lunarYear) => {
|
// 农历年份计算生肖:年份减去4后除以12取余数
|
// 因为鼠年对应4,牛年对应5,以此类推
|
const index = (lunarYear - 4) % 12
|
return index >= 0 ? zodiacAnimals[index] : zodiacAnimals[index + 12]
|
}
|
|
// 计算当前日期信息
|
const currentWeekDay = computed(() => {
|
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
return days[currentDate.value.getDay()]
|
})
|
|
const currentSolarDate = computed(() => {
|
const date = currentDate.value
|
return `${date.getMonth() + 1}月${date.getDate()}日`
|
})
|
|
const currentLunarDate = computed(() => {
|
try {
|
const date = currentDate.value
|
const year = date.getFullYear()
|
const month = date.getMonth() + 1
|
const day = date.getDate()
|
|
const lunar = lunarCalendar.solarToLunar(year, month, day)
|
if (lunar && lunar.lunarMonthName && lunar.lunarDayName) {
|
return `${lunar.lunarMonthName}${lunar.lunarDayName}`
|
}
|
return ''
|
} catch (error) {
|
console.error('获取农历日期错误:', error)
|
return ''
|
}
|
})
|
|
const currentLunarYear = computed(() => {
|
try {
|
const date = currentDate.value
|
const year = date.getFullYear()
|
const month = date.getMonth() + 1
|
const day = date.getDate()
|
|
const lunar = lunarCalendar.solarToLunar(year, month, day)
|
if (lunar && lunar.GanZhiYear) {
|
const zodiac = getZodiacYear(year)
|
return `${lunar.GanZhiYear}年【${zodiac}年】`
|
}
|
return ''
|
} catch (error) {
|
console.error('获取农历年份错误:', error)
|
return ''
|
}
|
})
|
|
// 月份导航方法
|
const prevMonth = () => {
|
const date = new Date(currentDate.value)
|
date.setMonth(date.getMonth() - 1)
|
currentDate.value = date
|
}
|
|
const nextMonth = () => {
|
const date = new Date(currentDate.value)
|
date.setMonth(date.getMonth() + 1)
|
currentDate.value = date
|
}
|
|
const goToday = () => {
|
currentDate.value = new Date()
|
}
|
|
// 添加备忘录
|
const addMemo = async () => {
|
if (state.memo.content) {
|
const data = {
|
id: state.memo.id || null,
|
companyId: state.noticeParams.companyId,
|
content: state.memo.content,
|
createById: userStore.id
|
}
|
const res = await updateMemo(data)
|
if(res.code == 200){
|
ElMessage.success('保存成功')
|
await getMemo()
|
}else{
|
ElMessage.warning(res.message)
|
}
|
newMemo.value = ''
|
}
|
}
|
|
// 获取公历日期(去掉前导零)
|
const getSolarDate = (dateString) => {
|
const date = new Date(dateString)
|
return date.getDate()
|
}
|
|
// 获取农历日期
|
const getLunarDate = (dateString) => {
|
try {
|
const date = new Date(dateString)
|
const year = date.getFullYear()
|
const month = date.getMonth() + 1
|
const day = date.getDate()
|
|
// 使用农历库获取农历信息
|
const lunar = lunarCalendar.solarToLunar(year, month, day)
|
|
if (lunar && lunar.lunarDayName) {
|
// 如果是初一,显示月份,否则显示日期
|
if (lunar.lunarDay === 1) {
|
return lunar.lunarMonthName + '月'
|
} else {
|
// 简化显示,只显示数字日期
|
const lunarDay = lunar.lunarDayName.replace('初', '').replace('十', '')
|
return lunarDay
|
}
|
}
|
|
return ''
|
} catch (error) {
|
console.error('获取农历日期错误:', error)
|
return ''
|
}
|
}
|
|
// 检查日期是否有事件
|
const hasEvent = (date) => {
|
const eventDates = []
|
return eventDates.includes(date)
|
}
|
|
// 检查是否是今天
|
const isToday = (dateString) => {
|
const today = new Date().toISOString().split('T')[0]
|
return dateString === today
|
}
|
|
// 检查是否是当前月份
|
const isCurrentMonth = (dateString) => {
|
const date = new Date(dateString)
|
const current = new Date(currentDate.value)
|
return date.getMonth() === current.getMonth() && date.getFullYear() === current.getFullYear()
|
}
|
|
const handleCardEnter = (event, id) => {
|
const card = event.currentTarget
|
cardStates.value[id] = {
|
...cardStates.value[id],
|
width: card.offsetWidth,
|
height: card.offsetHeight,
|
hover: true
|
}
|
}
|
|
const handleCardMove = (event, id) => {
|
if (!cardStates.value[id]?.hover) return
|
|
const card = event.currentTarget
|
const rect = card.getBoundingClientRect()
|
|
cardStates.value[id].mouseX = event.clientX - rect.left - cardStates.value[id].width / 2
|
cardStates.value[id].mouseY = event.clientY - rect.top - cardStates.value[id].height / 2
|
}
|
|
const handleCardLeave = (id) => {
|
cardStates.value[id].hover = false;
|
cardStates.value[id].mouseX = 0;
|
cardStates.value[id].mouseY = 0;
|
}
|
|
const getCardStyle = (id) => {
|
const state = cardStates.value[id] || {}
|
const mousePX = state.mouseX / (state.width || 1)
|
const mousePY = state.mouseY / (state.height || 1)
|
|
const rX = mousePX * 20
|
const rY = mousePY * -20
|
|
const tX = mousePX * -20
|
const tY = mousePY * -20
|
|
return {
|
transform: `rotateY(${rX}deg) rotateX(${rY}deg)`,
|
'--bg-transform': `translateX(${tX}px) translateY(${tY}px)`
|
}
|
}
|
|
function handleCommand(command) {
|
switch (command) {
|
case "info":
|
getInfo();
|
break;
|
case "logout":
|
logout();
|
break;
|
case "password":
|
editPsd();
|
break;
|
default:
|
break;
|
}
|
}
|
|
const enterSystem = (address,index) => {
|
if(index == 0){
|
router.push({ path: "/learn/standardSysTemp/sysStandardModule"})
|
}else{
|
window.open(address)
|
// ElMessage.warning('系统正在开发中...')
|
}
|
}
|
|
function getInfo() {
|
reviewRef.value.openDialog('view',userInfo.value)
|
}
|
|
function editPsd() {
|
reviewRef.value.openDialog('pwd',userInfo.value)
|
}
|
|
function logout() {
|
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning'
|
}).then(() => {
|
removeToken()
|
location.href = '/homePage';
|
}).catch(() => { });
|
}
|
</script>
|
|
<style scoped lang="scss">
|
.system-select-container {
|
height: 100vh;
|
display: flex;
|
flex-direction: column;
|
background-color: #f5f7fa;
|
}
|
|
.user-info-bar {
|
display: flex;
|
height: 100px;
|
align-items: center;
|
justify-content: space-between;
|
padding: 10px 20px;
|
background-color: #ffffff;
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
.user-left{
|
width: 20%;
|
}
|
|
.user-details{
|
width: 60%;
|
margin: 0;
|
color: #333;
|
text-align: center;
|
font-size: 28px;
|
letter-spacing: 2px;
|
}
|
|
.avatar-container {
|
width: 20%;
|
display: flex;
|
justify-content: center;
|
|
.avatar-wrapper {
|
position: relative;
|
|
.user-avatar {
|
cursor: pointer;
|
width: 40px;
|
height: 40px;
|
border-radius: 10px;
|
margin-right: 15px;
|
}
|
|
i {
|
cursor: pointer;
|
position: absolute;
|
right: -20px;
|
font-size: 12px;
|
}
|
}
|
}
|
}
|
|
.systems-container {
|
flex: 1;
|
padding: 20px;
|
overflow-y: auto;
|
width: 100%;
|
}
|
|
.layout-container {
|
display: flex;
|
gap: 20px;
|
width: 100%;
|
height: 100%;
|
max-width: none; /* 移除最大宽度限制 */
|
margin: 0; /* 移除居中margin */
|
}
|
|
// 左侧列 - 宽度占比2
|
.left-column {
|
flex: 2;
|
display: flex;
|
flex-direction: column;
|
gap: 20px;
|
min-width: 0; /* 防止内容溢出 */
|
}
|
|
// 右侧列 - 宽度占比1
|
.right-column {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
gap: 20px;
|
min-width: 0; /* 防止内容溢出 */
|
min-width: 400px; /* 设置最小宽度避免过窄 */
|
}
|
|
// 通用模块卡片样式
|
.module-card {
|
background: #ffffff;
|
border-radius: 8px;
|
padding: 20px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
transition: box-shadow 0.3s ease;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
|
&:hover {
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
}
|
}
|
|
.module-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 16px;
|
padding-bottom: 12px;
|
border-bottom: 1px solid #f0f0f0;
|
flex-shrink: 0; /* 防止header被压缩 */
|
|
h3 {
|
margin: 0;
|
color: #333;
|
font-size: 18px;
|
font-weight: 600;
|
}
|
|
.more-link {
|
color: #409eff;
|
font-size: 14px;
|
cursor: pointer;
|
|
&:hover {
|
color: #337ecc;
|
}
|
}
|
}
|
|
// 通知公告模块
|
.notice-module {
|
flex: 1;
|
.notice-list {
|
flex: 1;
|
overflow: hidden;
|
overflow-y: auto;
|
|
.notice-item {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 12px 0;
|
border-bottom: 1px solid #f5f5f5;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
.notice-title {
|
color: #333;
|
font-size: 16px;
|
cursor: pointer;
|
flex: 1;
|
margin-right: 12px;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
|
&:hover {
|
color: #409eff;
|
}
|
}
|
|
.notice-date {
|
color: #999;
|
font-size: 14px;
|
flex-shrink: 0;
|
}
|
}
|
}
|
}
|
|
// 流程中心模块
|
.process-module {
|
flex: 1;
|
overflow-y: auto;
|
.process-list {
|
flex: 1;
|
overflow-y: auto;
|
|
.process-item {
|
margin-bottom: 8px;
|
|
.process-info {
|
display: flex;
|
align-items: center;
|
padding-bottom: 8px;
|
margin-bottom: 8px;
|
box-sizing: border-box;
|
border-bottom: 1px dashed #f0f0f0;
|
cursor: pointer;
|
|
.process-name {
|
color: #333;
|
font-size: 16px;
|
flex: 1;
|
|
&:hover {
|
color: #409eff;
|
}
|
}
|
|
.process-status {
|
font-size: 12px;
|
padding: 2px 8px;
|
border-radius: 4px;
|
flex-shrink: 0;
|
margin-right: 10px;
|
|
&.processing {
|
background: #e6f7ff;
|
color: #1890ff;
|
}
|
|
&.pending {
|
background: #fff7e6;
|
color: #fa8c16;
|
}
|
|
&.success {
|
background: #edffdb;
|
color: #52c41a;
|
}
|
|
&.normal {
|
background: #ffebca;
|
color: #ff6518;
|
}
|
|
&.seal {
|
background: #ffe6e6;
|
color: #ff1818;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
// 备忘录模块
|
.memo-module {
|
flex: 1;
|
.memo-content {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
|
.memo-input {
|
flex: 1;
|
|
.memo-time{
|
color: #999;
|
font-size: 12px;
|
text-align: right;
|
margin-top: 10px;
|
}
|
}
|
}
|
}
|
|
// 快捷入口模块
|
.quick-access-module {
|
.module-header {
|
.pagination-controls {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
|
.page-info {
|
font-size: 12px;
|
color: #666;
|
min-width: 40px;
|
text-align: center;
|
}
|
|
.el-button {
|
width: 28px;
|
height: 28px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
}
|
}
|
|
.systems-grid-container {
|
flex: 1;
|
overflow: hidden;
|
}
|
|
.systems-grid {
|
display: grid;
|
grid-template-columns: repeat(3, 1fr);
|
grid-template-rows: repeat(3, 1fr);
|
gap: 16px;
|
height: 100%;
|
min-height: 400px; // 确保有足够的高度显示九宫格
|
perspective: 1000px;
|
}
|
|
.system-card {
|
position: relative;
|
background-color: #ffffff;
|
border-radius: 4px;
|
transition: transform 1s cubic-bezier(0.23, 1, 0.32, 1),
|
box-shadow 0.5s cubic-bezier(0.23, 1, 0.32, 1);
|
transform-style: preserve-3d;
|
cursor: pointer;
|
overflow: hidden;
|
min-height: 100px; // 确保卡片有最小高度
|
|
// 空白卡片样式
|
&.empty-card {
|
opacity: 0.6;
|
|
.system-icon {
|
color: #ccc;
|
}
|
|
h3 {
|
color: #999;
|
}
|
|
&:hover {
|
transform: none;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
&::after {
|
border-color: transparent;
|
}
|
|
&::before {
|
background: #fff;
|
}
|
}
|
}
|
|
&:hover{
|
border-radius: 8px;
|
}
|
|
&::after {
|
content: '';
|
position: absolute;
|
top: 4px;
|
left: 4px;
|
right: 4px;
|
bottom: 4px;
|
border-radius: 4px;
|
border: 1px solid transparent;
|
transition: border-color 0.3s ease;
|
pointer-events: none;
|
z-index: 3;
|
}
|
|
&::before {
|
content: '';
|
position: absolute;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
border-radius: 4px;
|
background: #fff;
|
transform: var(--bg-transform, translateX(0) translateY(0));
|
transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1);
|
z-index: 1;
|
}
|
|
&:hover::after {
|
border-color: rgba(37,99,235,1);
|
border-radius: 6px;
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
|
}
|
|
&:hover::before {
|
border-radius: 8px;
|
background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(20,20,20,0.05) 100%);
|
}
|
}
|
|
.system-card:hover {
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15),
|
0 0 0 1px rgba(255, 255, 255, 0.5) inset;
|
}
|
|
.card-content {
|
position: relative;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
padding: 12px;
|
z-index: 2;
|
transform-style: preserve-3d;
|
}
|
|
.system-icon {
|
margin-bottom: 8px;
|
|
.el-image {
|
width: 40px;
|
height: 40px;
|
transition: transform 0.5s;
|
}
|
|
.el-icon {
|
font-size: 40px;
|
color: #ccc;
|
}
|
}
|
|
.system-card:hover .system-icon img {
|
transform: scale(1.2);
|
}
|
|
.system-card h3 {
|
margin: 0;
|
color: #333;
|
font-size: 14px;
|
text-align: center;
|
transition: transform 0.3s;
|
line-height: 1.2;
|
font-weight: 500;
|
}
|
|
.system-card:hover h3 {
|
transform: translateZ(10px);
|
}
|
}
|
// 日历模块
|
.calendar-module {
|
.calendar-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding-bottom: 16px;
|
|
.lunar-info {
|
width: 33.33%;
|
display: flex;
|
flex-direction: column;
|
font-size: 16px;
|
.lunar-date-full {
|
color: #e6a23c;
|
font-weight: 500;
|
}
|
|
.lunar-year {
|
color: #999;
|
}
|
}
|
|
.current-date-info {
|
width: 33.33%;
|
font-size: 16px;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
.solar-date-large {
|
color: #333;
|
font-weight: 600;
|
}
|
.week-day {
|
color: #666;
|
margin-bottom: 4px;
|
font-weight: 500;
|
}
|
}
|
|
.calendar-actions {
|
display: flex;
|
justify-content: right;
|
width: 33.33%;
|
|
.el-button-group {
|
display: flex;
|
gap: 1px;
|
|
.el-button {
|
padding: 6px 12px;
|
border-radius: 4px;
|
|
&:first-child {
|
border-top-right-radius: 0;
|
border-bottom-right-radius: 0;
|
}
|
|
&:last-child {
|
border-top-left-radius: 0;
|
border-bottom-left-radius: 0;
|
}
|
|
&:not(:first-child):not(:last-child) {
|
border-radius: 0;
|
}
|
}
|
}
|
}
|
}
|
.calendar-content {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
|
:deep(.el-calendar) {
|
border: none;
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
|
.el-calendar__header {
|
padding: 0;
|
flex-shrink: 0;
|
}
|
|
.el-calendar__body {
|
flex: 1;
|
}
|
|
.el-calendar-table {
|
height: 100%;
|
|
.el-calendar-day {
|
height: 60px;
|
padding: 2px;
|
text-align: center;
|
}
|
|
td.is-selected {
|
background-color: transparent;
|
}
|
|
.el-calendar-day:hover {
|
background-color: #f5f7fa;
|
}
|
}
|
}
|
|
.calendar-date {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: flex-start;
|
height: 100%;
|
position: relative;
|
padding-top: 4px;
|
|
.solar-date {
|
font-size: 14px;
|
font-weight: 500;
|
color: #333;
|
margin-bottom: 2px;
|
}
|
|
.lunar-date {
|
font-size: 10px;
|
color: #999;
|
margin-bottom: 4px;
|
}
|
|
.calendar-event-dot {
|
width: 4px;
|
height: 4px;
|
background: #409eff;
|
border-radius: 50%;
|
margin-bottom: 2px;
|
}
|
|
.today-indicator {
|
position: absolute;
|
top: 2px;
|
right: 2px;
|
width: 16px;
|
height: 16px;
|
background: #409eff;
|
color: white;
|
border-radius: 50%;
|
font-size: 10px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
line-height: 1;
|
}
|
}
|
|
// 今天日期的样式
|
.calendar-date.is-today {
|
background-color: #ecf5ff;
|
.solar-date {
|
color: #409eff;
|
font-weight: 600;
|
}
|
|
&::before {
|
content: '';
|
position: absolute;
|
top: 2px;
|
left: 50%;
|
transform: translateX(-50%);
|
width: 24px;
|
height: 24px;
|
background: #409eff;
|
border-radius: 50%;
|
z-index: -1;
|
opacity: 0.1;
|
}
|
}
|
|
.calendar-legend {
|
display: flex;
|
justify-content: center;
|
gap: 16px;
|
margin-top: 12px;
|
padding-top: 12px;
|
border-top: 1px solid #f0f0f0;
|
flex-shrink: 0;
|
|
.legend-item {
|
display: flex;
|
align-items: center;
|
gap: 6px;
|
|
.legend-dot {
|
width: 8px;
|
height: 8px;
|
border-radius: 50%;
|
}
|
|
.event-dot {
|
background: #409eff;
|
}
|
|
.today-dot {
|
background: #409eff;
|
}
|
|
span {
|
font-size: 12px;
|
color: #666;
|
}
|
}
|
}
|
}
|
}
|
|
// 响应式设计
|
@media (max-width: 1200px) {
|
.layout-container {
|
flex-direction: column;
|
}
|
|
.left-column,
|
.right-column {
|
flex: 1;
|
min-width: auto;
|
}
|
}
|
|
// 超大屏幕优化
|
@media (min-width: 1920px) {
|
.systems-container {
|
padding: 30px 40px;
|
}
|
|
.layout-container {
|
gap: 30px;
|
}
|
|
.module-card {
|
padding: 25px;
|
}
|
|
// 在超大屏幕上可以显示3列的系统卡片
|
.quick-access-module .systems-grid {
|
grid-template-columns: repeat(3, 1fr);
|
}
|
}
|
</style>
|