feat: 添加新建证书的页面和功能;页面添加过渡动画;添加证书有效期, 证书分类菜单;添加证书预览功能
This commit is contained in:
parent
7c6c19d8f9
commit
4f90499ada
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 525 B |
Before Width: | Height: | Size: 536 B After Width: | Height: | Size: 536 B |
@ -194,7 +194,23 @@ const downloadCertificate = (certificate: any) => {
|
||||
}
|
||||
|
||||
const editCertificate = (certificate: any) => {
|
||||
message.info(`编辑证书: ${certificate.name}`)
|
||||
// 从当前路径中提取课程ID
|
||||
const currentPath = route.path;
|
||||
const courseIdMatch = currentPath.match(/\/course-editor\/(\d+)/);
|
||||
const courseId = courseIdMatch ? courseIdMatch[1] : '1';
|
||||
|
||||
// 跳转到新建证书页面,并传递证书信息作为参数
|
||||
router.push({
|
||||
path: `/teacher/certificate/new`,
|
||||
query: {
|
||||
courseId: courseId,
|
||||
certificateId: certificate.id,
|
||||
mode: 'edit',
|
||||
name: certificate.name,
|
||||
category: certificate.category
|
||||
}
|
||||
});
|
||||
|
||||
activeFileMenu.value = null
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,95 @@
|
||||
<template>
|
||||
<div class="certificate-new">
|
||||
<div class="top-section">
|
||||
<h1 class="page-title">新建证书11</h1>
|
||||
<!-- 顶部信息栏 -->
|
||||
<div class="top-info-bar">
|
||||
<!-- 返回按钮 -->
|
||||
<button class="return-btn" @click="goBack">
|
||||
<span>返回</span>
|
||||
</button>
|
||||
|
||||
<!-- 证书信息 -->
|
||||
<div class="certificate-info">
|
||||
<div class="certificate-name-section">
|
||||
<h2 class="certificate-name">证书证书证书名称</h2>
|
||||
<img :src="editIconHovered ? '/images/teacher/rechristen-active.png' : '/images/teacher/rechristen.png'"
|
||||
alt="编辑" class="edit-icon" :class="{ 'preview-mode': isPreviewMode }" @mouseenter="editIconHovered = true"
|
||||
@mouseleave="editIconHovered = false" v-show="!isPreviewMode" />
|
||||
</div>
|
||||
|
||||
<div class="certificate-details" :class="{ 'preview-mode': isPreviewMode }" v-show="!isPreviewMode">
|
||||
<div class="detail-item">
|
||||
<span class="label">证书分类:</span>
|
||||
<span class="value">{{ selectedCategory }}</span>
|
||||
<n-popover ref="categoryPopoverRef" trigger="click" placement="bottom-center" :show-arrow="false"
|
||||
:style="{ '--n-space': '23px', '--n-padding': '0' }">
|
||||
<template #trigger>
|
||||
<a href="#" class="link-btn">重新选择</a>
|
||||
</template>
|
||||
<div class="category-popover-content">
|
||||
<div class="category-header">证书分类:</div>
|
||||
<div class="category-options">
|
||||
<label class="category-option">
|
||||
<input type="radio" class="radio-input" value="考试" v-model="selectedCategory"
|
||||
@change="closeCategoryPopover">
|
||||
<span class="radio-label">考试</span>
|
||||
</label>
|
||||
<label class="category-option">
|
||||
<input type="radio" class="radio-input" value="学习项目" v-model="selectedCategory"
|
||||
@change="closeCategoryPopover">
|
||||
<span class="radio-label">学习项目</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</n-popover>
|
||||
</div>
|
||||
<n-divider vertical class="detail-divider" />
|
||||
<div class="detail-item">
|
||||
<span class="label">有效期:</span>
|
||||
<span class="value">{{ currentValidityText }}</span>
|
||||
<n-popover ref="validityPopoverRef" trigger="click" placement="bottom-start" :show-arrow="false"
|
||||
:style="{ '--n-space': '23px' }">
|
||||
<template #trigger>
|
||||
<a href="#" class="link-btn">调整</a>
|
||||
</template>
|
||||
<div class="validity-popover-content">
|
||||
<div class="popover-header">设置证书有效期:</div>
|
||||
<div class="validity-options">
|
||||
<label class="validity-option">
|
||||
<input type="radio" class="radio-input" value="duration" v-model="validityType">
|
||||
<span class="radio-label">自颁发日起</span>
|
||||
<div class="duration-input">
|
||||
<input type="number" class="number-input" v-model="validityDuration" min="0">
|
||||
<span class="duration-text">月内有效</span>
|
||||
</div>
|
||||
</label>
|
||||
<span class="hint-text"> (设置0则代表证书发出终生有效)</span>
|
||||
<label class="validity-option">
|
||||
<input type="radio" class="radio-input" value="date" v-model="validityType">
|
||||
<span class="radio-label">有效期至</span>
|
||||
<div class="date-input">
|
||||
<input type="datetime-local" class="date-field" v-model="validityEndDate">
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="popover-footer">
|
||||
<button class="btn-cancel" @click="closePopover">取消</button>
|
||||
<button class="btn-confirm" @click="confirmValidity">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</n-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧区域 -->
|
||||
<div class="right-section" :class="{ 'collapsed': isCollapsed }">
|
||||
<div class="right-section" :class="{ 'collapsed': isCollapsed, 'preview-mode': isPreviewMode }"
|
||||
v-show="!isPreviewMode">
|
||||
<div class="content">
|
||||
<!-- 顶部按钮 -->
|
||||
<div class="header-buttons">
|
||||
<button class="btn btn-preview">证书预览</button>
|
||||
<button class="btn btn-preview" @click="togglePreviewMode">证书预览</button>
|
||||
<button class="btn btn-save">保存</button>
|
||||
</div>
|
||||
|
||||
@ -16,29 +97,37 @@
|
||||
<div class="section">
|
||||
<h3 class="section-title">证书背景</h3>
|
||||
<div class="color-palette">
|
||||
<div class="color-item rainbow" :class="{ 'selected': selectedColor === customColor }" @click="showColorPicker = !showColorPicker">
|
||||
<input
|
||||
v-if="showColorPicker"
|
||||
type="color"
|
||||
:value="customColor"
|
||||
@input="selectCustomColor($event.target.value)"
|
||||
class="color-input-inline"
|
||||
@click.stop
|
||||
/>
|
||||
<div class="color-item rainbow" :class="{ 'selected': selectedColor === customColor }"
|
||||
@click="showColorPicker = !showColorPicker">
|
||||
<input v-if="showColorPicker" type="color" :value="customColor"
|
||||
@input="selectCustomColor($event.target.value)" class="color-input-inline" @click.stop />
|
||||
</div>
|
||||
<div class="color-item black" :class="{ 'selected': selectedColor === '#000000' }" @click="selectColor('#000000')"></div>
|
||||
<div class="color-item dark-gray" :class="{ 'selected': selectedColor === '#333333' }" @click="selectColor('#333333')"></div>
|
||||
<div class="color-item gray" :class="{ 'selected': selectedColor === '#666666' }" @click="selectColor('#666666')"></div>
|
||||
<div class="color-item light-gray" :class="{ 'selected': selectedColor === '#999999' }" @click="selectColor('#999999')"></div>
|
||||
<div class="color-item white" :class="{ 'selected': selectedColor === '#ffffff' }" @click="selectColor('#ffffff')"></div>
|
||||
<div class="color-item red" :class="{ 'selected': selectedColor === '#ff0000' }" @click="selectColor('#ff0000')"></div>
|
||||
<div class="color-item orange" :class="{ 'selected': selectedColor === '#ff8000' }" @click="selectColor('#ff8000')"></div>
|
||||
<div class="color-item pink" :class="{ 'selected': selectedColor === '#ff80ff' }" @click="selectColor('#ff80ff')"></div>
|
||||
<div class="color-item purple" :class="{ 'selected': selectedColor === '#8000ff' }" @click="selectColor('#8000ff')"></div>
|
||||
<div class="color-item green" :class="{ 'selected': selectedColor === '#00ff00' }" @click="selectColor('#00ff00')"></div>
|
||||
<div class="color-item teal" :class="{ 'selected': selectedColor === '#00ffff' }" @click="selectColor('#00ffff')"></div>
|
||||
<div class="color-item light-blue" :class="{ 'selected': selectedColor === '#80ffff' }" @click="selectColor('#80ffff')"></div>
|
||||
<div class="color-item blue" :class="{ 'selected': selectedColor === '#0080ff' }" @click="selectColor('#0080ff')"></div>
|
||||
<div class="color-item black" :class="{ 'selected': selectedColor === '#000000' }"
|
||||
@click="selectColor('#000000')"></div>
|
||||
<div class="color-item dark-gray" :class="{ 'selected': selectedColor === '#333333' }"
|
||||
@click="selectColor('#333333')"></div>
|
||||
<div class="color-item gray" :class="{ 'selected': selectedColor === '#666666' }"
|
||||
@click="selectColor('#666666')"></div>
|
||||
<div class="color-item light-gray" :class="{ 'selected': selectedColor === '#999999' }"
|
||||
@click="selectColor('#999999')"></div>
|
||||
<div class="color-item white" :class="{ 'selected': selectedColor === '#ffffff' }"
|
||||
@click="selectColor('#ffffff')"></div>
|
||||
<div class="color-item red" :class="{ 'selected': selectedColor === '#ff0000' }"
|
||||
@click="selectColor('#ff0000')"></div>
|
||||
<div class="color-item orange" :class="{ 'selected': selectedColor === '#ff8000' }"
|
||||
@click="selectColor('#ff8000')"></div>
|
||||
<div class="color-item pink" :class="{ 'selected': selectedColor === '#ff80ff' }"
|
||||
@click="selectColor('#ff80ff')"></div>
|
||||
<div class="color-item purple" :class="{ 'selected': selectedColor === '#8000ff' }"
|
||||
@click="selectColor('#8000ff')"></div>
|
||||
<div class="color-item green" :class="{ 'selected': selectedColor === '#00ff00' }"
|
||||
@click="selectColor('#00ff00')"></div>
|
||||
<div class="color-item teal" :class="{ 'selected': selectedColor === '#00ffff' }"
|
||||
@click="selectColor('#00ffff')"></div>
|
||||
<div class="color-item light-blue" :class="{ 'selected': selectedColor === '#80ffff' }"
|
||||
@click="selectColor('#80ffff')"></div>
|
||||
<div class="color-item blue" :class="{ 'selected': selectedColor === '#0080ff' }"
|
||||
@click="selectColor('#0080ff')"></div>
|
||||
</div>
|
||||
<div class="template-preview">
|
||||
<div class="certificate-template">
|
||||
@ -98,7 +187,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="certificate-content" :style="{ backgroundColor: selectedColor }">
|
||||
<div class="certificate-content" :class="{ 'preview-mode': isPreviewMode }"
|
||||
:style="{ backgroundColor: selectedColor }">
|
||||
<img src="/images/teacher/certificate.png" alt="">
|
||||
</div>
|
||||
|
||||
@ -108,16 +198,28 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 保存原始样式
|
||||
let originalPadding = ''
|
||||
|
||||
// 弹出框引用
|
||||
const validityPopoverRef = ref()
|
||||
const categoryPopoverRef = ref()
|
||||
|
||||
// 右侧面板收起状态
|
||||
const isCollapsed = ref(false)
|
||||
|
||||
// 预览模式状态
|
||||
const isPreviewMode = ref(false)
|
||||
|
||||
// 证书分类
|
||||
const selectedCategory = ref('考试')
|
||||
|
||||
// 选中的背景颜色
|
||||
const selectedColor = ref('#ffffff')
|
||||
|
||||
@ -127,11 +229,30 @@ const customColor = ref('#ff0000')
|
||||
// 显示颜色选择器
|
||||
const showColorPicker = ref(false)
|
||||
|
||||
// 编辑图标悬停状态
|
||||
const editIconHovered = ref(false)
|
||||
|
||||
// 有效期设置
|
||||
const validityType = ref('duration')
|
||||
const validityDuration = ref(60)
|
||||
const validityEndDate = ref('2000-10-11T09:00')
|
||||
const currentValidityText = ref('永久有效')
|
||||
|
||||
// 切换收起/展开状态
|
||||
const toggleCollapse = () => {
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
// 切换预览模式
|
||||
const togglePreviewMode = () => {
|
||||
isPreviewMode.value = !isPreviewMode.value
|
||||
}
|
||||
|
||||
// 选择背景颜色
|
||||
const selectColor = (color: string) => {
|
||||
selectedColor.value = color
|
||||
@ -144,11 +265,69 @@ const selectCustomColor = (color: string) => {
|
||||
showColorPicker.value = false
|
||||
}
|
||||
|
||||
// 关闭弹出框
|
||||
const closePopover = () => {
|
||||
if (validityPopoverRef.value) {
|
||||
validityPopoverRef.value.setShow(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 确认有效期设置
|
||||
const confirmValidity = () => {
|
||||
console.log('有效期设置:', {
|
||||
type: validityType.value,
|
||||
duration: validityDuration.value,
|
||||
endDate: validityEndDate.value
|
||||
})
|
||||
|
||||
// 更新显示的有效期文本
|
||||
if (validityType.value === 'duration') {
|
||||
if (validityDuration.value === 0) {
|
||||
currentValidityText.value = '永久有效'
|
||||
} else {
|
||||
currentValidityText.value = `自颁发日起${validityDuration.value}月内有效`
|
||||
}
|
||||
} else if (validityType.value === 'date') {
|
||||
if (validityEndDate.value) {
|
||||
const date = new Date(validityEndDate.value)
|
||||
currentValidityText.value = `有效期至${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||||
} else {
|
||||
currentValidityText.value = '永久有效'
|
||||
}
|
||||
}
|
||||
|
||||
// 确认后关闭弹出框
|
||||
closePopover()
|
||||
}
|
||||
|
||||
// 关闭分类弹出框
|
||||
const closeCategoryPopover = () => {
|
||||
if (categoryPopoverRef.value) {
|
||||
categoryPopoverRef.value.setShow(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 获取URL参数
|
||||
onMounted(() => {
|
||||
console.log('新建证书页面参数:', route.query)
|
||||
|
||||
// 检查是否为编辑模式
|
||||
if (route.query.mode === 'edit') {
|
||||
// 编辑模式:加载证书数据
|
||||
const certificateName = route.query.name as string
|
||||
const certificateCategory = route.query.category as string
|
||||
|
||||
if (certificateName) {
|
||||
// 这里可以设置证书名称,如果有编辑功能的话
|
||||
console.log('编辑证书:', certificateName, certificateCategory)
|
||||
}
|
||||
}
|
||||
|
||||
// 动态修改上级容器的内边距,只影响当前页面
|
||||
const routerViewContainer = document.querySelector('.router-view-container') as HTMLElement
|
||||
if (routerViewContainer) {
|
||||
@ -189,14 +368,388 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 预览模式过渡效果 */
|
||||
.right-section {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.right-section.preview-mode {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.certificate-content {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.certificate-content.preview-mode {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.certificate-details {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.certificate-details.preview-mode {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.edit-icon.preview-mode {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
.top-section {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 顶部信息栏样式 */
|
||||
.top-info-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
padding: 20px 30px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 返回按钮 */
|
||||
.return-btn {
|
||||
background: #F5F5F5;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
padding: 8px 23px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.return-btn:hover {
|
||||
background: #E8E8E8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 证书信息区域 */
|
||||
.certificate-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* 证书名称区域 */
|
||||
.certificate-name-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.certificate-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.edit-icon:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 证书详情区域 */
|
||||
.certificate-details {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
color: #0288D1;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.link-btn:hover {
|
||||
color: #0277BD;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 详情分隔线样式 */
|
||||
:deep(.detail-divider) {
|
||||
height: 16px;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
/* 有效期弹出框样式 */
|
||||
.validity-popover-content {
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
width: 444px;
|
||||
height: 301px;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
/* 分类弹出框样式 */
|
||||
.category-popover-content {
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
width: 230px;
|
||||
height: 204px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 2px 24px 0px rgba(220, 220, 220, 0.5);
|
||||
}
|
||||
|
||||
/* 覆盖 Naive UI 弹出框的默认背景 */
|
||||
:deep(.n-popover__content) {
|
||||
background: #FFFFFF !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* 确保弹出框容器也是白色背景 */
|
||||
:deep(.n-popover) {
|
||||
background: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
background: #fff;
|
||||
border-bottom: 1.5px solid #E6E6E6;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
text-align: left;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.category-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% - 60px);
|
||||
padding: 10px 20px 20px;
|
||||
}
|
||||
|
||||
.category-option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 16px 0;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.category-option:hover {
|
||||
background: #f8f9fa !important;
|
||||
}
|
||||
|
||||
/* 覆盖 Naive UI 弹出框的默认内边距 */
|
||||
:deep(.n-popover__content) {
|
||||
padding: 30px !important;
|
||||
}
|
||||
|
||||
.popover-header {
|
||||
background: #fff;
|
||||
border-bottom: 1.5px solid #E6E6E6;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
text-align: left;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.validity-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.validity-option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 16px 0 16px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
.validity-option:hover {
|
||||
border-color: #0288D1;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.radio-input {
|
||||
margin-right: 12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.duration-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: nowrap;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.number-input {
|
||||
width: 95px;
|
||||
height: 44px;
|
||||
padding: 6px 8px;
|
||||
border: 1.5px solid #E6E6E6;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
background-color: #FCFCFC;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.duration-text {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
margin-left: 40px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.date-input {
|
||||
margin-left: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.date-field {
|
||||
padding: 8px 8px;
|
||||
border: 1.5px solid #E6E6E6;
|
||||
background-color: #FCFCFC;
|
||||
color: #666666;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
background-image: url('/images/teacher/日历-选中.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: left 8px center;
|
||||
background-size: 16px 16px;
|
||||
padding-left: 32px;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
/* 隐藏WebKit浏览器的默认日历图标 */
|
||||
.date-field::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 隐藏WebKit浏览器的默认清除按钮 */
|
||||
.date-field::-webkit-clear-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 隐藏WebKit浏览器的默认内部阴影 */
|
||||
.date-field::-webkit-inner-spin-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popover-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
padding: 20px 0;
|
||||
height: 61px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-cancel {
|
||||
color: #0288D1;
|
||||
padding: 2px 10px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
width: 66px;
|
||||
height: 32px;
|
||||
background: #E2F5FF;
|
||||
border: 1px solid #0288D1;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: #f0f8ff;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
width: 66px;
|
||||
height: 32px;
|
||||
background: #0288D1;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 2px 10px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover {
|
||||
background: #0277BD;
|
||||
}
|
||||
.right-section {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -211,6 +764,10 @@ onUnmounted(() => {
|
||||
transform: translateX(240px);
|
||||
}
|
||||
|
||||
.right-section.collapsed .content {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.collapse-button {
|
||||
position: absolute;
|
||||
left: -40px;
|
||||
@ -389,7 +946,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.color-item.selected {
|
||||
border: 2px solid #0288D1;
|
||||
border: 2px solid #8F93A4;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,11 @@
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="content-area" :class="{ 'full-width': hideSidebar }">
|
||||
<router-view />
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="content-transition" mode="out-in" appear>
|
||||
<component :is="Component" :key="route.path" class="content-component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -305,4 +309,29 @@ const hideSidebar = computed(() => {
|
||||
background: #e6f7ff;
|
||||
color: #0288D1;
|
||||
}
|
||||
/* 内容区域过渡动画 */
|
||||
.content-transition-enter-active,
|
||||
.content-transition-leave-active {
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.content-transition-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.content-transition-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
.content-transition-enter-to,
|
||||
.content-transition-leave-from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.content-component {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user