Browse Source

项目完善

old
wangqing 5 years ago
parent
commit
c59438a303
  1. 2
      .env.development
  2. 1
      package.json
  3. 7
      public/index.html
  4. 34
      src/utils/index.js
  5. 110
      src/utils/ut.js
  6. 10
      src/views/form/write.vue
  7. 74
      src/views/home/dashboard.vue
  8. 3
      src/views/home/index.vue
  9. 25
      yarn.lock

2
.env.development

@ -6,5 +6,5 @@ VUE_APP_API_ROOT = /tduck-api
# 详情介绍请阅读 http://eoner.gitee.io/vue-automation/#/cdn
VUE_APP_CDN = OFF
# 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
VUE_APP_DEBUG_TOOL =
VUE_APP_DEBUG_TOOL = eruda

1
package.json

@ -21,6 +21,7 @@
"file-saver": "^2.0.2",
"js-cookie": "^2.2.1",
"nprogress": "^0.2.0",
"ua-parser-js": "^0.7.23",
"vue": "^2.6.12",
"vue-clipboard2": "^0.3.1",
"vue-image-crop-upload": "^2.5.0",

7
public/index.html

@ -14,7 +14,7 @@
<!-- 使用CDN的JS文件 -->
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="external nofollow preload" as="script">
<% } %>
</head>
<!--百度访问统计-->
<script>
var _hmt = _hmt || [];
(function() {
@ -24,12 +24,13 @@
s.parentNode.insertBefore(hm, s);
})();
</script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<!-- built files will be auto injected 移动端调试工具-->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
@ -41,6 +42,6 @@
<script src="https://cdn.jsdelivr.net/npm/vconsole/dist/vconsole.min.js"></script>
<script>new VConsole();</script>
<% } %>
</body>
</body>
</html>

34
src/utils/index.js

@ -207,3 +207,37 @@ export function debounce(func, wait, immediate) {
}
}
}
/**
* 将毫秒数转为hh:mm:ss或mm:ss格式
* 如果毫秒数超过60分钟则以hh:mm:ss格式显示反之以mm:ss格式显示
* @param ms 毫秒数
* @returns {string}
*/
export function timeFormat(ms)
{
let h = Math.floor(ms / 1000 / 60 / 60)
let m = Math.floor(ms / 1000 / 60)
let s = Math.floor(ms / 1000)
if (h === 1) {
// 当时间刚好是60分钟时,让它以mm:ss格式显示,即显示60:00,而不是显示01:00:00
if (m / 60 === 1 && s % 60 === 0) {
h = ''
m = '60:'
} else {
h = '01:'
m = addZero(m % 60) + ':'
}
s = addZero(s % 60)
} else {
h = h === 0 ? '' : addZero(h) + ':'
m = addZero(m % 60) + ':'
s = addZero(s % 60)
}
return h + m + s
}
// 进行补0操作
function addZero(n) {
return n < 10 ? '0' + n : n
}

110
src/utils/ut.js

@ -0,0 +1,110 @@
var uaParser = require('ua-parser-js')
//https://afantasy.ninja/2017/05/08/user-tracking-iii/
// 用户停留时间
// addEvent() 是包装了 `window.addEventListener` 和 `window.attachEvent` 的事件监听函数
var r = window.requestAnimationFrame
var c = window.cancelAnimationFrame
var h
var lt = 0
var ltStart
var inActiveTime
var inActiveThreshold = 60 * 60 * 100
// 创建一个心跳闭包,负责向 lifetime 增加累计时间
var h = (function() {
var timer
function beat() {
var now = new Date()
var diff = now - ltStart
lt = lt + diff
inActiveTime = inActiveTime + diff
ltStart = now
if (inActiveTime <= inActiveThreshold) {
timer = r(beat)
} else {
timer = null
}
document.getElementById('inActiveTime').innerText = inActiveTime
}
return {
start: function() {
if (!timer) {
ltStart = new Date()
timer = r(beat)
}
},
stop: function() {
if (timer) {
c(timer)
timer = null
}
}
}
})()
function onFocus() {
h.start()
}
function onBlur() {
h.stop()
}
// 在 PC 端使用 focusin / focusout / focus / blur 事件
if ('onfocusin' in document) {
document.onfocusin = onFocus
document.onfocusout = onBlur
} else {
window.onfocus = onFocus
window.onblur = onBlur
}
// 在移动端使用 Page Visibility API 检查页面是否 active
var prefixes = ['', 'webkit', 'moz', 'ms', 'o']
var pf
var hiddenKey
var eventKey
if (isMobile) {
for (var i = 0; i < prefixes.length; i++) {
pf = prefixes[i]
hiddenKey = pf ? (pf + 'Hidden') : 'hidden'
if (hiddenKey in document) {
eventKey = pf + 'visibilitychange'
break
}
}
if (eventKey) {
addEvent(document, eventKey, function() {
document[hiddenKey] ? onBlur() : onFocus()
})
}
}
inActiveTime = 0
h.start() // 开始计算 lifetime
function isMobile() {
const ua = uaParser(navigator.userAgent)
if (ua.device) {
let {type} = ua.device
if (type && type == 'mobile') {
return true
}
}
return false
}
function addEvent(element, event, callback) {
if (element.addEventListener) { // 支持使用 addEventListener()
if (event.slice(0, 2) === 'on') // 以 "on" 开头,不需要,则去掉
event = event.slice(2)
element.addEventListener(event, callback)
} else if (element.attachEvent) { // 支持使用 attachEvent()
if (event.slice(0, 2) !== 'on') // 没有以 "on" 开头,需要,则加上
event = 'on' + event
element.attachEvent(event, callback)
} else {
event.slice(0, 2) !== 'on' ? element['on' + event] = callback : element[event] = callback
}
}

10
src/views/form/write.vue

@ -1,5 +1,6 @@
<template>
<div class="write-container">
<h1 id="inActiveTime" style="display: none"></h1>
<div v-if="writeStatus==0" class="title-icon-view">
<div class="icon-view">
<i class="el-icon-check success-icon"/>
@ -38,13 +39,14 @@ import loadWXJs from '@/utils/loadWxSdk'
import defaultValue from '@/utils/defaultValue'
import {getCurrentDomain, getQueryString} from '@/utils'
require('@/utils/ut')
let wx
export default {
name: 'WriteView',
props: {},
data() {
return {
inActiveTime: 0,
projectConfig: {
projectKey: '',
preview: false,
@ -86,6 +88,7 @@ export default {
this.wxSignature = res.data
this.setWxConfig()
})
},
mounted() {
this.viewProjectHandle()
@ -93,7 +96,7 @@ export default {
components: {
ProjectForm
}, methods: {
viewProjectHandle(){
viewProjectHandle() {
//
this.$api.post(`/user/project/result/view/${this.projectConfig.projectKey}`, {params: {projectKey: this.projectConfig.projectKey}}).then(res => {
@ -216,7 +219,10 @@ export default {
}
},
submitForm(data) {
//
let inActiveTime = document.getElementById('inActiveTime').innerText
this.$api.post('/user/project/result/create', {
'completeTime': inActiveTime,
'projectKey': this.projectConfig.projectKey,
'originalData': data.formModel,
'processData': data.labelFormModel

74
src/views/home/dashboard.vue

@ -1,5 +1,5 @@
<template>
<el-row>
<el-row style="min-width: 1024px;">
<el-row type="flex" align="top" justify="space-around">
<el-col :offset="1" :span="11">
<el-row type="flex" align="middle" justify="center">
@ -7,12 +7,15 @@
<p class="tagTitle">回收概览</p>
</el-col>
<el-col :offset="1" :span="21">
<el-select v-model="value" placeholder="请选择">
<el-select v-model="activeProjectKey"
placeholder="请选择"
@change="projectChangeHandle"
>
<el-option
v-for="item in ['表单1']"
:key="item"
:label="item"
:value="item"
v-for="item in projectListData"
:key="item.key"
:label="item.name"
:value="item.key"
/>
</el-select>
</el-col>
@ -33,28 +36,24 @@
<span>回收率</span>
</el-col>
<el-col :span="5">
<span>完成时间</span>
<span>完成时间</span>
</el-col>
</el-row>
<el-row type="flex" justify="space-around">
<el-col :offset="2" :span="5">
<count-to style="font-size: 20px;">
1231
</count-to>
<count-to :end-val="projectStats.completeCount" style="font-size: 20px; text-align: center;" />
</el-col>
<el-col :span="5">
<count-to style="font-size: 20px;">
3921
</count-to>
<count-to :end-val="projectStats.viewCount" style="font-size: 20px;" />
</el-col>
<el-col :span="5">
<count-to style="font-size: 20px;">
28
</count-to>
<count-to :end-val="projectStats.completeRate" style="font-size: 20px;" />
%
</el-col>
<el-col :span="5">
<span style="font-size: 20px;"> 12分16秒</span>
<span style="font-size: 20px;">
{{ projectStats.avgCompleteFormatStr }}
</span>
</el-col>
</el-row>
</el-row>
@ -107,6 +106,7 @@ import LineChart from '@/components/echarts/LineChart'
import MapChart from '@/components/echarts/MapChart'
import CountTo from '@/components/CountTo'
import BarChart from '@/components/echarts/BarChart'
import {timeFormat} from '@/utils/index'
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
@ -136,9 +136,49 @@ export default {
},
data() {
return {
projectListData: null,
projectStats: {
avgCompleteTime: 0,
avgCompleteFormatStr: 0,
viewCount: 0,
completeCount: 0,
completeRate: 0
},
projectSituation: null,
activeProjectKey: null,
lineChartData: lineChartData.newVisitis
}
},
created() {
this.$api.get('/user/project/list', {params: {status: 2}}).then(res => {
this.projectListData = res.data
if (res.data) {
this.activeProjectKey = res.data[0].key
this.projectChangeHandle()
}
})
}, methods: {
projectChangeHandle() {
this.getProjectStats()
this.getProjectSituation()
},
getProjectStats() {
this.$api.get('/user/project/report/stats', {params: {projectKey: this.activeProjectKey}}).then(res => {
this.projectStats = res.data
if (this.projectStats.completeCount) {
let rate = this.projectStats.completeCount / this.projectStats.viewCount
this.projectStats.completeRate = rate > 1 ? 100 : rate * 100
this.projectStats.avgCompleteFormatStr = timeFormat(this.projectStats.avgCompleteTime)
}
})
},
getProjectSituation() {
this.$api.get('/user/project/report/situation', {params: {projectKey: this.activeProjectKey}}).then(res => {
this.projectSituation = res.data
})
}
}
}
</script>

3
src/views/home/index.vue

@ -165,7 +165,7 @@ export default {
.home-header-view {
line-height: 92px;
height: 92px;
min-width: 960px;
min-width: 1024px;
background-color: rgba(255, 255, 255, 100);
color: rgba(16, 16, 16, 100);
font-size: 14px;
@ -206,6 +206,7 @@ export default {
}
.home-main-view {
height: calc(100vh - 92px);
min-width: 1024px;
background-color: #f7f7f7;
padding: 0;
}

25
yarn.lock

@ -4086,6 +4086,14 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
echarts@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.0.0.tgz#66ec0ea47e3c93b2aadfbb2d719869932ad13ce0"
integrity sha512-6SDcJbLVOcfQyjPg+spNU1+JVrkU1B9fzUa5tpbP/mMNUPyigCOJwcEIQAJSbp9jt5UP3EXvQR0vtYXIo9AjyA==
dependencies:
tslib "1.10.0"
zrender "5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -10544,6 +10552,11 @@ ts-pnp@^1.1.6:
resolved "https://registry.npm.taobao.org/ts-pnp/download/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
integrity sha1-pQCtCEsHmPHDBxrzkeZZEshrypI=
tslib@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
tslib@^1.10.0, tslib@^1.9.0:
version "1.11.1"
resolved "https://registry.npm.taobao.org/tslib/download/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
@ -10613,6 +10626,11 @@ typedarray@^0.0.6, typedarray@~0.0.5:
resolved "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
ua-parser-js@^0.7.23:
version "0.7.23"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.23.tgz#704d67f951e13195fbcd3d78818577f5bc1d547b"
integrity sha512-m4hvMLxgGHXG3O3fQVAyyAQpZzDOvwnhOTjYz5Xmr7r/+LpkNy3vJXdVRWgd1TkAb7NGROZuSy96CrlNVjA7KA==
uglify-js@3.4.x:
version "3.4.10"
resolved "https://registry.npm.taobao.org/uglify-js/download/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
@ -11485,3 +11503,10 @@ yorkie@^2.0.0:
is-ci "^1.0.10"
normalize-path "^1.0.0"
strip-indent "^2.0.0"
zrender@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.0.1.tgz#cc377136a9d00d0a177ca7f65c32aed2901928da"
integrity sha512-i8FNCKAKfF0EfZFJ6w2p30umBrCyy481/PePFQqPdtNgCl5Hp5z7/dovqb7soEoFkhNvhjJ/J4W9zFALeae6yA==
dependencies:
tslib "1.10.0"

Loading…
Cancel
Save