updatepull/948/head
@@ -17,6 +17,7 @@ require ( | |||
gitea.com/macaron/macaron v1.4.0 | |||
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | |||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 | |||
github.com/360EntSecGroup-Skylar/excelize/v2 v2.0.2 | |||
github.com/BurntSushi/toml v0.3.1 | |||
github.com/PuerkitoBio/goquery v1.5.0 | |||
github.com/RichardKnop/machinery v1.6.9 | |||
@@ -107,7 +108,6 @@ require ( | |||
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141 | |||
github.com/urfave/cli v1.22.1 | |||
github.com/xanzy/go-gitlab v0.31.0 | |||
github.com/360EntSecGroup-Skylar/excelize/v2 v2.0.2 | |||
github.com/yohcop/openid-go v1.0.0 | |||
github.com/yuin/goldmark v1.1.27 | |||
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 | |||
@@ -22,7 +22,6 @@ var customMigrations = []CustomMigration{ | |||
} | |||
var customMigrationsStatic = []CustomMigrationStatic{ | |||
{"Alter user static table field type ", alterUserStaticTable}, | |||
{"Delete organization user history data ", deleteNotDisplayUser}, | |||
{"update issue_fixed_rate to 1 if num_issues is 0 ", updateIssueFixedRate}, | |||
} | |||
@@ -59,14 +58,6 @@ func syncTopicStruct(x *xorm.Engine) error { | |||
return err | |||
} | |||
func alterUserStaticTable(x *xorm.Engine, static *xorm.Engine) error { | |||
alterSql := "alter table public.user_business_analysis alter column open_i_index type double precision" | |||
_, err := static.Exec(alterSql) | |||
return err | |||
} | |||
func deleteNotDisplayUser(x *xorm.Engine, static *xorm.Engine) error { | |||
querySQL := "select id,name from public.user where type=1" | |||
@@ -172,3 +172,10 @@ func UpdateRepoStat(repoStat *RepoStatistic) error { | |||
_, err := xStatistic.Exec(sql, repoStat.Impact, repoStat.Completeness, repoStat.Liveness, repoStat.ProjectHealth, repoStat.TeamHealth, repoStat.Growth, repoStat.RadarTotal, repoStat.RepoID, repoStat.Date) | |||
return err | |||
} | |||
func UpdateRepoStatVisits(repoStat *RepoStatistic) error { | |||
sql := "update repo_statistic set num_visits=? where repo_id=? and date=?" | |||
_, err := xStatistic.Exec(sql, repoStat.NumVisits, repoStat.RepoID, repoStat.Date) | |||
return err | |||
} |
@@ -153,6 +153,76 @@ func getLastCountDate() int64 { | |||
return pageStartTime.Unix() | |||
} | |||
func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysis, int64) { | |||
log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime) + " isAll=" + fmt.Sprint(opts.IsAll)) | |||
statictisSess := xStatistic.NewSession() | |||
defer statictisSess.Close() | |||
resultMap := make(map[int64]*UserBusinessAnalysis) | |||
var newAndCond = builder.NewCond() | |||
if len(opts.UserName) > 0 { | |||
newAndCond = newAndCond.And( | |||
builder.Like{"name", opts.UserName}, | |||
) | |||
} | |||
if !opts.IsAll { | |||
newAndCond = newAndCond.And( | |||
builder.Gte{"count_date": opts.StartTime}, | |||
) | |||
newAndCond = newAndCond.And( | |||
builder.Lte{"count_date": opts.EndTime}, | |||
) | |||
} | |||
allCount, err := statictisSess.Where(newAndCond).Count(new(UserBusinessAnalysis)) | |||
if err != nil { | |||
log.Info("query error." + err.Error()) | |||
return nil, 0 | |||
} | |||
log.Info("query return total:" + fmt.Sprint(allCount)) | |||
pageSize := 200 | |||
totalPage := int(allCount) / pageSize | |||
for i := 0; i <= int(totalPage); i++ { | |||
userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) | |||
if err := statictisSess.Table("user_business_analysis").Where(newAndCond).OrderBy("count_date desc").Limit(pageSize, i*pageSize). | |||
Find(&userBusinessAnalysisList); err != nil { | |||
return nil, 0 | |||
} | |||
log.Info("query " + fmt.Sprint(i+1) + " result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||
for _, userRecord := range userBusinessAnalysisList { | |||
if _, ok := resultMap[userRecord.ID]; !ok { | |||
resultMap[userRecord.ID] = userRecord | |||
} else { | |||
resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||
resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||
resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||
resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||
resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||
resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||
resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||
resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||
resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||
resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||
resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||
resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||
resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||
resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||
} | |||
} | |||
} | |||
userBusinessAnalysisReturnList := UserBusinessAnalysisList{} | |||
for _, v := range resultMap { | |||
userBusinessAnalysisReturnList = append(userBusinessAnalysisReturnList, v) | |||
} | |||
sort.Sort(userBusinessAnalysisReturnList) | |||
log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||
return userBusinessAnalysisReturnList, allCount | |||
} | |||
func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysis, int64) { | |||
log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime) + " isAll=" + fmt.Sprint(opts.IsAll)) | |||
@@ -219,32 +289,43 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus | |||
builder.Lte{"count_date": opts.EndTime}, | |||
) | |||
} | |||
userBusinessAnalysisList = make([]*UserBusinessAnalysis, 0) | |||
if err := statictisSess.Table("user_business_analysis").Where(newAndCond).OrderBy("count_date desc"). | |||
Find(&userBusinessAnalysisList); err != nil { | |||
allCount, err := statictisSess.Where(newAndCond).Count(new(UserBusinessAnalysis)) | |||
if err != nil { | |||
log.Info("query error." + err.Error()) | |||
return nil, 0 | |||
} | |||
} | |||
log.Info("query result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||
for _, userRecord := range userBusinessAnalysisList { | |||
if _, ok := resultMap[userRecord.ID]; !ok { | |||
resultMap[userRecord.ID] = userRecord | |||
} else { | |||
resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||
resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||
resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||
resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||
resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||
resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||
resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||
resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||
resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||
resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||
resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||
resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||
resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||
resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||
pageSize := 1000 | |||
totalPage := int(allCount) / pageSize | |||
for i := 0; i <= int(totalPage); i++ { | |||
userBusinessAnalysisList = make([]*UserBusinessAnalysis, 0) | |||
if err := statictisSess.Table("user_business_analysis").Where(newAndCond).OrderBy("count_date desc").Limit(pageSize, i*pageSize). | |||
Find(&userBusinessAnalysisList); err != nil { | |||
return nil, 0 | |||
} | |||
log.Info("query result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||
for _, userRecord := range userBusinessAnalysisList { | |||
if _, ok := resultMap[userRecord.ID]; !ok { | |||
resultMap[userRecord.ID] = userRecord | |||
} else { | |||
resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||
resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||
resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||
resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||
resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||
resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||
resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||
resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||
resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||
resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||
resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||
resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||
resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||
resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||
} | |||
} | |||
} | |||
} | |||
@@ -4,6 +4,7 @@ import ( | |||
"bytes" | |||
"encoding/base64" | |||
"encoding/json" | |||
"fmt" | |||
"io/ioutil" | |||
"net/http" | |||
@@ -113,13 +114,23 @@ func GetResultFromElk(resultInfo ResultInfo, jsonStr []byte) (loaded int, totalV | |||
client := &http.Client{} | |||
resp, err := client.Do(req) | |||
if err != nil { | |||
panic(err) | |||
// panic(err) | |||
return 0, 0, err | |||
} | |||
defer resp.Body.Close() | |||
body, _ := ioutil.ReadAll(resp.Body) | |||
errs := json.Unmarshal([]byte(string(body)), &resultInfo) | |||
log.Info("Get resultJson failed", errs) | |||
if resp.StatusCode != 200 { | |||
log.Error("ConnectToElk failed:%s", resp.Status) | |||
return 0, 0, fmt.Errorf("ConnectToElk failed:%s", resp.Status) | |||
} | |||
body, err := ioutil.ReadAll(resp.Body) | |||
if err != nil { | |||
return 0, 0, err | |||
} | |||
err = json.Unmarshal([]byte(string(body)), &resultInfo) | |||
if err != nil { | |||
log.Error("Get resultJson failed", err) | |||
return 0, 0, err | |||
} | |||
return resultInfo.Result.Loaded, resultInfo.Result.RawResponse.Hits.Total, err | |||
} | |||
@@ -138,6 +149,7 @@ func ProjectViewInit(User string, Project string, Gte string, Lte string) (proje | |||
var timeRange Range | |||
timeRange.Timestamptest.Gte = Gte | |||
timeRange.Timestamptest.Lte = Lte | |||
timeRange.Timestamptest.Format = "strict_date_optional_time" | |||
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Range = &timeRange | |||
//限定用户 | |||
var userName FilterMatchPhrase | |||
@@ -208,9 +220,10 @@ func TagNameInit(MessageInfo string, Tagname string, Gte string, Lte string) (pr | |||
//向elk发送请求,将获取的结果只保留访问量,输入是初始化后的数据结构,返回访问量 | |||
func ViewInfo(viewInfo InputInfo) (totalView int, err error) { | |||
jsons, errs := json.Marshal(viewInfo) | |||
if errs != nil { | |||
log.Info("errs:", errs) | |||
jsons, err := json.Marshal(viewInfo) | |||
if err != nil { | |||
log.Error("jsons failed", err) | |||
return 0, err | |||
} | |||
var jsonStr = []byte(jsons) | |||
var resultInfo ResultInfo | |||
@@ -221,7 +234,6 @@ func ViewInfo(viewInfo InputInfo) (totalView int, err error) { | |||
if loaded == 0 { | |||
loaded_next, totalView, err := GetResultFromElk(resultInfo, jsonStr) | |||
time++ | |||
log.Info("time:", time) | |||
if loaded_next != 0 && time < 100 { | |||
return totalView, err | |||
} | |||
@@ -816,6 +816,9 @@ total_count_get_error=查询总页数失败。 | |||
last_update_time_error=查询最新更新时间失败。 | |||
get_repo_stat_error=查询当前仓库的统计信息失败。 | |||
get_repo_info_error=查询当前仓库信息失败。 | |||
generate_statistic_file_error=生成文件失败。 | |||
repo_stat_inspect=项目分析 | |||
all=所有 | |||
modelarts.status=状态 | |||
modelarts.createtime=创建时间 | |||
@@ -529,6 +529,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/restoreFork", adminReq, repo.RestoreForkNumber) | |||
m.Get("/downloadAll", adminReq, repo.ServeAllProjectsPeriodStatisticsFile) | |||
m.Get("/downloadAllOpenI", adminReq, repo.ServeAllProjectsOpenIStatisticsFile) | |||
m.Group("/project", func() { | |||
m.Get("", adminReq, repo.GetAllProjectsPeriodStatistics) | |||
@@ -207,6 +207,71 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | |||
} | |||
func ServeAllProjectsOpenIStatisticsFile(ctx *context.Context) { | |||
page := ctx.QueryInt("page") | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
pageSize := 1000 | |||
_, latestDate, err := models.GetRepoStatLastUpdatedTime() | |||
if err != nil { | |||
log.Error("Can not query the last updated time.", err) | |||
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error")) | |||
return | |||
} | |||
date := ctx.QueryTrim("date") | |||
if date == "" { | |||
date = latestDate | |||
} | |||
countSql := generateOpenICountSql(date) | |||
total, err := models.CountRepoStatByRawSql(countSql) | |||
if err != nil { | |||
log.Error("Can not query total count.", err) | |||
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) | |||
return | |||
} | |||
var projectAnalysis = ctx.Tr("repo.repo_stat_inspect") | |||
fileName := "项目分析_OPENI_" + date + ".xlsx" | |||
totalPage := getTotalPage(total, pageSize) | |||
f := excelize.NewFile() | |||
index := f.NewSheet(projectAnalysis) | |||
f.DeleteSheet("Sheet1") | |||
for k, v := range allProjectsOpenIHeader() { | |||
f.SetCellValue(projectAnalysis, k, v) | |||
} | |||
var row = 2 | |||
for i := 0; i <= totalPage; i++ { | |||
pageRecords := models.GetRepoStatisticByRawSql(generateTypeAllOpenISql(date, i+1, pageSize)) | |||
for _, record := range pageRecords { | |||
for k, v := range allProjectsOpenIValues(row, record, ctx) { | |||
f.SetCellValue(projectAnalysis, k, v) | |||
} | |||
row++ | |||
} | |||
} | |||
f.SetActiveSheet(index) | |||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(fileName)) | |||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
f.WriteTo(ctx.Resp) | |||
} | |||
func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, projectAnalysis string) string { | |||
baseName := projectAnalysis + "_" | |||
@@ -237,6 +302,32 @@ func allProjectsPeroidValues(row int, rs *models.RepoStatistic, ctx *context.Con | |||
} | |||
} | |||
func allProjectsOpenIHeader() map[string]string { | |||
return map[string]string{"A1": "ID", "B1": "项目名称", "C1": "拥有者", "D1": "是否私有", "E1": "OpenI指数", | |||
"F1": "影响力", "G1": "成熟度", "H1": "活跃度", "I1": "项目健康度", "J1": "团队健康度", "K1": "项目发展趋势", | |||
"L1": "关注数", "M1": "点赞数", "N1": "派生数", "O1": "代码下载量", "P1": "评论数", "Q1": "浏览量", "R1": "已解决任务数", "S1": "版本发布数量", "T1": "有效开发年龄", | |||
"U1": "数据集", "V1": "模型数", "W1": "百科页面数量", "X1": "提交数", "Y1": "任务数", "Z1": "PR数", "AA1": "版本发布数量", "AB1": "任务完成比例", "AC1": "贡献者数", "AD1": "关键贡献者数", | |||
"AE1": "新人增长量", "AF1": "代码规模增长量", "AG1": "任务增长量", "AH1": "新人增长量", "AI1": "提交增长量", "AJ1": "评论增长量", | |||
} | |||
} | |||
func allProjectsOpenIValues(row int, rs *models.RepoStatistic, ctx *context.Context) map[string]string { | |||
return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.Name, getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
getCellName("F", row): strconv.FormatFloat(rs.Impact, 'f', 2, 64), getCellName("G", row): strconv.FormatFloat(rs.Completeness, 'f', 2, 64), getCellName("H", row): strconv.FormatFloat(rs.Liveness, 'f', 2, 64), getCellName("I", row): strconv.FormatFloat(rs.ProjectHealth, 'f', 2, 64), getCellName("J", row): strconv.FormatFloat(rs.TeamHealth, 'f', 2, 64), getCellName("K", row): strconv.FormatFloat(rs.Growth, 'f', 2, 64), | |||
getCellName("L", row): strconv.FormatInt(rs.NumWatches, 10), getCellName("M", row): strconv.FormatInt(rs.NumStars, 10), getCellName("N", row): strconv.FormatInt(rs.NumForks, 10), getCellName("O", row): strconv.FormatInt(rs.NumDownloads, 10), | |||
getCellName("P", row): strconv.FormatInt(rs.NumComments, 10), getCellName("Q", row): strconv.FormatInt(rs.NumVisits, 10), getCellName("R", row): strconv.FormatInt(rs.NumClosedIssues, 10), getCellName("S", row): strconv.FormatInt(rs.NumVersions, 10), | |||
getCellName("T", row): strconv.FormatInt(rs.NumDevMonths, 10), getCellName("U", row): strconv.FormatInt(rs.DatasetSize, 10), getCellName("V", row): strconv.FormatInt(rs.NumModels, 10), getCellName("W", row): strconv.FormatInt(rs.NumWikiViews, 10), | |||
getCellName("X", row): strconv.FormatInt(rs.NumCommits, 10), getCellName("Y", row): strconv.FormatInt(rs.NumIssues, 10), getCellName("Z", row): strconv.FormatInt(rs.NumPulls, 10), getCellName("AA", row): strconv.FormatInt(rs.NumVersions, 10), | |||
getCellName("AB", row): strconv.FormatFloat(float64(rs.IssueFixedRate), 'f', 2, 64), getCellName("AC", row): strconv.FormatInt(rs.NumContributor, 10), getCellName("AD", row): strconv.FormatInt(rs.NumKeyContributor, 10), getCellName("AE", row): strconv.FormatInt(rs.NumContributorsGrowth, 10), | |||
getCellName("AF", row): strconv.FormatInt(rs.NumCommitLinesGrowth, 10), getCellName("AG", row): strconv.FormatInt(rs.NumIssuesGrowth, 10), getCellName("AH", row): strconv.FormatInt(rs.NumContributorsGrowth, 10), getCellName("AI", row): strconv.FormatInt(rs.NumCommitsGrowth, 10), getCellName("AJ", row): strconv.FormatInt(rs.NumCommentsGrowth, 10), | |||
} | |||
} | |||
func getCellName(col string, row int) string { | |||
return col + strconv.Itoa(row) | |||
} | |||
@@ -381,6 +472,13 @@ func generateCountSql(beginTime time.Time, endTime time.Time, latestDate string, | |||
return countSql | |||
} | |||
func generateOpenICountSql(latestDate string) string { | |||
countSql := "SELECT count(*) FROM " + | |||
"public.repo_statistic where date='" + latestDate + "'" | |||
return countSql | |||
} | |||
func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
sql := "SELECT A.repo_id,name,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
"(SELECT repo_id,sum(num_visits) as num_visits " + | |||
@@ -396,6 +494,14 @@ func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate strin | |||
return sql | |||
} | |||
func generateTypeAllOpenISql(latestDate string, page int, pageSize int) string { | |||
sql := "SELECT id, repo_id, date, num_watches, num_stars, num_forks, num_downloads, num_comments, num_visits, num_closed_issues, num_versions, num_dev_months, repo_size, dataset_size, num_models, num_wiki_views, num_commits, num_issues, num_pulls, issue_fixed_rate, num_contributor, num_key_contributor, num_contributors_growth, num_commits_growth, num_commit_lines_growth, num_issues_growth, num_comments_growth, impact, completeness, liveness, project_health, team_health, growth, radar_total, name, is_private, owner_name FROM " + | |||
" public.repo_statistic where date='" + latestDate + "'" | |||
sql = sql + " order by radar_total desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
return sql | |||
} | |||
func generatePageSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
sql := "SELECT A.repo_id,name,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
@@ -43,7 +43,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Post("/manager/restart", Restart) | |||
m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) | |||
m.Post("/tool/update_all_repo_commit_cnt", UpdateAllRepoCommitCnt) | |||
m.Post("/tool/repo_stat", RepoStatisticManually) | |||
m.Post("/tool/repo_stat/:date", RepoStatisticManually) | |||
m.Post("/tool/update_repo_visit/:date", UpdateRepoVisit) | |||
}, CheckInternalToken) | |||
} |
@@ -39,8 +39,35 @@ func UpdateAllRepoCommitCnt(ctx *macaron.Context) { | |||
} | |||
func RepoStatisticManually(ctx *macaron.Context) { | |||
date := ctx.Query("date") | |||
date := ctx.Params("date") | |||
repo.RepoStatisticDaily(date) | |||
repo.SummaryStatisticDaily(date) | |||
repo.TimingCountDataByDate(date) | |||
} | |||
func UpdateRepoVisit(ctx *macaron.Context) { | |||
date := ctx.Params("date") | |||
log.Info("date(%s)", date) | |||
repos, err := models.GetAllRepositories() | |||
if err != nil { | |||
log.Error("GetAllRepositories failed:%v", err.Error(), ctx.Data["MsgID"]) | |||
ctx.JSON(http.StatusInternalServerError, map[string]string{ | |||
"error_msg": "GetAllRepositories failed", | |||
}) | |||
return | |||
} | |||
for i, repoStat := range repos { | |||
log.Info("%d:begin UpdateRepoVisits(id = %d, name = %s)", i, repoStat.ID, repoStat.Name) | |||
if err = repo.UpdateRepoVisits(ctx, repoStat, date); err != nil { | |||
log.Error("UpdateRepoVisits(id = %d, name = %s) failed:%v", repoStat.ID, repoStat.Name, err.Error()) | |||
continue | |||
} | |||
log.Info("%d:finish UpdateRepoVisits(id = %d, name = %s)", i, repoStat.ID, repoStat.Name) | |||
} | |||
ctx.JSON(http.StatusOK, map[string]string{ | |||
"error_msg": "", | |||
}) | |||
} |
@@ -1,15 +1,17 @@ | |||
package repo | |||
import ( | |||
"errors" | |||
"time" | |||
"code.gitea.io/gitea/services/mailer" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/normalization" | |||
"code.gitea.io/gitea/modules/repository" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/services/mailer" | |||
"gitea.com/macaron/macaron" | |||
) | |||
func StatisticAuto() { | |||
@@ -48,7 +50,7 @@ func RepoStatisticDaily(date string) { | |||
var maxRepoRadar models.RepoStatistic | |||
for i, repo := range repos { | |||
log.Info("start statistic: %s", repo.Name) | |||
log.Info("start statistic: %s", getDistinctProjectName(repo)) | |||
var numDevMonths, numWikiViews, numContributor, numKeyContributor, numCommitsGrowth, numCommitLinesGrowth, numContributorsGrowth int64 | |||
repoGitStat, err := models.GetRepoKPIStats(repo) | |||
if err != nil { | |||
@@ -230,7 +232,7 @@ func RepoStatisticDaily(date string) { | |||
} | |||
log.Info("finish statistic: %s", repo.Name) | |||
log.Info("finish statistic: %s", getDistinctProjectName(repo)) | |||
} | |||
//radar map | |||
@@ -274,3 +276,29 @@ func getStatTime(timeStr string) (string, string) { | |||
return beginTime, endTime | |||
} | |||
func UpdateRepoVisits(ctx *macaron.Context, repo *models.Repository, date string) error { | |||
beginTime, endTime := getStatTime(date) | |||
var numVisits int | |||
numVisits, err := repository.AppointProjectView(repo.OwnerName, repo.Name, beginTime, endTime) | |||
if err != nil { | |||
log.Error("AppointProjectView failed(%s): %v", getDistinctProjectName(repo), err) | |||
return err | |||
} | |||
repoStat, err := models.GetRepoStatisticByDate(date, repo.ID) | |||
if err != nil { | |||
log.Error("GetRepoStatisticByDate failed(%s): %v", getDistinctProjectName(repo), err) | |||
return err | |||
} else if len(repoStat) != 1 { | |||
log.Error("GetRepoStatisticByDate failed(%s): %v", getDistinctProjectName(repo), err) | |||
return errors.New("not find repo") | |||
} | |||
repoStat[0].NumVisits = int64(numVisits) | |||
if err = models.UpdateRepoStatVisits(repoStat[0]); err != nil { | |||
log.Error("UpdateRepoStatVisits failed(%s): %v", getDistinctProjectName(repo), err) | |||
return err | |||
} | |||
return nil | |||
} |
@@ -49,13 +49,13 @@ func QueryUserStaticDataPage(ctx *context.Context) { | |||
startTime = time.Now() | |||
endTime = time.Now() | |||
} else { | |||
startTime, _ = time.Parse("2006-01-02", startDate) | |||
startTime, _ = time.ParseInLocation("2006-01-02", startDate, time.Local) | |||
settingStartTime, _ := time.Parse("2006-01-02", setting.RadarMap.RecordBeginTime) | |||
if startTime.Unix() < settingStartTime.Unix() { | |||
startTime = settingStartTime | |||
startDate = settingStartTime.Format("2006-01-02") | |||
} | |||
endTime, _ = time.Parse("2006-01-02", endDate) | |||
endTime, _ = time.ParseInLocation("2006-01-02", endDate, time.Local) | |||
endTime = endTime.AddDate(0, 0, 1) | |||
isAll = false | |||
log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) | |||
@@ -76,17 +76,15 @@ func QueryUserStaticDataPage(ctx *context.Context) { | |||
EndTime: endTime.Unix(), | |||
IsAll: isAll, | |||
} | |||
mapInterface := make(map[string]interface{}) | |||
re, count := models.QueryUserStaticDataPage(pageOpts) | |||
mapInterface["data"] = re | |||
mapInterface["count"] = count | |||
if IsReturnFile { | |||
re, count := models.QueryUserStaticDataAll(pageOpts) | |||
log.Info("return count=" + fmt.Sprint(count)) | |||
//writer exec file. | |||
xlsx := excelize.NewFile() | |||
xlsx.DeleteSheet("Sheet1") | |||
sheetName := ctx.Tr("user.static.sheetname") | |||
index := xlsx.NewSheet(sheetName) | |||
xlsx.DeleteSheet("Sheet1") | |||
dataHeader := map[string]string{ | |||
"A1": ctx.Tr("user.static.id"), | |||
"B1": ctx.Tr("user.static.name"), | |||
@@ -129,8 +127,12 @@ func QueryUserStaticDataPage(ctx *context.Context) { | |||
xlsx.SetCellValue(sheetName, "M"+rows, userRecord.EncyclopediasCount) | |||
xlsx.SetCellValue(sheetName, "N"+rows, userRecord.CreateRepoCount) | |||
xlsx.SetCellValue(sheetName, "O"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex)) | |||
xlsx.SetCellValue(sheetName, "P"+rows, userRecord.RegistDate.Format("2006-01-02")) | |||
xlsx.SetCellValue(sheetName, "Q"+rows, time.Unix(userRecord.CountDate, 0).Format("2006-01-02")) | |||
formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05") | |||
xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3]) | |||
formatTime = time.Unix(userRecord.CountDate, 0).Format("2006-01-02 15:04:05") | |||
xlsx.SetCellValue(sheetName, "Q"+rows, formatTime[0:len(formatTime)-3]) | |||
} | |||
//设置默认打开的表单 | |||
@@ -160,6 +162,10 @@ func QueryUserStaticDataPage(ctx *context.Context) { | |||
} | |||
} else { | |||
mapInterface := make(map[string]interface{}) | |||
re, count := models.QueryUserStaticDataPage(pageOpts) | |||
mapInterface["data"] = re | |||
mapInterface["count"] = count | |||
ctx.JSON(http.StatusOK, mapInterface) | |||
} | |||
} | |||
@@ -0,0 +1,42 @@ | |||
<footer> | |||
<div class="ui fluid container" style="padding: 0px 10px;"> | |||
<div class="ui grid"> | |||
<div class="sixteen wide mobile eight wide tablet eight wide computer column"> | |||
<div class="ui three column grid"> | |||
<div class="column ui vertical text menu"> | |||
<div class="header item">{{.i18n.Tr "custom.head.community"}}</div> | |||
<a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">{{.i18n.Tr "custom.foot.council"}}</a> | |||
<a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">{{.i18n.Tr "custom.foot.technical_committee"}}</a> | |||
<a href="https://openi.org.cn/html/Club/2019/0228/17.html" class="item">{{.i18n.Tr "custom.foot.join"}}</a> | |||
</div> | |||
<div class="column ui vertical text menu"> | |||
<div class="header item">{{.i18n.Tr "custom.foot.news"}}</div> | |||
<a href="https://openi.org.cn/html/news/dongtai/" class="item">{{.i18n.Tr "custom.foot.community_news"}}</a> | |||
<a href="https://openi.org.cn/html/news/huodong/" class="item">{{.i18n.Tr "custom.foot.member_news"}}</a> | |||
<a href="https://openi.org.cn/html/news/zixun/" class="item">{{.i18n.Tr "custom.foot.industry_advisory"}}</a> | |||
</div> | |||
<div class="column ui vertical text menu"> | |||
<div class="header item">{{.i18n.Tr "custom.foot.help"}}</div> | |||
<div class="ui language bottom floating slide up dropdown link item"> | |||
<i class="world icon"></i> | |||
<div class="text">{{.LangName}}</div> | |||
<div class="menu"> | |||
{{range .AllLangs}} | |||
<a lang="{{.Lang}}" class="item {{if eq $.Lang .Lang}}active selected{{end}}" href="{{if eq $.Lang .Lang}}#{{else}}{{$.Link}}?lang={{.Lang}}{{end}}">{{.Name}}</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{if .EnableSwagger}}<a href="/api/swagger" class="ui item">API</a>{{end}} | |||
{{template "custom/extra_links_footer" .}} | |||
</div> | |||
</div> | |||
</div> | |||
<div class="sixteen wide mobile eight wide tablet eight wide computer column" style=" margin:2.0rem 0"> | |||
{{.i18n.Tr "custom.foot.copyright"}} <a href="http://beian.miit.gov.cn/" target="_blank">京ICP备18004880号</a> | |||
<br> | |||
{{.i18n.Tr "Powered_by 鹏城实验室云脑、"}}<a href="https://www.trustie.net/" target="_blank">Trustie确实</a>{{.i18n.Tr "、gitea"}} | |||
<br> | |||
</div> | |||
</div> | |||
</div> | |||
</footer> |
@@ -0,0 +1,48 @@ | |||
{{/* | |||
<html> | |||
<body> | |||
<div> | |||
*/}} | |||
{{template "custom/body_inner_post" .}} | |||
</div> | |||
{{template "custom/body_outer_post" .}} | |||
{{template "base/footer_content_fluid" .}} | |||
<script src="{{StaticUrlPrefix}}/js/jquery.js?v={{MD5 AppVer}}"></script> | |||
{{if .RequireSimpleMDE}} | |||
<script src="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.js"></script> | |||
<script src="{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js"></script> | |||
<script src="{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js"></script> | |||
<script> | |||
CodeMirror.modeURL = "{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/%N/%N.js"; | |||
</script> | |||
{{end}} | |||
<!-- Third-party libraries --> | |||
{{if .RequireMinicolors}} | |||
<script src="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js"></script> | |||
{{end}} | |||
{{if .RequireU2F}} | |||
<script src="{{StaticUrlPrefix}}/vendor/plugins/u2f/index.js"></script> | |||
{{end}} | |||
{{if .EnableCaptcha}} | |||
{{if eq .CaptchaType "recaptcha"}} | |||
<script src='{{ URLJoin .RecaptchaURL "api.js"}}' async></script> | |||
{{end}} | |||
{{end}} | |||
{{if .RequireTribute}} | |||
<script src="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.min.js"></script> | |||
{{end}} | |||
{{if .PageIsHome}} | |||
<script rel="stylesheet" src="{{StaticUrlPrefix}}/vendor/plugins/jquery.particleground/jquery.particleground.min.js"></script> | |||
{{end}} | |||
<script src="{{StaticUrlPrefix}}/fomantic/semantic.min.js?v={{MD5 AppVer}}"></script> | |||
<script src="{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script> | |||
{{template "custom/footer" .}} | |||
</body> | |||
</html> | |||
@@ -0,0 +1,208 @@ | |||
<!DOCTYPE html> | |||
<html lang="{{.Language}}"> | |||
<head data-suburl="{{AppSubUrl}}"> | |||
<meta charset="utf-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<meta http-equiv="x-ua-compatible" content="ie=edge"> | |||
<title>{{if .Title}}{{.Title}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title> | |||
<link rel="manifest" href="{{AppSubUrl}}/manifest.json" crossorigin="use-credentials"> | |||
{{if UseServiceWorker}} | |||
<script> | |||
if ('serviceWorker' in navigator) { | |||
navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) { | |||
// Registration was successful | |||
console.info('ServiceWorker registration successful with scope: ', registration.scope); | |||
}, function(err) { | |||
// registration failed :( | |||
console.info('ServiceWorker registration failed: ', err); | |||
}); | |||
} | |||
</script> | |||
{{else}} | |||
<script> | |||
if ('serviceWorker' in navigator) { | |||
navigator.serviceWorker.getRegistrations().then(function(registrations) { | |||
registrations.forEach(function(registration) { | |||
registration.unregister(); | |||
console.info('ServiceWorker unregistered'); | |||
}); | |||
}); | |||
} | |||
</script> | |||
{{end}} | |||
<meta name="theme-color" content="{{ThemeColorMetaTag}}"> | |||
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" /> | |||
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" /> | |||
<meta name="keywords" content="{{MetaKeywords}}"> | |||
<meta name="referrer" content="no-referrer" /> | |||
<meta name="_csrf" content="{{.CsrfToken}}" /> | |||
{{if .IsSigned}} | |||
<meta name="_uid" content="{{.SignedUser.ID}}" /> | |||
{{end}} | |||
{{if .ContextUser}} | |||
<meta name="_context_uid" content="{{.ContextUser.ID}}" /> | |||
{{end}} | |||
{{if .SearchLimit}} | |||
<meta name="_search_limit" content="{{.SearchLimit}}" /> | |||
{{end}} | |||
{{if .GoGetImport}} | |||
<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}"> | |||
<meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}"> | |||
{{end}} | |||
<script> | |||
{{SafeJS `/* | |||
@licstart The following is the entire license notice for the | |||
JavaScript code in this page. | |||
Copyright (c) 2016 The Gitea Authors | |||
Copyright (c) 2015 The Gogs Authors | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
--- | |||
Licensing information for additional javascript libraries can be found at: | |||
{{StaticUrlPrefix}}/vendor/librejs.html | |||
@licend The above is the entire license notice | |||
for the JavaScript code in this page. | |||
*/`}} | |||
</script> | |||
<script> | |||
window.config = { | |||
AppSubUrl: '{{AppSubUrl}}', | |||
StaticUrlPrefix: '{{StaticUrlPrefix}}', | |||
csrf: '{{.CsrfToken}}', | |||
HighlightJS: {{if .RequireHighlightJS}}true{{else}}false{{end}}, | |||
Minicolors: {{if .RequireMinicolors}}true{{else}}false{{end}}, | |||
SimpleMDE: {{if .RequireSimpleMDE}}true{{else}}false{{end}}, | |||
Tribute: {{if .RequireTribute}}true{{else}}false{{end}}, | |||
U2F: {{if .RequireU2F}}true{{else}}false{{end}}, | |||
Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}}, | |||
heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}}, | |||
NotificationSettings: { | |||
MinTimeout: {{NotificationSettings.MinTimeout}}, | |||
TimeoutStep: {{NotificationSettings.TimeoutStep}}, | |||
MaxTimeout: {{NotificationSettings.MaxTimeout}}, | |||
EventSourceUpdateTime: {{NotificationSettings.EventSourceUpdateTime}}, | |||
}, | |||
{{if .RequireTribute}} | |||
tributeValues: [ | |||
{{ range .Assignees }} | |||
{key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', | |||
name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.RelAvatarLink}}'}, | |||
{{ end }} | |||
], | |||
{{end}} | |||
}; | |||
</script> | |||
<link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png"> | |||
<link rel="mask-icon" href="{{StaticUrlPrefix}}/img/openi-safari.svg" color="#609926"> | |||
<link rel="fluid-icon" href="{{StaticUrlPrefix}}/img/gitea-lg.png" title="{{AppName}}"> | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css"> | |||
<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/icons.woff2" type="font/woff2" crossorigin="anonymous"> | |||
<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/outline-icons.woff2" type="font/woff2" crossorigin="anonymous"> | |||
{{if .RequireSimpleMDE}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | |||
{{end}} | |||
{{if .RequireTribute}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css"> | |||
{{end}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}"> | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}"> | |||
<noscript> | |||
<style> | |||
.dropdown:hover > .menu { display: block; } | |||
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; } | |||
</style> | |||
</noscript> | |||
{{if .RequireMinicolors}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css"> | |||
{{end}} | |||
<style class="list-search-style"></style> | |||
{{if .PageIsUserProfile}} | |||
<meta property="og:title" content="{{.Owner.Name}}" /> | |||
<meta property="og:type" content="profile" /> | |||
<meta property="og:image" content="{{.Owner.AvatarLink}}" /> | |||
<meta property="og:url" content="{{.Owner.HTMLURL}}" /> | |||
{{if .Owner.Description}} | |||
<meta property="og:description" content="{{.Owner.Description}}"> | |||
{{end}} | |||
{{else if .Repository}} | |||
{{if .Issue}} | |||
<meta property="og:title" content="{{.Issue.Title}}" /> | |||
<meta property="og:url" content="{{.Issue.HTMLURL}}" /> | |||
{{if .Issue.Content}} | |||
<meta property="og:description" content="{{.Issue.Content}}" /> | |||
{{end}} | |||
{{else}} | |||
<meta property="og:title" content="{{.Repository.Name}}" /> | |||
<meta property="og:url" content="{{.Repository.HTMLURL}}" /> | |||
{{if .Repository.Description}} | |||
<meta property="og:description" content="{{.Repository.Description}}" /> | |||
{{end}} | |||
{{end}} | |||
<meta property="og:type" content="object" /> | |||
<meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" /> | |||
{{else}} | |||
<meta property="og:title" content="{{AppName}}"> | |||
<meta property="og:type" content="website" /> | |||
<meta property="og:image" content="{{StaticUrlPrefix}}/img/gitea-lg.png" /> | |||
<meta property="og:url" content="{{AppUrl}}" /> | |||
<meta property="og:description" content="{{MetaDescription}}"> | |||
{{end}} | |||
<meta property="og:site_name" content="{{AppName}}" /> | |||
{{if .IsSigned }} | |||
{{ if ne .SignedUser.Theme "gitea" }} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}"> | |||
{{end}} | |||
{{else if ne DefaultTheme "gitea"}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | |||
{{end}} | |||
{{template "custom/header" .}} | |||
<script> | |||
var _hmt = _hmt || []; | |||
(function() { | |||
var hm = document.createElement("script"); | |||
hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; | |||
var s = document.getElementsByTagName("script")[0]; | |||
s.parentNode.insertBefore(hm, s); | |||
})(); | |||
</script> | |||
<script src="/self/func.js" type="text/javascript"></script> | |||
</head> | |||
<body> | |||
{{template "custom/body_outer_pre" .}} | |||
<div class="full height"> | |||
<noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | |||
{{template "custom/body_inner_pre" .}} | |||
{{if not .PageIsInstall}} | |||
<div class="ui top secondary stackable main menu following bar dark"> | |||
{{template "base/head_navbar_fluid" .}} | |||
</div><!-- end bar --> | |||
{{end}} | |||
{{/* | |||
</div> | |||
</body> | |||
</html> | |||
*/}} |
@@ -0,0 +1,177 @@ | |||
<div class="ui fluid container" style = "padding: 0px 20px;" id="navbar"> | |||
<div class="item brand" style="justify-content: space-between;"> | |||
<a href="https://openi.org.cn/"> | |||
<img class="ui mini image" src="{{StaticUrlPrefix}}/img/logo-w.svg"> | |||
</a> | |||
<div class="ui basic icon button mobile-only" id="navbar-expand-toggle"> | |||
<i class="sidebar icon"></i> | |||
</div> | |||
</div> | |||
<div style="width:1px;background:#606266;height:80%;margin:auto 0.5rem"></div> | |||
<div class="item brand" style="margin-left: 0.9rem;"> | |||
<a href="/"> | |||
<img class="ui mini image" style="height: 1.3rem;" src="{{StaticUrlPrefix}}/img/git-logo.svg"> | |||
</a> | |||
</div> | |||
{{if .IsSigned}} | |||
<a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a> | |||
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a> | |||
{{if not .UnitIssuesGlobalDisabled}} | |||
<a class="item {{if .PageIsIssues}}active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> | |||
{{end}} | |||
{{if not .UnitPullsGlobalDisabled}} | |||
<a class="item {{if .PageIsPulls}}active{{end}}" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a> | |||
{{end}} | |||
{{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}} | |||
{{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}} | |||
{{end}} | |||
<div class="ui dropdown item"> | |||
{{.i18n.Tr "explore"}} | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a> | |||
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a> | |||
<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> | |||
{{if .IsAdmin}} | |||
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{else if .IsLandingPageHome}} | |||
<a class="item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/dashboard">{{.i18n.Tr "home"}}</a> | |||
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a> | |||
<div class="ui dropdown item"> | |||
{{.i18n.Tr "explore"}} | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a> | |||
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a> | |||
<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> | |||
{{if .IsAdmin}} | |||
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{else if .IsLandingPageExplore}} | |||
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "home"}}</a> | |||
{{else if .IsLandingPageOrganizations}} | |||
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "home"}}</a> | |||
{{end}} | |||
{{template "custom/extra_links" .}} | |||
{{/* | |||
<div class="item"> | |||
<div class="ui icon input"> | |||
<input class="searchbox" type="text" placeholder="{{.i18n.Tr "search_project"}}"> | |||
<i class="search icon"></i> | |||
</div> | |||
</div> | |||
*/}} | |||
{{if .IsSigned}} | |||
<div class="right stackable menu"> | |||
<a href="{{AppSubUrl}}/notifications" class="item poping up" data-content='{{.i18n.Tr "notifications"}}' data-variation="tiny inverted"> | |||
<span class="text"> | |||
<span class="fitted">{{svg "octicon-bell" 16}}</span> | |||
<span class="sr-mobile-only">{{.i18n.Tr "notifications"}}</span> | |||
{{$notificationUnreadCount := 0}} | |||
{{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}} | |||
<span class="ui red label {{if not $notificationUnreadCount}}hidden{{end}} notification_count"> | |||
{{$notificationUnreadCount}} | |||
</span> | |||
</span> | |||
</a> | |||
<div class="ui dropdown jump item poping up" data-content="{{.i18n.Tr "create_new"}}" data-variation="tiny inverted"> | |||
<span class="text"> | |||
<span class="fitted">{{svg "octicon-plus" 16}}</span> | |||
<span class="sr-mobile-only">{{.i18n.Tr "create_new"}}</span> | |||
<span class="fitted not-mobile">{{svg "octicon-triangle-down" 16}}</span> | |||
</span> | |||
<div class="menu"> | |||
<a class="item" href="{{AppSubUrl}}/repo/create"> | |||
<span class="fitted">{{svg "octicon-plus" 16}}</span> {{.i18n.Tr "new_repo"}} | |||
</a> | |||
<a class="item" href="{{AppSubUrl}}/repo/migrate"> | |||
<span class="fitted">{{svg "octicon-repo-clone" 16}}</span> {{.i18n.Tr "new_migrate"}} | |||
</a> | |||
{{if .SignedUser.CanCreateOrganization}} | |||
<a class="item" href="{{AppSubUrl}}/org/create"> | |||
<span class="fitted">{{svg "octicon-organization" 16}}</span> {{.i18n.Tr "new_org"}} | |||
</a> | |||
{{end}} | |||
</div><!-- end content create new menu --> | |||
</div><!-- end dropdown menu create new --> | |||
<div class="ui dropdown jump item poping up" tabindex="-1" data-content="{{.i18n.Tr "user_profile_and_more"}}" data-variation="tiny inverted"> | |||
<span class="text"> | |||
<img class="ui tiny avatar image" width="24" height="24" src="{{.SignedUser.RelAvatarLink}}"> | |||
<span class="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span> | |||
<span class="mobile-only">{{.SignedUser.Name}}</span> | |||
<span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down" 16}}</span> | |||
</span> | |||
<div class="menu user-menu" tabindex="-1"> | |||
<div class="ui header"> | |||
{{.i18n.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong> | |||
</div> | |||
<div class="divider"></div> | |||
<a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}"> | |||
{{svg "octicon-person" 16}} | |||
{{.i18n.Tr "your_profile"}}<!-- Your profile --> | |||
</a> | |||
<a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}?tab=stars"> | |||
{{svg "octicon-star" 16}} | |||
{{.i18n.Tr "your_starred"}} | |||
</a> | |||
<a class="{{if .PageIsUserSettings}}active{{end}} item" href="{{AppSubUrl}}/user/settings"> | |||
{{svg "octicon-settings" 16}} | |||
{{.i18n.Tr "your_settings"}}<!-- Your settings --> | |||
</a> | |||
<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io"> | |||
{{svg "octicon-question" 16}} | |||
{{.i18n.Tr "help"}}<!-- Help --> | |||
</a--> | |||
{{if .IsAdmin}} | |||
<div class="divider"></div> | |||
<a class="{{if .PageIsAdmin}}active{{end}} item" href="{{AppSubUrl}}/admin"> | |||
<i class="icon settings"></i> | |||
{{.i18n.Tr "admin_panel"}}<!-- Admin Panel --> | |||
</a> | |||
{{end}} | |||
<div class="divider"></div> | |||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout" data-redirect="{{AppSubUrl}}/"> | |||
{{svg "octicon-sign-out" 16}} | |||
{{.i18n.Tr "sign_out"}}<!-- Sign Out --> | |||
</a> | |||
</div><!-- end content avatar menu --> | |||
</div><!-- end dropdown avatar menu --> | |||
</div><!-- end signed user right menu --> | |||
{{else}} | |||
<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a--> | |||
<div class="right stackable menu"> | |||
{{if .ShowRegistrationButton}} | |||
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up"> | |||
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
</a> | |||
{{end}} | |||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login"> | |||
{{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
</a> | |||
</div><!-- end anonymous right menu --> | |||
{{end}} | |||
</div> |
@@ -1,14 +1,15 @@ | |||
{{template "base/head" .}} | |||
{{template "base/head_fluid" .}} | |||
<div id="data_analysis" style="height: 100%;"> | |||
</div> | |||
{{template "base/footer" .}} | |||
{{template "base/footer_fluid" .}} | |||
<style> | |||
.full.height { | |||
flex-grow: 1; | |||
/* flex-grow: 1; */ | |||
padding-bottom: 53px; | |||
} | |||
</style> | |||
</style> |
@@ -11,7 +11,7 @@ | |||
{{.i18n.Tr "repo.cloudbrain"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
<a class="section" href="{{.RepoLink}}/cloudbrain"> | |||
<a class="section" href="{{.RepoLink}}/modelarts/notebook"> | |||
{{$.i18n.Tr "repo.modelarts.notebook"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
@@ -328,7 +328,7 @@ | |||
</div> | |||
<!-- 版本数量 --> | |||
<div class="one wide column text center padding0"> | |||
<span>{{.VersionCount}} </span> | |||
<span style="font-size: 12px;">{{.VersionCount}} </span> | |||
</div> | |||
<!-- 任务状态 --> | |||
<div class="two wide column padding0" style="padding-left: 2.2rem !important;"> | |||
@@ -8,13 +8,13 @@ | |||
<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="resetPage(),getAllProList('yesterday',1)">昨天</button> | |||
<button type="button" class='btnFirst' id ="yesterday" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getAllProList('yesterday',1)">昨天</button> | |||
<button type="button" class='btn' id = "current_week" v-bind:class="{colorChange:2==dynamic}" @click="resetPage(),getAllProList('current_week',2)">本周</button> | |||
<button type="button" class='btn' id = "current_month" v-bind:class="{colorChange:3==dynamic}" @click="resetPage(),getAllProList('current_month',3)">本月</button> | |||
<button type="button" class='btn' id = "last_month" v-bind:class="{colorChange:4==dynamic}" @click="resetPage(),getAllProList('last_month',4)">上月</button> | |||
<button type="button" class='btn' id = "monthly" v-bind:class="{colorChange:5==dynamic}" @click="resetPage(),getAllProList('monthly',5)">近30天</button> | |||
<button type="button" class='btn' id = "current_year" v-bind:class="{colorChange:6==dynamic}" @click="resetPage(),getAllProList('current_year',6)">今年</button> | |||
<button type="button" class='btn' id = "all" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getAllProList('all',7)">所有</button> | |||
<button type="button" class='btnLast' id = "all" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getAllProList('all',7)">所有</button> | |||
<span style="margin-left: 20px;"> | |||
<el-date-picker | |||
v-model="value_time" | |||
@@ -160,7 +160,7 @@ | |||
</div> | |||
<div id ="pro_detail" style="display:none;width: 100%;"> | |||
<div style="margin-top: 10px;"> | |||
<b class="pro_item">{{this.ownerName}} / {{this.pro_name}}</b> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{tableDataIDTotal.lastUpdatedTime}} / 从{{tableDataIDTotal.recordBeginTime}}开始统计</span> | |||
<a class="pro_item" :href="'../../../'+this.ownerName+'/'+this.pro_name">{{this.ownerName}} / {{this.pro_name}}</a> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{tableDataIDTotal.lastUpdatedTime}} / 从{{tableDataIDTotal.recordBeginTime}}开始统计</span> | |||
</div> | |||
<div style="margin-top: 10px;"> | |||
项目描述:{{tableDataIDTotal.description | discriptionFun}} | |||
@@ -207,44 +207,46 @@ | |||
</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="left" | |||
prop="user"> | |||
<template slot-scope="scope"> | |||
<a v-if="scope.row.mode!=-1" :href="AppSubUrl +'../../../'+ scope.row.user"><img class="ui avatar s16 image js-popover-card" :src="scope.row.relAvatarLink">{{scope.row.user}} </a> | |||
<a v-else :href="'mailto:'+ scope.row.email "> <img class="ui avatar s16 image js-popover-card" :avatar="scope.row.email"> {{scope.row.user}}</a> | |||
</template> | |||
</el-table-column> | |||
<el-table-column | |||
label="身份" | |||
align="center" | |||
prop="mode" | |||
v-if='0'> | |||
<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 style="margin:0 5px;"> | |||
<div style="font-size:14px;color:rgb(0,0,0);margin:10px 0px">贡献者TOP10</div> | |||
<div> | |||
<el-table | |||
:data="tableDataContTop10" | |||
style="width: 100%" | |||
stripe | |||
:header-cell-style="tableHeaderStyle" | |||
> | |||
<el-table-column | |||
label="用户名" | |||
align="left" | |||
prop="user"> | |||
<template slot-scope="scope"> | |||
<a v-if="scope.row.relAvatarLink!=''" :href="AppSubUrl +'../../../'+ scope.row.user"><img class="ui avatar s16 image js-popover-card" :src="scope.row.relAvatarLink">{{scope.row.user}} </a> | |||
<a v-else :href="'mailto:'+ scope.row.email "> <img class="ui avatar s16 image js-popover-card" :avatar="scope.row.email"> {{scope.row.user}}</a> | |||
</template> | |||
</el-table-column> | |||
<el-table-column | |||
label="身份" | |||
align="center" | |||
prop="mode" | |||
v-if='0'> | |||
<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> | |||
</div> | |||
</el-col> | |||
@@ -252,13 +254,13 @@ | |||
</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="resetCurrentPage(),getOneProList(pro_id,'yesterday',false,1)">昨天</button> | |||
<button type="button" class='btnFirst' id ="yesterday_pro" v-bind:class="{colorChange:1==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'yesterday',false,1)">昨天</button> | |||
<button type="button" class='btn' id = "current_week_pro" v-bind:class="{colorChange:2==dynamic_pro}" @click="resetCurrentPage(),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="resetCurrentPage(),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="resetCurrentPage(),getOneProList(pro_id,'last_month',false,4)">上月</button> | |||
<button type="button" class='btn' id = "monthly_pro" v-bind:class="{colorChange:5==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'monthly',false,5)">近30天</button> | |||
<button type="button" class='btn' id = "current_year_pro" v-bind:class="{colorChange:6==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'current_year',false,6)">今年</button> | |||
<button type="button" class='btn' id = "all_pro" v-bind:class="{colorChange:7==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'all',false,7)">所有</button> | |||
<button type="button" class='btnLast' id = "all_pro" v-bind:class="{colorChange:7==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'all',false,7)">所有</button> | |||
<span style="margin-left: 20px;"> | |||
<el-date-picker | |||
v-model="create_time_pro" | |||
@@ -788,6 +790,10 @@ | |||
}, | |||
slpitNumber:5, | |||
center: ['50%', '50%'], | |||
splitArea: { // 坐标轴在 grid 区域中的分隔区域 | |||
show: false, | |||
}, | |||
indicator: [{ | |||
name: '社区影响力', | |||
max: 100 | |||
@@ -1170,16 +1176,43 @@ | |||
color:rgba(187, 187, 187, 100); | |||
margin-left: 10px; | |||
} | |||
.btnFirst{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px 0px 0px 4px; | |||
} | |||
.btn{ | |||
line-height: 1.5; | |||
margin: -3px; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
width: 60px; | |||
height: 30px; | |||
} | |||
.btnLast{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
/* border-right: none; */ | |||
background: #FFFF; | |||
color: #409eff; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px ; | |||
border-radius:0px 4px 4px 0px; | |||
} | |||
.btnFirst, .btn, .btnLast { | |||
cursor: pointer; | |||
} | |||
/* | |||
.btn:focus, | |||
.btn:active{ | |||
@@ -1203,6 +1236,7 @@ | |||
.colorChange { | |||
background-color: #409effd6; | |||
color: #FFFF; | |||
cursor: default; | |||
} | |||
.items{ | |||
text-align: center; | |||
@@ -5,13 +5,13 @@ | |||
</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="resetPage(),getUserList('yesterday_usr',1)">昨天</button> | |||
<button type="button" class='btnFirst' id ="yesterday_usr" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getUserList('yesterday_usr',1)">昨天</button> | |||
<button type="button" class='btn' id = "current_week_usr" v-bind:class="{colorChange:2==dynamic}" @click="resetPage(),getUserList('current_week_usr',2)">本周</button> | |||
<button type="button" class='btn' id = "current_month_usr" v-bind:class="{colorChange:3==dynamic}" @click="resetPage(),getUserList('current_month_usr',3)">本月</button> | |||
<button type="button" class='btn' id = "last_month_usr" v-bind:class="{colorChange:4==dynamic}" @click="resetPage(),getUserList('last_month_usr',4)">上月</button> | |||
<button type="button" class='btn' id = "monthly_usr" v-bind:class="{colorChange:5==dynamic}" @click="resetPage(),getUserList('monthly_usr',5)">近30天</button> | |||
<button type="button" class='btn' id = "current_year_usr" v-bind:class="{colorChange:6==dynamic}" @click="resetPage(),getUserList('current_year_usr',6)">今年</button> | |||
<button type="button" class='btn' id = "all_usr" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getUserList('all_usr',7)">所有</button> | |||
<button type="button" class='btnLast' id = "all_usr" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getUserList('all_usr',7)">所有</button> | |||
<span style="margin-left: 20px;"> | |||
<el-date-picker | |||
v-model="value_time" | |||
@@ -462,7 +462,7 @@ | |||
color:rgba(187, 187, 187, 100); | |||
margin-left: 10px; | |||
} | |||
.btn{ | |||
/* .btn{ | |||
line-height: 1.5; | |||
margin: -3px; | |||
border: 1px solid #409effd6; | |||
@@ -471,7 +471,43 @@ | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px ; | |||
} */ | |||
.btnFirst{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px 0px 0px 4px; | |||
} | |||
.btn{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
width: 60px; | |||
height: 30px; | |||
} | |||
.btnLast{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
/* border-right: none; */ | |||
background: #FFFF; | |||
color: #409eff; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:0px 4px 4px 0px; | |||
} | |||
.btnFirst,.btn,.btnLast { | |||
cursor: pointer; | |||
} | |||
/* .btn:focus, | |||
.btn:active{ | |||
@@ -492,8 +528,9 @@ | |||
} | |||
.colorChange { | |||
background-color: #409effd6; | |||
color: #FFFF; | |||
background-color: #409effd6; | |||
color: #FFFF; | |||
cursor: default; | |||
} | |||
</style> |