| @@ -353,6 +353,7 @@ type FlavorInfos struct { | |||
| type FlavorInfo struct { | |||
| Id int `json:"id"` | |||
| Value string `json:"value"` | |||
| Desc string `json:"desc"` | |||
| } | |||
| type PoolInfos struct { | |||
| @@ -19,6 +19,7 @@ type CreateModelArtsNotebookForm struct { | |||
| JobName string `form:"job_name" binding:"Required"` | |||
| Attachment string `form:"attachment"` | |||
| Description string `form:"description"` | |||
| Flavor string `form:"flavor"` | |||
| } | |||
| func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| @@ -99,7 +99,7 @@ type ResourcePool struct { | |||
| } `json:"resource_pool"` | |||
| } | |||
| func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | |||
| func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor string) error { | |||
| var dataActualPath string | |||
| if uuid != "" { | |||
| dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | |||
| @@ -128,7 +128,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||
| JobName: jobName, | |||
| Description: description, | |||
| ProfileID: setting.ProfileID, | |||
| Flavor: setting.Flavor, | |||
| Flavor: flavor, | |||
| Pool: models.Pool{ | |||
| ID: poolInfos.PoolInfo[0].PoolId, | |||
| Name: poolInfos.PoolInfo[0].PoolName, | |||
| @@ -229,6 +229,7 @@ organizations=组织 | |||
| images = 云脑镜像 | |||
| search=搜索 | |||
| code=代码 | |||
| data_analysis=数字看板 | |||
| repo_no_results=未找到匹配的项目。 | |||
| dataset_no_results = 未找到匹配的数据集。 | |||
| user_no_results=未找到匹配的用户。 | |||
| @@ -19,11 +19,13 @@ | |||
| "cssnano": "4.1.10", | |||
| "domino": "2.1.5", | |||
| "dropzone": "5.7.2", | |||
| "echarts": "3.8.5", | |||
| "element-ui": "2.15.5", | |||
| "esdk-obs-browserjs": "3.20.7", | |||
| "esdk-obs-nodejs": "3.20.11", | |||
| "fast-glob": "3.2.2", | |||
| "file-loader": "6.0.0", | |||
| "file-saver": "2.0.5", | |||
| "fomantic-ui": "2.8.4", | |||
| "fs": "0.0.1-security", | |||
| "highlight.js": "10.4.1", | |||
| @@ -55,13 +57,15 @@ | |||
| "webpack": "4.43.0", | |||
| "webpack-cli": "3.3.11", | |||
| "webpack-fix-style-only-entries": "0.4.0", | |||
| "worker-loader": "2.0.0" | |||
| "worker-loader": "2.0.0", | |||
| "xlsx": "0.17.3" | |||
| }, | |||
| "devDependencies": { | |||
| "eslint": "6.8.0", | |||
| "eslint-config-airbnb-base": "14.1.0", | |||
| "eslint-plugin-import": "2.20.2", | |||
| "eslint-plugin-vue": "6.2.2", | |||
| "script-loader": "0.7.2", | |||
| "stylelint": "13.3.3", | |||
| "stylelint-config-standard": "20.0.0", | |||
| "updates": "10.2.11" | |||
| @@ -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> | |||
| @@ -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" | |||
| @@ -204,7 +204,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||
| resourceSpecId := form.ResourceSpecId | |||
| if !jobNamePattern.MatchString(jobName) { | |||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainNew, &form) | |||
| return | |||
| } | |||
| @@ -27,15 +27,10 @@ import ( | |||
| ) | |||
| const ( | |||
| // tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | |||
| tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | |||
| tplModelArtsNotebookNew base.TplName = "repo/modelarts/notebook/new" | |||
| tplModelArtsNotebookShow base.TplName = "repo/modelarts/notebook/show" | |||
| tplModelArtsIndex base.TplName = "repo/modelarts/index" | |||
| tplModelArtsNew base.TplName = "repo/modelarts/new" | |||
| tplModelArtsShow base.TplName = "repo/modelarts/show" | |||
| tplModelArtsTrainJobIndex base.TplName = "repo/modelarts/trainjob/index" | |||
| tplModelArtsTrainJobNew base.TplName = "repo/modelarts/trainjob/new" | |||
| tplModelArtsTrainJobShow base.TplName = "repo/modelarts/trainjob/show" | |||
| @@ -50,229 +45,6 @@ func MustEnableModelArts(ctx *context.Context) { | |||
| } | |||
| } | |||
| func ModelArtsIndex(ctx *context.Context) { | |||
| MustEnableModelArts(ctx) | |||
| repo := ctx.Repo.Repository | |||
| page := ctx.QueryInt("page") | |||
| if page <= 0 { | |||
| page = 1 | |||
| } | |||
| ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||
| ListOptions: models.ListOptions{ | |||
| Page: page, | |||
| PageSize: setting.UI.IssuePagingNum, | |||
| }, | |||
| RepoID: repo.ID, | |||
| Type: models.TypeCloudBrainTwo, | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("Cloudbrain", err) | |||
| return | |||
| } | |||
| for i, task := range ciTasks { | |||
| if task.Status == string(models.JobRunning) { | |||
| ciTasks[i].CanDebug = true | |||
| } else { | |||
| ciTasks[i].CanDebug = false | |||
| } | |||
| ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task) | |||
| } | |||
| pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) | |||
| pager.SetDefaultParams(ctx) | |||
| ctx.Data["Page"] = pager | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| ctx.Data["Tasks"] = ciTasks | |||
| ctx.HTML(200, tplModelArtsIndex) | |||
| } | |||
| func ModelArtsNew(ctx *context.Context) { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| t := time.Now() | |||
| var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| ctx.Data["job_name"] = jobName | |||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | |||
| if err != nil { | |||
| ctx.ServerError("GetAllUserAttachments failed:", err) | |||
| return | |||
| } | |||
| ctx.Data["attachments"] = attachs | |||
| ctx.Data["dataset_path"] = modelarts.DataSetMountPath | |||
| ctx.Data["env"] = modelarts.NotebookEnv | |||
| ctx.Data["notebook_type"] = modelarts.NotebookType | |||
| if modelarts.FlavorInfos == nil { | |||
| json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||
| } | |||
| ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||
| ctx.HTML(200, tplModelArtsNew) | |||
| } | |||
| func ModelArtsCreate(ctx *context.Context, form auth.CreateModelArtsForm) { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| jobName := form.JobName | |||
| uuid := form.Attachment | |||
| description := form.Description | |||
| //repo := ctx.Repo.Repository | |||
| if !jobNamePattern.MatchString(jobName) { | |||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||
| return | |||
| } | |||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description) | |||
| if err != nil { | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsNew, &form) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||
| } | |||
| func ModelArtsShow(ctx *context.Context) { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||
| return | |||
| } | |||
| result, err := modelarts.GetJob(jobID) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||
| return | |||
| } | |||
| if result != nil { | |||
| task.Status = result.Status | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||
| return | |||
| } | |||
| createTime, _ := com.StrTo(result.CreationTimestamp).Int64() | |||
| result.CreateTime = time.Unix(int64(createTime/1000), 0).Format("2006-01-02 15:04:05") | |||
| endTime, _ := com.StrTo(result.LatestUpdateTimestamp).Int64() | |||
| result.LatestUpdateTime = time.Unix(int64(endTime/1000), 0).Format("2006-01-02 15:04:05") | |||
| result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
| result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
| } | |||
| ctx.Data["task"] = task | |||
| ctx.Data["jobID"] = jobID | |||
| ctx.Data["result"] = result | |||
| ctx.HTML(200, tplModelArtsShow) | |||
| } | |||
| func ModelArtsDebug(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| _, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| result, err := modelarts.GetJob(jobID) | |||
| if err != nil { | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||
| return | |||
| } | |||
| res, err := modelarts.GetJobToken(jobID) | |||
| if err != nil { | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||
| return | |||
| } | |||
| urls := strings.Split(result.Spec.Annotations.Url, "/") | |||
| urlPrefix := result.Spec.Annotations.TargetDomain | |||
| for i, url := range urls { | |||
| if i > 2 { | |||
| urlPrefix += "/" + url | |||
| } | |||
| } | |||
| //urlPrefix := result.Spec.Annotations.TargetDomain + "/modelarts/internal/hub/notebook/user/" + task.JobID | |||
| log.Info(urlPrefix) | |||
| debugUrl := urlPrefix + "?token=" + res.Token | |||
| ctx.Redirect(debugUrl) | |||
| } | |||
| func ModelArtsStop(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| log.Info(jobID) | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| if task.Status != string(models.JobRunning) { | |||
| log.Error("the job(%s) is not running", task.JobName) | |||
| ctx.ServerError("the job is not running", errors.New("the job is not running")) | |||
| return | |||
| } | |||
| param := models.NotebookAction{ | |||
| Action: models.ActionStop, | |||
| } | |||
| res, err := modelarts.StopJob(jobID, param) | |||
| if err != nil { | |||
| log.Error("StopJob(%s) failed:%v", task.JobName, err.Error()) | |||
| ctx.ServerError("StopJob failed", err) | |||
| return | |||
| } | |||
| task.Status = res.CurrentStatus | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| ctx.ServerError("UpdateJob failed", err) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||
| } | |||
| func ModelArtsDel(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { | |||
| log.Error("the job(%s) has not been stopped", task.JobName) | |||
| ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | |||
| return | |||
| } | |||
| _, err = modelarts.DelJob(jobID) | |||
| if err != nil { | |||
| log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | |||
| ctx.ServerError("DelJob failed", err) | |||
| return | |||
| } | |||
| err = models.DeleteJob(task) | |||
| if err != nil { | |||
| ctx.ServerError("DeleteJob failed", err) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||
| } | |||
| func NotebookIndex(ctx *context.Context) { | |||
| MustEnableModelArts(ctx) | |||
| repo := ctx.Repo.Repository | |||
| @@ -342,8 +114,9 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) | |||
| jobName := form.JobName | |||
| uuid := form.Attachment | |||
| description := form.Description | |||
| flavor := form.Flavor | |||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description) | |||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description, flavor) | |||
| if err != nil { | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form) | |||
| return | |||
| @@ -15,6 +15,7 @@ import ( | |||
| "net/http" | |||
| "net/url" | |||
| "path" | |||
| "sort" | |||
| "strings" | |||
| "time" | |||
| @@ -964,6 +965,9 @@ func ContributorsAPI(ctx *context.Context) { | |||
| } | |||
| } | |||
| } | |||
| sort.Slice(contributorInfos, func(i, j int) bool { | |||
| return contributorInfos[i].CommitCnt > contributorInfos[j].CommitCnt | |||
| }) | |||
| } else { | |||
| log.Error("GetContributors failed: %v", err) | |||
| errorCode = -1 | |||
| @@ -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) | |||
| @@ -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}} | |||
| @@ -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}} | |||
| @@ -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> | |||
| @@ -1,485 +0,0 @@ | |||
| <!-- 头部导航栏 --> | |||
| {{template "base/head" .}} | |||
| <style> | |||
| .selectcloudbrain .active.item{ | |||
| color: #0087f5 !important; | |||
| border: 1px solid #0087f5; | |||
| margin: -1px; | |||
| background: #FFF !important; | |||
| } | |||
| #deletemodel { | |||
| width: 100%; | |||
| height: 100%; | |||
| } | |||
| /* 弹窗 */ | |||
| #mask { | |||
| position: fixed; | |||
| top: 0px; | |||
| left: 0px; | |||
| right: 0px; | |||
| bottom: 0px; | |||
| filter: alpha(opacity=60); | |||
| background-color: #777; | |||
| z-index: 1000; | |||
| display: none; | |||
| opacity: 0.8; | |||
| -moz-opacity: 0.5; | |||
| padding-top: 100px; | |||
| color: #000000 | |||
| } | |||
| #loadingPage { | |||
| margin: 200px auto; | |||
| width: 50px; | |||
| height: 40px; | |||
| text-align: center; | |||
| font-size: 10px; | |||
| display: block; | |||
| } | |||
| #loadingPage>div { | |||
| background-color: green; | |||
| height: 100%; | |||
| width: 6px; | |||
| display: inline-block; | |||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| } | |||
| #loadingPage .rect2 { | |||
| -webkit-animation-delay: -1.1s; | |||
| animation-delay: -1.1s; | |||
| } | |||
| #loadingPage .rect3 { | |||
| -webkit-animation-delay: -1.0s; | |||
| animation-delay: -1.0s; | |||
| } | |||
| #loadingPage .rect4 { | |||
| -webkit-animation-delay: -0.9s; | |||
| animation-delay: -0.9s; | |||
| } | |||
| #loadingPage .rect5 { | |||
| -webkit-animation-delay: -0.8s; | |||
| animation-delay: -0.8s; | |||
| } | |||
| @-webkit-keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| -webkit-transform: scaleY(0.4) | |||
| } | |||
| 20% { | |||
| -webkit-transform: scaleY(1.0) | |||
| } | |||
| } | |||
| @keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| transform: scaleY(0.4); | |||
| -webkit-transform: scaleY(0.4); | |||
| } | |||
| 20% { | |||
| transform: scaleY(1.0); | |||
| -webkit-transform: scaleY(1.0); | |||
| } | |||
| } | |||
| /* 消息框 */ | |||
| .alert { | |||
| display: none; | |||
| position: fixed; | |||
| width: 100%; | |||
| z-index: 1001; | |||
| padding: 15px; | |||
| border: 1px solid transparent; | |||
| border-radius: 4px; | |||
| text-align: center; | |||
| font-weight: bold; | |||
| } | |||
| .alert-success { | |||
| color: #3c763d; | |||
| background-color: #dff0d8; | |||
| border-color: #d6e9c6; | |||
| } | |||
| .alert-info { | |||
| color: #31708f; | |||
| background-color: #d9edf7; | |||
| border-color: #bce8f1; | |||
| } | |||
| .alert-warning { | |||
| color: #8a6d3b; | |||
| background-color: #fcf8e3; | |||
| border-color: #faebcc; | |||
| } | |||
| .alert-danger { | |||
| color: #a94442; | |||
| background-color: #f2dede; | |||
| border-color: #ebccd1; | |||
| } | |||
| .pusher { | |||
| width: calc(100% - 260px); | |||
| box-sizing: border-box; | |||
| } | |||
| /* 弹窗 (background) */ | |||
| #imageModal { | |||
| display: none; | |||
| position: fixed; | |||
| z-index: 1; | |||
| left: 0; | |||
| top: 0; | |||
| width: 100%; | |||
| height: 100%; | |||
| overflow: auto; | |||
| background-color: rgb(0, 0, 0); | |||
| background-color: rgba(0, 0, 0, 0.4); | |||
| } | |||
| /* 弹窗内容 */ | |||
| .modal-content { | |||
| background-color: #fefefe; | |||
| margin: 15% auto; | |||
| padding: 20px; | |||
| border: 1px solid #888; | |||
| width: 30%; | |||
| } | |||
| /* 关闭按钮 */ | |||
| .close { | |||
| color: #aaa; | |||
| float: right; | |||
| font-size: 28px; | |||
| font-weight: bold; | |||
| } | |||
| .close:hover, | |||
| .close:focus { | |||
| color: black; | |||
| text-decoration: none; | |||
| cursor: pointer; | |||
| } | |||
| .dis { | |||
| margin-bottom: 20px; | |||
| } | |||
| .disabled { | |||
| cursor: pointer; | |||
| pointer-events: none; | |||
| } | |||
| </style> | |||
| <!-- 弹窗 --> | |||
| <div id="mask"> | |||
| <div id="loadingPage"> | |||
| <div class="rect1"></div> | |||
| <div class="rect2"></div> | |||
| <div class="rect3"></div> | |||
| <div class="rect4"></div> | |||
| <div class="rect5"></div> | |||
| </div> | |||
| </div> | |||
| <!-- 提示框 --> | |||
| <div class="alert"></div> | |||
| <div class="repository release dataset-list view"> | |||
| {{template "repo/header" .}} | |||
| <!-- 列表容器 --> | |||
| <div class="ui container"> | |||
| <!-- 中间云脑和新建任务按钮 --> | |||
| <div class="ui two column stackable grid "> | |||
| <div class="column"> | |||
| <div class="ui blue small menu compact selectcloudbrain"> | |||
| <a class="active item" href="{{.RepoLink}}/modelarts/notebook">调试任务</a> | |||
| <a class="item" href="{{.RepoLink}}/modelarts/train-job">训练任务</a> | |||
| </div> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
| {{svg "octicon-server" 16}} | |||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||
| <i class="dropdown icon"></i> | |||
| <div class="menu"> | |||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||
| <a class="item" href="{{.RepoLink}}/modelarts" data-value="22">Ascend NPU</a> | |||
| </div> | |||
| </div> | |||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/create">新建调试任务</a> | |||
| </div> | |||
| </div> | |||
| <!-- 中下列表展示区 --> | |||
| <div class="ui grid"> | |||
| <div class="row"> | |||
| <div class="ui sixteen wide column"> | |||
| <!-- 排序区 --> | |||
| <!-- <div class="ui sixteen wide column"> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column"> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <div class="ui right dropdown type jump item"> | |||
| <span class="text"> | |||
| {{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i> | |||
| </span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> --> | |||
| <!-- 任务展示 --> | |||
| <div class="dataset list"> | |||
| <!-- 表头 --> | |||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||
| <div class="row"> | |||
| <div class="five wide column"> | |||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||
| </div> | |||
| <div class="three wide column"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span> | |||
| </div> | |||
| <div class="one wide column"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||
| </div> | |||
| <div class="seven wide column text center"> | |||
| <span style="margin-left: 10rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{range .Tasks}} | |||
| <div class="ui grid stackable item"> | |||
| <div class="row"> | |||
| <!-- 任务名 --> | |||
| <div class="five wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||
| </a> | |||
| </div> | |||
| <div class="three wide column"> | |||
| <!--任务状态 --> | |||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| {{.Status}} | |||
| </span> --> | |||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| <span><i style="vertical-align: middle;" class="{{.Status}}"></i><span style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| </span> | |||
| <!-- 任务创建时间 --> | |||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||
| </div> | |||
| <div class="one wide column"> | |||
| {{if .User.Name}} | |||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||
| {{else}} | |||
| <a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||
| {{end}} | |||
| </div> | |||
| <div class="seven wide column text right"> | |||
| <div class="ui compact buttons" style="margin-right:10px;"> | |||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}"> | |||
| 查看 | |||
| </a> | |||
| <a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||
| 调试 | |||
| </a> | |||
| <form id="stopForm-{{.JobID}}" action="{{if ne .Status "RUNNING"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/stop{{end}}" method="post" style="margin-left:-1px;"> | |||
| {{$.CsrfTokenHtml}} | |||
| <a class="ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
| 停止 | |||
| </a> | |||
| </form> | |||
| </div> | |||
| <!-- 删除任务 --> | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| <a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| 删除 | |||
| </a> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{end}} {{template "base/paginate" .}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- 确认模态框 --> | |||
| <div id="deletemodel"> | |||
| <div class="ui basic modal"> | |||
| <div class="ui icon header"> | |||
| <i class="trash icon"></i> 删除任务 | |||
| </div> | |||
| <div class="content"> | |||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||
| </div> | |||
| <div class="actions"> | |||
| <div class="ui red basic inverted cancel button"> | |||
| <i class="remove icon"></i> 取消操作 | |||
| </div> | |||
| <div class="ui green basic inverted ok button"> | |||
| <i class="checkmark icon"></i> 确定操作 | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| // 调试和评分新开窗口 | |||
| function stop(obj) { | |||
| if (obj.style.color != "rgb(204, 204, 204)") { | |||
| obj.target = '_blank' | |||
| } else { | |||
| return | |||
| } | |||
| } | |||
| // 删除时用户确认 | |||
| function assertDelete(obj) { | |||
| if (obj.style.color == "rgb(204, 204, 204)") { | |||
| return | |||
| } else { | |||
| var delId = obj.parentNode.id | |||
| flag = 1; | |||
| $('.ui.basic.modal') | |||
| .modal({ | |||
| onDeny: function() { | |||
| flag = false | |||
| }, | |||
| onApprove: function() { | |||
| document.getElementById(delId).submit() | |||
| flag = true | |||
| }, | |||
| onHidden: function() { | |||
| if (flag == false) { | |||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
| } | |||
| } | |||
| }) | |||
| .modal('show') | |||
| } | |||
| } | |||
| // 加载任务状态 | |||
| var timeid = window.setInterval(loadJobStatus, 15000); | |||
| $(document).ready(loadJobStatus); | |||
| function loadJobStatus() { | |||
| $(".job-status").each((index, job) => { | |||
| const jobID = job.dataset.jobid; | |||
| const repoPath = job.dataset.repopath; | |||
| if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'START_FAILED' || job.textContent.trim() == 'CREATE_FAILED') { | |||
| return | |||
| } | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/${jobID}`, (data) => { | |||
| const jobID = data.JobID | |||
| const status = data.JobStatus | |||
| if (status != job.textContent.trim()) { | |||
| //$('#' + jobID).text(status) | |||
| //if (status == 'STOPPED') { | |||
| window.location.reload() | |||
| //} | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| }); | |||
| }; | |||
| // 获取弹窗 | |||
| var modal = document.getElementById('imageModal'); | |||
| // 打开弹窗的按钮对象 | |||
| var btns = document.getElementsByClassName("imageBtn"); | |||
| // 获取 <span> 元素,用于关闭弹窗 | |||
| var spans = document.getElementsByClassName('close'); | |||
| // 点击按钮打开弹窗 | |||
| for (i = 0; i < btns.length; i++) { | |||
| btns[i].onclick = function() { | |||
| modal.style.display = "block"; | |||
| } | |||
| } | |||
| // 点击 <span> (x), 关闭弹窗 | |||
| for (i = 0; i < spans.length; i++) { | |||
| spans[i].onclick = function() { | |||
| modal.style.display = "none"; | |||
| } | |||
| } | |||
| // 在用户点击其他地方时,关闭弹窗 | |||
| window.onclick = function(event) { | |||
| if (event.target == modal) { | |||
| modal.style.display = "none"; | |||
| } | |||
| } | |||
| // 显示弹窗,弹出相应的信息 | |||
| function showmask() { | |||
| $('#imageModal').css('display', 'none') | |||
| $('#mask').css('display', 'block') | |||
| $("iframe[name=iframeContent]").on("load", function() { | |||
| var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||
| var json1 = JSON.parse(responseText) | |||
| $('#mask').css('display', 'none') | |||
| parent.location.href | |||
| if (json1.result_code === "0") { | |||
| $('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||
| } else { | |||
| $('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||
| } | |||
| }) | |||
| } | |||
| </script> | |||
| @@ -1,240 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| <style> | |||
| /* 遮罩层css效果图 */ | |||
| #mask { | |||
| position: fixed; | |||
| top: 0px; | |||
| left: 0px; | |||
| right: 0px; | |||
| bottom: 0px; | |||
| filter: alpha(opacity=60); | |||
| background-color: #777; | |||
| z-index: 1000; | |||
| display: none; | |||
| opacity: 0.8; | |||
| -moz-opacity: 0.5; | |||
| padding-top: 100px; | |||
| color: #000000 | |||
| } | |||
| /* 加载圈css效果图 */ | |||
| #loadingPage { | |||
| margin: 200px auto; | |||
| width: 50px; | |||
| height: 40px; | |||
| text-align: center; | |||
| font-size: 10px; | |||
| display: block; | |||
| } | |||
| #loadingPage>div { | |||
| background-color: green; | |||
| height: 100%; | |||
| width: 6px; | |||
| display: inline-block; | |||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| } | |||
| #loadingPage .rect2 { | |||
| -webkit-animation-delay: -1.1s; | |||
| animation-delay: -1.1s; | |||
| } | |||
| #loadingPage .rect3 { | |||
| -webkit-animation-delay: -1.0s; | |||
| animation-delay: -1.0s; | |||
| } | |||
| #loadingPage .rect4 { | |||
| -webkit-animation-delay: -0.9s; | |||
| animation-delay: -0.9s; | |||
| } | |||
| #loadingPage .rect5 { | |||
| -webkit-animation-delay: -0.8s; | |||
| animation-delay: -0.8s; | |||
| } | |||
| @-webkit-keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| -webkit-transform: scaleY(0.4) | |||
| } | |||
| 20% { | |||
| -webkit-transform: scaleY(1.0) | |||
| } | |||
| } | |||
| @keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| transform: scaleY(0.4); | |||
| -webkit-transform: scaleY(0.4); | |||
| } | |||
| 20% { | |||
| transform: scaleY(1.0); | |||
| -webkit-transform: scaleY(1.0); | |||
| } | |||
| } | |||
| .inline.required.field.cloudbrain_benchmark { | |||
| display: none; | |||
| } | |||
| </style> | |||
| <div id="mask"> | |||
| <div id="loadingPage"> | |||
| <div class="rect1"></div> | |||
| <div class="rect2"></div> | |||
| <div class="rect3"></div> | |||
| <div class="rect4"></div> | |||
| <div class="rect5"></div> | |||
| </div> | |||
| </div> | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="repository new repo ui middle very relaxed page grid"> | |||
| <div class="column"> | |||
| {{template "base/alert" .}} | |||
| <div class="ui negative message" id="messageInfo"> | |||
| <p></p> | |||
| </div> | |||
| <form class="ui form" id="form_id" action="{{.Link}}" method="post"> | |||
| {{.CsrfTokenHtml}} | |||
| <h3 class="ui top attached header"> | |||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||
| </h3> | |||
| <div class="ui attached segment"> | |||
| <!-- <br> --> | |||
| <div class="inline required field"> | |||
| <label>任务名称</label> | |||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label>数据集</label> | |||
| <input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | |||
| <datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | |||
| {{range .attachments}} | |||
| <option name="attachment" data-value="{{.UUID}}">{{.Attachment.Name}}</option> | |||
| {{end}} | |||
| </datalist> | |||
| <input type="hidden" name="attachment" id="answerInput-hidden"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>工作环境</label> | |||
| <input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>类型</label> | |||
| <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>规格</label> | |||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||
| {{range .flavors}} | |||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>数据集存放路径</label> | |||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label>描述</label> | |||
| <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label></label> | |||
| <button class="ui green button"> | |||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||
| </button> | |||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| // 取消创建跳转 | |||
| let url_href = window.location.pathname.split('create')[0] | |||
| $(".ui.button").attr('href',url_href) | |||
| // 判断必填选项是否填写正确 | |||
| let form = document.getElementById('form_id'); | |||
| $('#messageInfo').css('display','none') | |||
| form.onsubmit = function(e){ | |||
| let value_task = $("input[name='job_name']").val() | |||
| let re = /^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$/ | |||
| let flag = re.test(value_task) | |||
| if(!flag){ | |||
| $('#messageInfo').css('display','block') | |||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。' | |||
| $('#messageInfo p').text(str) | |||
| return false | |||
| } | |||
| let min_value_task = value_task.toLowerCase() | |||
| $("input[name='job_name']").attr("value",min_value_task) | |||
| document.getElementById("mask").style.display = "block" | |||
| } | |||
| // 点击按钮后遮罩层显示 | |||
| // function showmask() { | |||
| // document.getElementById("mask").style.display = "block" | |||
| // } | |||
| // 页面加载完毕后遮罩层隐藏 | |||
| document.onreadystatechange = function() { | |||
| if (document.readyState === "complete") { | |||
| document.getElementById("mask").style.display = "none" | |||
| } | |||
| } | |||
| $('select.dropdown') | |||
| .dropdown(); | |||
| $(function() { | |||
| $("#cloudbrain_job_type").change(function() { | |||
| if ($(this).val() == 'BENCHMARK') { | |||
| $(".cloudbrain_benchmark").show(); | |||
| } else { | |||
| $(".cloudbrain_benchmark").hide(); | |||
| } | |||
| }) | |||
| }) | |||
| document.querySelector('input[list]').addEventListener('input',function(e){ | |||
| var input = e.target, | |||
| list = input.getAttribute('list'), | |||
| options = document.querySelectorAll('#'+list+' option'), | |||
| hiddenInput = document.getElementById(input.getAttribute('id')+'-hidden'), | |||
| inputValue = input.value; | |||
| hiddenInput.value = inputValue; | |||
| for (let i=0;i<options.length;i++){ | |||
| var option = options[i] | |||
| if(option.innerText===inputValue){ | |||
| hiddenInput.value = option.getAttribute('data-value'); | |||
| break | |||
| } | |||
| } | |||
| }) | |||
| </script> | |||
| @@ -138,7 +138,7 @@ | |||
| <label>规格</label> | |||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||
| {{range .flavors}} | |||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||
| <option name="flavor" value="{{.Value}}">{{.Desc}}</option> | |||
| {{end}} | |||
| </select> | |||
| @@ -1,122 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="repository new repo ui middle very relaxed page grid"> | |||
| <div class="column"> | |||
| {{template "base/alert" .}} | |||
| <h4 class="ui header" id="vertical-segment"> | |||
| <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> | |||
| </h4> | |||
| <div> | |||
| <div class="ui yellow segment"> | |||
| {{with .task}} | |||
| <p>任务名称: {{.JobName}}</p> | |||
| {{end}} | |||
| </div> | |||
| <div class="ui green segment"> | |||
| <p>任务结果:</p> | |||
| {{with .result}} | |||
| <table class="ui celled striped table"> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> 状态 </td> | |||
| <td> {{.Status}} </td> | |||
| </tr> | |||
| <tr> | |||
| <td> 开始时间 </td> | |||
| <td>{{.CreateTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 最后更新时间 </td> | |||
| <td>{{.LatestUpdateTime}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| {{end}} | |||
| </div> | |||
| <div class="ui blue segment"> | |||
| {{with .result}} | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> 配置信息 </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> 开发环境类型 </td> | |||
| <td>{{.Profile.DeType}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 硬件类型 </td> | |||
| <td>{{.Profile.FlavorType}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> 机器规格详情 </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> 机器规格 </td> | |||
| <td> {{.Flavor}} </td> | |||
| </tr> | |||
| <tr> | |||
| <td> 规格名称 </td> | |||
| <td>{{.FlavorDetails.Name}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 规格销售状态 </td> | |||
| <td>{{.FlavorDetails.Status}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 排队个数 </td> | |||
| <td>{{.FlavorDetails.QueuingNum}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 排到队的剩余时间(秒) </td> | |||
| <td>{{.FlavorDetails.QueueLeftTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 自动停止时间(秒) </td> | |||
| <td>{{.FlavorDetails.Duration}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}> | |||
| <thead> | |||
| <tr> <th colspan="2"> 排队信息 </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td> 实例状态 </td> | |||
| <td>{{.QueuingInfo.Status}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 实例排队的开始时间 </td> | |||
| <td>{{.QueuingInfo.BeginTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 排到队的剩余时间(秒) </td> | |||
| <td>{{.QueuingInfo.RemainTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 实例排队的预计停止时间 </td> | |||
| <td>{{.QueuingInfo.EndTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 实例在队列中的排位 </td> | |||
| <td>{{.QueuingInfo.Rank}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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)); | |||
| @@ -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") | |||
| } | |||
| @@ -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); | |||
| }) | |||
| } | |||
| @@ -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'); | |||