完善http模块和日志输出级别

This commit is contained in:
DESKTOP-VMMLSOQ\wangzg 2024-02-20 00:02:13 +08:00
parent 91ad9927f2
commit c61cfa7ee6
16 changed files with 504 additions and 713 deletions

View File

@ -13,6 +13,7 @@
"axios": "^1.6.1",
"core-js": "^3.25.3",
"echarts": "^5.4.3",
"loglevel": "^1.9.1",
"nuxt": "^2.15.8",
"vue": "^2.7.10",
"vue-echarts": "^4.1.0",

View File

@ -1,656 +1,7 @@
<template>
<div class="visual-container" :style="autoStyle">
<div class="header">
<div class="date">
{{ date }}
</div>
<div class="time">
{{ time }}
</div>
<div class="title">
{{ title }}
</div>
</div>
<div class="body">
<div class="left-panel">
<CustomTitle>教育资源</CustomTitle>
<Split num="10"></Split>
<div class="jyzy">
<div class="jyzy-t">
<a-row>
<a-col :span="12">
<CustomDesc title="年级数" :num="resourcesStatistics.gradeNumber || 0" :icon="njs" />
</a-col>
<a-col :span="12" style="padding-left: 23px;">
<CustomDesc title="班级数" :num="resourcesStatistics.classNumber || 0" :icon="bjs" />
</a-col>
</a-row>
</div>
<div class="jyzy-b">
<a-row>
<a-col :span="12">
<CustomDesc title="教职工数" :num="resourcesStatistics.staffNumber || 0" :icon="jzgs" />
</a-col>
<a-col :span="12" style="padding-left: 23px;">
<CustomDesc title="学生总数(人)" :num="resourcesStatistics.studentNumber || 0" :icon="xszs" />
</a-col>
</a-row>
</div>
</div>
<CustomLine />
<Split num="23"></Split>
<CustomSubTitle>各年级学生分布</CustomSubTitle>
<Split num="12">
</Split>
<div style="height: 190px;">
<Pie :data="studentGradStatistics" name="学生总数" :total="resourcesStatistics.studentNumber || 0"></Pie>
</div>
<Split num="12"></Split>
<CustomSubTitle>性别分布</CustomSubTitle>
<div style="height: 126px;">
<Split num="8"></Split>
<a-row class="xbfb" :gutter="30">
<a-col :span="12">
<div class="tp">
<div class="sex"></div>
<div class="info">
<span class="num">{{ studentSexStatistics.men.total }}</span>
<div class="info-line"></div>
<span class="num">{{ studentSexStatistics.men.percent }}%</span>
</div>
</div>
<div class="process">
<div class="persen men" v-for="num in personArr" :key="num.value" :class="{ select: studentSexStatistics.men.percent >= num.value }"></div>
</div>
</a-col>
<a-col :span="12" style="padding-left: 23px;">
<div class="tp">
<div class="sex"></div>
<div class="info">
<span class="num">{{ studentSexStatistics.women.total }}</span>
<div class="info-line"></div>
<span class="num">{{ studentSexStatistics.women.percent }}%</span>
</div>
</div>
<div class="process">
<div class="persen women" v-for="num in personArr" :key="num.value" :class="{ select: studentSexStatistics.women.percent >= num.value }"></div>
</div>
</a-col>
</a-row>
<Split num="23"></Split>
</div>
<CustomTitle>电子班牌概况</CustomTitle>
<div style="height: 106px; box-sizing: border-box;padding: 13px 0;">
<div class="jkdgk">
<CustomDesc title="总数(个)" :num="deviceStatistics.deviceTotalNumber || 0" color="#08EBF5" />
<CustomDesc title="在线(个)" :num="deviceStatistics.deviceOnlineNumber || 0" color="#2DFBC4" />
<CustomDesc title="离线(个)" :num="deviceStatistics.deviceOfflineNumber || 0" color="#FF7011" />
<CustomDesc title="待检测(个)" :num="deviceStatistics.deviceOtherNumber || 0" color="#E6F6FF" />
</div>
</div>
<CustomTitle>今日门禁进出</CustomTitle>
<div style="height: 217px;padding-top: 14px;">
<div class="mjjc">
<div class="mjjc-item-1 mjjc-item">
<CustomDesc2 title="教职工" :num="accessStatistics.staffNumber || 0"></CustomDesc2>
</div>
<div class="mjjc-item-2 mjjc-item">
<CustomDesc2 title="学生" :num="accessStatistics.studentNumber || 0"></CustomDesc2>
</div>
<div class="mjjc-item-3 mjjc-item">
<CustomDesc2 title="校外人员" :num="accessStatistics.otherNumber || 0"></CustomDesc2>
</div>
<div class="mjjc-item-4 mjjc-item">
<CustomDesc2 title="出入总数" :num="accessStatistics.atSchoolNumber || 0" color="#E6F6FF" font-size="24"></CustomDesc2>
</div>
</div>
</div>
</div>
<div class="right-panel">
<CustomTitle>今日在校人数</CustomTitle>
<div style="height: 98px;padding: 15px 0;box-sizing: border-box">
<div class="zxrs-main">
<NumCon :num="studentAttendanceStatistics.atSchoolNumber || 0"></NumCon>
</div>
</div>
<CustomTitle>考勤统计</CustomTitle>
<div style="height: 258px;" class="kqtj">
<div class="kqtj-t">
<div class="top-info">
<CustomDesc titleColor="#08EBF5" paddingLeft="0" align="center" title="可打卡学生总数" :num="studentAttendanceStatistics.studentTotalNumber || 0" />
<CustomDesc titleColor="#08EBF5" paddingLeft="0" align="center" title="出勤人数" :num="studentAttendanceStatistics.studentAttendanceNumber || 0" />
<CustomDesc titleColor="#08EBF5" paddingLeft="0" align="center" title="出勤率" :num="(studentAttendanceStatistics.studentAttendanceProportion || 0) + '%'" />
</div>
</div>
<div style="height: 160px;">
<Pie :data="studentAttendanceStatisticsPie" name="出勤率" :total="(studentAttendanceStatistics.studentAttendanceProportion || 0) + '%'"></Pie>
</div>
</div>
<CustomTitle>各年级数人分布</CustomTitle>
<div style="height: 222px;">
<div class="tabs">
<TabItem @click="classClickHandler(1)" :selected="cls === 1">一年级</TabItem>
<TabItem @click="classClickHandler(2)" :selected="cls === 2">二年级</TabItem>
<TabItem @click="classClickHandler(3)" :selected="cls === 3">三年级</TabItem>
<TabItem @click="classClickHandler(4)" :selected="cls === 4">四年级</TabItem>
<TabItem @click="classClickHandler(5)" :selected="cls === 5">五年级</TabItem>
<TabItem @click="classClickHandler(6)" :selected="cls === 6">六年级</TabItem>
</div>
<div style="height: 169px;">
<Pie :data="gradeStatistics"></Pie>
</div>
</div>
<CustomTitle>近7日请假统计</CustomTitle>
<div style="height: 303px;">
<Bar :colors="barColors" :data="leaveStatistics"></Bar>
</div>
</div>
<div class="center">
<div class="center-img">
<img :src="schSrc" alt="">
</div>
<CustomTitle type="2" style="margin-top: 12px;">电子班牌使用情况</CustomTitle>
<Split num="22"></Split>
<div style="height: 94px;">
<a-row type="flex" justify="center">
<a-col :span="8" class="fc">
<a-row type="flex" justify="center">
<a-col :span="12">
<CustomDesc title="信息发布数" :num="classBrandStatistics.releaseNumber || 0" :icon="xxfbs"></CustomDesc>
</a-col>
<a-col :span="12">
<CustomDesc title="刷脸数" :num="classBrandStatistics.brushFaceNumber || 0" :icon="sls"></CustomDesc>
</a-col>
</a-row>
<CustomLine type="2" />
</a-col>
<a-col :span="16" style="padding-left: 32px;">
<a-row :gutter="16">
<a-col :span="12">
<div class="dzbpboard b1">
<div class="board-main">
<CustomDesc title="留言数量" :num="classBrandStatistics.brandMessageCount || 0" padding-left="0"></CustomDesc>
<CustomDesc title="发言人数" :num="classBrandStatistics.brandMessagePersonCount || 0" color="#34FF66" padding-left="0"></CustomDesc>
</div>
</div>
</a-col>
<a-col :span="12">
<div class="dzbpboard b2">
<div class="board-main">
<CustomDesc title="照片数量" :num="classBrandStatistics.photoNumber || 0" padding-left="0"></CustomDesc>
<CustomDesc title="视频数量" :num="classBrandStatistics.videoNumber || 0" color="#34FF66" padding-left="0"></CustomDesc>
</div>
</div>
</a-col>
</a-row>
</a-col>
</a-row>
</div>
<CustomTitle type="2" >签到记录</CustomTitle>
<div style="height: 278px;">
<Split num="12"></Split>
<div style="height: 213px;overflow: hidden">
<vue-seamless-scroll
:data="accessInfo"
:class-option="classOption"
class="warp"
>
<SwiperCard v-for="item in accessInfo" :key="item.uuid" :card-info="item"></SwiperCard>
</vue-seamless-scroll>
</div>
</div>
</div>
</div>
</div>
<div>hello world</div>
</template>
<script>
import moment from 'moment'
import schSrc from 'assets/images/学校全景.jpg'
import njs from 'assets/images/编组 20 2.png'
import bjs from 'assets/images/编组 20.png'
import jzgs from 'assets/images/图标.png'
import xszs from 'assets/images/图标 2.png'
import xxfbs from 'assets/images/编组 11备份 2.png'
import sls from 'assets/images/编组 11备份 2 2.png'
import { raf, caf } from '@/utils/animate'
import https from '@/mixins/https'
import vueSeamlessScroll from 'vue-seamless-scroll'
const nowDate = moment().format('YYYY-MM-DD')
const nowTime = moment().format('HH:mm:ss')
export default {
name: 'IndexPage',
mixins: [https],
components: {
vueSeamlessScroll
},
data () {
return {
autoStyle: {},
classOption: {
step: 1, //
limitMoveNum: 6, // this.dataList.length
hoverStop: true, // stop
direction: 2, // 0 1 2 3
openWatch: true, // dom
singleHeight: 0, // (0) direction => 0/1
singleWidth: 0, // (0) direction => 2/3
waitTime: 1000 // (1000ms)
},
rate: 50,
personArr: [
{ value: 12.5 },
{ value: 25 },
{ value: 37.5 },
{ value: 50 },
{ value: 62.5 },
{ value: 75 },
{ value: 87.5 },
{ value: 100 },
],
sls,
xxfbs,
xszs,
jzgs,
njs,
bjs,
schSrc,
date: nowDate,
time: nowTime,
title: '西乌旗第三小学智慧校园可视化管理平台',
barColors: [
[ '#8AFFE2', '#00BFFF' ],
[ '#FF7313', '#EFD351' ],
],
cls: 1
}
},
mounted() {
this.getTime()
this.getAutoStyle()
window.addEventListener('resize', (e) => {
e.stopPropagation()
this.getAutoStyle()
}, false)
},
computed: {
},
methods: {
getAutoStyle () {
const { scale, origin } = this.getScale()
this.autoStyle = {
transform: `scale(${scale})`,
transformOrigin: origin
}
},
getScale () {
let scale = 1
let origin = 'top center'
const baseWidth = 1920
const baseHeight = 1080
const innerWidth = window.innerWidth
const innerHeight = window.innerHeight
const widthScale = innerWidth / baseWidth
const scaleHeight = baseHeight * widthScale
if (scaleHeight <= innerHeight) {
origin = 'left center'
scale = widthScale
if (scale > 1) {
origin = 'left top'
}
} else {
scale = innerHeight / baseHeight
}
return { scale, origin }
},
classClickHandler (cls) {
this.cls = cls
this.getGradeStatisticsPieData()
},
getTime () {
this.date = moment().format('YYYY-MM-DD')
this.time = moment().format('HH:mm:ss')
if (this.raf) {
caf(this.raf)
}
this.raf = raf(() => {
this.getTime()
})
},
}
}
export default {}
</script>
<style>
body, html, #__nuxt, #__layout {
width: 100%;
height: 100%;
background-color: #000;
overflow: hidden;
}
.list-enter-active, .list-leave-active {
transition: all 0.5s;
}
.list-enter, .list-leave-to
/* .list-leave-active for below version 2.1.8 */ {
opacity: 0;
transform: translateY(30px);
}
</style>
<style scoped lang="less">
@import "assets/styles/mixin";
.warp {
width: 100%;
.swiper-con {
.clear-fix;
}
}
.visual-container {
width: 1920px;
height: 1080px;
overflow: hidden;
position: relative;
background: url("assets/images/bg.png") left top no-repeat;
background-size: 100% 100%;
user-select: none;
.header {
height: 56px;
width: 100%;
background-color: #333;
line-height: 56px;
box-sizing: border-box;
padding: 0 32px;
background: url("assets/images/顶部导航.png") left 4px no-repeat;
background-size: 100% 100%;
.clear-fix;
.date, .time {
width: 100px;
font-family: HIKLDH-Number-CondensedMedium;
font-size: 16px;
color: #08EBF5;
letter-spacing: 0;
font-weight: 500;
}
.date {
.float(left)
}
.time {
.float(right);
text-align: right;
}
.title {
overflow: hidden;
font-family: HYHeiFangJ;
font-size: 30px;
color: #fff;
text-align: center;
text-shadow: 3px 3px 3px rgba(2,36,40,0.25);
font-weight: 700;
}
}
.body {
height: calc(100% - 56px);
box-sizing: border-box;
padding: 16px 32px;
.clear-fix;
.left-panel {
height: 100%;
width: 488px;
.float(left);
padding-right: 27px;
box-sizing: border-box;
.xbfb {
position: relative;
height: 103px;
.tp {
height: 33px;
line-height: 33px;
.clear-fix;
.sex {
float: left;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #9FF7FF;
letter-spacing: 0;
font-weight: 400;
}
.info {
float: right;
font-family: HIKLDH-Number-CondensedMedium;
font-size: 24px;
color: #08EBF5;
letter-spacing: 0;
line-height: 33px;
font-weight: 500;
.clear-fix;
.num, .info-line {
float: left;
}
.info-line {
width: 2px;
height: 33px;
background-color: #08EBF5;
margin: 0 11px;
}
}
}
.process {
.clear-fix;
padding-top: 13px;
.persen {
float: left;
margin-left: 4px;
width: 22px;
height: 50px;
&:first-child {
margin-left: 0;
}
&.men {
background: url('assets/images/形状结合备份 16.png') left top no-repeat;
background-size: 100% 100%;
&.select {
background: url('assets/images/男性(亮).png') left top no-repeat;
background-size: 100% 100%;
}
}
&.women {
background: url('assets/images/形状备份 3.png') left top no-repeat;
background-size: 100% 100%;
&.select {
background: url('assets/images/女性(亮).png') left top no-repeat;
background-size: 100% 100%;
}
}
}
}
&:after {
position: absolute;
content: " ";
display: table;
top: 0;
left: 50%;
transform: translateX(-50%);
height: 100%;
width: 1px;
background: linear-gradient(to top, transparent, rgba(255, 255, 255, 0.5), #08EBF5, rgba(255, 255, 255, 0.5), transparent);//
}
}
.jyzy {
position: relative;
&:after {
position: absolute;
content: " ";
display: table;
top: 0;
left: 50%;
transform: translateX(-50%);
height: 100%;
width: 1px;
background: linear-gradient(to top, transparent, rgba(255, 255, 255, 0.5), #08EBF5, rgba(255, 255, 255, 0.5), transparent);//
}
}
.jyzy-t {
position: relative;
padding: 15px;
box-sizing: border-box;
&:after {
position: absolute;
content: " ";
display: table;
bottom: 0;
left: 0;
height: 1px;
width: 100%;
background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.5), #08EBF5, rgba(255, 255, 255, 0.5), transparent);//
}
}
.jyzy-b {
position: relative;
padding: 15px;
box-sizing: border-box;
}
.jkdgk {
height: 80px;
background: url('assets/images/1状态数量统计.png') left top no-repeat;
background-size: 100% 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.mjjc {
height: 176px;
background: url('assets/images/编组 18.png') left top no-repeat;
background-size: 100% 100%;
position: relative;
.mjjc-item {
position: absolute;
width: 96px;
height: 64px;
display: flex;
justify-content: center;
align-items: center;
&.mjjc-item-1 {
top: 0;
left: 0;
}
&.mjjc-item-2 {
right: 0px;
top: 50%;
transform: translateY(-50%);
}
&.mjjc-item-3 {
bottom: 5px;
left: 0;
}
&.mjjc-item-4 {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
}
.right-panel {
height: 100%;
width: 488px;
.float(right);
padding-left: 27px;
box-sizing: border-box;
.zxrs-main {
height: 67px;
background: url("assets/images/编组 4.png") left 4px no-repeat;
background-size: 100% 100%;
padding-left: 130px;
padding-top: 13px;
}
}
.center {
height: 100%;
overflow: hidden;
.center-img {
width: 100%;
height: 538px;
padding: 10px;
background: url("assets/images/编组212.png") left 4px no-repeat;
background-size: 100% 535px;
> img {
width: 100%;
display: block;
height: 518px;
}
}
.board-main {
width: 214px;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 37px;
box-sizing: border-box;
padding-top: 10px;
}
.fc {
display: flex;
justify-content: center;
align-items: center;
> div:nth-child(1) {
width: 100% ;
};
}
.dzbpboard {
height: 72px;
box-sizing: border-box;
padding-left: 70px;
&.b1 {
background: url("assets/images/顶部本月的粉 2.png") left 4px no-repeat;
background-size: 100% 100%;
}
&.b2 {
background: url("assets/images/顶部本月的粉.png") left 4px no-repeat;
background-size: 100% 100%;
}
}
}
}
.kqtj-t {
padding: 10px 16px 0;
box-sizing: border-box;
}
.top-info {
background: url("assets/images/编组 21.png") center 22px no-repeat;
background-size: 100% 50%;
display: flex;
justify-content: space-between;
align-items: flex-start;
height: 64px;
box-sizing: border-box;
padding: 0 20px;
}
.tabs {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
width: 100%;
}
}
</style>

View File

@ -73,6 +73,11 @@ export default {
}
},
mounted() {
this.$log.info('test')
this.$log.error('test')
this.$log.trace('test')
this.$log.info('test')
this.$log.warn('test')
this.getAutoStyle()
window.addEventListener('resize', (e) => {
e.stopPropagation()
@ -161,11 +166,13 @@ body, html, #__nuxt, #__layout {
height: 2721px;
background: url("assets/peakCoalImages/left/left-content-bg.png") no-repeat center 60px;
padding: 300px 476px 100px 412px;
perspective: 500px;
.left-data-panel {
height: 100%;
width: 100%;
transform: rotate(2deg);
transform: rotateY(2deg);
transform-origin: left center;
}
}
@ -177,11 +184,13 @@ body, html, #__nuxt, #__layout {
height: 2721px;
background: url("assets/peakCoalImages/right/right-content-bg.png") no-repeat center 60px;
padding: 300px 476px 100px 480px;
perspective: 500px;
.right-data-panel {
height: 100%;
width: 100%;
transform: rotate(-2deg);
transform: rotateY(-2deg);
transform-origin: right center;
}
}

View File

@ -1,5 +1,11 @@
import Vue from 'vue'
import Antd from 'ant-design-vue/lib'
import log from 'loglevel';
const logLevel = parseInt(localStorage.getItem('loglevel') || '-1')
if (logLevel >= 0) {
log.setDefaultLevel(logLevel)
}
Vue.prototype.$log = log
Vue.use(Antd)

View File

@ -1,54 +1,10 @@
import { get, post } from './axios'
// 教育资源统计信息
export const getResourcesStatistics = function () {
return get('/cloudStatistics/getResourcesStatistics')
import { SupportLog, Request, Get, Post, Delete, Log } from './httpModule'
@SupportLog
@Request('/aaa')
class Apis {
@Log('test')
@Get('test')
test () {}
}
// 各年级学生分布信息
export const getStudentGradStatistics = function () {
return get('/cloudStatistics/getStudentGradStatistics')
}
// 性别分布
export const getStudentSexStatistics = function () {
return get('/cloudStatistics/getStudentSexStatistics')
}
// 设备概况
export const getDeviceStatistics = function () {
return get('/cloudStatistics/getDeviceStatistics')
}
// 门禁进出人数
export const getAccessStatistics = function () {
return get('/cloudStatistics/getAccessStatistics')
}
// 查询班牌统计信息
export const getClassBrandStatistics = function () {
return get('/cloudStatistics/getClassBrandStatistics')
}
// 签到记录-最新10条
export const getAccessInfo = function () {
return get('/cloudStatistics/getAccessInfo')
}
// 在校人数与考勤统计
export const getStudentAttendanceStatistics = function () {
return get('/cloudStatistics/getStudentAttendanceStatistics')
}
// 各年级人数及出勤分布
export const getGradeStatistics = function () {
return get('/cloudStatistics/getGradeStatistics')
}
// 近7日请假统计
export const getLeaveStatistics = function () {
return get('/cloudStatistics/getLeaveStatistics')
}
export default new Apis()

View File

@ -41,14 +41,7 @@ service.interceptors.response.use(
}
)
const get = (url, params = {}, config) => {
return service.get(url, { params: params || {}, ...(config ||{}) })
}
const post = (url, params = {}, config) => {
return service.post(url, params, config || {})
}
export { get, post }
export default service

175
services/httpModule/http.js Normal file
View File

@ -0,0 +1,175 @@
import checkTypes from "@/utils/checkTypes";
import {urlJoin} from "@/utils/url";
import {isEmpty} from "@/utils/tools";
import http from './axios'
const isDev = process.env.NODE_ENV === 'development'
const getUrlByCode = function (url, obj = {}) {
const reg = /\{([a-zA-Z0-9]+)\}/g
const match = url.match(reg) || []
let str = url
match.forEach(field => {
const _field = field.substr(1, field.length - 2)
str = str.replace(field, obj[_field] || field)
})
return str
}
/**
* 获取请求地址
* @param prefix
* @param url
*/
const getUrl = (prefix, url) => {
if (url.startsWith('http') || !prefix) return url
if (prefix.startsWith('http')) {
return urlJoin(`${prefix}/${url}`)
}
return urlJoin(`/${prefix}/${url}`)
}
/**
* 请求处理
* @param httpMethod { String } 请求类型
* @param fetchUrl { String } 请求地址
* @returns {function(...[*]=)}
*/
const decoratorHandler = function (httpMethod, fetchUrl) {
return function (target, propertyKey, descriptor) {
const method = descriptor.value
descriptor.value = async function (params = {}, options = {}, $logName) {
const res = await method.apply(this, [params, options])
let _params = params
let _options = options
if (!isEmpty(res) && checkTypes.isObject(res) && res.params) {
_params = res.params
_options = res.options
}
if (this._commonAxiosOptions) {
_options = {
...this._commonAxiosOptions,
..._options
}
}
// 生成请求参数
const httpOptions = {
method: httpMethod,
url: getUrl(this.prefix, getUrlByCode(fetchUrl || propertyKey, _params)),
baseURL: '',
..._options
}
if (httpMethod === 'get') {
httpOptions.params = _params
} else {
httpOptions.data = _params
}
try {
const response = await http(httpOptions)
if (this.log) return this.log($logName, httpOptions, response)
return response
} catch (error) {
if (this.log) throw this.log($logName, httpOptions, null, error)
throw error
}
}
}
}
/**
* 根据环境过滤地址
* @param devUrl { String }
* @param prodUrl { String }
* @returns {String}
*/
const filterUrl = function (devUrl, prodUrl) {
return (isDev ? devUrl : prodUrl || devUrl) || undefined
}
/**
* 初始化请求接口前缀
* @param devPrefix { String } 开发环境请求地址前缀
* @param prodPrefix { String } 生产环境请求地址前缀
* @returns {function(*): {new(): {prefix: *}, prototype: {prefix: *}}}
* @constructor
*/
export const Request = (devPrefix, prodPrefix) => {
return function (constructor) {
return class extends constructor {
prefix = isDev ? devPrefix : prodPrefix || devPrefix
}
}
}
/**
* Post请求
* @param devUrl { String }
* @param prodUrl { String }
* @returns {function(...[*]=)}
* @constructor
*/
export const Post = (devUrl, prodUrl) => {
let _devUrl = typeof devUrl === 'string' ? devUrl : prodUrl
return decoratorHandler.call(this, 'post', filterUrl(_devUrl, prodUrl))
}
/**
* Get请求
* @param devUrl { String }
* @param prodUrl { String }
* @returns {function(...[*]=)}
* @constructor
*/
export const Get = (devUrl, prodUrl) => {
let _devUrl = typeof devUrl === 'string' ? devUrl : prodUrl
return decoratorHandler.call(this, 'get', filterUrl(_devUrl, prodUrl))
}
/**
* Put请求注解
* @param devUrl { String }
* @param prodUrl { String }
* @returns {function(...[*]=)}
* @constructor
*/
export const Put = (devUrl, prodUrl) => {
let _devUrl = typeof devUrl === 'string' ? devUrl : prodUrl
return decoratorHandler.call(this, 'put', filterUrl(_devUrl, prodUrl))
}
/**
* Delete请求注解
* @param devUrl { String }
* @param prodUrl { String }
* @returns {function(...[*]=)}
* @constructor
*/
export const Delete = (devUrl, prodUrl) => {
let _devUrl = typeof devUrl === 'string' ? devUrl : prodUrl
return decoratorHandler.call(this, 'delete', filterUrl(_devUrl, prodUrl))
}
/**
* 为当前方法添加其他内容
* @param _options
* @return {function(...[*]=)}
* @constructor
*/
export const AxiosOptions = function (_options = {}) {
return function (target, propertyKey, descriptor) {
// 类
if (target.prototype) {
return class extends target {
_commonAxiosOptions = _options || {}
}
} else {
const method = descriptor.value
descriptor.value = function (params = {}, options = {}, $logName) {
return method.apply(this, [params, {
...options,
..._options
}, $logName])
}
}
}
}

View File

@ -0,0 +1,50 @@
import moment from "moment";
const isDev = process.env.NODE_ENV === 'development'
// 打印日志
export const httpLog = function (logName, { method, url, params, data }, response, error) {
const logColor = response ? '#0ad554' : '#FF0000'
const logLevel = parseInt(localStorage.getItem('loglevel') || '-1')
if (logLevel >= 0) {
const time = moment().format('YYYY-MM-DD HH:mm:ss')
console.groupCollapsed(`%c [DEBUG] HTTP: ${time}-${method.toUpperCase()}@${url}@${logName || ''}`,
`color: #fff;background-color: ${logColor};`
)
if (data) {
console.log(' query: %o', data)
}
if (params) {
console.log(' query: %o', params)
}
if (response) {
console.log('response: %o', response)
}
if (error) {
console.log(' error: %o', error)
}
console.groupEnd()
}
if (error) throw error
if (response) return response
}
/**
* 为服务支持日志功能
* @param constructor
* @return {{new(): {httpLog: *}, prototype: {httpLog: *}}}
* @constructor
*/
export const SupportLog = function (constructor) {
return class extends constructor {
log = httpLog
}
}
export const Log = function (logName) {
return function (target, propertyKey, descriptor) {
const method = descriptor.value || target[propertyKey]
descriptor.value = function (params = {}, options = {}) {
return method && method.apply(this, [params, options, logName])
}
}
}

View File

@ -0,0 +1,2 @@
export * from './http'
export * from './httpLog'

8
utils/checkTypes.js Normal file
View File

@ -0,0 +1,8 @@
const checkTypes = {};
['Array', 'Object', 'String', 'Number', 'Boolean', 'Null', 'Undefined', 'RegExp', 'Function'].forEach(type => {
checkTypes[`is${type}`] = function (data) {
return Object.prototype.toString.call(data) === `[object ${type}]`
}
})
export default checkTypes

27
utils/query.js Normal file
View File

@ -0,0 +1,27 @@
/**
* @function 将url中的查询字符串转换为obj
* @param search { String } 查询字符串默认为当前url中的查询字符串
* @return { Object }
*/
export const query2object = (search = location.search.substr(1)) => {
if (!search.length) return {}
const query = {}
for (const queryString of search.split('&')) {
const kv = queryString.split('=')
query[kv[0]] = kv[1]
}
return query
}
/**
* @function 将对象转换为查询字符串不带#?
* @param object
*/
export const object2query = (object) => {
if (Object.keys(object).length === 0) return ''
let search = ''
for (const key in object) {
search += `${key}=${object[key]}&`
}
return search.substr(0, search.length - 1)
}

49
utils/screen.js Normal file
View File

@ -0,0 +1,49 @@
/**
* @function 开启全屏方法
* @param el { Element } 全屏的dom默认是document.documentElement
*/
function fullScreen(el = document.documentElement) {
let rfs = el.requestFullscreen
|| el.webkitRequestFullScreen
|| el.mozRequestFullScreen
|| el.msRequestFullscreen
try {
if (typeof rfs !== 'undefined' && rfs) rfs.call(el)
} catch (e) {
console.error('fullScreen: %o', e);
}
}
/**
* @function 监测是否全屏
* @return {boolean}
*/
function isFullscreen() {
return !!(
document.webkitIsFullScreen ||
document.mozFullScreen ||
document.mozFullScreenElement ||
document.webkitFullscreenElement ||
document.msFullscreenElement ||
document.fullscreenElement ||
window.fullScreen
)
}
/**
* @function 退出全屏
*/
function exitScreen() {
const exitFn = document.exitFullscreen || document.mozCancelFullScreen || document.webkitCancelFullScreen || document.msExitFullscreen
try {
if (typeof exitFn !== 'undefined' && exitFn) exitFn.call(document)
} catch (e) {
console.error('exitScreen: %o', e)
}
}
export {
fullScreen,
exitScreen,
isFullscreen
}

81
utils/timer.js Normal file
View File

@ -0,0 +1,81 @@
/*
开发环境不开启定时器
*/
const isStartTimer = process.env.NODE_ENV !== 'development'
/**
* @function 定时器
* @param fn { function } 执行过程
* @param duration { timestamp } 间隔时间
* @param immediate { boolean } 是否立即执行
* @returns {{stop(): void, restart: restart}}
*/
export default function timer(fn, duration = 5 * 1000, immediate = true) {
// 执行队列
let processes = []
let _args = []
// 定义定时器
let timer = null
let _duration = duration
// 过程支持list和单一函数
if (Array.isArray(fn)) {
processes.push.apply(processes, fn)
} else {
processes.push(fn)
}
/**
* @function 过程执行器
* @param processes { Array<function> | function }
* @param args { any }
* @returns {Promise<void>}
*/
const exec = async (processes, ...args) => {
_args = args
try {
if (timer) {
clearTimeout(timer)
timer = null
}
for (let process of processes) {
if (typeof process === 'function') {
await process(..._args)
}
}
} catch (e) {
console.error('TimerError:', e)
} finally {
if (!isStartTimer) return false
timer = setTimeout(() => {
exec(processes, ..._args)
}, _duration)
}
}
if (immediate) {
exec(processes)
}
return {
stop () {
if (timer) {
clearTimeout(timer)
timer = null
processes = null
_args = null
}
},
clearParams () {
_args = []
},
/**
* 定时器重启
* @param args { { params: Array<any>, duration: number } } 回调的参数
*/
restart: function (options = {}) {
const { params = [], duration } = options
// 不能用全等对象中的undefined 与 undefined 不是全等
if (duration != undefined || duration != null) {
_duration = duration
}
exec(processes, ...(params || []))
}
}
}

View File

@ -0,0 +1,71 @@
import checkTypes from "@/utils/checkTypes";
export function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0
const v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}
export const getTheTruth = function (data) {
if (typeof data !== 'object' && typeof data !== 'string') return data
if (typeof data === 'string') {
if (data.startsWith('{') || data.startsWith('['))
return getTheTruth(JSON.parse(data))
return data
}
if (Array.isArray(data)) return data.map((item) => getTheTruth(item))
for (const key in data) data[key] = getTheTruth(data[key])
return data
}
export const isEmpty = (data) => {
return checkTypes.isUndefined(data) || checkTypes.isNull(data) || data == NaN
}
export function isDef(v) {
return v !== undefined && v !== null
}
export function isPromise(val) {
return (
isDef(val) &&
typeof val.then === 'function' &&
typeof val.catch === 'function'
)
}
export const toPromise = (task, data) => {
return new Promise((resolve, reject) => {
let _isPromise = false // 当前任务是不是一个promise
try {
// 执行任务
const results = task(data)
// 任务是一个异步函数执行promise
if (isPromise(results)) {
_isPromise = true
results.then(resolve, reject)
} else {
// 非异步任务
resolve(results)
}
} catch (e) {
if (!_isPromise) {
reject(e)
}
}
})
}
export function getSize(size, defaultValue = undefined) {
if (!size) {
if (checkTypes.isNumber(size)) return size
return defaultValue || undefined
}
if (/^\-?\d+$/.test(`${size}`)) return `${size}px`
if (/(%|px)$/.test(`${size}`)) return size
if (/calc/.test(`${size}`)) return size
return size
}

7
utils/url.js Normal file
View File

@ -0,0 +1,7 @@
export function urlJoin() {
return [].slice
.call(arguments)
.join('/')
.replace(/\/+/g, '/')
.replace(':/', '://')
}

View File

@ -5337,6 +5337,11 @@ lodash@^4.15.0, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loglevel@^1.9.1:
version "1.9.1"
resolved "https://registry.npmmirror.com/loglevel/-/loglevel-1.9.1.tgz#d63976ac9bcd03c7c873116d41c2a85bafff1be7"
integrity sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==
loose-envify@^1.0.0:
version "1.4.0"
resolved "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"