注释了整个全部前端文件包

feature/lxh
李炫好 1 month ago
parent 140a059809
commit 45bf601850

@ -1,5 +1,7 @@
<template>
<!-- 使用a-locale-provider组件设置locale属性为locale -->
<a-locale-provider :locale="locale">
<!-- 在div中渲染router-view组件 -->
<div id="app">
<router-view />
</div>
@ -7,12 +9,16 @@
</template>
<script>
// zh_CN
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
// AppDeviceEnquire
import { AppDeviceEnquire } from './utils/mixin'
export default {
// 使AppDeviceEnquire
mixins: [AppDeviceEnquire],
data () {
// localezh_CN
return {
locale: zhCN
}
@ -23,34 +29,41 @@ export default {
}
</script>
<style>
/* 设置#app的高度为100% */
#app {
height: 100%;
}
/* 设置h1, h2, h3, h4, h5, h6的行高、字体大小和字体粗细为初始值 */
h1, h2, h3, h4, h5, h6 {
line-height: inherit !important;
font-size: initial !important;
font-weight: initial !important;
}
/* 设置.page-header .breadcrumb的背景颜色和内边距为初始值 */
.page-header .breadcrumb {
background-color: transparent !important;
padding: inherit !important;
}
/* 设置.row的左边距为初始值 */
.row {
margin-left: inherit !important;
}
/* 设置a标签的鼠标悬停效果为无下划线行高为初始值 */
a:hover {
text-decoration: none !important;
line-height: inherit !important;
}
/* 设置i标签下的svg的垂直对齐方式为初始值 */
i > svg {
vertical-align: initial !important;
}
/* 设置ul标签的下边距为初始值 */
ul {
margin-bottom: inherit !important;
}

@ -2,19 +2,23 @@ const VueAxios = {
vm: {},
// eslint-disable-next-line no-unused-vars
install (Vue, instance) {
// 判断是否已经安装
if (this.installed) {
return
}
this.installed = true
// 判断是否传入axios实例
if (!instance) {
// eslint-disable-next-line no-console
console.error('You have to install axios')
return
}
// 将axios实例挂载到Vue实例上
Vue.axios = instance
// 将axios实例挂载到Vue原型上方便在组件中使用
Object.defineProperties(Vue.prototype, {
axios: {
get: function get () {

@ -1,30 +1,36 @@
import enquireJs from 'enquire.js'
// 定义设备类型
export const DEVICE_TYPE = {
DESKTOP: 'desktop',
TABLET: 'tablet',
MOBILE: 'mobile'
}
// 设备查询函数
export const deviceEnquire = function (callback) {
// 匹配桌面设备
const matchDesktop = {
match: () => {
callback && callback(DEVICE_TYPE.DESKTOP)
}
}
// 匹配平板设备
const matchLablet = {
match: () => {
callback && callback(DEVICE_TYPE.TABLET)
}
}
// 匹配手机设备
const matchMobile = {
match: () => {
callback && callback(DEVICE_TYPE.MOBILE)
}
}
// 注册设备查询
// screen and (max-width: 1087.99px)
enquireJs
.register('screen and (max-width: 576px)', matchMobile)

@ -1,19 +1,31 @@
// 导出一个函数,用于设置文档标题
export const setDocumentTitle = function (title) {
// 设置文档标题
document.title = title
// 获取用户代理信息
const ua = navigator.userAgent
// eslint-disable-next-line
// 定义一个正则表达式,用于匹配微信浏览器
const regex = /\bMicroMessenger\/([\d\.]+)/
// 如果用户代理信息中包含微信浏览器并且是iOS设备
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
// 创建一个iframe元素
const i = document.createElement('iframe')
// 设置iframe的src属性为/favicon.ico
i.src = '/favicon.ico'
// 设置iframe的display属性为none
i.style.display = 'none'
// 当iframe加载完成后执行回调函数
i.onload = function () {
// 设置一个定时器9毫秒后移除iframe元素
setTimeout(function () {
i.remove()
}, 9)
}
// 将iframe元素添加到body元素中
document.body.appendChild(i)
}
}
// 导出一个常量,用于存储文档标题
export const domTitle = 'Online Exam'

@ -4,22 +4,32 @@ import 'moment/locale/zh-cn'
import $ from 'jquery'
moment.locale('zh-cn')
// 定义一个名为NumberFormat的过滤器用于将数字格式化为千分位
Vue.filter('NumberFormat', function (value) {
// 如果value为空则返回'0'
if (!value) {
return '0'
}
const intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') // 将整数部分逢三一断
// 将整数部分逢三一断
const intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
// 返回格式化后的整数部分
return intPartFormat
})
// 定义一个名为dayjs的过滤器用于将日期格式化为指定格式
Vue.filter('dayjs', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
// 使用moment库将日期格式化为指定格式
return moment(dataStr).format(pattern)
})
// 定义一个名为moment的过滤器用于将日期格式化为指定格式
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
// 使用moment库将日期格式化为指定格式
return moment(dataStr).format(pattern)
})
// 定义一个名为imgSrcFilter的过滤器用于获取图片的src属性
Vue.filter('imgSrcFilter', function (content) {
// 使用jQuery选择器获取图片的src属性
return $(content).children('img').attr('src')
})

@ -1,3 +1,4 @@
// 定义权限枚举
const PERMISSION_ENUM = {
'add': { key: 'add', label: '新增' },
'delete': { key: 'delete', label: '删除' },
@ -10,21 +11,30 @@ const PERMISSION_ENUM = {
'export': { key: 'export', label: '导出' }
}
// 定义插件
function plugin (Vue) {
// 如果插件已经安装,则返回
if (plugin.installed) {
return
}
// 如果Vue实例中没有$auth属性则定义$auth属性
!Vue.prototype.$auth && Object.defineProperties(Vue.prototype, {
$auth: {
get () {
// 获取当前实例
const _this = this
// 返回一个函数,用于判断权限
return (permissions) => {
// 将权限字符串拆分为permission和action
const [permission, action] = permissions.split('.')
// 获取当前用户的权限列表
const permissionList = _this.$store.getters.roles.permissions
// 在权限列表中查找对应的permission
return permissionList.find((val) => {
return val.permissionId === permission
}).actionList.findIndex((val) => {
// 在actionList中查找对应的action
return val === action
}) > -1
}
@ -32,15 +42,21 @@ function plugin (Vue) {
}
})
// 如果Vue实例中没有$enum属性则定义$enum属性
!Vue.prototype.$enum && Object.defineProperties(Vue.prototype, {
$enum: {
get () {
// const _this = this;
// 返回一个函数,用于获取枚举值
return (val) => {
// 初始化result为PERMISSION_ENUM
let result = PERMISSION_ENUM
// 如果val存在则将val按'.'拆分为数组
val && val.split('.').forEach(v => {
// 在result中查找对应的值
result = result && result[v] || null
})
// 返回result
return result
}
}

@ -5,67 +5,105 @@ import { mapState } from 'vuex'
// const mixinsComputed = Vue.config.optionMergeStrategies.computed
// const mixinsMethods = Vue.config.optionMergeStrategies.methods
// 定义一个mixin对象用于计算属性和方法的混入
const mixin = {
// 计算属性
computed: {
// 使用mapState函数将state中的app属性映射到计算属性中
...mapState({
// 布局模式
layoutMode: state => state.app.layout,
// 导航主题
navTheme: state => state.app.theme,
// 主色调
primaryColor: state => state.app.color,
// 色弱模式
colorWeak: state => state.app.weak,
// 固定头部
fixedHeader: state => state.app.fixedHeader,
// 固定侧边栏
fixSiderbar: state => state.app.fixSiderbar,
fixSidebar: state => state.app.fixSiderbar,
// 内容宽度
contentWidth: state => state.app.contentWidth,
// 自动隐藏头部
autoHideHeader: state => state.app.autoHideHeader,
// 侧边栏是否打开
sidebarOpened: state => state.app.sidebar,
// 是否开启多标签页
multiTab: state => state.app.multiTab
})
},
// 方法
methods: {
// 判断是否为顶部菜单
isTopMenu () {
return this.layoutMode === 'topmenu'
},
// 判断是否为侧边菜单
isSideMenu () {
return !this.isTopMenu()
}
}
}
// 定义一个mixinDevice对象用于计算设备类型的混入
const mixinDevice = {
// 计算属性
computed: {
// 使用mapState函数将state中的app属性映射到计算属性中
...mapState({
// 设备类型
device: state => state.app.device
})
},
// 方法
methods: {
// 判断是否为移动设备
isMobile () {
return this.device === DEVICE_TYPE.MOBILE
},
// 判断是否为桌面设备
isDesktop () {
return this.device === DEVICE_TYPE.DESKTOP
},
// 判断是否为平板设备
isTablet () {
return this.device === DEVICE_TYPE.TABLET
}
}
}
// 定义一个AppDeviceEnquire对象用于在组件挂载时检测设备类型
const AppDeviceEnquire = {
// 组件挂载时调用
mounted () {
// 获取store
const { $store } = this
// 检测设备类型
deviceEnquire(deviceType => {
// 根据设备类型进行不同的操作
switch (deviceType) {
// 桌面设备
case DEVICE_TYPE.DESKTOP:
// 将设备类型设置为桌面
$store.commit('TOGGLE_DEVICE', 'desktop')
// 设置侧边栏为打开状态
$store.dispatch('setSidebar', true)
break
// 平板设备
case DEVICE_TYPE.TABLET:
// 将设备类型设置为平板
$store.commit('TOGGLE_DEVICE', 'tablet')
// 设置侧边栏为关闭状态
$store.dispatch('setSidebar', false)
break
// 移动设备
case DEVICE_TYPE.MOBILE:
default:
// 将设备类型设置为移动
$store.commit('TOGGLE_DEVICE', 'mobile')
// 设置侧边栏为打开状态
$store.dispatch('setSidebar', true)
break
}
@ -73,4 +111,5 @@ const AppDeviceEnquire = {
}
}
// 导出mixin、AppDeviceEnquire、mixinDevice对象
export { mixin, AppDeviceEnquire, mixinDevice }

@ -15,6 +15,7 @@ const service = axios.create({
timeout: 6000 // 请求超时时间
})
// 错误处理函数
const err = (error) => {
if (error.response) {
const data = error.response.data
@ -42,7 +43,7 @@ const err = (error) => {
return Promise.reject(error)
}
// request interceptor
// 请求拦截器
service.interceptors.request.use(config => {
const token = Vue.ls.get(ACCESS_TOKEN)
if (token) { // 如果localStorage中有"Access-Token"属性,就在请求头里加上
@ -51,11 +52,12 @@ service.interceptors.request.use(config => {
return config
}, err)
// response interceptor
// 响应拦截器
service.interceptors.response.use((response) => {
return response.data
}, err)
// 安装器
const installer = {
vm: {},
install (Vue) {
@ -63,6 +65,7 @@ const installer = {
}
}
// 导出
export {
installer as VueAxios,
service as axios

@ -29,24 +29,33 @@ export const setStore = (name, content, maxAge = null) => {
* @param name
* @returns {*}
*/
// 导出一个函数getStore用于获取存储在localStorage中的数据
export const getStore = name => {
// 如果没有window对象或者没有传入name则返回
if (!global.window || !name) {
return
}
// 获取存储在localStorage中的数据
const content = window.localStorage.getItem(name)
// 获取存储在localStorage中的过期时间
const _expire = window.localStorage.getItem(`${name}_expire`)
// 如果有过期时间
if (_expire) {
// 获取当前时间
const now = parseInt(new Date().getTime() / 1000)
// 如果当前时间大于过期时间,则返回
if (now > _expire) {
return
}
}
// 尝试将数据解析为JSON格式
try {
return JSON.parse(content)
} catch (e) {
// 如果解析失败,则返回原始数据
return content
}
}
@ -56,22 +65,29 @@ export const getStore = name => {
*
* @param name
*/
// 导出一个函数clearStore用于清除localStorage中的数据
export const clearStore = name => {
// 如果没有window对象或者没有传入name则返回
if (!global.window || !name) {
return
}
// 清除localStorage中的数据
window.localStorage.removeItem(name)
// 清除localStorage中的过期时间
window.localStorage.removeItem(`${name}_expire`)
}
/**
* Clear all storage
*/
// 导出一个函数clearAll用于清除localStorage中的所有数据
export const clearAll = () => {
// 如果没有window对象或者没有传入name则返回
if (!global.window || !name) {
return
}
// 清除localStorage中的所有数据
window.localStorage.clear()
}

@ -4,40 +4,62 @@ export function timeFix () {
return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好'
}
// 导出一个函数,用于生成欢迎语
export function welcome () {
// 定义一个数组,包含一些欢迎语
const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
// 生成一个随机数,用于从数组中随机选择一条欢迎语
const index = Math.floor(Math.random() * arr.length)
// 返回随机选择的欢迎语
return arr[index]
}
/**
* 触发 window.resize
*/
// 导出一个函数,用于触发 window.resize 事件
export function triggerWindowResizeEvent () {
// 创建一个 HTMLEvents 事件
const event = document.createEvent('HTMLEvents')
// 初始化事件,事件类型为 resize
event.initEvent('resize', true, true)
// 设置事件类型为 message
event.eventType = 'message'
// 触发 window.resize 事件
window.dispatchEvent(event)
}
// 导出一个函数,用于处理滚动头部
export function handleScrollHeader (callback) {
// 定义一个定时器
let timer = 0
// 定义一个变量,用于存储滚动前的页面位置
let beforeScrollTop = window.pageYOffset
// 如果没有传入回调函数,则使用空函数
callback = callback || function () {}
// 监听 window.scroll 事件
window.addEventListener(
'scroll',
event => {
// 清除定时器
clearTimeout(timer)
// 设置定时器,延迟 50 毫秒执行回调函数
timer = setTimeout(() => {
// 定义一个变量,用于存储滚动后的页面位置
let direction = 'up'
const afterScrollTop = window.pageYOffset
// 计算滚动距离
const delta = afterScrollTop - beforeScrollTop
// 如果滚动距离为 0则返回
if (delta === 0) {
return false
}
// 如果滚动距离大于 0则表示向下滚动否则表示向上滚动
direction = delta > 0 ? 'down' : 'up'
// 执行回调函数,传入滚动方向
callback(direction)
// 更新滚动前的页面位置
beforeScrollTop = afterScrollTop
}, 50)
},
@ -50,11 +72,15 @@ export function handleScrollHeader (callback) {
* @param id parent element id or class
* @param timeout
*/
// 导出一个函数,用于移除加载动画
export function removeLoadingAnimate (id = '', timeout = 1500) {
// 如果没有传入 id则返回
if (id === '') {
return
}
// 设置定时器,延迟 timeout 毫秒后执行回调函数
setTimeout(() => {
// 移除 id 对应的元素
document.body.removeChild(document.getElementById(id))
}, timeout)
}

@ -95,18 +95,30 @@ export default {
<style lang="less" scoped>
//
.avatar-upload-preview {
//
position: absolute;
// 50%
top: 50%;
//
transform: translate(50%, -50%);
// 180px
width: 180px;
// 180px
height: 180px;
//
border-radius: 50%;
//
box-shadow: 0 0 4px #ccc;
//
overflow: hidden;
//
img {
// 100%
width: 100%;
// 100%
height: 100%;
}
}

@ -87,9 +87,11 @@ export default {
},
methods: {
onOpenChange (openKeys) {
// /
this.openKeys = openKeys
},
updateMenu () {
//
const routes = this.$route.matched.concat()
this.defaultSelectedKeys = [ routes.pop().path ]
}
@ -98,15 +100,18 @@ export default {
</script>
<style lang="less" scoped>
//
.account-settings-info-main {
width: 100%;
display: flex;
height: 100%;
overflow: auto;
//
&.mobile {
display: block;
//
.account-settings-info-left {
border-right: unset;
border-bottom: 1px solid #e8e8e8;
@ -115,20 +120,24 @@ export default {
overflow-x: auto;
overflow-y: scroll;
}
//
.account-settings-info-right {
padding: 20px 40px;
}
}
//
.account-settings-info-left {
border-right: 1px solid #e8e8e8;
width: 224px;
}
//
.account-settings-info-right {
flex: 1 1;
padding: 8px 40px;
//
.account-settings-info-title {
color: rgba(0,0,0,.85);
font-size: 20px;
@ -136,6 +145,7 @@ export default {
line-height: 28px;
margin-bottom: 12px;
}
//
.account-settings-info-view {
padding-top: 12px;
}

Loading…
Cancel
Save