diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index a6d40355b..035c40d92 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -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=所有者 diff --git a/public/img/name.png b/public/img/name.png new file mode 100755 index 000000000..11a5c8751 Binary files /dev/null and b/public/img/name.png differ diff --git a/public/img/overview.png b/public/img/overview.png new file mode 100755 index 000000000..b704ead84 Binary files /dev/null and b/public/img/overview.png differ diff --git a/public/img/pro.png b/public/img/pro.png new file mode 100755 index 000000000..7d44e180e Binary files /dev/null and b/public/img/pro.png differ diff --git a/public/img/pro.svg b/public/img/pro.svg new file mode 100755 index 000000000..c7bcfff8b --- /dev/null +++ b/public/img/pro.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/routers/home.go b/routers/home.go index dfdb030e4..d2c93c771 100755 --- a/routers/home.go +++ b/routers/home.go @@ -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" diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 383a308b6..a3de68373 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -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) diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 57df408c9..67d8fc1b7 100755 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -37,6 +37,7 @@ {{.i18n.Tr "explore.users"}} {{.i18n.Tr "explore.organizations"}} {{.i18n.Tr "explore.images"}} + {{.i18n.Tr "explore.data_analysis"}} {{else if .IsLandingPageHome}} diff --git a/templates/base/head_navbar_home.tmpl b/templates/base/head_navbar_home.tmpl index e6729da62..e334efba8 100644 --- a/templates/base/head_navbar_home.tmpl +++ b/templates/base/head_navbar_home.tmpl @@ -29,6 +29,7 @@ {{.i18n.Tr "explore.users"}} {{.i18n.Tr "explore.organizations"}} {{.i18n.Tr "explore.images"}} + {{.i18n.Tr "explore.data_analysis"}} {{else if .IsLandingPageHome}} diff --git a/templates/explore/data_analysis.tmpl b/templates/explore/data_analysis.tmpl new file mode 100755 index 000000000..8277dc902 --- /dev/null +++ b/templates/explore/data_analysis.tmpl @@ -0,0 +1,14 @@ +{{template "base/head" .}} +
+ + +
+ +{{template "base/footer" .}} + + diff --git a/web_src/js/components/DataAnalysis.vue b/web_src/js/components/DataAnalysis.vue new file mode 100755 index 000000000..8c632bccb --- /dev/null +++ b/web_src/js/components/DataAnalysis.vue @@ -0,0 +1,123 @@ + + + + + \ No newline at end of file diff --git a/web_src/js/components/ProAnalysis.vue b/web_src/js/components/ProAnalysis.vue new file mode 100755 index 000000000..7df0854e5 --- /dev/null +++ b/web_src/js/components/ProAnalysis.vue @@ -0,0 +1,923 @@ + + + + + \ No newline at end of file diff --git a/web_src/js/components/UserAnalysis.vue b/web_src/js/components/UserAnalysis.vue new file mode 100755 index 000000000..547a077f4 --- /dev/null +++ b/web_src/js/components/UserAnalysis.vue @@ -0,0 +1,415 @@ + + + + + \ No newline at end of file diff --git a/web_src/js/excel/Blob.js b/web_src/js/excel/Blob.js new file mode 100755 index 000000000..c43291d34 --- /dev/null +++ b/web_src/js/excel/Blob.js @@ -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)); \ No newline at end of file diff --git a/web_src/js/excel/Export2Excel.js b/web_src/js/excel/Export2Excel.js new file mode 100755 index 000000000..3c6d022af --- /dev/null +++ b/web_src/js/excel/Export2Excel.js @@ -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") +} \ No newline at end of file diff --git a/web_src/js/excel/util.js b/web_src/js/excel/util.js new file mode 100755 index 000000000..b600e5e37 --- /dev/null +++ b/web_src/js/excel/util.js @@ -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); + }) +} \ No newline at end of file diff --git a/web_src/js/index.js b/web_src/js/index.js index 560683b7e..a6d07d0bc 100755 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -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('
') .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');