Browse Source

Merge branch 'V20211115' into show-real-flavor

tags/v1.21.12.1
lewis 4 years ago
parent
commit
ac647fbafb
17 changed files with 1846 additions and 18 deletions
  1. +1
    -13
      options/locale/locale_zh-CN.ini
  2. BIN
      public/img/name.png
  3. BIN
      public/img/overview.png
  4. BIN
      public/img/pro.png
  5. +1
    -0
      public/img/pro.svg
  6. +7
    -2
      routers/home.go
  7. +1
    -0
      routers/routes/routes.go
  8. +1
    -0
      templates/base/head_navbar.tmpl
  9. +1
    -0
      templates/base/head_navbar_home.tmpl
  10. +14
    -0
      templates/explore/data_analysis.tmpl
  11. +123
    -0
      web_src/js/components/DataAnalysis.vue
  12. +923
    -0
      web_src/js/components/ProAnalysis.vue
  13. +415
    -0
      web_src/js/components/UserAnalysis.vue
  14. +179
    -0
      web_src/js/excel/Blob.js
  15. +141
    -0
      web_src/js/excel/Export2Excel.js
  16. +17
    -0
      web_src/js/excel/util.js
  17. +22
    -3
      web_src/js/index.js

+ 1
- 13
options/locale/locale_zh-CN.ini View File

@@ -229,6 +229,7 @@ organizations=组织
images = 云脑镜像
search=搜索
code=代码
data_analysis=数字看板
repo_no_results=未找到匹配的项目。
dataset_no_results = 未找到匹配的数据集。
user_no_results=未找到匹配的用户。
@@ -2162,19 +2163,6 @@ repos.stars=点赞数
repos.forks=派生数
repos.issues=任务数
repos.size=大小
repos.id=ID
repos.projectName=项目名称
repos.isPrivate=私有
repos.openi=OpenI指数
repos.visit=浏览量
repos.download=代码下载量
repos.pr=PR数
repos.commit=Commit数
repos.closedIssues=已解决任务数
repos.contributor=贡献者数
repos.yes=是
repos.no=否


datasets.dataset_manage_panel=数据集管理
datasets.owner=所有者


BIN
public/img/name.png View File

Before After
Width: 300  |  Height: 300  |  Size: 5.1 kB

BIN
public/img/overview.png View File

Before After
Width: 64  |  Height: 64  |  Size: 1.5 kB

BIN
public/img/pro.png View File

Before After
Width: 64  |  Height: 64  |  Size: 2.0 kB

+ 1
- 0
public/img/pro.svg View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1636355832057" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8359" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M1024 767.6928c0 7.168-2.8672 12.0832-8.4992 14.9504L571.2896 1021.8496 569.1392 1021.8496C567.7056 1023.2832 565.5552 1024 562.688 1024c-2.8672 0-5.0176-0.7168-6.4512-2.1504L554.1888 1021.8496 109.9776 782.6432C104.2432 779.776 101.376 774.8608 101.376 767.6928L101.376 255.1808 101.376 253.0304c0-1.3312 0.7168-2.8672 2.1504-4.3008L103.5264 246.5792l4.3008-4.3008 2.1504-2.1504L554.1888 1.024c5.7344-1.3312 11.3664-1.3312 17.1008 0l444.2112 239.2064 2.1504 2.1504 4.3008 4.3008 0 2.1504L1024 253.0304l0 2.1504L1024 767.6928zM135.5776 757.0432l410.0096 222.1056 0-474.112L135.5776 282.9312 135.5776 757.0432zM154.8288 255.1808 562.688 475.136l407.8592-219.9552L562.688 35.2256 154.8288 255.1808zM989.7984 757.0432l0-474.112L579.7888 505.0368l0 474.112L989.7984 757.0432z" p-id="8360"></path></svg>

+ 7
- 2
routers/home.go View File

@@ -33,8 +33,9 @@ const (
// tplExploreOrganizations explore organizations page template
tplExploreOrganizations base.TplName = "explore/organizations"
// tplExploreCode explore code page template
tplExploreCode base.TplName = "explore/code"
tplExploreImages base.TplName = "explore/images"
tplExploreCode base.TplName = "explore/code"
tplExploreImages base.TplName = "explore/images"
tplExploreExploreDataAnalysis base.TplName = "explore/data_analysis"
)

// Home render home page
@@ -501,6 +502,10 @@ func ExploreImages(ctx *context.Context) {
ctx.HTML(200, tplExploreImages)
}

func ExploreDataAnalysis(ctx *context.Context) {
ctx.HTML(200, tplExploreExploreDataAnalysis)
}

// NotFound render 404 page
func NotFound(ctx *context.Context) {
ctx.Data["Title"] = "Page Not Found"


+ 1
- 0
routers/routes/routes.go View File

@@ -325,6 +325,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/organizations", routers.ExploreOrganizations)
m.Get("/code", routers.ExploreCode)
m.Get("/images", routers.ExploreImages)
m.Get("/data_analysis", routers.ExploreDataAnalysis)
}, ignSignIn)
m.Combo("/install", routers.InstallInit).Get(routers.Install).
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)


+ 1
- 0
templates/base/head_navbar.tmpl View File

@@ -37,6 +37,7 @@
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}


+ 1
- 0
templates/base/head_navbar_home.tmpl View File

@@ -29,6 +29,7 @@
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}


+ 14
- 0
templates/explore/data_analysis.tmpl View File

@@ -0,0 +1,14 @@
{{template "base/head" .}}
<div id="data_analysis" style="height: 100%;">

</div>

{{template "base/footer" .}}

<style>
.full.height {
flex-grow: 1;
padding-bottom: 53px;
}
</style>

+ 123
- 0
web_src/js/components/DataAnalysis.vue View File

@@ -0,0 +1,123 @@
<template>
<div style="height:100%">
<el-tabs tab-position="left" v-model="activeName" style="height:100%" @tab-click="handleClick" >
<el-tab-pane label="概览" name="first" >
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/overview.png">
</el-image>
概览
</span>
<div >概览.......</div>
<div >概览.......</div>
<div >概览.......</div><div >概览.......</div>
<div >概览.......</div>
</el-tab-pane>
<el-tab-pane label="项目分析" name="second" id="second" >
<ProAnalysis ref='ProAnalysis'id="pro" v-if="isRouterAlive"></ProAnalysis>
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/pro.svg">
</el-image>
项目分析
</span>
</el-tab-pane>
<el-tab-pane name="third" >
<span slot='label'>
<el-image style="width: 13px; height: 13px" src="/img/name.png">
</el-image>
用户分析
</span>
<UserAnalysis ref='UserAnalysis' id ="usr"></UserAnalysis>
</el-tab-pane>
</el-tabs>
</div>
</template>

<script>
import ProAnalysis from './ProAnalysis.vue'
import UserAnalysis from './UserAnalysis.vue'

export default {

components:{
'ProAnalysis':ProAnalysis,
'UserAnalysis':UserAnalysis,
},
data() {
return {
activeName:"second",
loading:true,
loading1:true,
isRouterAlive: true,
isSecond:true,
isThird:false,
}
},
methods:{
handleClick(tab, event){
if(tab.name=="second"){
this.reload()
//document.getElementById('usr').style.display="none"
//document.getElementById("pro").style.display='block'
//this.$refs.ProAnalysis.getAllProList("all",7)

this.isSecond = true
this.isThird = false
this.$refs.ProAnalysis.getAllProList("all",7)
}
if(tab.name=="third"){
// document.getElementById('usr').style.display="block"
// document.getElementById("pro").style.display='none'
//this.$refs.UserAnalysis.getUserList("all_usr",7)

this.isSecond = false
this.isThird = true
}

},
reload () {
this.isRouterAlive = false
this.$nextTick(() => (this.isRouterAlive = true))
}

},
}
</script>
<style scoped>
/deep/ .is-active{
color: #238BFC ;
background-color: #FFFF ;
}
/deep/ .ui-container{
background-color: #FFFF;
}
/deep/ .el-tabs--left .el-tabs__header.is-left{
background-color:#F5F5F6;
width: 12.43%;
}
.el-tabs--left .el-tabs__header.is-left
html,
body,
/deep/ .el-container {
padding: 0px;
margin: 0px;
height: 100%;
}
/deep/ .el-tabs--left .el-tabs__item.is-left {
text-align: left;
}
/deep/ .el-tabs__item {
padding: 0px 20px 0px 20px;
}

</style>

+ 923
- 0
web_src/js/components/ProAnalysis.vue View File

@@ -0,0 +1,923 @@
<template>
<div style="width: 100%;">
<div id = "pro_main">
<div style="margin-top: 10px;">
<b class="pro_item">项目分析</b> <span class="update_time">数据更新时间:{{lastUpdatedTime}}/{{recordBeginTime}}</span>
</div>
<bar-label :width="'95%'" :height="'500px'"></bar-label>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday" v-bind:class="{colorChange:1==dynamic}" @click="getAllProList('yesterday',1)">昨天</button>
<button type="button" class='btn' id = "current_week" v-bind:class="{colorChange:2==dynamic}" @click="getAllProList('current_week',2)">本周</button>
<button type="button" class='btn' id = "current_month" v-bind:class="{colorChange:3==dynamic}" @click="getAllProList('current_month',3)">本月</button>
<button type="button" class='btn' id = "last_month" v-bind:class="{colorChange:4==dynamic}" @click="getAllProList('last_month',4)">上月</button>
<button type="button" class='btn' id = "monthly" v-bind:class="{colorChange:5==dynamic}" @click="getAllProList('monthly',5)">近30天</button>
<button type="button" class='btn' id = "current_year" v-bind:class="{colorChange:6==dynamic}" @click="getAllProList('current_year',6)">今年</button>
<button type="button" class='btn' id = "all" v-bind:class="{colorChange:7==dynamic}" @click="getAllProList('all',7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="value_time"
prefix-icon="el-icon-time"
@change="getAllProList('',0)"
type="daterange"
size='small'
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</span>
<span style="float:right; margin-right: 20px;">
<div style="display:inline-block;margin-left: 20px; ">
<i class="el-icon-download"></i>
<span ><a>下载报告</a> </span>
</div>
<span style="display:inline-block;margin-left: 20px; ">
<el-input size="small" placeholder="输入项目关键字搜索" v-model="search" class="input-with-select" @keyup.enter.native="searchName() "><i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</span>
</span>
</div>
<div style="margin-top: 30px;">
<el-table
:data="tableData"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:cell-style='cellStyle'>
<el-table-column
label="ID"
align="center"
prop="repo_id"
stripe
>
</el-table-column>
<el-table-column
label="项目名称"
width="125px"
align="center"
prop="name"
style="color:#0366D6 100%;"
>
<template slot-scope="scope">
<a @click=goToDetailPage(scope.row.repo_id,scope.row.name)>{{scope.row.name}} </a>
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
label="私有"
align="center">
<template slot-scope="scope">
{{scope.row.isPrivate|changeType}}
</template>
</el-table-column>
<el-table-column
prop="openi"
label="OpenI指数"
align="center">
<template slot-scope="scope">
{{scope.row.openi | rounding}}
</template>
</el-table-column>
<el-table-column
prop="view"
label="浏览量"
align="center">
</el-table-column>
<el-table-column
prop="download"
label="代码下载量"
align="center">
</el-table-column>
<el-table-column
prop="pr"
label="PR"
align="center">
</el-table-column>
<el-table-column
prop="commit"
label="Commit数"
align="center">
</el-table-column>
<el-table-column
prop="watch"
label="关注数"
align="center">
</el-table-column>
<el-table-column
prop="star"
label="点赞数"
align="center">
</el-table-column>
<el-table-column
prop="fork"
label="派生数"
align="center">
</el-table-column>
<el-table-column
prop="issue"
label="任务数"
align="center">
</el-table-column>
<el-table-column
prop="issueClosed"
label="已解决任务"
align="center">
</el-table-column>
<el-table-column
prop="contributor"
label="贡献者数"
align="center">
</el-table-column>
</el-table>
</div>
<div style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="page"
:page-size="pageSize"
layout="prev, pager, next"
:total="totalNum">
</el-pagination>
</div>
</div>
<div id ="pro_detail" style="display:none;width: 100%;">
<div style="margin-top: 10px;">
<b class="pro_item">OpenI / {{this.pro_name}}</b> <span class="update_time">数据更新时间:{{tableDataIDTotal.lastUpdatedTime}}/{{tableDataIDTotal.recordBeginTime}}</span>
</div>
<div style="margin-top: 10px;">
项目描述:{{tableDataIDTotal.description | discriptionFun}}
</div>
<div style="margin-top:20px">
<el-row >
<el-col :span='4' class="items">
项目创建时间 </br>
{{tableDataIDTotal.creatTime}}
</el-col>
<el-col :span='4' class="items">
OPenI指数 </br>
{{tableDataIDTotal.openi | rounding}}
</el-col>
<el-col :span='4' class="items">
评论数 </br>
{{tableDataIDTotal.comment}}
</el-col>
<el-col :span='4' class="items">
浏览数 </br>
{{tableDataIDTotal.view}}
</el-col>
<el-col :span='4' class="items">
代码下载量 </br>
{{tableDataIDTotal.download}}
</el-col>
<el-col :span='4' style="text-align: center;">
任务完成比例 </br>
{{tableDataIDTotal.issueClosedRatio}}
</el-col>
</el-row>
</div>
<div style="margin-top:30px;">
<el-row :gutter="20">
<el-col :span=18 >
<div class="item_l" id="charts">
<div style="font-size:14px;color:#409eff;margin:20px 5px;">OpenI指数:{{tableDataIDTotal.openi | rounding}}</div>
<div >
<el-col :span='8' id="radar_openi" :style="{width: '400px', height: '300px'}"></el-col>
<el-col :span='16' id="line_openi" :style="{width: '600px', height: '300px',float:'right'}"></el-col>
</div>
</div>
</el-col>
<el-col :span=6 >
<div class="item_r">
<div style="font-size:14px;color:rgb(0,0,0);margin:20px 5px;">贡献者TOP10</div>
<div>
<el-table
:data="tableDataContTop10"
style="width: 100%"
stripe
:header-cell-style="tableHeaderStyle"
>
<el-table-column
label="用户名"
align="center"
prop="user">
</el-table-column>
<el-table-column
label="身份"
align="center"
prop="mode">
<template slot-scope="scope">
{{scope.row.mode | showMode}}
</template>
</el-table-column>
<el-table-column
prop="PR"
label="pr"
align="center">
</el-table-column>
<el-table-column
prop="commit"
label="commit"
align="center">
</el-table-column>
</el-table>
</div>
</div>
</el-col>
</el-row>
</div>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday_pro" v-bind:class="{colorChange:1==dynamic_pro}" @click="getOneProList(pro_id,'yesterday',true,1),getOneProList(pro_id,'yesterday',false,1)">昨天</button>
<button type="button" class='btn' id = "current_week_pro" v-bind:class="{colorChange:2==dynamic_pro}" @click="getOneProList(pro_id,'current_week',true,2),getOneProList(pro_id,'current_week',false,2)">本周</button>
<button type="button" class='btn' id = "current_month_pro" v-bind:class="{colorChange:3==dynamic_pro}" @click="getOneProList(pro_id,'current_month',true,3),getOneProList(pro_id,'current_month',false,3)">本月</button>
<button type="button" class='btn' id = "last_month_pro" v-bind:class="{colorChange:4==dynamic_pro}" @click="getOneProList(pro_id,'last_month',true,4),getOneProList(pro_id,'last_month',false,4)">上月</button>
<button type="button" class='btn' id = "monthly_pro" v-bind:class="{colorChange:5==dynamic_pro}" @click="getOneProList(pro_id,'monthly',true,5),getOneProList(pro_id,'monthly',false,5)">近30天</button>
<button type="button" class='btn' id = "current_year_pro" v-bind:class="{colorChange:6==dynamic}" @click="getOneProList(pro_id,'current_year',true,6),getOneProList(pro_id,'current_year',false,6)">今年</button>
<button type="button" class='btn' id = "all_pro" v-bind:class="{colorChange:7==dynamic_pro}" @click="getOneProList(pro_id,'all',true,7),getOneProList(pro_id,'all',false,7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="create_time_pro"
prefix-icon="el-icon-time"
@change="getOneProList(pro_id,'',true,0),getOneProList(pro_id,'',false,0)"
type="daterange"
size='small'
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</span>
<span style="float:right; margin-right: 20px;">
<div style="display:inline-block;margin-left: 20px; ">
<i class="el-icon-download"></i>
<span ><a>下载报告</a> </span>
</div>
</span>
</div>
<div class="item_echart" id ='linecharts'>
<div style="margin-top:10px;margin-left: 5px;">
<label for="label" @change='clickCheckBox'>
<input type="checkbox" class="checkboxchart" name="checkboxchart" checked="checked" value="浏览量"/>浏览量
<input type="checkbox" class="checkboxchart" name="checkboxchart" checked="checked" value="下载量"/>下载量
<input type="checkbox" class="checkboxchart" name="checkboxchart" checked="checked" value="commit"/>commit
</label>
</div>
<div id ="selectData" style="width: 1280px;height: 300px;">

</div>
</div>
<div style="margin-top: 30px;">
<el-table
:data="tableDataID.slice((currentPage-1)*pageSize,currentPage*pageSize)"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:cell-style='cellStyle'>
<el-table-column
label="日期"
align="center"
prop="date"
stripe
>
</el-table-column>
<el-table-column
label="浏览量"
align="center"
prop="view">
</el-table-column>
<el-table-column
prop="download"
label="下载量"
align="center">
</el-table-column>
<el-table-column
prop="commit"
label="commit"
align="center">
</el-table-column>
</el-table>
</div>
<div style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChangeID"
:current-page="currentPage"
:page-size="pageSize1"
layout="prev, pager, next"
:total="tableDataID.length">
</el-pagination>
</div>
</div>
</div>
</template>
<script>
// import barLabel from './basic/barLabel.vue';
export default{
name:'ProAnalysis',
components: {
// barLabel,
},
data() {
return {
recordBeginTime:'',
lastUpdatedTime:'',
page:1,
pageSize:10,
params:{type:'all',page:1,pagesize:10,beginTime:'',endTime:'',q:'',sort:'openi'},
tableData: [],
totalPage:0,
totalNum:0,
pickerOptions: {
},
value_time: '',
search:'',
dynamic:7,

//单个项目参数
currentPage:1,
pageSize1:10,
paramsID:{type:'all' ,beginTime:'',endTime:'',openi:'false'},
tableDataIDTotal: [],
tableDataID: [],
tableDataIDOpenI:[],
tableDataContTop10:[],
create_time_pro: '',
dynamic_pro:7,
pro_name:'',
pro_id:'',
radarOpenI:'',
echartsOITd:'',
echartsSelectData:'',
option:'',

};
},
methods: {
handleCurrentChange(val){
console.log(val)
this.params.page = val
switch(this.params.type){
case "yesterday":{
this.value_time=''
this.getAllProList(this.params.type,1)
break
}
case "current_week":{
this.value_time=''
this.getAllProList(this.params.type,2)
break
}
case "current_month":{
this.value_time=''
this.getAllProList(this.params.type,3)
break
}
case "last_month":{
this.value_time=''
this.getAllProList(this.params.type,4)
break
}
case "monthly":{
this.value_time=''
this.getAllProList(this.params.type,5)
break
}
case "current_year":{
this.value_time=''
this.getAllProList(this.params.type,6)
break
}
case "all":{
this.value_time=''
this.getAllProList(this.params.type,7)
break
}
case "":{
// this.value_time=''
this.getAllProList(this.params.type,0)
break
}
}
},
formatDate(myyear,mymonth,myweekday) {
// var myyear = this.date.getFullYear();
// var mymonth = this.date.getMonth() + 1;
// var myweekday = this.date.getDate();
if (mymonth < 10) {
mymonth = "0" + mymonth;
}
if (myweekday < 10) {
myweekday = "0" + myweekday;
}
return (myyear + "-" + mymonth + "-" + myweekday);
},
getAllProList(type_val,index){
console.log("类型:"+type_val)
this.dynamic = index
if (typeof type_val=="undefined" || type_val=="null" || type_val==""){
this.params.type=''
this.params.beginTime=this.formatDate(this.value_time[0].getFullYear(),this.value_time[0].getMonth() + 1,this.value_time[0].getDate())
this.params.endTime=this.formatDate(this.value_time[1].getFullYear(),this.value_time[1].getMonth() + 1,this.value_time[1].getDate())
}else{
this.params.type=type_val
this.params.beginTime=''
this.params.endTime=''
this.value_time=[]
}
this.$axios.get('../api/v1/projectboard/project',{
params:this.params
}).then((res)=>{
this.recordBeginTime=res.data.recordBeginTime
this.lastUpdatedTime=res.data.lastUpdatedTime
this.tableData = res.data.pageRecords
this.totalPage=res.data.totalPage
this.totalNum = this.totalPage*this.params.pagesize
console.log("this.totalPage:"+this.totalPage)
})
},
searchName(){
this.params.q = this.search
this.params.page = 1
this.getAllProList("all",7)
},
goToDetailPage(pro_id,pro_name){
document.getElementById("pro_main").style.display="none";
document.getElementById("pro_detail").style.display="block";
console.log(pro_id)
console.log(pro_name)
this.pro_name=pro_name;
this.pro_id=pro_id;
this.getOneProData(pro_id);
this.getOneProList(pro_id,"monthly",true,5);
this.getOneProList(pro_id,"monthly",false,5);
},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
cellStyle({row,column,rowIndex,columnIndex}){
if(rowIndex%2 === 1){
return 'background:#f5f5f6;color:#606266'
}
},
handleCurrentChangeID(currentPage){
this.currentPage = currentPage;
},
getOneProData(pro_id){
this.$axios.get('../api/v1/projectboard/project/'+pro_id,{
}).then((res)=>{
this.tableDataIDTotal = res.data
this.tableDataContTop10=res.data.top10
// this.drawLine()
this.drawRadarOpenI()
})
},
getOneProList(pro_id,type_val,bool_val,index){
this.dynamic_pro=index
console.log("日期类型:"+type_val)
if (typeof type_val=="undefined" || type_val=="null" || type_val==""){
this.paramsID.type=''
this.paramsID.beginTime= this.formatDate(this.create_time_pro[0].getFullYear(),this.create_time_pro[0].getMonth() + 1,this.create_time_pro[0].getDate())
this.paramsID.endTime=this.formatDate(this.create_time_pro[1].getFullYear(),this.create_time_pro[1].getMonth() + 1,this.create_time_pro[1].getDate())
}else{
this.create_time_pro=[]
this.paramsID.type=type_val
this.paramsID.beginTime=''
this.paramsID.endTime=''
}
this.paramsID.openi=bool_val
this.$axios.get('../api/v1/projectboard/project/'+pro_id+"/period",{
params:this.paramsID
}).then((res)=>{
if (bool_val){
this.tableDataIDOpenI = res.data
this.drawOpenItrend()
}else{
this.tableDataID = res.data
this.drawSelectData()
}
})
},
drawRadarOpenI(){
var ydata = [this.roundingF(this.tableDataIDTotal.impact),this.roundingF(this.tableDataIDTotal.completeness),this.roundingF(this.tableDataIDTotal.liveness),this.tableDataIDTotal.projectHealth,this.roundingF(this.tableDataIDTotal.teamHealth),this.roundingF(this.tableDataIDTotal.growth)]
console.log("ydata:",ydata)
var i = -1;
var option = {
titile:{
text:""
},
tooltip: {
trigger: 'item',
backgroundColor:'rgba(255,255,255,0.8)',
color:'black',
borderWidth:'1',
borderColor:'gray',
textStyle:{
color:'black'
},
position: 'right'
},//提示层
legend: {
data: ['name1']
},
radar: {
name: {
textStyle: {
color: 'rgb(0,0,0)', //字体颜色
borderRadius: 3, //圆角
padding: [3, 5] //padding
}
},
slpitNumber:5,
center: ['50%', '50%'],
indicator: [{
name: '社区影响力',
max: 100
},
{
name: '项目成熟度',
max: 100
},
{
name: '开发活跃度',
max: 100
},
{
name: '项目健康度',
max: 100
},
{
name: '团队健康度',
max: 100
},
{
name: '项目发展趋势',
max: 100
}
],
},
series: [{
type: 'radar',
lineStyle:{
width:2,
color: 'rgb(0,255,0)'
},
data: [{
value: ydata,
}]
}]
}
this.radarOpenI.setOption(option)
},
drawOpenItrend(){
var xdata_openI=[]
var ydata_openI=[]
for(var i =0;i<this.tableDataIDOpenI.length;i++){
xdata_openI.push(this.tableDataIDOpenI[this.tableDataIDOpenI.length-1-i].date);
ydata_openI.push(this.roundingF(this.tableDataIDOpenI[i].openi))
}
console.log("ydata_openI:"+ydata_openI)
console.log(xdata_openI)
var option = {
title : {
text: 'OpenI指数趋势',


textStyle: {
                fontSize: 12,
            },
left:'center',
top:'bottom',

subtext: '',

},
tooltip : {
trigger: 'axis',
backgroundColor:'rgba(255,255,255,0.8)',
color:'black',
borderWidth:'1',
borderColor:'gray',
textStyle:{
color:'black'
},
},
legend: {
orient: 'vertical',
top:'top',  
},
// calculable : true,
xAxis : [
{
type : 'category',
boundaryGap: false,
data : xdata_openI,
}
],
yAxis : [
{
type : 'value',
}
],

series : [
{
data: ydata_openI,
type: 'line',
areaStyle: {
color:'red',
opacity: 0.3,
origin:"start"
},
// lineStyle:{
// width:2,
// color: '#409effd6'
// },
}
]
};
this.echartsOITd.setOption(option)
},
drawSelectData(){
var xdata=[]
var ydata_view=[]
var ydata_download=[]
var ydata_commit=[]
for(var i =0;i<this.tableDataIDOpenI.length;i++){
xdata.push(this.tableDataIDOpenI[this.tableDataID.length-1-i].date);
ydata_view.push(this.roundingF(this.tableDataID[i].view))
ydata_download.push(this.roundingF(this.tableDataID[i].download))
ydata_commit.push(this.roundingF(this.tableDataID[i].commit))
}
console.log("ydata_openI:"+ydata_download)
console.log(xdata)
this.option = {
title : {
text: '',


textStyle: {
                fontSize: 12,
            },
left:'center',
top:'bottom',

subtext: '',

},
tooltip : {
trigger: 'axis',
backgroundColor:'rgba(255,255,255,0.8)',
color:'black',
borderWidth:'1',
borderColor:'gray',
textStyle:{
color:'black'
},
},
legend: {
data:['浏览量','下载量','commit'],
// orient: 'vertical',
// top:'top',  
},
toolbox: {
show : false,
feature : {
mark : {show: true},
dataView : {show: false, readOnly: false},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: false},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis : [
{
type : 'category',
data : xdata,
}
],
yAxis : [
{
type : 'value',
}
],
series : [
{ name:"浏览量",
data: ydata_view,
type: 'line',
areaStyle: {},
},
{
name:"下载量",
data: ydata_download,
type: 'line',
areaStyle: {},
},
{
name:"commit",
data: ydata_commit,
type: 'line',
areaStyle: {},
},

]
};
this.echartsSelectData.setOption(this.option)

// // 使用刚指定的选择项数据显示图表。
// var selectArr = this.echartsSelectData.getOption().legend[0].data;//legend所有值
// var checkboxs=document.getElementsByName('checkboxchart');
// $(".checkboxchart").click(function(){
// var obj = {};
// for(var i=0; i<checkboxs.length; i++){
// if(checkboxs[i].checked){
// obj[selectArr[i]] = true;
// }else{
// obj[selectArr[i]] = false;
// }
// }
// option.legend.selected = obj;
// this.echartsSelectData.setOption(option);
// });


},
roundingF(value){
return Number(value).toFixed(2)
},
clickCheckBox(){
// 使用刚指定的选择项数据显示图表。
var selectArr = this.echartsSelectData.getOption().legend[0].data;//legend所有值
var checkboxs=document.getElementsByName('checkboxchart');
// $(".checkboxchart").click(function(){
var obj = {};
for(var i=0; i<checkboxs.length; i++){
if(checkboxs[i].checked){
obj[selectArr[i]] = true;
}else{
obj[selectArr[i]] = false;
}
}
this.option.legend.selected = obj;
this.echartsSelectData.setOption(this.option);
// });

}
},
filters:{
rounding (value) {
return Number(value).toFixed(2)
},
changeType(value){
if(value=='false'){
return "否"
}else{
return "是"
}
},
discriptionFun(value){
if(value==''){
return "暂无描述"
}
},
showMode(value){
if(value==1){
return "可读权限"
}else if(value==2){
return "可写权限"
}else if(value==3){
return "管理员"
}else if(value==4){
return "所有者"
}else{
return "未定义"
}
}
},
mounted() {
// document.getElementById("all").style.outline="none"
// document.getElementById("all").focus()
this.getAllProList("all",7)
console.log("id:"+this.pro_id);
// document.getElementById('radar_openi').style.width = document.getElementById('charts').offsetWidth*0.4+'px'
// document.getElementById('line_openi').style.width = document.getElementById('charts').offsetWidth*0.6+'px'
// document.getElementById('selectData').style.width = document.getElementById('linecharts').offsetWidth+'px'
this.radarOpenI = this.$echarts.init(document.getElementById('radar_openi'))
this.echartsOITd = this.$echarts.init(document.getElementById('line_openi'))
this.echartsSelectData = this.$echarts.init(document.getElementById('selectData'))
// window.onresize=function(){
// this.radarOpenI.resize();
// this.echartsOITd.resize();
// this.echartsSelectData.resize();
// }
// console.log("this.radarOpenI:"+this.radarOpenI)
},
created() {
}
}
</script>
<style scoped>
.pro_item{
font-size: 16px;
color: rgba(16, 16, 16, 100);
font-family: SourceHanSansSC-bold;
}
.sta_item{
font-size: 14px;
color: rgb(0 0 0);
font-family: SourceHanSansSC-bold;
}
.update_time{
line-height: 17px;
font-size: 12px;
color:rgba(187, 187, 187, 100);
margin-left: 10px;
}
.btn{
line-height: 1.5;
margin: -3px;
border: 1px solid #409eff;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px ;
}
.btn:focus,
.btn:active{
background-color:#409effd6 ;
}
/deep/ .el-date-picker {
width: 200px;
}
.colorChange {
background-color: #409effd6;
}
.items{
text-align: center;
border-right:2px solid rgba(219, 219, 219, 100);
}
.item_l{
margin-right: 5px;
border:1px solid rgba(219, 219, 219, 100);
height: 370px;
width: 100%;
}
.item_r{
margin-right:5px;
border:1px solid rgba(219, 219, 219, 100);
height: 370px;
}
.item_echart{
margin-top: 10px;
margin-right: 5px;
border:1px solid rgba(219, 219, 219, 100);
height: 350px;
width: 100%;
}
</style>

+ 415
- 0
web_src/js/components/UserAnalysis.vue View File

@@ -0,0 +1,415 @@
<template>
<div>
<div style="margin-top: 10px;">
<b class="pro_item">用户分析</b> <span class="update_time">数据更新时间:{{lastUpdatedTime}}/{{recordBeginTime}}</span>
</div>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday_usr" v-bind:class="{colorChange:1==dynamic}" @click="getUserList('yesterday_usr',1)">昨天</button>
<button type="button" class='btn' id = "current_week_usr" v-bind:class="{colorChange:2==dynamic}" @click="getUserList('current_week_usr',2)">本周</button>
<button type="button" class='btn' id = "current_month_usr" v-bind:class="{colorChange:3==dynamic}" @click="getUserList('current_month_usr',3)">本月</button>
<button type="button" class='btn' id = "last_month_usr" v-bind:class="{colorChange:4==dynamic}" @click="getUserList('last_month_usr',4)">上月</button>
<button type="button" class='btn' id = "monthly_usr" v-bind:class="{colorChange:5==dynamic}" @click="getUserList('monthly_usr',5)">近30天</button>
<button type="button" class='btn' id = "current_year_usr" v-bind:class="{colorChange:6==dynamic}" @click="getUserList('current_year_usr',6)">今年</button>
<button type="button" class='btn' id = "all_usr" v-bind:class="{colorChange:7==dynamic}" @click="getUserList('all_usr',7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="value_time"
prefix-icon="el-icon-time"
@change="getUserList('',0)"
type="daterange"
size='small'
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</span>
<span style="float:right; margin-right: 20px;">
<a style="display:inline-block;margin-left: 20px; " id = 'download'>
<i class="el-icon-download"></i>
<span ><a @click="exportData()">下载报告</a> </span>
</a>
<span style="display:inline-block;margin-left: 20px; ">
<el-input size="small" placeholder="输入用户名搜索" v-model="search" class="input-with-select" @keyup.enter.native="searchName() "><i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</span>
</span>
</div>
<div style="margin-top: 30px;">
<el-table
:data="tableData.slice((currentPage-1)*pageSize,currentPage*pageSize)"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:cell-style='cellStyle'>
<el-table-column
label="ID"
prop="ID"
align="center"
stripe
>
</el-table-column>
<el-table-column
label="用户名"
align="center"
prop="Name"
width="100px">
</el-table-column>
<el-table-column
prop="CodeMergeCount"
label="PR数"
align="center">
</el-table-column>
<el-table-column
prop="CommitCount"
label="commit数"
align="center">
</el-table-column>
<el-table-column
prop="IssueCount"
label="提出任务数"
align="center">
</el-table-column>
<el-table-column
prop="CommentCount"
label="评论数"
align="center">
</el-table-column>
<el-table-column
prop="FocusRepoCount"
label="关注项目数"
align="center">
</el-table-column>
<el-table-column
prop="StarRepoCount"
label="点赞项目数"
align="center">
</el-table-column>

<el-table-column
prop="LoginCount"
label="登录次数"
align="center">
</el-table-column>
<el-table-column
prop="WatchedCount"
label="关注者数"
align="center">
</el-table-column>
<el-table-column
prop="CommitCodeSize"
label="commit代码行数"
width="115px"
align="center">
</el-table-column>
<el-table-column
prop="SolveIssueCount"
label="已解决任务数"
align="center">
</el-table-column>
<el-table-column
prop="EncyclopediasCount"
label="百科页面贡献次数"
width="130px"
align="center">
</el-table-column>
<el-table-column
prop="CreateRepoCount"
label="创建项目"
align="center">
</el-table-column>
<el-table-column
prop="RegistDate"
label="用户注册时间"
width="120px"
align="center">
<template slot-scope="scope">
{{scope.row.RegistDate | transformTimestamp}}
</template>
</el-table-column>
<el-table-column
prop="CountDate"
label="系统统计时间"
width="120px"
align="center">
<template slot-scope="scope">
{{scope.row.CountDate | transformTimestamp}}
</template>
</el-table-column>
</el-table>
</div>
<div style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
layout="prev, pager, next"
:total="tableData.length">
</el-pagination>
</div>
</div>
</template>

<script>
import { export2Excel } from '../excel/util.js'
export default{
name:'UserAnalysis',
data() {
return {
type_val:'',
recordBeginTime:'',
lastUpdatedTime:'',
currentPage:1,
pageSize:10,
params:{startDate:'',endDate:''},
tableData: [],
pickerOptions: {
},
value_time: '',
search:'',
data:'',
columns: [{title: 'ID',key: 'ID'},{title: '用户名',key: 'Name'},{title: 'PR数',key: 'CommitCount'},{title: '提出任务数',key: 'IssueCount'},{title: '评论数',key: 'CommentCount'},{title: '关注项目数',key: 'FocusRepoCount'},{title: '点赞项目数',key: 'StarRepoCount'},{title: '登录次数',key: 'LoginCount'},{title:'关注者数',key:'WatchedCount'},{title:'commit代码行数',key:'CommitCodeSize'},{title:'已解决任务数',key:'SolveIssueCount'},{title:'百科页面贡献次数',key:'EncyclopediasCount'},{title:'创建项目',key:'CreateRepoCount'},{title:'加入时间',key:'RegistDate'}],
blob:'',
fileName:'',
dynamic:7,

params_pro:{type:'all',page:1,pagesize:10,beginTime:'',endTime:'',q:'',sort:'openi'},
};
},
methods: {
exportData(){
export2Excel(this.columns,this.tableData,"测试下载excel")
},
handleCurrentChange(currentPage){
this.currentPage = currentPage;
},
formatDate(myyear,mymonth,myweekday) {
// var myyear = this.date.getFullYear();
// var mymonth = this.date.getMonth() + 1;
// var myweekday = this.date.getDate();
if (mymonth < 10) {
mymonth = "0" + mymonth;
}
if (myweekday < 10) {
myweekday = "0" + myweekday;
}
return (myyear + "-" + mymonth + "-" + myweekday);
},

// 获得某月的天数
getMonthDays(nowYear,month){
let monthStartDate = new Date(nowYear, month, 1);
let monthEndDate = new Date(nowYear, month + 1, 1);
let days = (monthEndDate - monthStartDate)/(1000 * 60 * 60 * 24);
return days;
},

getUserList(type_val,index){
this.dynamic = index;
console.log("dj:"+type_val)
var now = new Date(); // 当前日期
var nowDayOfWeek = now.getDay(); // 今天本周的第几天
var nowDay = now.getDate(); // 当前日
var nowMonth = now.getMonth(); // 当前月
var nowYear = now.getFullYear(); // 当前年
var today = this.formatDate(nowYear,nowMonth+1,nowDay);

let lastMonthDate = new Date(); // 上月日期
lastMonthDate.setDate(1);
lastMonthDate.setMonth(lastMonthDate.getMonth()-1);
let lastYear = lastMonthDate.getYear();
let lastMonth = lastMonthDate.getMonth();

if (typeof type_val=="undefined" || type_val=="null" || type_val==""){
this.params.startDate= this.formatDate(this.value_time[0].getFullYear(),this.value_time[0].getMonth() + 1,this.value_time[0].getDate());
this.params.endDate = this.formatDate(this.value_time[1].getFullYear(),this.value_time[1].getMonth() + 1,this.value_time[1].getDate());
}else{
switch(type_val){
case "yesterday_usr":{
var now = new Date();
var tmp = new Date(now.setTime(now.getTime()-24*60*60*1000));
var yesterday = this.formatDate(tmp.getFullYear(),tmp.getMonth()+1,tmp.getDate());
this.params.startDate = yesterday
this.params.endDate = yesterday
this.value_time=[]
document.getElementById("yesterday_usr").style.backgroundColor="409effd6"
document.getElementById("current_week_usr")
break
}
case "current_week_usr":{
var now = new Date(); // 当前日期
var nowDayOfWeek = now.getDay(); // 今天本周的第几天
var day = nowDayOfWeek || 7;
this.params.startDate = this.formatDate(now.getFullYear(), nowMonth+1, nowDay + 1 - day);
this.params.endDate = today
this.value_time=[]
break
}
case "current_month_usr":{
this.params.startDate = this.formatDate(nowYear,nowMonth+1,1);
this.params.endDate = today
this.value_time=[]
break
}
case "last_month_usr":{
this.params.startDate=this.formatDate(nowYear, lastMonth+1, 1);
this.params.endDate=this.formatDate(nowYear, lastMonth+1, this.getMonthDays(nowYear,lastMonth));
this.value_time=[]
break

}
case "monthly_usr":{
var temp=new Date(now - 1000 * 60 * 60 * 24 * 30)
this.params.startDate = this.formatDate(temp.getFullYear(),temp.getMonth()+1,temp.getDate());
this.params.endDate = today
this.value_time=[]
break
}
case "current_year_usr":{
this.params.startDate = this.formatDate(now.getFullYear(), 1, 1);
this.params.endDate = today
this.value_time=[]
break
}
case "all_usr":{
console.log("e:"+today)
this.params.startDate = this.formatDate(2000, 1, 1); //this.recordBeginTime//
this.params.endDate = today
this.value_time=[]
break
}
}
};
this.$axios.get('../tool/query_user_static',{
params:this.params
}).then((res)=>{
this.currentPage = 1
this.tableData = res.data
})
this.$axios.get('../api/v1/projectboard/project',{
params:this.params_pro
}).then((res)=>{
this.recordBeginTime=res.data.recordBeginTime
this.lastUpdatedTime=res.data.lastUpdatedTime
})

},
searchName(){
// this.params.q = this.search
// this.params.page = 1
// this.getUserList("all_usr")
var search = this.search;
this.getUserList("all_usr",7)
this.tableData = this.tableData.filter(data => !search || data.Name.toLowerCase().includes(search.toLowerCase()))

},
goToDetailPage(pro_id,pro_name){
sessionStorage.setItem("pro_id",pro_id);
sessionStorage.setItem("pro_name",pro_name);
document.getElementById("pro_main").style.display="none";
document.getElementById("pro_detail").style.display="block";

},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
cellStyle({row,column,rowIndex,columnIndex}){
if(rowIndex%2 === 1){
return 'background:#f5f5f6;color:#606266'
}
},
},
filters:{
transformTimestamp(timestamp){
console.log("timestamp",timestamp)
let a = new Date(timestamp*1000);
const date = new Date(a);
const Y = date.getFullYear() + '/';
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '/';
const D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' ';
const h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
const m = (date.getMinutes() <10 ? '0'+date.getMinutes() : date.getMinutes());// + ':' ;
// const s = (date.getSeconds() <10 ? '0'+date.getSeconds() : date.getSeconds()) ; // 秒
const dateString = Y + M + D + h + m ;//+ s;
console.log('dateString', dateString); // > dateString 2021-07-06 14:23
return dateString;
},
// transformTimestamp(timestamp){
// var dateString= new Date(timestamp);
// return dateString.toLocaleDateString().replace(/\//g, "-") + " " + dateString.toTimeString().substr(0, 8);
// },
},
mounted() {
document.getElementById("all_usr").style.outline="none"
document.getElementById("all_usr").focus()
this.getUserList("all_usr",7)
},
created() {
},
watch:{
search(val){
if(!val){
this.getUserList("all_usr",7)
}
}
},
}
</script>

<style scoped>
.pro_item{
font-size: 16px;
color: rgba(16, 16, 16, 100);
font-family: SourceHanSansSC-bold;
}
.sta_item{
font-size: 14px;
color: rgb(0 0 0);
font-family: SourceHanSansSC-bold;
}
.update_time{
line-height: 17px;
font-size: 12px;
color:rgba(187, 187, 187, 100);
margin-left: 10px;
}
.btn{
line-height: 1.5;
margin: -3px;
border: 1px solid #409effd6;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px ;
}

.btn:focus,
.btn:active{
background-color:#409effd6 ;
}
/deep/ .el-date-picker {
width: 200px;
}
/deep/ .el-table {
font-size: 12px;
}

.colorChange {
background-color: #409effd6;
}

</style>

+ 179
- 0
web_src/js/excel/Blob.js View File

@@ -0,0 +1,179 @@
/* eslint-disable */
/* Blob.js
* A Blob implementation.
* 2014-05-27
*
* By Eli Grey, http://eligrey.com
* By Devin Samarin, https://github.com/eboyjr
* License: X11/MIT
* See LICENSE.md
*/
/*global self, unescape */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
plusplus: true */
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
(function (view) {
"use strict";
view.URL = view.URL || view.webkitURL;
if (view.Blob && view.URL) {
try {
new Blob;
return;
} catch (e) {}
}
// Internally we use a BlobBuilder implementation to base Blob off of
// in order to support older browsers that only have BlobBuilder
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
var
get_class = function(object) {
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
}
, FakeBlobBuilder = function BlobBuilder() {
this.data = [];
}
, FakeBlob = function Blob(data, type, encoding) {
this.data = data;
this.size = data.length;
this.type = type;
this.encoding = encoding;
}
, FBB_proto = FakeBlobBuilder.prototype
, FB_proto = FakeBlob.prototype
, FileReaderSync = view.FileReaderSync
, FileException = function(type) {
this.code = this[this.name = type];
}
, file_ex_codes = (
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
).split(" ")
, file_ex_code = file_ex_codes.length
, real_URL = view.URL || view.webkitURL || view
, real_create_object_URL = real_URL.createObjectURL
, real_revoke_object_URL = real_URL.revokeObjectURL
, URL = real_URL
, btoa = view.btoa
, atob = view.atob
, ArrayBuffer = view.ArrayBuffer
, Uint8Array = view.Uint8Array
;
FakeBlob.fake = FB_proto.fake = true;
while (file_ex_code--) {
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
}
if (!real_URL.createObjectURL) {
URL = view.URL = {};
}
URL.createObjectURL = function(blob) {
var
type = blob.type
, data_URI_header
;
if (type === null) {
type = "application/octet-stream";
}
if (blob instanceof FakeBlob) {
data_URI_header = "data:" + type;
if (blob.encoding === "base64") {
return data_URI_header + ";base64," + blob.data;
} else if (blob.encoding === "URI") {
return data_URI_header + "," + decodeURIComponent(blob.data);
} if (btoa) {
return data_URI_header + ";base64," + btoa(blob.data);
} else {
return data_URI_header + "," + encodeURIComponent(blob.data);
}
} else if (real_create_object_URL) {
return real_create_object_URL.call(real_URL, blob);
}
};
URL.revokeObjectURL = function(object_URL) {
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
real_revoke_object_URL.call(real_URL, object_URL);
}
};
FBB_proto.append = function(data/*, endings*/) {
var bb = this.data;
// decode data to a binary string
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
var
str = ""
, buf = new Uint8Array(data)
, i = 0
, buf_len = buf.length
;
for (; i < buf_len; i++) {
str += String.fromCharCode(buf[i]);
}
bb.push(str);
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
if (FileReaderSync) {
var fr = new FileReaderSync;
bb.push(fr.readAsBinaryString(data));
} else {
// async FileReader won't work as BlobBuilder is sync
throw new FileException("NOT_READABLE_ERR");
}
} else if (data instanceof FakeBlob) {
if (data.encoding === "base64" && atob) {
bb.push(atob(data.data));
} else if (data.encoding === "URI") {
bb.push(decodeURIComponent(data.data));
} else if (data.encoding === "raw") {
bb.push(data.data);
}
} else {
if (typeof data !== "string") {
data += ""; // convert unsupported types to strings
}
// decode UTF-16 to binary string
bb.push(unescape(encodeURIComponent(data)));
}
};
FBB_proto.getBlob = function(type) {
if (!arguments.length) {
type = null;
}
return new FakeBlob(this.data.join(""), type, "raw");
};
FBB_proto.toString = function() {
return "[object BlobBuilder]";
};
FB_proto.slice = function(start, end, type) {
var args = arguments.length;
if (args < 3) {
type = null;
}
return new FakeBlob(
this.data.slice(start, args > 1 ? end : this.data.length)
, type
, this.encoding
);
};
FB_proto.toString = function() {
return "[object Blob]";
};
FB_proto.close = function() {
this.size = this.data.length = 0;
};
return FakeBlobBuilder;
}(view));
view.Blob = function Blob(blobParts, options) {
var type = options ? (options.type || "") : "";
var builder = new BlobBuilder();
if (blobParts) {
for (var i = 0, len = blobParts.length; i < len; i++) {
builder.append(blobParts[i]);
}
}
return builder.getBlob(type);
};
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

+ 141
- 0
web_src/js/excel/Export2Excel.js View File

@@ -0,0 +1,141 @@
/* eslint-disable */
require('script-loader!file-saver');
require('./Blob');
require('script-loader!xlsx/dist/xlsx.core.min');
function generateArray(table) {
var out = [];
var rows = table.querySelectorAll('tr');
var ranges = [];
for (var R = 0; R < rows.length; ++R) {
var outRow = [];
var row = rows[R];
var columns = row.querySelectorAll('td');
for (var C = 0; C < columns.length; ++C) {
var cell = columns[C];
var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan');
var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges
ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
});
//Handle Row Span
if (rowspan || colspan) {
rowspan = rowspan || 1;
colspan = colspan || 1;
ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
}
;
//Handle Value
outRow.push(cellValue !== "" ? cellValue : null);
//Handle Colspan
if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
return [out, ranges];
};
function datenum(v, date1904) {
if (date1904) v += 1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
for (var R = 0; R != data.length; ++R) {
for (var C = 0; C != data[R].length; ++C) {
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
var cell = {v: data[R][C]};
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
}
else cell.t = 's';
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
}
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = [];
this.Sheets = {};
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
export function export_table_to_excel(id) {
var theTable = document.getElementById(id);
console.log('a')
var oo = generateArray(theTable);
var ranges = oo[1];
/* original data */
var data = oo[0];
var ws_name = "SheetJS";
console.log(data);
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges;
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
}
function formatJson(jsonData) {
console.log(jsonData)
}
export function export_json_to_excel(th, jsonData, defaultTitle) {
/* original data */
var data = jsonData;
data.unshift(th);
var ws_name = "SheetJS";
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
var title = defaultTitle || '列表'
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
}

+ 17
- 0
web_src/js/excel/util.js View File

@@ -0,0 +1,17 @@
export function export2Excel(columns,list,filename){
require.ensure([], () => {
const { export_json_to_excel } = require('./Export2Excel');
let tHeader = []
let filterVal = []
console.log(columns)
if(!columns){
return;
}
columns.forEach(item =>{
tHeader.push(item.title)
filterVal.push(item.key)
})
const data = list.map(v => filterVal.map(j => v[j]))
export_json_to_excel(tHeader, data, filename);
})
}

+ 22
- 3
web_src/js/index.js View File

@@ -13,7 +13,7 @@ import qs from 'qs';
import 'jquery.are-you-sure';
import './vendor/semanticdropdown.js';
import {svg} from './utils.js';
import echarts from 'echarts'
import initContextPopups from './features/contextpopup.js';
import initGitGraph from './features/gitgraph.js';
import initClipboard from './features/clipboard.js';
@@ -35,8 +35,9 @@ import {createCodeEditor} from './features/codeeditor.js';
import MinioUploader from './components/MinioUploader.vue';
import ObsUploader from './components/ObsUploader.vue';
import EditAboutInfo from './components/EditAboutInfo.vue';
import Images from './components/Images.vue'
import EditTopics from './components/EditTopics.vue'
import Images from './components/Images.vue';
import EditTopics from './components/EditTopics.vue';
import DataAnalysis from './components/DataAnalysis.vue'
import Contributors from './components/Contributors.vue'

Vue.use(ElementUI);
@@ -44,6 +45,10 @@ Vue.prototype.$axios = axios;
Vue.prototype.qs = qs;
const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

Object.defineProperty(Vue.prototype, '$echarts', {
value: echarts
})

function htmlEncode(text) {
return jQuery('<div />')
.text(text)
@@ -2972,6 +2977,7 @@ $(document).ready(async () => {
initVueEditTopic();
initVueContributors();
initVueImages();
initVueDataAnalysis();
initTeamSettings();
initCtrlEnterSubmit();
initNavbarContentToggle();
@@ -3713,7 +3719,20 @@ function initVueImages() {
render: h => h(Images)
});
}
function initVueDataAnalysis() {
const el = document.getElementById('data_analysis');
console.log("el",el)
if (!el) {
return;
}

new Vue({
el: '#data_analysis',
render: h => h(DataAnalysis)
});
}
// 新增
function initObsUploader() {
const el = document.getElementById('obsUploader');


Loading…
Cancel
Save