修改src下的第三个子文件

feature/wy
王勇 1 month ago
parent a7286df9fc
commit ab46dd349a

@ -6,13 +6,91 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>logo.png">
<title>在线考试系统</title>
<style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
<!-- 定义加载中的样式 -->
<style>#loading-mask{
position:fixed;left:0;top:0;height:100%;
width:100%;background:#fff;user-select:none;
z-index:9999;overflow:hidden}
.loading-wrapper{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-100%)
}
.loading-dot{
animation:antRotate 1.2s infinite linear;
transform:rotate(45deg);
position:relative;
display:inline-block;
font-size:64px;
width:64px;
height:64px;
box-sizing:border-box}
.loading-dot i{
width:22px;
height:22px;
position:absolute;
display:block;
background-color:#1890ff;
border-radius:100%;
transform:scale(.75);
transform-origin:50% 50%;
opacity:.3;
animation:antSpinMove 1s infinite linear alternate
}
.loading-dot i:nth-child(1){
top:0;
left:0
}
.loading-dot i:nth-child(2){
top:0;
right:0;
-webkit-animation-delay:.4s;
animation-delay:.4s
}
.loading-dot i:nth-child(3){
right:0;
bottom:0;
-webkit-animation-delay:.8s;
animation-delay:.8s
}
.loading-dot i:nth-child(4){
bottom:0;
left:0;
-webkit-animation-delay:1.2s;
animation-delay:1.2s
}
@keyframes antRotate{
to{
-webkit-transform:rotate(405deg);
transform:rotate(405deg)
}
}
@-webkit-keyframes antRotate{to{
-webkit-transform:rotate(405deg);
transform:rotate(405deg)
}
}
@keyframes antSpinMove{
to{
opacity:1
}
}
@-webkit-keyframes antSpinMove{
to{
opacity:1
}
}
</style>
</head>
<body>
<!-- 当JavaScript被禁用时显示提示信息 -->
<noscript>
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- 应用的主容器 -->
<div id="app">
<!-- 加载中的遮罩层 -->
<div id="loading-mask">
<div class="loading-wrapper">
<span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span>

@ -1 +1,52 @@
#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}
#preloadingAnimation{
position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;
z-index: 9999;overflow: hidden}
.lds-roller{
display:inline-block;
position:relative;
left:50%;
top:50%;
transform:translate(-50%,-50%);
width:64px;
height:64px;
}
.lds-roller div{
animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;
transform-origin:32px 32px;
}
.lds-roller div:after{
content:" ";
display:block;
position:absolute;
width:6px;
height:6px;
border-radius:50%;
background:#13c2c2;
margin:-3px 0 0 -3px;
}
.lds-roller div:nth-child(1){animation-delay:-0.036s;}
.lds-roller div:nth-child(1):after{top:50px;left:50px;}
.lds-roller div:nth-child(2){animation-delay:-0.072s;}
.lds-roller div:nth-child(2):after{top:54px;left:45px;}
.lds-roller div:nth-child(3){animation-delay:-0.108s;}
.lds-roller div:nth-child(3):after{top:57px;left:39px;}
.lds-roller div:nth-child(4){animation-delay:-0.144s;}
.lds-roller div:nth-child(4):after{top:58px;left:32px;}
.lds-roller div:nth-child(5){animation-delay:-0.18s;}
.lds-roller div:nth-child(5):after{top:57px;left:25px;}
.lds-roller div:nth-child(6){animation-delay:-0.216s;}
.lds-roller div:nth-child(6):after{top:54px;left:19px;}
.lds-roller div:nth-child(7){animation-delay:-0.252s;}
.lds-roller div:nth-child(7):after{top:50px;left:14px;}
.lds-roller div:nth-child(8){animation-delay:-0.288s;}
.lds-roller div:nth-child(8):after{top:45px;left:10px;}
#preloadingAnimation .load-tips{
color: #13c2c2;
font-size:2rem;
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);margin-top:80px;
text-align:center;width:400px;height:64px;}
@keyframes lds-roller{0%{transform:rotate(0deg);}
100%{transform:rotate(360deg);}}

@ -1 +1,27 @@
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>
<div id="preloadingAnimation"><div class=lds-roller><div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
</div>
<div class=load-tips>Loading</div>
</div>

@ -1,5 +1,31 @@
/**
* 预加载动画
*/
<div class="preloading-animate">
<!--预加载的包装容器 -->
<div class="preloading-wrapper">
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
<svg class="preloading-balls"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle cx="67.802" cy="59.907" r="6" fill="#51CACC">
<animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle>
<circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77">
<animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle>
<circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
<animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/>
</circle>
</svg>
</div>
</div>

@ -1 +1,20 @@
.preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;}
.preloading-animate{
background:#ffffff;
width:100%;
height:100%;
position:fixed;
left:0;
top:0;
z-index:299;
}
.preloading-animate .preloading-wrapper{
position:absolute;
width:5rem;
height:5rem;
left:50%;
top:50%;
transform:translate(-50%,-50%);
}
.preloading-animate .preloading-wrapper .preloading-balls{
font-size:5rem;
}

@ -3,6 +3,11 @@
import api from './index'
import { axios } from '../utils/request'
/**
* 获取问题列表
* @param parameter
* @returns {AxiosPromise}
*/
export function getQuestionList (parameter) {
return axios({
url: api.ExamQuestionList,
@ -11,6 +16,10 @@ export function getQuestionList (parameter) {
})
}
/**
* 获取所有问题
* @returns {AxiosPromise}
*/
export function getQuestionAll () {
return axios({
url: api.ExamQuestionAll,
@ -18,6 +27,11 @@ export function getQuestionAll () {
})
}
/**
* 更新问题
* @param parameter
* @returns {AxiosPromise}
*/
export function questionUpdate (parameter) {
console.log(parameter)
return axios({
@ -27,6 +41,10 @@ export function questionUpdate (parameter) {
})
}
/**
* 获取问题选项
* @returns {AxiosPromise}
*/
export function getQuestionSelection () {
return axios({
url: api.ExamQuestionSelection,
@ -37,6 +55,11 @@ export function getQuestionSelection () {
})
}
/**
* 创建问题
* @param parameter
* @returns {AxiosPromise}
*/
export function questionCreate (parameter) {
console.log(parameter)
return axios({
@ -46,6 +69,11 @@ export function questionCreate (parameter) {
})
}
/**
* 获取考试列表
* @param parameter
* @returns {AxiosPromise}
*/
export function getExamList (parameter) {
return axios({
url: api.ExamList,
@ -54,6 +82,10 @@ export function getExamList (parameter) {
})
}
/**
* 获取所有考试
* @returns {AxiosPromise}
*/
export function getExamAll () {
return axios({
url: api.ExamAll,
@ -72,6 +104,11 @@ export function getExamQuestionTypeList () {
})
}
/**
* 获取考试卡片列表
* @returns {AxiosPromise}
*/
export function getExamCardList () {
return axios({
url: api.ExamCardList,
@ -82,6 +119,11 @@ export function getExamCardList () {
})
}
/**
* 创建考试
* @param parameter
* @returns {AxiosPromise}
*/
export function examCreate (parameter) {
console.log(parameter)
return axios({
@ -100,6 +142,11 @@ export function examUpdate (parameter) {
})
}
/**
* 获取考试详情
* @param examId
* @returns {AxiosPromise}
*/
export function getExamDetail (examId) {
return axios({
url: api.ExamDetail + examId,
@ -110,6 +157,11 @@ export function getExamDetail (examId) {
})
}
/**
* 获取考试记录详情
* @param recordId
* @returns {AxiosPromise}
*/
export function getExamRecordDetail (recordId) {
return axios({
url: api.recordDetail + recordId,
@ -120,6 +172,11 @@ export function getExamRecordDetail (recordId) {
})
}
/**
* 获取考试问题详情
* @param questionId
* @returns {AxiosPromise}
*/
export function getQuestionDetail (questionId) {
return axios({
url: api.QuestionDetail + questionId,
@ -130,6 +187,13 @@ export function getQuestionDetail (questionId) {
})
}
/**
* 完成考试
* @param examId
* @param answersMap
* @returns {AxiosPromise}
*/
export function finishExam (examId, answersMap) {
console.log(answersMap)
return axios({
@ -142,6 +206,10 @@ export function finishExam (examId, answersMap) {
})
}
/**
* 获取考试记录列表
* @returns {AxiosPromise}
*/
export function getExamRecordList () {
return axios({
url: api.ExamRecordList,

@ -1,3 +1,7 @@
/**
* api接口列表
* @type {{ExamQuestionList: string, SendSmsErr: string, ExamCardList: string, UserInfo: string, UserRegister: string, ExamRecordList: string, ExamQuestionCreate: string, Logout: string, ExamCreate: string, ExamQuestionAll: string, FinishExam: string, UserLogin: string, ForgePassword: string, ExamQuestionTypeList: string, SendSms: string, Login: string, twoStepCode: string, QuestionDetail: string, recordDetail: string, Register: string, ExamUpdate: string, ExamDetail: string, ExamQuestionSelection: string, ExamList: string, ExamQuestionUpdate: string, ExamAll: string}}
*/
const api = {
Login: '/auth/login',
Logout: '/auth/logout',

@ -21,6 +21,11 @@ export function login (parameter) {
})
}
/**
*获取短信验证码
* @param parameter
* @returns {AxiosPromise}
*/
export function getSmsCaptcha (parameter) {
return axios({
url: api.SendSms,
@ -29,6 +34,10 @@ export function getSmsCaptcha (parameter) {
})
}
/**
* 获取用户信息
* @returns {AxiosPromise}
*/
export function getInfo () {
return axios({
url: api.UserInfo,
@ -39,6 +48,10 @@ export function getInfo () {
})
}
/**
* 用户登出
* @returns {AxiosPromise}
*/
export function logout () {
return axios({
url: api.Logout,
@ -50,7 +63,7 @@ export function logout () {
}
/**
* get user 2step code open?
* 获取用户两步验证设置状态
* @param parameter {*}
*/
export function get2step (parameter) {

@ -2,7 +2,7 @@
import api from './index'
import { axios } from '../utils/request'
// 用户登录
export function login (parameter) {
return axios({
url: api.UserLogin,
@ -10,7 +10,7 @@ export function login (parameter) {
data: parameter
})
}
// 用户注册
export function register (parameter) {
return axios({
url: api.UserRegister,

@ -1,24 +1,33 @@
<template>
<!--异常页面-->
<div class="exception">
<!--背景图-->
<div class="imgBlock">
<!-- 图片元素根据传入的type动态设置背景图片 -->
<div class="imgEle" :style="{backgroundImage: `url(${config[type].img})`}">
</div>
</div>
<!-- 内容区块 -->
<div class="content">
<!-- 根据传入的type动态显示标题 -->
<h1>{{ config[type].title }}</h1>
<!-- 根据传入的type动态显示描述 -->
<div class="desc">{{ config[type].desc }}</div>
<!-- 操作按钮区块 -->
<div class="actions">
<!-- 点击按钮返回首页 -->
<a-button type="primary" @click="handleToHome"></a-button>
</div>
</div>
</div>
</template>
<script>
<script>//
import types from './type'
export default {
name: 'Exception',
//
props: {
type: {
type: String,
@ -27,11 +36,14 @@ export default {
},
data () {
return {
//
config: types
}
},
methods: {
//
handleToHome () {
// 使Vue Routerdashboard
this.$router.push({ name: 'dashboard' })
}
}

@ -1,19 +1,27 @@
/**
* 定义一个常量对象types用于存储不同HTTP状态码的信息
* 每个状态码都包含一个图片URL状态码标题和描述信息
* 这些信息可以用于在界面上展示更友好的错误提示
*/
const types = {
// 403状态码信息禁止访问
403: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
title: '403',
desc: '抱歉,你无权访问该页面'
},
// 404状态码信息页面未找到
404: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
title: '404',
desc: '抱歉,你访问的页面不存在或仍在开发中'
},
// 500状态码信息服务器内部错误
500: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
title: '500',
desc: '抱歉,服务器出错了'
}
}
// 将types对象导出以便在其他模块中使用
export default types

@ -1,18 +1,25 @@
<template>
<!-- 底部信息栏 -->
<div class="footer">
<!-- 友情链接区域 -->
<div class="links">
<!-- 链接项目代码仓库 -->
<a href="https://github.com/19920625lsg/spring-boot-online-exam" target="_blank">代码仓</a>
<!-- 链接关于我的页面 -->
<a href="https://19920625lsg.github.io" target="_blank">关于我</a>
<!-- 链接联系我 -->
<a href="mailto:liangshanguang2@gmail.com">联系我</a>
</div>
<!-- 版权信息区域 -->
<div class="copyright">
Copyright
<!-- 版权图标 -->
<a-icon type="copyright" /> 2020 <span>Liang Shan Guang</span>
</div>
</div>
</template>
<script>
<script>//
export default {
name: 'GlobalFooter',
data () {
@ -21,7 +28,7 @@ export default {
}
</script>
<style lang="less" scoped>
<style lang="less" scoped>//
.footer {
padding: 0 16px;
margin: 24px 0 24px;

@ -1,22 +1,31 @@
<template>
<!-- 使用 transition 组件为头部添加动画效果 -->
<transition name="showHeader">
<!-- 根据 visible 属性控制头部的显示与隐藏 -->
<div v-if="visible" class="header-animat">
<!-- 根据 visible 属性fixedHeadersidebarOpened 等属性动态设置头部样式 -->
<a-layout-header
v-if="visible"
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
:style="{ padding: '0' }">
<!-- 根据 mode 属性判断菜单类型并根据不同设备类型显示不同的折叠/展开图标 -->
<div v-if="mode === 'sidemenu'" class="header">
<a-icon v-if="device==='mobile'" class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle"/>
<a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle"/>
<!-- 渲染用户菜单组件 -->
<user-menu></user-menu>
</div>
<!-- 对于顶部菜单根据设备类型和折叠状态显示不同布局和图标 -->
<div v-else :class="['top-nav-header-index', theme]">
<div class="header-index-wide">
<div class="header-index-left">
<!-- 渲染 logo 组件根据设备类型决定是否显示标题 -->
<logo class="top-nav-header" :show-title="device !== 'mobile'"/>
<!-- 根据设备类型和折叠状态渲染菜单 -->
<s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" />
<a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" />
</div>
<!-- 渲染用户菜单组件 -->
<user-menu class="header-index-right"></user-menu>
</div>
</div>
@ -25,10 +34,11 @@
</transition>
</template>
<script>
<script>// logo
import UserMenu from '../tools/UserMenu'
import SMenu from '../Menu/'
import Logo from '../tools/Logo'
// 使
import { mixin } from '../../utils/mixin'
export default {
@ -39,6 +49,7 @@ export default {
Logo
},
mixins: [mixin],
//
props: {
mode: {
type: String,
@ -67,14 +78,18 @@ export default {
},
data () {
return {
//
visible: true,
//
oldScrollTop: 0
}
},
// /
mounted () {
document.body.addEventListener('scroll', this.handleScroll, { passive: true })
},
methods: {
//
handleScroll () {
if (!this.autoHideHeader) {
return
@ -96,17 +111,19 @@ export default {
})
}
},
// toggle
toggle () {
this.$emit('toggle')
}
},
//
beforeDestroy () {
document.body.removeEventListener('scroll', this.handleScroll, true)
}
}
</script>
<style lang="less">
<style lang="less">//
.header-animat{
position: relative;
z-index: 2;

@ -1,11 +1,14 @@
<template>
<!-- 侧边栏组件包含logo和菜单 -->
<a-layout-sider
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
width="256px"
:collapsible="collapsible"
v-model="collapsed"
:trigger="null">
<!-- Logo组件 -->
<logo />
<!-- 菜单组件 -->
<s-menu
:collapsed="collapsed"
:menu="menus"
@ -17,7 +20,7 @@
</template>
<script>
<script>// LogoSMenu
import Logo from '../../components/tools/Logo'
import SMenu from './index'
import { mixin, mixinDevice } from '../../utils/mixin'

@ -26,22 +26,35 @@
-->
<script>
/**
* 多标签页组件
*/
export default {
name: 'MultiTab',
data () {
return {
// fullPath
fullPathList: [],
//
pages: [],
// fullPath
activeKey: '',
//
newTabIndex: 0
}
},
created () {
//
this.pages.push(this.$route)
this.fullPathList.push(this.$route.fullPath)
this.selectedLastPath()
},
methods: {
/**
* 处理标签页编辑事件
* @param {String} targetKey - 目标标签页的 fullPath
* @param {String} action - 操作类型例如'remove'
*/
onEdit (targetKey, action) {
this[action](targetKey)
},
@ -53,11 +66,14 @@ export default {
this.selectedLastPath()
}
},
/**
* 选择最后一个标签页
*/
selectedLastPath () {
this.activeKey = this.fullPathList[this.fullPathList.length - 1]
},
// content menu
//
closeThat (e) {
this.remove(e)
},
@ -93,6 +109,10 @@ export default {
}
})
},
/**
* 处理右键菜单点击事件
* @param {Object} { key, item, domEvent } - 菜单项的相关信息
*/
closeMenuClick ({ key, item, domEvent }) {
const vkey = domEvent.target.getAttribute('data-vkey')
switch (key) {
@ -111,6 +131,10 @@ export default {
break
}
},
/**
* 渲染标签页右键菜单
* @param {String} e - 标签页的 fullPath
*/
renderTabPaneMenu (e) {
return (
<a-menu {...{ on: { click: this.closeMenuClick } }}>
@ -122,6 +146,11 @@ export default {
)
},
// render
/**
* 渲染标签页
* @param {String} title - 标签页的标题
* @param {String} keyPath - 标签页的 fullPath
*/
renderTabPane (title, keyPath) {
const menu = this.renderTabPaneMenu(keyPath)
@ -133,6 +162,10 @@ export default {
}
},
watch: {
/**
* 监听路由变化更新标签页列表和激活的标签页
* @param {Object} newVal - 新的路由信息
*/
'$route': function (newVal) {
this.activeKey = newVal.fullPath
if (this.fullPathList.indexOf(newVal.fullPath) < 0) {
@ -140,6 +173,10 @@ export default {
this.pages.push(newVal)
}
},
/**
* 监听激活的标签页变化更新路由
* @param {String} newPathKey - 新的激活标签页的 fullPath
*/
activeKey: function (newPathKey) {
this.$router.push({ path: newPathKey })
}

@ -1,28 +1,38 @@
<template>
<!-- 页面头部组件 -->
<div class="page-header">
<div class="page-header-index-wide">
<!-- 面包屑导航 -->
<s-breadcrumb />
<div class="detail">
<!-- 主要内容区域 -->
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
<div class="row">
<!-- Logo图片 -->
<img v-if="logo" :src="logo" class="logo"/>
<!-- 页面标题 -->
<h1 v-if="title" class="title">{{ title }}</h1>
<!-- 自定义操作区域 -->
<div class="action">
<slot name="action"></slot>
</div>
</div>
<div class="row">
<!-- 头像 -->
<div v-if="avatar" class="avatar">
<a-avatar :src="avatar" />
</div>
<!-- 自定义内容区域 -->
<div v-if="this.$slots.content" class="headerContent">
<slot name="content"></slot>
</div>
<!-- 额外的自定义区域 -->
<div v-if="this.$slots.extra" class="extra">
<slot name="extra"></slot>
</div>
</div>
<div>
<!-- 页面菜单 -->
<slot name="pageMenu"></slot>
</div>
</div>
@ -31,25 +41,28 @@
</div>
</template>
<script>
<script>//
import Breadcrumb from '../../components/tools/Breadcrumb'
//
export default {
name: 'PageHeader',
components: {
's-breadcrumb': Breadcrumb
},
props: {
// true
title: {
type: [String, Boolean],
default: true,
required: false
},
// Logo
logo: {
type: String,
default: '',
required: false
},
//
avatar: {
type: String,
default: '',
@ -62,7 +75,7 @@ export default {
}
</script>
<style lang="less" scoped>
<style lang="less" scoped>//
.page-header {
background: #fff;
padding: 16px 32px 0;
@ -152,7 +165,7 @@ export default {
}
}
}
//
.mobile .page-header {
.main {
.row {

@ -1,28 +1,34 @@
<template>
<!-- 结果展示组件 -->
<div class="result">
<!-- 根据成功或错误状态动态显示对应图标 -->
<div>
<a-icon :class="{ 'icon': true, [`${type}`]: true }" :type="localIsSuccess ? 'check-circle' : 'close-circle'"/>
</div>
<!-- 标题区域支持自定义插槽内容 -->
<div class="title">
<slot name="title">
{{ title }}
</slot>
</div>
<!-- 描述区域支持自定义插槽内容 -->
<div class="description">
<slot name="description">
{{ description }}
</slot>
</div>
<!-- 额外信息区域根据插槽内容动态显示 -->
<div class="extra" v-if="$slots.default">
<slot></slot>
</div>
<!-- 操作区域根据插槽内容动态显示 -->
<div class="action" v-if="$slots.action">
<slot name="action"></slot>
</div>
</div>
</template>
<script>
<script>//
const resultEnum = ['success', 'error']
export default {
@ -33,6 +39,7 @@ export default {
type: Boolean,
default: false
},
// success error
type: {
type: String,
default: resultEnum[0],
@ -40,16 +47,19 @@ export default {
return (val) => resultEnum.includes(val)
}
},
//
title: {
type: String,
default: ''
},
//
description: {
type: String,
default: ''
}
},
computed: {
//
localIsSuccess: function () {
return this.type === resultEnum[0]
}
@ -69,9 +79,11 @@ export default {
line-height: 72px;
margin-bottom: 24px;
}
//
.success {
color: #52c41a;
}
//
.error {
color: red;
}

@ -1,12 +1,22 @@
<template>
<!-- 设置项容器 -->
<div class="setting-drawer-index-item">
<!-- 设置项标题 -->
<h3 class="setting-drawer-index-title">{{ title }}</h3>
<!-- 插槽内容用于显示设置项的具体内容 -->
<slot></slot>
<!-- 根据props中的divider决定是否显示分割线 -->
<a-divider v-if="divider"/>
</div>
</template>
<script>
/**
* 设置项组件
*
* @param {String} title - 设置项的标题默认为空字符串
* @param {Boolean} divider - 是否显示设置项后的分割线默认不显示
*/
export default {
name: 'SettingItem',
props: {
@ -22,11 +32,11 @@ export default {
}
</script>
<style lang="less" scoped>
<style lang="less" scoped>/* 设置项样式 */
.setting-drawer-index-item {
margin-bottom: 24px;
/* 设置项标题样式 */
.setting-drawer-index-title {
font-size: 14px;
color: rgba(0, 0, 0, .85);

@ -1,15 +1,18 @@
<template>
<!-- 标准表单行组件根据不同的属性组合有不同的样式表现 -->
<div :class="[prefixCls, lastCls, blockCls, gridCls]">
<!-- 标题部分只有当title属性存在时才显示 -->
<div v-if="title" class="antd-pro-components-standard-form-row-index-label">
<span>{{ title }}</span>
</div>
<!-- 内容部分使用slot以便于自定义内容 -->
<div class="antd-pro-components-standard-form-row-index-content">
<slot></slot>
</div>
</div>
</template>
<script>
<script>//
const classes = [
'antd-pro-components-standard-form-row-index-standardFormRowBlock',
'antd-pro-components-standard-form-row-index-standardFormRowGrid',
@ -17,6 +20,7 @@ const classes = [
]
export default {
name: 'StandardFormRow',
//
props: {
prefixCls: {
type: String,
@ -36,6 +40,7 @@ export default {
type: Boolean
}
},
// props
computed: {
lastCls () {
return this.last ? classes[2] : null
@ -50,9 +55,9 @@ export default {
}
</script>
<style lang="less" scoped>
<style lang="less" scoped>//
@import '../index.less';
//
.antd-pro-components-standard-form-row-index-standardFormRow {
display: flex;
margin-bottom: 16px;
@ -71,7 +76,7 @@ export default {
padding: 0;
line-height: 32px;
}
//
.antd-pro-components-standard-form-row-index-label {
flex: 0 0 auto;
margin-right: 24px;
@ -87,27 +92,27 @@ export default {
}
}
}
//
.antd-pro-components-standard-form-row-index-content {
flex: 1 1 0;
/deep/ .ant-form-item:last-child {
margin-right: 0;
}
}
//
&.antd-pro-components-standard-form-row-index-standardFormRowLast {
margin-bottom: 0;
padding-bottom: 0;
border: none;
}
// 使block
&.antd-pro-components-standard-form-row-index-standardFormRowBlock {
/deep/ .ant-form-item,
div.ant-form-item-control-wrapper {
display: block;
}
}
// 使grid
&.antd-pro-components-standard-form-row-index-standardFormRowGrid {
/deep/ .ant-form-item,
div.ant-form-item-control-wrapper {

@ -1,6 +1,9 @@
<template>
<!-- 面包屑组件 -->
<a-breadcrumb class="breadcrumb">
<!-- 遍历面包屑列表生成每个面包屑项 -->
<a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
<!-- 根据条件渲染面包屑项的链接或文本 -->
<router-link
v-if="item.name != name && index != 1"
:to="{ path: item.path === '' ? '/' : item.path }"
@ -14,26 +17,35 @@
export default {
data () {
return {
//
name: '',
//
breadList: []
}
},
created () {
//
this.getBreadcrumb()
},
methods: {
/**
* 获取面包屑信息
* 该方法根据当前路由生成面包屑列表
*/
getBreadcrumb () {
this.breadList = []
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: ''}})
//
this.name = this.$route.name
this.$route.matched.forEach(item => {
// item.name !== 'index' && this.breadList.push(item)
//
this.breadList.push(item)
})
}
},
watch: {
//
$route () {
this.getBreadcrumb()
}

@ -1,14 +1,19 @@
<template>
<!-- 头部信息组件根据props调整样式和布局 -->
<div class="head-info" :class="center && 'center'">
<!-- 显示标题 -->
<span>{{ title }}</span>
<!-- 显示内容 -->
<p>{{ content }}</p>
<!-- 根据bordered prop决定是否显示边框 -->
<em v-if="bordered"/>
</div>
</template>
<script>
<script>//
export default {
name: 'HeadInfo',
//
props: {
title: {
type: String,

@ -1,26 +1,34 @@
<template>
<!-- Logo组件主体 -->
<div class="logo">
<!-- 使用router-link组件创建到dashboard的链接 -->
<router-link :to="{name:'dashboard'}">
<!-- 渲染LogoSvg组件带alt属性说明 -->
<LogoSvg alt="logo" />
<!-- 根据showTitle属性决定是否显示标题 -->
<h1 v-if="showTitle">{{ title }}</h1>
</router-link>
</div>
</template>
<script>
<script>// logo.svg
import LogoSvg from '../../assets/logo.svg?inline'
// Logo
export default {
name: 'Logo',
//
components: {
LogoSvg
},
props: {
// titleString'Online Exam'
title: {
type: String,
default: 'Online Exam',
required: false
},
// showTitleBooleantrue
showTitle: {
type: Boolean,
default: true,

@ -1,19 +1,28 @@
<template>
<!-- 用户信息和操作菜单的容器 -->
<div class="user-wrapper">
<div class="content-box">
<!-- 用户下拉菜单 -->
<a-dropdown>
<!-- 用户信息展示区域 -->
<span class="action ant-dropdown-link user-dropdown-menu">
<!-- 用户头像 -->
<a-avatar class="avatar" size="small" :src="avatar()"/>
<!-- 用户昵称 -->
<span>{{ nickname() }}</span>
</span>
<!-- 下拉菜单内容 -->
<a-menu slot="overlay" class="user-dropdown-menu-wrapper">
<!-- 账户设置菜单项 -->
<a-menu-item key="1">
<router-link :to="{ name: 'settings' }">
<a-icon type="setting"/>
<span>账户设置</span>
</router-link>
</a-menu-item>
<!-- 菜单分割符 -->
<a-menu-divider/>
<!-- 退出登录菜单项 -->
<a-menu-item key="3">
<a href="javascript:;" @click="handleLogout">
<a-icon type="logout"/>
@ -44,6 +53,7 @@ export default {
return that.Logout({}).then(() => {
window.location.reload()
}).catch(err => {
//
that.$message.error({
title: '错误',
description: err.message
@ -51,6 +61,7 @@ export default {
})
},
onCancel () {
//
}
})
}

Loading…
Cancel
Save