@@ -523,17 +523,21 @@ DROP TRIGGER IF EXISTS es_udpate_repository_lang on public.language_stat; | |||
CREATE OR REPLACE FUNCTION public.udpate_repository_lang() RETURNS trigger AS | |||
$def$ | |||
DECLARE | |||
privateValue bigint; | |||
BEGIN | |||
if (TG_OP = 'UPDATE') then | |||
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id; | |||
select into privateValue updated_unix from public.repository where id=NEW.repo_id; | |||
update public.repository_es SET updated_unix=privateValue,lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id; | |||
elsif (TG_OP = 'INSERT') then | |||
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id; | |||
select into privateValue updated_unix from public.repository where id=NEW.repo_id; | |||
update public.repository_es SET updated_unix=privateValue,lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id; | |||
elsif (TG_OP = 'DELETE') then | |||
if exists(select 1 from public.repository where id=OLD.repo_id) then | |||
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=OLD.repo_id) where id=OLD.repo_id; | |||
end if; | |||
end if; | |||
return null; | |||
return NEW; | |||
END; | |||
$def$ | |||
LANGUAGE plpgsql; | |||
@@ -1554,6 +1554,11 @@ func GetAllMirrorRepositoriesCount() (int64, error) { | |||
return x.Where("is_mirror = ?", true).Count(repo) | |||
} | |||
func GetAllOrgRepositoriesCount() (int64, error) { | |||
repo := new(Repository) | |||
return x.Table("repository").Join("INNER", []string{"\"user\"", "u"}, "repository.owner_id = u.id and u.type=1").Count(repo) | |||
} | |||
func GetAllForkRepositoriesCount() (int64, error) { | |||
repo := new(Repository) | |||
return x.Where("is_fork = ?", true).Count(repo) | |||
@@ -2,6 +2,8 @@ package models | |||
import ( | |||
"fmt" | |||
"strconv" | |||
"time" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
) | |||
@@ -45,6 +47,7 @@ type SummaryStatistic struct { | |||
NumRepoFork int64 `xorm:"NOT NULL DEFAULT 0"` | |||
NumRepoMirror int64 `xorm:"NOT NULL DEFAULT 0"` | |||
NumRepoSelf int64 `xorm:"NOT NULL DEFAULT 0"` | |||
NumRepoOrg int64 `xorm:"NOT NULL DEFAULT 0"` | |||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
} | |||
@@ -69,6 +72,51 @@ func DeleteSummaryStatisticDaily(date string) error { | |||
return nil | |||
} | |||
func GetLatest2SummaryStatistic() ([]*SummaryStatistic, error) { | |||
summaryStatistics := make([]*SummaryStatistic, 0) | |||
err := xStatistic.Desc("created_unix").Limit(2).Find(&summaryStatistics) | |||
return summaryStatistics, err | |||
} | |||
func GetSummaryStatisticByTimeCount(beginTime time.Time, endTime time.Time) (int64, error) { | |||
summaryStatistics := new(SummaryStatistic) | |||
total, err := xStatistic.Asc("created_unix").Where("created_unix>=" + strconv.FormatInt(beginTime.Unix(), 10) + " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10)).Count(summaryStatistics) | |||
return total, err | |||
} | |||
func GetSummaryStatisticByDateCount(dates []string) (int64, error) { | |||
summaryStatistics := new(SummaryStatistic) | |||
total, err := xStatistic.Asc("created_unix").In("date", dates).Count(summaryStatistics) | |||
return total, err | |||
} | |||
func GetAllSummaryStatisticByTime(beginTime time.Time, endTime time.Time) ([]*SummaryStatistic, error) { | |||
summaryStatistics := make([]*SummaryStatistic, 0) | |||
err := xStatistic.Asc("created_unix").Where("created_unix>=" + strconv.FormatInt(beginTime.Unix(), 10) + " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10)).Find(&summaryStatistics) | |||
return summaryStatistics, err | |||
} | |||
func GetSummaryStatisticByTime(beginTime time.Time, endTime time.Time, page int, pageSize int) ([]*SummaryStatistic, error) { | |||
summaryStatistics := make([]*SummaryStatistic, 0) | |||
err := xStatistic.Asc("created_unix").Limit(pageSize+1, (page-1)*pageSize).Where("created_unix>=" + strconv.FormatInt(beginTime.Unix(), 10) + " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10)).Find(&summaryStatistics) | |||
return summaryStatistics, err | |||
} | |||
func GetAllSummaryStatisticByDates(dates []string) ([]*SummaryStatistic, error) { | |||
summaryStatistics := make([]*SummaryStatistic, 0) | |||
err := xStatistic.Asc("created_unix").In("date", dates).Find(&summaryStatistics) | |||
return summaryStatistics, err | |||
} | |||
func GetSummaryStatisticByDates(dates []string, page int, pageSize int) ([]*SummaryStatistic, error) { | |||
summaryStatistics := make([]*SummaryStatistic, 0) | |||
err := xStatistic.Asc("created_unix").In("date", dates).Limit(pageSize+1, (page-1)*pageSize).Find(&summaryStatistics) | |||
return summaryStatistics, err | |||
} | |||
func InsertSummaryStatistic(summaryStatistic *SummaryStatistic) (int64, error) { | |||
return xStatistic.Insert(summaryStatistic) | |||
} |
@@ -4,6 +4,7 @@ import ( | |||
"fmt" | |||
"sort" | |||
"strconv" | |||
"strings" | |||
"time" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -227,7 +228,27 @@ func getLastCountDate() int64 { | |||
return pageStartTime.Unix() | |||
} | |||
func QueryMetrics(start int64, end int64) ([]*UserMetrics, int64) { | |||
func QueryMetricsPage(start int64, end int64, page int, pageSize int) ([]*UserMetrics, int64) { | |||
statictisSess := xStatistic.NewSession() | |||
defer statictisSess.Close() | |||
cond := "count_date >" + fmt.Sprint(start) + " and count_date<" + fmt.Sprint(end) | |||
allCount, err := statictisSess.Where(cond).Count(new(UserMetrics)) | |||
if err != nil { | |||
log.Info("query error." + err.Error()) | |||
return nil, 0 | |||
} | |||
userMetricsList := make([]*UserMetrics, 0) | |||
//.Limit(pageSize, page*pageSize) | |||
if err := statictisSess.Table(new(UserMetrics)).Where(cond).OrderBy("count_date desc"). | |||
Find(&userMetricsList); err != nil { | |||
return nil, 0 | |||
} | |||
postDeal(userMetricsList) | |||
return userMetricsList, allCount | |||
} | |||
func QueryMetrics(start int64, end int64) ([]*UserMetrics, int) { | |||
statictisSess := xStatistic.NewSession() | |||
defer statictisSess.Close() | |||
userMetricsList := make([]*UserMetrics, 0) | |||
@@ -235,7 +256,76 @@ func QueryMetrics(start int64, end int64) ([]*UserMetrics, int64) { | |||
Find(&userMetricsList); err != nil { | |||
return nil, 0 | |||
} | |||
return userMetricsList, int64(len(userMetricsList)) | |||
postDeal(userMetricsList) | |||
return userMetricsList, len(userMetricsList) | |||
} | |||
func postDeal(userMetricsList []*UserMetrics) { | |||
for _, userMetrics := range userMetricsList { | |||
userMetrics.DisplayDate = userMetrics.DataDate | |||
userMetrics.TotalRegistUser = userMetrics.ActivateRegistUser + userMetrics.NotActivateRegistUser | |||
userMetrics.TotalNotActivateRegistUser = userMetrics.TotalUser - userMetrics.TotalActivateRegistUser | |||
} | |||
} | |||
func QueryMetricsForAll() []*UserMetrics { | |||
statictisSess := xStatistic.NewSession() | |||
defer statictisSess.Close() | |||
userMetricsList := make([]*UserMetrics, 0) | |||
if err := statictisSess.Table(new(UserMetrics)).OrderBy("count_date desc"). | |||
Find(&userMetricsList); err != nil { | |||
return nil | |||
} | |||
return makeResultForMonth(userMetricsList, len(userMetricsList)) | |||
} | |||
func QueryMetricsForYear() []*UserMetrics { | |||
currentTimeNow := time.Now() | |||
currentYearEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
currentYearStartTime := time.Date(currentTimeNow.Year(), 1, 1, 0, 0, 0, 0, currentTimeNow.Location()) | |||
allUserInfo, count := QueryMetrics(currentYearStartTime.Unix(), currentYearEndTime.Unix()) | |||
return makeResultForMonth(allUserInfo, count) | |||
} | |||
func makeResultForMonth(allUserInfo []*UserMetrics, count int) []*UserMetrics { | |||
monthMap := make(map[string]*UserMetrics) | |||
if count > 0 { | |||
for _, userMetrics := range allUserInfo { | |||
dateTime := time.Unix(userMetrics.CountDate, 0) | |||
month := fmt.Sprint(dateTime.Year()) + "-" + fmt.Sprint(int(dateTime.Month())) | |||
if _, ok := monthMap[month]; !ok { | |||
monthUserMetrics := &UserMetrics{ | |||
DisplayDate: month, | |||
ActivateRegistUser: userMetrics.ActivateRegistUser, | |||
NotActivateRegistUser: userMetrics.NotActivateRegistUser, | |||
TotalUser: userMetrics.TotalUser, | |||
TotalNotActivateRegistUser: userMetrics.TotalUser - userMetrics.TotalActivateRegistUser, | |||
TotalActivateRegistUser: userMetrics.TotalActivateRegistUser, | |||
TotalHasActivityUser: userMetrics.TotalHasActivityUser, | |||
HasActivityUser: userMetrics.HasActivityUser, | |||
DaysForMonth: 1, | |||
TotalRegistUser: userMetrics.ActivateRegistUser + userMetrics.NotActivateRegistUser, | |||
} | |||
monthMap[month] = monthUserMetrics | |||
} else { | |||
value := monthMap[month] | |||
value.ActivateRegistUser += userMetrics.ActivateRegistUser | |||
value.NotActivateRegistUser += userMetrics.NotActivateRegistUser | |||
value.HasActivityUser += userMetrics.HasActivityUser | |||
value.TotalRegistUser += userMetrics.TotalRegistUser | |||
value.DaysForMonth += 1 | |||
} | |||
} | |||
} | |||
result := make([]*UserMetrics, 0) | |||
for _, value := range monthMap { | |||
result = append(result, value) | |||
} | |||
sort.Slice(result, func(i, j int) bool { | |||
return strings.Compare(result[i].DisplayDate, result[j].DisplayDate) > 0 | |||
}) | |||
return result | |||
} | |||
func QueryRankList(key string, tableName string, limit int) ([]*UserBusinessAnalysisAll, int64) { | |||
@@ -540,6 +630,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS | |||
if minUserIndex > dateRecordAll.UserIndexPrimitive { | |||
minUserIndex = dateRecordAll.UserIndexPrimitive | |||
} | |||
dateRecordBatch = append(dateRecordBatch, dateRecordAll) | |||
if len(dateRecordBatch) >= BATCH_INSERT_SIZE { | |||
insertTable(dateRecordBatch, tableName, statictisSess) | |||
@@ -695,7 +786,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||
log.Info("query user error. return.") | |||
return err | |||
} | |||
userNewAddActivity := make(map[int64]map[int64]int64) | |||
ParaWeight := getParaWeight() | |||
userMetrics := make(map[string]int) | |||
var indexTotal int64 | |||
@@ -767,6 +858,9 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||
dateRecord.UserIndexPrimitive = getUserIndex(dateRecord, ParaWeight) | |||
setUserMetrics(userMetrics, userRecord, start_unix, end_unix, dateRecord) | |||
if getUserActivate(dateRecord) > 0 { | |||
addUserToMap(userNewAddActivity, userRecord.CreatedUnix, dateRecord.ID) | |||
} | |||
_, err = statictisSess.Insert(&dateRecord) | |||
if err != nil { | |||
log.Info("insert daterecord failed." + err.Error()) | |||
@@ -785,18 +879,71 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||
//insert userMetrics table | |||
var useMetrics UserMetrics | |||
useMetrics.CountDate = CountDate.Unix() | |||
useMetrics.DataDate = DataDate | |||
statictisSess.Delete(&useMetrics) | |||
useMetrics.ActivateRegistUser = getMapKeyStringValue("ActivateRegistUser", userMetrics) | |||
useMetrics.HasActivityUser = getMapKeyStringValue("HasActivityUser", userMetrics) | |||
useMetrics.RegistActivityUser = 0 | |||
useMetrics.NotActivateRegistUser = getMapKeyStringValue("NotActivateRegistUser", userMetrics) | |||
useMetrics.TotalActivateRegistUser = getMapKeyStringValue("TotalActivateRegistUser", userMetrics) | |||
useMetrics.TotalHasActivityUser = getMapKeyStringValue("TotalHasActivityUser", userMetrics) | |||
statictisSess.Insert(&useMetrics) | |||
count, err = sess.Where("type=0").Count(new(User)) | |||
if err != nil { | |||
log.Info("query user error. return.") | |||
} | |||
useMetrics.TotalUser = int(count) | |||
if useMetrics.ActivateRegistUser+useMetrics.NotActivateRegistUser == 0 { | |||
useMetrics.ActivateIndex = 0 | |||
} else { | |||
useMetrics.ActivateIndex = float64(useMetrics.ActivateRegistUser) / float64(useMetrics.ActivateRegistUser+useMetrics.NotActivateRegistUser) | |||
} | |||
statictisSess.Insert(&useMetrics) | |||
//update new user activity | |||
updateNewUserAcitivity(userNewAddActivity, statictisSess) | |||
return nil | |||
} | |||
func updateNewUserAcitivity(currentUserActivity map[int64]map[int64]int64, statictisSess *xorm.Session) { | |||
for key, value := range currentUserActivity { | |||
useMetrics := &UserMetrics{CountDate: key} | |||
has, err := statictisSess.Get(useMetrics) | |||
if err == nil && has { | |||
userIdArrays := strings.Split(useMetrics.HasActivityUserJson, ",") | |||
for _, userIdStr := range userIdArrays { | |||
userIdInt, err := strconv.ParseInt(userIdStr, 10, 64) | |||
if err == nil { | |||
value[userIdInt] = userIdInt | |||
} | |||
} | |||
userIdArray := "" | |||
for _, tmpValue := range value { | |||
userIdArray += fmt.Sprint(tmpValue) + "," | |||
} | |||
useMetrics.HasActivityUser = len(value) | |||
if len(userIdArray) > 0 { | |||
useMetrics.HasActivityUserJson = userIdArray[0 : len(userIdArray)-1] | |||
} | |||
updateSql := "update public.user_metrics set has_activity_user_json=" + useMetrics.HasActivityUserJson + ",regist_activity_user=" + fmt.Sprint(useMetrics.HasActivityUser) + " where count_date=" + fmt.Sprint(key) | |||
statictisSess.Exec(updateSql) | |||
} | |||
} | |||
} | |||
func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate timeutil.TimeStamp, userId int64) { | |||
CountDateTime := time.Date(registDate.Year(), registDate.AsTime().Month(), registDate.AsTime().Day(), 0, 1, 0, 0, registDate.AsTime().Location()) | |||
CountDate := CountDateTime.Unix() | |||
if _, ok := currentUserActivity[CountDate]; !ok { | |||
userIdMap := make(map[int64]int64, 0) | |||
userIdMap[userId] = userId | |||
currentUserActivity[CountDate] = userIdMap | |||
} else { | |||
currentUserActivity[CountDate][userId] = userId | |||
} | |||
} | |||
func setUserMetrics(userMetrics map[string]int, user *User, start_time int64, end_time int64, dateRecord UserBusinessAnalysis) { | |||
//ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
//NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
@@ -400,10 +400,19 @@ type UserAnalysisPara struct { | |||
} | |||
type UserMetrics struct { | |||
CountDate int64 `xorm:"pk"` | |||
ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
HasActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
TotalActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
TotalHasActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
CountDate int64 `xorm:"pk"` | |||
ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
ActivateIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||
RegistActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
HasActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
TotalUser int `xorm:"NOT NULL DEFAULT 0"` | |||
TotalRegistUser int `xorm:"-"` | |||
TotalActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
TotalNotActivateRegistUser int `xorm:"-"` | |||
TotalHasActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
DisplayDate string `xorm:"-"` | |||
DataDate string `xorm:"NULL"` | |||
DaysForMonth int `xorm:"NOT NULL DEFAULT 0"` | |||
HasActivityUserJson string `xorm:"text NULL"` | |||
} |
@@ -740,3 +740,9 @@ type CreateCourseForm struct { | |||
func (f *CreateCourseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
return validate(errs, ctx.Data, f, ctx.Locale) | |||
} | |||
// RenameRepoFileForm form for renaming repository file | |||
type RenameRepoFileForm struct { | |||
TreePath string `binding:"Required;MaxSize(500)"` | |||
LastCommit string | |||
} |
@@ -109,6 +109,34 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro | |||
return filelist, nil | |||
} | |||
// LsFilesStage list all files with stage format in index for the given paths | |||
// if the given path is directory ,then return all files under it | |||
// if the given path is file ,then return the file | |||
func (t *TemporaryUploadRepository) LsFilesStage(paths ...string) ([]string, error) { | |||
stdOut := new(bytes.Buffer) | |||
stdErr := new(bytes.Buffer) | |||
cmdArgs := []string{"ls-files", "-z", "-s", "--"} | |||
for _, arg := range paths { | |||
if arg != "" { | |||
cmdArgs = append(cmdArgs, arg) | |||
} | |||
} | |||
if err := git.NewCommand(cmdArgs...).RunInDirPipeline(t.basePath, stdOut, stdErr); err != nil { | |||
log.Error("Unable to run git ls-files for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) | |||
err = fmt.Errorf("Unable to run git ls-files for temporary repo of: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) | |||
return nil, err | |||
} | |||
filelist := make([]string, 0) | |||
for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) { | |||
filelist = append(filelist, string(line)) | |||
} | |||
return filelist, nil | |||
} | |||
// RemoveFilesFromIndex removes the given files from the index | |||
func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) error { | |||
stdOut := new(bytes.Buffer) | |||
@@ -756,3 +756,210 @@ func createCommitRepoActions(repo *models.Repository, gitRepo *git.Repository, o | |||
} | |||
return actions, nil | |||
} | |||
// RenameRepoFileOptions | |||
type RenameRepoFileOptions struct { | |||
LastCommitID string | |||
BranchName string | |||
TreePath string | |||
FromTreePath string | |||
Message string | |||
Author *IdentityOptions | |||
Committer *IdentityOptions | |||
} | |||
// RenameRepoFile rename file in the given repository | |||
func RenameRepoFile(repo *models.Repository, doer *models.User, opts *RenameRepoFileOptions) error { | |||
// Branch must exist for this operation | |||
if _, err := repo_module.GetBranch(repo, opts.BranchName); err != nil { | |||
return err | |||
} | |||
//make sure user can commit to the given branch | |||
if err := checkBranchProtection(doer, repo, opts.BranchName, opts.TreePath); err != nil { | |||
return err | |||
} | |||
// Check that the path given in opts.treePath is valid (not a git path) | |||
treePath := CleanUploadFileName(opts.TreePath) | |||
if treePath == "" { | |||
return models.ErrFilenameInvalid{ | |||
Path: opts.TreePath, | |||
} | |||
} | |||
// If there is a fromTreePath (we are copying it), also clean it up | |||
fromTreePath := CleanUploadFileName(opts.FromTreePath) | |||
if fromTreePath == "" && opts.FromTreePath != "" { | |||
return models.ErrFilenameInvalid{ | |||
Path: opts.FromTreePath, | |||
} | |||
} | |||
author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) | |||
t, err := NewTemporaryUploadRepository(repo) | |||
if err != nil { | |||
log.Error("%v", err) | |||
} | |||
defer t.Close() | |||
if err := t.Clone(opts.BranchName); err != nil { | |||
return err | |||
} | |||
if err := t.SetDefaultIndex(); err != nil { | |||
return err | |||
} | |||
// Get the commit of the original branch | |||
commit, err := t.GetBranchCommit(opts.BranchName) | |||
if err != nil { | |||
return err // Couldn't get a commit for the branch | |||
} | |||
lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID) | |||
if err != nil { | |||
return fmt.Errorf("DeleteRepoFile: Invalid last commit ID: %v", err) | |||
} | |||
opts.LastCommitID = lastCommitID.String() | |||
if opts.LastCommitID == "" { | |||
// When updating a file, a lastCommitID needs to be given to make sure other commits | |||
// haven't been made. We throw an error if one wasn't provided. | |||
return models.ErrSHAOrCommitIDNotProvided{} | |||
} | |||
//if fromTreePath not exist,return error | |||
_, err = commit.GetTreeEntryByPath(fromTreePath) | |||
if err != nil { | |||
return err | |||
} | |||
// If a lastCommitID was given and it doesn't match the commitID of the head of the branch throw | |||
// an error. | |||
if commit.ID.String() != opts.LastCommitID { | |||
if changed, err := commit.FileChangedSinceCommit(fromTreePath, opts.LastCommitID); err != nil { | |||
return err | |||
} else if changed { | |||
return models.ErrCommitIDDoesNotMatch{ | |||
GivenCommitID: opts.LastCommitID, | |||
CurrentCommitID: opts.LastCommitID, | |||
} | |||
} | |||
} | |||
//if treePath has been exist,return error | |||
_, err = commit.GetTreeEntryByPath(treePath) | |||
if err == nil || !git.IsErrNotExist(err) { | |||
// Means the file has been exist in new path | |||
return models.ErrFilePathInvalid{ | |||
Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", treePath), | |||
Path: treePath, | |||
Name: treePath, | |||
Type: git.EntryModeBlob, | |||
} | |||
} | |||
//move and add files to index | |||
if err = moveAndAddFiles(fromTreePath, treePath, t); err != nil { | |||
return err | |||
} | |||
// Now write the tree | |||
treeHash, err := t.WriteTree() | |||
if err != nil { | |||
return err | |||
} | |||
// Now commit the tree | |||
message := strings.TrimSpace(opts.Message) | |||
commitHash, err := t.CommitTree(author, committer, treeHash, message) | |||
if err != nil { | |||
return err | |||
} | |||
// Then push this tree to NewBranch | |||
if err := t.Push(doer, commitHash, opts.BranchName); err != nil { | |||
log.Error("%T %v", err, err) | |||
return err | |||
} | |||
return nil | |||
} | |||
func checkBranchProtection(doer *models.User, repo *models.Repository, branchName, treePath string) error { | |||
//make sure user can commit to the given branch | |||
protectedBranch, err := repo.GetBranchProtection(branchName) | |||
if err != nil { | |||
return err | |||
} | |||
if protectedBranch != nil { | |||
if !protectedBranch.CanUserPush(doer.ID) { | |||
return models.ErrUserCannotCommit{ | |||
UserName: doer.LowerName, | |||
} | |||
} | |||
if protectedBranch.RequireSignedCommits { | |||
_, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), branchName) | |||
if err != nil { | |||
if !models.IsErrWontSign(err) { | |||
return err | |||
} | |||
return models.ErrUserCannotCommit{ | |||
UserName: doer.LowerName, | |||
} | |||
} | |||
} | |||
patterns := protectedBranch.GetProtectedFilePatterns() | |||
for _, pat := range patterns { | |||
if pat.Match(strings.ToLower(treePath)) { | |||
return models.ErrFilePathProtected{ | |||
Path: treePath, | |||
} | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
func moveAndAddFiles(oldTreePath, newTreePath string, t *TemporaryUploadRepository) error { | |||
array, err := t.LsFilesStage(oldTreePath) | |||
if err != nil { | |||
return err | |||
} | |||
if len(array) == 0 { | |||
return git.ErrNotExist{RelPath: oldTreePath} | |||
} | |||
stdOut := new(bytes.Buffer) | |||
stdErr := new(bytes.Buffer) | |||
stdIn := new(bytes.Buffer) | |||
//write all files in stage format to the stdin, | |||
//for each file,remove old tree path and add new tree path | |||
//see the update-index help document at https://git-scm.com/docs/git-update-index | |||
//especially see the content of "USING --INDEX-INFO" | |||
for _, v := range array { | |||
if v == "" { | |||
continue | |||
} | |||
//example for v(mode SHA-1 stage file) | |||
//100755 d294c88235ac05d3dece028d8a65590f28ec46ac 0 custom/conf/app.ini | |||
v = strings.ReplaceAll(v, "0\t", "") | |||
tmpArray := strings.Split(v, " ") | |||
oldPath := tmpArray[2] | |||
newPath := newTreePath + strings.TrimPrefix(oldPath, oldTreePath) | |||
// mode 0 means remove file | |||
stdIn.WriteString("0 0000000000000000000000000000000000000000\t") | |||
stdIn.WriteString(oldPath) | |||
stdIn.WriteByte('\000') | |||
stdIn.WriteString(tmpArray[0] + " ") | |||
stdIn.WriteString(tmpArray[1] + "\t") | |||
stdIn.WriteString(newPath) | |||
stdIn.WriteByte('\000') | |||
} | |||
if err := git.NewCommand("update-index", "--replace", "-z", "--index-info").RunInDirFullPipeline(t.basePath, stdOut, stdErr, stdIn); err != nil { | |||
log.Error("Unable to update-index for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) | |||
return fmt.Errorf("Unable to update-index for temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) | |||
} | |||
return nil | |||
} |
@@ -36,7 +36,7 @@ func getHookTemplates() (hookNames, hookTpls, giteaHookTpls, sizeLimitTpls []str | |||
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf), | |||
} | |||
sizeLimitTpls = []string{ | |||
fmt.Sprintf("#!/usr/bin/env %s\n\n\nset -o pipefail\n\nreadonly DEFAULT_FILE_MAXSIZE_MB=\"30\" \nreadonly CONFIG_NAME=\"hooks.maxfilesize\"\nreadonly NULLSHA=\"0000000000000000000000000000000000000000\"\nreadonly EXIT_SUCCESS=0\nreadonly EXIT_FAILURE=1\nreadonly DEFAULT_REPO_MAXSIZE_MB=\"1024\" \nreadonly CHECK_FLAG_ON=1\n\n\nstatus=\"$EXIT_SUCCESS\"\n\n# skip this hook entirely if shell check is not open\ncheck_flag=${PUSH_SIZE_CHECK_FLAG}\nif [[ $check_flag != $CHECK_FLAG_ON ]]; then\nexit $EXIT_SUCCESS\nfi\n\n\n#######################################\n# check the file max size limit\n#######################################\n\n# get maximum filesize (from repository-specific config)\nmaxsize_mb=\"${REPO_MAX_FILE_SIZE}\"\n\nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\necho \"failed to get ${CONFIG_NAME} from config\"\nexit \"$EXIT_FAILURE\"\nfi\n\npush_size=\"0\"\n# read lines from stdin (format: \"<oldref> <newref> <refname>\\n\")\nwhile read oldref newref refname; do\n# skip branch deletions\nif [[ \"$newref\" == \"$NULLSHA\" ]]; then\n continue\nfi\n\n# find large objects\n# check all objects from $oldref (possible $NULLSHA) to $newref, but\n# skip all objects that have already been accepted (i.e. are referenced by\n# another branch or tag).\n\nif [[ \"$oldref\" == \"$NULLSHA\" ]]; then\n target=\"$newref\"\nelse\n target=\"${oldref}..${newref}\"\nfi\nmaxsize=`expr $maxsize_mb \\* 1048576` \n\n# find objects in this push_size\n# print like:\n# 08da8e2ab9ae4095bf94dd71ac913132b880b463 commit 214\n# 43e993b768ede5740e8c65de2ed6edec25053ea1 tree 185\n# 4476971d76569039df7569af1b8d03c288f6b193 blob 20167318 b0417e6593a1.zip\nfiles=\"$(git rev-list --objects \"$target\" --tags=\\* | \\\n git cat-file $'--batch-check=%%(objectname) %%(objecttype) %%(objectsize) %%(rest)')\"\n \nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\n echo \"failed to check for large files in ref ${refname}\"\n continue\nfi\n\n# rewrite IFS to seperate line in $files\nIFS=$'\\n'\nfor file in $files; do\n # if don't unset IFS,temp_array=(${file}) will get error answer\n unset IFS\n temp_array=(${file})\n # add all commit files size\n push_size=`expr $push_size + ${temp_array[2]}`\n if [[ ${temp_array[2]} -gt $maxsize ]]; then\n\t if [[ \"$status\" == $EXIT_SUCCESS ]]; then\n\t\techo -e \"Error: Your push was rejected because it contains files larger than $(numfmt --to=iec \"$maxsize_mb\") Mb\"\n\t\techo \"oversize files:\"\n\t\tstatus=\"$EXIT_FAILURE\"\n\t fi\n\t echo -e \"\\033[31m- ${temp_array[3]} \\033[0m (ref: ${refname}) \"\n fi\ndone\n\nif [[ \"$status\" != $EXIT_SUCCESS ]]; then\n\texit \"$status\"\nfi\n\ndone\n\n#######################################\n# check the repo max size limit\n#######################################\nif [[ $push_size -eq \"0\" ]]; then\n\texit $EXIT_SUCCESS\nfi\n\n\nsizelimit_mb=\"${REPO_MAX_SIZE}\"\nlet sizelimit_b=$sizelimit_mb*1024*1024\n\n# repo size at here means the size of repo directory in server \nreposize_b=${REPO_CURRENT_SIZE}\n\ntotal=`expr $push_size + $reposize_b`\n\nif [ $total -gt $sizelimit_b ]; then\n echo \"Error: Your push was rejected because the repository size is large than $sizelimit_mb Mb\"\n exit $EXIT_FAILURE\nfi\n\n\nexit $EXIT_SUCCESS\n", setting.ScriptType, setting.CustomConf), | |||
fmt.Sprintf("#!/usr/bin/env %s\n\n\nset -o pipefail\n\nreadonly DEFAULT_FILE_MAXSIZE_MB=\"30\" \nreadonly CONFIG_NAME=\"hooks.maxfilesize\"\nreadonly NULLSHA=\"0000000000000000000000000000000000000000\"\nreadonly EXIT_SUCCESS=0\nreadonly EXIT_FAILURE=1\nreadonly DEFAULT_REPO_MAXSIZE_MB=\"1024\" \nreadonly CHECK_FLAG_ON=1\n\n\nstatus=\"$EXIT_SUCCESS\"\n\n# skip this hook entirely if shell check is not open\ncheck_flag=${PUSH_SIZE_CHECK_FLAG}\nif [[ $check_flag != $CHECK_FLAG_ON ]]; then\nexit $EXIT_SUCCESS\nfi\n\n\n#######################################\n# check the file max size limit\n#######################################\n\n# get maximum filesize (from repository-specific config)\nmaxsize_mb=\"${REPO_MAX_FILE_SIZE}\"\n\nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\necho \"failed to get ${CONFIG_NAME} from config\"\nexit \"$EXIT_FAILURE\"\nfi\n\npush_size=\"0\"\n# read lines from stdin (format: \"<oldref> <newref> <refname>\\n\")\nwhile read oldref newref refname; do\n# skip branch deletions\nif [[ \"$newref\" == \"$NULLSHA\" ]]; then\n continue\nfi\n\n# find large objects\n# check all objects from $oldref (possible $NULLSHA) to $newref, but\n# skip all objects that have already been accepted (i.e. are referenced by\n# another branch or tag).\n\nnew_branch_flag=0\nif [[ \"$oldref\" == \"$NULLSHA\" ]]; then\n target=\"$newref\"\n new_branch_flag=1\n echo \"You are creating a new remote branch,openI will check all files in commit history to find oversize files\"\nelse\n target=\"${oldref}..${newref}\"\nfi\nmaxsize=`expr $maxsize_mb \\* 1048576` \n\n# find objects in this push_size\n# print like:\n# 08da8e2ab9ae4095bf94dd71ac913132b880b463 commit 214\n# 43e993b768ede5740e8c65de2ed6edec25053ea1 tree 185\n# 4476971d76569039df7569af1b8d03c288f6b193 blob 20167318 b0417e6593a1.zip\nfiles=\"$(git rev-list --objects \"$target\" | \\\n git cat-file $'--batch-check=%%(objectname) %%(objecttype) %%(objectsize) %%(rest)' | \\\n awk -F ' ' -v maxbytes=\"$maxsize\" 'BEGIN {totalIn=0} {if( $3 > maxbytes && $2 == \"blob\") { totalIn+=$3; print $4} else { totalIn+=$3}} END { printf (\"totalIn=\\t%%s\",totalIn)}' )\"\n \nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\n echo \"failed to check for large files in ref ${refname}\"\n continue\nfi\n\nIFS=$'\\n'\n# rewrite IFS to seperate line in $files\nfor file in $files; do\n # if don't unset IFS,temp_array=(${file}) will get error answer\n \n if [[ ${file} == totalIn=* ]]; then\n\tIFS=$'\\t'\n\ttemp_array=(${file})\n\tpush_size=${temp_array[1]}\n\tcontinue\n fi\n\tunset IFS\n if [[ \"$status\" == $EXIT_SUCCESS ]]; then\n\t\techo -e \"Error: Your push was rejected because it contains files larger than $(numfmt --to=iec \"$maxsize_mb\") Mb\"\n\t\techo \"help document -- https://git.openi.org.cn/zeizei/OpenI_Learning/src/branch/master/docs/git/repository_capacity_help.md\"\n\t\techo \"oversize files:\"\n\t\tstatus=\"$EXIT_FAILURE\"\t\n fi\n echo -e \"\\033[31m- ${file}\\033[0m \"\ndone\n\nif [[ \"$status\" != $EXIT_SUCCESS ]]; then\n\texit \"$status\"\nfi\n\ndone\n\n#######################################\n# check the repo max size limit\n#######################################\nif [[ $push_size -eq \"0\" ]]; then\n\texit $EXIT_SUCCESS\nfi\n\n# if create new branch or tag,use count-objects -v to get pack size\nif [[ $new_branch_flag -eq 1 ]]; then\n size_kb=`git count-objects -v | grep 'size-pack' | sed 's/.*\\(size-pack:\\).//'`\n size_pack_kb=`git count-objects -v | grep 'size:' | sed 's/.*\\(size:\\).//'`\n\ttotal_kb=`expr $size_kb + $size_pack_kb`\n\tlet push_size=$total_kb*1024\nfi\n\nsizelimit_mb=\"${REPO_MAX_SIZE}\"\nlet sizelimit_b=$sizelimit_mb*1024*1024\n\n# repo size at here means the size of repo directory in server \nreposize_b=${REPO_CURRENT_SIZE}\n\ntotal=`expr $push_size + $reposize_b`\n\nif [ $total -gt $sizelimit_b ]; then\n echo \"Error: Your push was rejected because the repository size is large than $sizelimit_mb Mb\"\n echo \"see the help document--https://git.openi.org.cn/zeizei/OpenI_Learning/src/branch/master/docs/git/repository_capacity_help.md\"\n exit $EXIT_FAILURE\nfi\n\n\nexit $EXIT_SUCCESS", setting.ScriptType), | |||
fmt.Sprintf(""), | |||
fmt.Sprintf(""), | |||
} | |||
@@ -263,7 +263,7 @@ search_issue=Issue | |||
search_pr=Pull Request | |||
search_user=User | |||
search_org=Organization | |||
search_finded=Find | |||
search_finded=Find | |||
search_related=related | |||
search_maybe=maybe | |||
search_ge= | |||
@@ -276,7 +276,7 @@ use_plt__fuction = To use the AI collaboration functions provided by this platfo | |||
provide_resoure = Computing resources of CPU/GPU/NPU are provided freely for various types of AI tasks. | |||
activity = Activity | |||
no_events = There are no events related | |||
or_t = or | |||
or_t = or | |||
[explore] | |||
repos = Repositories | |||
@@ -527,6 +527,19 @@ static.public.user_business_analysis_last30_day=Last_30_day | |||
static.public.user_business_analysis_last_month=Last_Month | |||
static.public.user_business_analysis_yesterday=Yesterday | |||
static.public.user_business_analysis_all=All | |||
metrics.sheetname=User Trend Analysis | |||
metrics.date=Count Date | |||
metrics.newregistuser=New registered user | |||
metrics.newregistandactiveuser=New activated | |||
metrics.hasactivateuser=New contributing activities | |||
metrics.newregistnotactiveuser=New inactive | |||
metrics.averageuser=Average new users | |||
metrics.newuseractiveindex=Activation rate of new users | |||
metrics.totalregistuser=Cumulative registered users | |||
metrics.totalactiveduser=Cumulative activated users | |||
metrics.totalhasactivityuser=Cumulative active users | |||
[settings] | |||
profile = Profile | |||
account = Account | |||
@@ -947,6 +960,15 @@ model_manager = Model | |||
model_noright=No right | |||
model_rename=Duplicate model name, please modify model name. | |||
date=Date | |||
repo_add=Project Increment | |||
repo_total=Project Total | |||
repo_public_add=Public Project Increment | |||
repo_private_add=Private Project Increment | |||
repo_fork_add=Fork Project Increment | |||
repo_mirror_add=Mirror Project Increment | |||
repo_self_add=Custom Project Increment | |||
debug=Debug | |||
debug_again=Restart | |||
stop=Stop | |||
@@ -1011,7 +1033,9 @@ get_repo_stat_error=Can not get the statistics of the repository. | |||
get_repo_info_error=Can not get the information of the repository. | |||
generate_statistic_file_error=Failed to generate file. | |||
repo_stat_inspect=ProjectAnalysis | |||
repo_stat_develop=ProjectGrowthAnalysis | |||
all=All | |||
current_year=Current_Year | |||
computing.all = All | |||
computing.Introduction=Introduction | |||
@@ -1301,6 +1325,8 @@ editor.require_signed_commit = Branch requires a signed commit | |||
editor.repo_too_large = Repository can not exceed %d MB | |||
editor.repo_file_invalid = Upload files are invalid | |||
editor.upload_file_too_much = Can not upload more than %d files at a time | |||
editor.rename = rename "%s" to %s" | |||
editor.file_changed_while_renaming=The version of the file or folder to be renamed has changed. Please refresh the page and try again | |||
commits.desc = Browse source code change history. | |||
@@ -1365,7 +1391,7 @@ issues.add_milestone_at = `added this to the <b>%s</b> milestone %s` | |||
issues.change_milestone_at = `modified the milestone from <b>%s</b> to <b>%s</b> %s` | |||
issues.remove_milestone_at = `removed this from the <b>%s</b> milestone %s` | |||
issues.add_branch_at=`added this to the <b>%s</b> branch %s` | |||
issues.add_branch_at=`added this to the <b>%s</b> branch %s` | |||
issues.add_tag_at =`added this to the <b>%s</b> tag %s` | |||
issues.change_branch_tag_at= `modified the branch/tag from <b>%s</b> to <b>%s</b> %s` | |||
issues.remove_branch_at=`removed this from the <b>%s</b> branch %s` | |||
@@ -3017,4 +3043,4 @@ SNN4IMAGENET = SNN4IMAGENET | |||
BRAINSCORE = BRAINSCORE | |||
TRAIN = TRAIN | |||
INFERENCE = INFERENCE | |||
BENCHMARK = BENCHMARK | |||
BENCHMARK = BENCHMARK |
@@ -271,7 +271,7 @@ search_maybe=约为 | |||
search_ge=个 | |||
wecome_AI_plt=欢迎来到启智AI协作平台! | |||
explore_AI = 探索更好的AI,来这里发现更有意思的 | |||
explore_AI = 探索更好的AI,来这里发现更有意思的 | |||
datasets = 数据集 | |||
repositories = 项目 | |||
use_plt__fuction = 使用本平台提供的AI协作功能,如:托管代码、共享数据、调试算法或训练模型,请先 | |||
@@ -279,7 +279,7 @@ provide_resoure = 平台目前免费提供CPU、GPU、NPU的算力资源,可 | |||
create_pro = 创建项目 | |||
activity = 活动 | |||
no_events = 还没有与您相关的活动 | |||
or_t = 或 | |||
or_t = 或 | |||
[explore] | |||
@@ -532,6 +532,19 @@ static.public.user_business_analysis_last30_day=近30天 | |||
static.public.user_business_analysis_last_month=上月 | |||
static.public.user_business_analysis_yesterday=昨天 | |||
static.public.user_business_analysis_all=所有 | |||
metrics.sheetname=用户趋势分析 | |||
metrics.date=日期 | |||
metrics.newregistuser=新增注册用户 | |||
metrics.newregistandactiveuser=新增已激活 | |||
metrics.hasactivateuser=新增有贡献活动 | |||
metrics.newregistnotactiveuser=新增未激活 | |||
metrics.averageuser=平均新增用户 | |||
metrics.newuseractiveindex=新增用户激活率 | |||
metrics.totalregistuser=累计注册用户 | |||
metrics.totalactiveduser=累计已激活 | |||
metrics.totalhasactivityuser=累计有贡献活动 | |||
[settings] | |||
profile=个人信息 | |||
account=账号 | |||
@@ -948,6 +961,16 @@ model_manager = 模型 | |||
model_noright=无权限操作 | |||
model_rename=模型名称重复,请修改模型名称 | |||
date=日期 | |||
repo_add=新增项目 | |||
repo_total=累计项目 | |||
repo_public_add=新增公开项目 | |||
repo_private_add=新增私有项目 | |||
repo_fork_add=新增派生项目 | |||
repo_mirror_add=新增镜像项目 | |||
repo_self_add=新增自建项目 | |||
debug=调试 | |||
debug_again=再次调试 | |||
stop=停止 | |||
@@ -1019,7 +1042,9 @@ get_repo_stat_error=查询当前仓库的统计信息失败。 | |||
get_repo_info_error=查询当前仓库信息失败。 | |||
generate_statistic_file_error=生成文件失败。 | |||
repo_stat_inspect=项目分析 | |||
repo_stat_develop=项目增长趋势 | |||
all=所有 | |||
current_year=今年 | |||
computing.all=全部 | |||
computing.Introduction=简介 | |||
@@ -1313,6 +1338,8 @@ editor.require_signed_commit=分支需要签名提交 | |||
editor.repo_too_large = 代码仓总大小不能超过%dMB | |||
editor.repo_file_invalid = 提交的文件非法 | |||
editor.upload_file_too_much = 不能同时提交超过%d个文件 | |||
editor.rename = 重命名"%s"为"%s" | |||
editor.file_changed_while_renaming=待重命名的文件或文件夹版本已发生变化,请您刷新页面后重试 | |||
commits.desc=浏览代码修改历史 | |||
commits.commits=次代码提交 | |||
@@ -535,6 +535,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/restoreFork", repo.RestoreForkNumber) | |||
m.Get("/downloadAll", repo.ServeAllProjectsPeriodStatisticsFile) | |||
m.Get("/downloadAllOpenI", repo.ServeAllProjectsOpenIStatisticsFile) | |||
m.Get("/summary", repo.GetLatestProjectsSummaryData) | |||
m.Get("/summary/period", repo.GetProjectsSummaryData) | |||
m.Get("/summary/download", repo.GetProjectsSummaryDataFile) | |||
m.Group("/project", func() { | |||
m.Get("", repo.GetAllProjectsPeriodStatistics) | |||
m.Get("/numVisit", repo.ProjectNumVisit) | |||
@@ -547,7 +550,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}) | |||
}, operationReq) | |||
m.Get("/query_user_metrics", operationReq, repo_ext.QueryMetrics) | |||
m.Get("/query_metrics_current_month", operationReq, repo_ext.QueryUserMetricsCurrentMonth) | |||
m.Get("/query_metrics_current_week", operationReq, repo_ext.QueryUserMetricsCurrentWeek) | |||
m.Get("/query_metrics_current_year", operationReq, repo_ext.QueryUserMetricsCurrentYear) | |||
m.Get("/query_metrics_last30_day", operationReq, repo_ext.QueryUserMetricsLast30Day) | |||
m.Get("/query_metrics_last_month", operationReq, repo_ext.QueryUserMetricsLastMonth) | |||
m.Get("/query_metrics_yesterday", operationReq, repo_ext.QueryUserMetricsYesterday) | |||
m.Get("/query_metrics_all", operationReq, repo_ext.QueryUserMetricsAll) | |||
m.Get("/query_user_metrics_page", operationReq, repo_ext.QueryUserMetricDataPage) | |||
m.Get("/query_user_rank_list", operationReq, repo_ext.QueryRankingList) | |||
m.Get("/query_user_static_page", operationReq, repo_ext.QueryUserStaticDataPage) | |||
m.Get("/query_user_current_month", operationReq, repo_ext.QueryUserStaticCurrentMonth) | |||
@@ -20,8 +20,10 @@ import ( | |||
const DEFAULT_PAGE_SIZE = 10 | |||
const DATE_FORMAT = "2006-01-02" | |||
const MONTH_FORMAT = "2006-01" | |||
const EXCEL_DATE_FORMAT = "20060102" | |||
const CREATE_TIME_FORMAT = "2006/01/02 15:04:05" | |||
const UPDATE_TIME_FORMAT = "2006-01-02 15:04:05" | |||
type ProjectsPeriodData struct { | |||
RecordBeginTime string `json:"recordBeginTime"` | |||
@@ -60,6 +62,38 @@ type ProjectLatestData struct { | |||
Top10 []UserInfo `json:"top10"` | |||
} | |||
type ProjectSummaryBaseData struct { | |||
NumReposAdd int64 `json:"numReposAdd"` | |||
NumRepoPublicAdd int64 `json:"numRepoPublicAdd"` | |||
NumRepoPrivateAdd int64 `json:"numRepoPrivateAdd"` | |||
NumRepoForkAdd int64 `json:"numRepoForkAdd"` | |||
NumRepoMirrorAdd int64 `json:"numRepoMirrorAdd"` | |||
NumRepoSelfAdd int64 `json:"numRepoSelfAdd"` | |||
NumRepos int64 `json:"numRepos"` | |||
CreatTime string `json:"creatTime"` | |||
} | |||
type ProjectSummaryData struct { | |||
ProjectSummaryBaseData | |||
NumRepoPublic int64 `json:"numRepoPublic"` | |||
NumRepoPrivate int64 `json:"numRepoPrivate"` | |||
NumRepoFork int64 `json:"numRepoFork"` | |||
NumRepoMirror int64 `json:"numRepoMirror"` | |||
NumRepoSelf int64 `json:"numRepoSelf"` | |||
NumRepoOrgAdd int64 `json:"numRepoOrgAdd"` | |||
NumRepoNotOrgAdd int64 `json:"numRepoNotOrgAdd"` | |||
NumRepoOrg int64 `json:"numRepoOrg"` | |||
NumRepoNotOrg int64 `json:"numRepoNotOrg"` | |||
} | |||
type ProjectSummaryPeriodData struct { | |||
RecordBeginTime string `json:"recordBeginTime"` | |||
TotalCount int64 `json:"totalCount"` | |||
PageRecords []*ProjectSummaryBaseData `json:"pageRecords"` | |||
} | |||
func RestoreForkNumber(ctx *context.Context) { | |||
repos, err := models.GetAllRepositories() | |||
if err != nil { | |||
@@ -73,6 +107,146 @@ func RestoreForkNumber(ctx *context.Context) { | |||
ctx.JSON(http.StatusOK, struct{}{}) | |||
} | |||
func GetLatestProjectsSummaryData(ctx *context.Context) { | |||
stat, err := models.GetLatest2SummaryStatistic() | |||
data := ProjectSummaryData{} | |||
if err == nil && len(stat) > 0 { | |||
data.NumRepos = stat[0].NumRepos | |||
data.NumRepoOrg = stat[0].NumRepoOrg | |||
data.NumRepoNotOrg = stat[0].NumRepos - stat[0].NumRepoOrg | |||
data.NumRepoFork = stat[0].NumRepoFork | |||
data.NumRepoMirror = stat[0].NumRepoMirror | |||
data.NumRepoSelf = stat[0].NumRepoSelf | |||
data.NumRepoPrivate = stat[0].NumRepoPrivate | |||
data.NumRepoPublic = stat[0].NumRepoPublic | |||
data.CreatTime = stat[0].CreatedUnix.Format(UPDATE_TIME_FORMAT) | |||
if len(stat) == 2 { | |||
data.NumReposAdd = stat[0].NumRepos - stat[1].NumRepos | |||
data.NumRepoOrgAdd = stat[0].NumRepoOrg - stat[1].NumRepoOrg | |||
data.NumRepoNotOrgAdd = (stat[0].NumRepos - stat[0].NumRepoOrg) - (stat[1].NumRepos - stat[1].NumRepoOrg) | |||
data.NumRepoForkAdd = stat[0].NumRepoFork - stat[1].NumRepoFork | |||
data.NumRepoMirrorAdd = stat[0].NumRepoMirror - stat[1].NumRepoMirror | |||
data.NumRepoSelfAdd = stat[0].NumRepoSelf - stat[1].NumRepoSelf | |||
data.NumRepoPrivateAdd = stat[0].NumRepoPrivate - stat[1].NumRepoPrivate | |||
data.NumRepoPublicAdd = stat[0].NumRepoPublic - stat[1].NumRepoPublic | |||
} | |||
} | |||
ctx.JSON(200, data) | |||
} | |||
func GetProjectsSummaryData(ctx *context.Context) { | |||
var datas = make([]*ProjectSummaryBaseData, 0) | |||
recordBeginTime, err := getRecordBeginTime() | |||
if err != nil { | |||
log.Error("Can not get record begin time", err) | |||
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
return | |||
} | |||
beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime) | |||
beginTime = beginTime.AddDate(0, 0, -1) | |||
queryType := ctx.QueryTrim("type") | |||
var count int64 | |||
if queryType == "all" || queryType == "current_year" { | |||
dates := getEndOfMonthDates(beginTime, endTime) | |||
count, _ = models.GetSummaryStatisticByDateCount(dates) | |||
stats, err := models.GetAllSummaryStatisticByDates(dates) | |||
if err != nil { | |||
log.Warn("can not get summary data", err) | |||
} else { | |||
for i, v := range stats { | |||
if i == 0 { | |||
continue | |||
} | |||
data := ProjectSummaryBaseData{} | |||
setStatisticsData(&data, v, stats[i-1]) | |||
createTime, _ := time.Parse(DATE_FORMAT, v.Date) | |||
data.CreatTime = createTime.Format(MONTH_FORMAT) | |||
datas = append(datas, &data) | |||
} | |||
} | |||
} else { | |||
count, _ = models.GetSummaryStatisticByTimeCount(beginTime, endTime) | |||
stats, err := models.GetAllSummaryStatisticByTime(beginTime, endTime) | |||
if err != nil { | |||
log.Warn("can not get summary data", err) | |||
} else { | |||
for i, v := range stats { | |||
if i == 0 { | |||
continue | |||
} | |||
data := ProjectSummaryBaseData{} | |||
setStatisticsData(&data, v, stats[i-1]) | |||
data.CreatTime = v.Date | |||
datas = append(datas, &data) | |||
} | |||
} | |||
} | |||
projectSummaryPeriodData := ProjectSummaryPeriodData{ | |||
TotalCount: count - 1, | |||
RecordBeginTime: recordBeginTime.Format(DATE_FORMAT), | |||
PageRecords: reverse(datas), | |||
} | |||
ctx.JSON(200, projectSummaryPeriodData) | |||
} | |||
func reverse(datas []*ProjectSummaryBaseData ) []*ProjectSummaryBaseData { | |||
for i := 0; i < len(datas)/2; i++ { | |||
j := len(datas) - i - 1 | |||
datas[i], datas[j] = datas[j], datas[i] | |||
} | |||
return datas | |||
} | |||
func setStatisticsData(data *ProjectSummaryBaseData, v *models.SummaryStatistic, stats *models.SummaryStatistic) { | |||
data.NumReposAdd = v.NumRepos - stats.NumRepos | |||
data.NumRepoPublicAdd = v.NumRepoPublic - stats.NumRepoPublic | |||
data.NumRepoPrivateAdd = v.NumRepoPrivate - stats.NumRepoPrivate | |||
data.NumRepoMirrorAdd = v.NumRepoMirror - stats.NumRepoMirror | |||
data.NumRepoForkAdd = v.NumRepoFork - stats.NumRepoFork | |||
data.NumRepoSelfAdd = v.NumRepoSelf - stats.NumRepoSelf | |||
data.NumRepos = v.NumRepos | |||
} | |||
func getEndOfMonthDates(beginTime time.Time, endTime time.Time) []string { | |||
var dates = []string{} | |||
date := endOfMonth(beginTime.AddDate(0, -1, 0)) | |||
dates = append(dates, date.Format(DATE_FORMAT)) | |||
tempDate := endOfMonth(beginTime) | |||
for { | |||
if tempDate.Before(endTime) { | |||
dates = append(dates, tempDate.Format(DATE_FORMAT)) | |||
tempDate = endOfMonth(tempDate.AddDate(0, 0, 1)) | |||
} else { | |||
break | |||
} | |||
} | |||
return dates | |||
} | |||
func endOfMonth(date time.Time) time.Time { | |||
return date.AddDate(0, 1, -date.Day()) | |||
} | |||
func GetAllProjectsPeriodStatistics(ctx *context.Context) { | |||
recordBeginTime, err := getRecordBeginTime() | |||
@@ -210,6 +384,122 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | |||
} | |||
func GetProjectsSummaryDataFile(ctx *context.Context) { | |||
recordBeginTime, err := getRecordBeginTime() | |||
if err != nil { | |||
log.Error("Can not get record begin time", err) | |||
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
return | |||
} | |||
beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime) | |||
beginTime = beginTime.AddDate(0, 0, -1) | |||
if err != nil { | |||
log.Error("Parameter is wrong", err) | |||
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.parameter_is_wrong")) | |||
return | |||
} | |||
page := ctx.QueryInt("page") | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
pageSize := 100 | |||
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 | |||
} | |||
var projectAnalysis = ctx.Tr("repo.repo_stat_develop") | |||
fileName := getSummaryFileName(ctx, beginTime, endTime, projectAnalysis) | |||
f := excelize.NewFile() | |||
index := f.NewSheet(projectAnalysis) | |||
f.DeleteSheet("Sheet1") | |||
for k, v := range allProjectsPeriodSummaryHeader(ctx) { | |||
f.SetCellValue(projectAnalysis, k, v) | |||
} | |||
var total int64 | |||
queryType := ctx.QueryTrim("type") | |||
var datas = make([]*ProjectSummaryBaseData, 0) | |||
if queryType == "all" || queryType == "current_year" { | |||
dates := getEndOfMonthDates(beginTime, endTime) | |||
total, _ = models.GetSummaryStatisticByDateCount(dates) | |||
totalPage := getTotalPage(total, pageSize) | |||
for i := 0; i < totalPage; i++ { | |||
stats, err := models.GetSummaryStatisticByDates(dates, i+1, pageSize) | |||
if err != nil { | |||
log.Warn("can not get summary data", err) | |||
} else { | |||
for j, v := range stats { | |||
if j == 0 { | |||
continue | |||
} | |||
data := ProjectSummaryBaseData{} | |||
setStatisticsData(&data, v, stats[j-1]) | |||
createTime, _ := time.Parse(DATE_FORMAT, v.Date) | |||
data.CreatTime = createTime.Format(MONTH_FORMAT) | |||
datas = append(datas, &data) | |||
} | |||
} | |||
} | |||
} else { | |||
total, _ = models.GetSummaryStatisticByTimeCount(beginTime, endTime) | |||
totalPage := getTotalPage(total, pageSize) | |||
for i := 0; i < totalPage; i++ { | |||
stats, err := models.GetSummaryStatisticByTime(beginTime, endTime, i+1, pageSize) | |||
if err != nil { | |||
log.Warn("can not get summary data", err) | |||
} else { | |||
for j, v := range stats { | |||
if j == 0 { | |||
continue | |||
} | |||
data := ProjectSummaryBaseData{} | |||
setStatisticsData(&data, v, stats[j-1]) | |||
data.CreatTime = v.Date | |||
datas = append(datas, &data) | |||
} | |||
} | |||
} | |||
} | |||
row := 2 | |||
datas = reverse(datas) | |||
for _, data := range datas { | |||
for k, v := range allProjectsPeriodSummaryValues(row, data, 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 ServeAllProjectsOpenIStatisticsFile(ctx *context.Context) { | |||
page := ctx.QueryInt("page") | |||
@@ -290,6 +580,20 @@ func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, p | |||
return frontName | |||
} | |||
func getSummaryFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, projectAnalysis string) string { | |||
baseName := projectAnalysis + "_" | |||
if ctx.QueryTrim("type") == "all" { | |||
baseName = baseName + ctx.Tr("repo.all") | |||
} else if ctx.QueryTrim("type") == "current_year" { | |||
baseName = baseName + ctx.Tr("repo.current_year") | |||
} else { | |||
baseName = baseName + beginTime.Format(EXCEL_DATE_FORMAT) + "_" + endTime.AddDate(0, 0, -1).Format(EXCEL_DATE_FORMAT) | |||
} | |||
frontName := baseName + ".xlsx" | |||
return frontName | |||
} | |||
func allProjectsPeroidHeader(ctx *context.Context) map[string]string { | |||
return map[string]string{"A1": ctx.Tr("admin.repos.id"), "B1": ctx.Tr("admin.repos.projectName"), "C1": ctx.Tr("repo.owner"), "D1": ctx.Tr("admin.repos.isPrivate"), "E1": ctx.Tr("admin.repos.openi"), "F1": ctx.Tr("admin.repos.visit"), "G1": ctx.Tr("admin.repos.download"), "H1": ctx.Tr("admin.repos.pr"), "I1": ctx.Tr("admin.repos.commit"), | |||
@@ -297,6 +601,19 @@ func allProjectsPeroidHeader(ctx *context.Context) map[string]string { | |||
} | |||
func allProjectsPeriodSummaryHeader(ctx *context.Context) map[string]string { | |||
return map[string]string{"A1": ctx.Tr("repo.date"), "B1": ctx.Tr("repo.repo_add"), "C1": ctx.Tr("repo.repo_total"), "D1": ctx.Tr("repo.repo_public_add"), "E1": ctx.Tr("repo.repo_private_add"), "F1": ctx.Tr("repo.repo_self_add"), "G1": ctx.Tr("repo.repo_fork_add"), "H1": ctx.Tr("repo.repo_mirror_add")} | |||
} | |||
func allProjectsPeriodSummaryValues(row int, rs *ProjectSummaryBaseData, ctx *context.Context) map[string]string { | |||
return map[string]string{getCellName("A", row): rs.CreatTime, getCellName("B", row): strconv.FormatInt(rs.NumReposAdd, 10), getCellName("C", row): strconv.FormatInt(rs.NumRepos, 10), getCellName("D", row): strconv.FormatInt(rs.NumRepoPublicAdd, 10), getCellName("E", row): strconv.FormatInt(rs.NumRepoPrivateAdd, 10), | |||
getCellName("F", row): strconv.FormatInt(rs.NumRepoSelfAdd, 10), getCellName("G", row): strconv.FormatInt(rs.NumRepoForkAdd, 10), getCellName("H", row): strconv.FormatInt(rs.NumRepoMirrorAdd, 10), | |||
} | |||
} | |||
func allProjectsPeroidValues(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.DisplayName(), getCellName("C", row): rs.OwnerName, getCellName("D", row): getBoolDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
getCellName("F", row): strconv.FormatInt(rs.NumVisits, 10), getCellName("G", row): strconv.FormatInt(rs.NumDownloads, 10), getCellName("H", row): strconv.FormatInt(rs.NumPulls, 10), getCellName("I", row): strconv.FormatInt(rs.NumCommits, 10), | |||
@@ -605,7 +605,32 @@ func ExploreImages(ctx *context.Context) { | |||
ctx.HTML(200, tplExploreImages) | |||
} | |||
func ExploreDataAnalysisUserTrend(ctx *context.Context) { | |||
ctx.Data["url_params"]="UserTrend" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
func ExploreDataAnalysisUserAnalysis(ctx *context.Context) { | |||
ctx.Data["url_params"]="UserAnalysis" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
func ExploreDataAnalysisProTrend(ctx *context.Context) { | |||
ctx.Data["url_params"]="ProTrend" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
func ExploreDataAnalysisProAnalysis(ctx *context.Context) { | |||
ctx.Data["url_params"]="ProAnalysis" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
func ExploreDataAnalysisOverview(ctx *context.Context) { | |||
ctx.Data["url_params"]="Overview" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
func ExploreDataAnalysisBrainAnalysis(ctx *context.Context) { | |||
ctx.Data["url_params"]="BrainAnalysis" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
func ExploreDataAnalysis(ctx *context.Context) { | |||
ctx.Data["url_params"]="" | |||
ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
} | |||
@@ -5,10 +5,12 @@ | |||
package repo | |||
import ( | |||
"code.gitea.io/gitea/routers/response" | |||
repo_service "code.gitea.io/gitea/services/repository" | |||
"encoding/json" | |||
"fmt" | |||
"io/ioutil" | |||
"net/http" | |||
"path" | |||
"path/filepath" | |||
"strings" | |||
@@ -795,3 +797,102 @@ func GetClosestParentWithFiles(treePath string, commit *git.Commit) string { | |||
} | |||
return treePath | |||
} | |||
// RenameFilePost response for editing file | |||
func RenameFilePost(ctx *context.Context, form auth.RenameRepoFileForm) { | |||
renameFilePost(ctx, form) | |||
} | |||
func renameFilePost(ctx *context.Context, form auth.RenameRepoFileForm) { | |||
if form.TreePath == "" || form.LastCommit == "" { | |||
ctx.JSON(http.StatusOK, response.ServerError("param error")) | |||
return | |||
} | |||
if form.TreePath == ctx.Repo.TreePath { | |||
ctx.JSON(http.StatusOK, response.Success()) | |||
return | |||
} | |||
canCommit := renderCommitRights(ctx) | |||
branchName := ctx.Repo.BranchName | |||
if ctx.HasError() { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Flash.ErrorMsg)) | |||
return | |||
} | |||
// Cannot commit to a an existing branch if user doesn't have rights | |||
if branchName == ctx.Repo.BranchName && !canCommit { | |||
ctx.Data["Err_NewBranchName"] = true | |||
ctx.Data["commit_choice"] = frmCommitChoiceNewBranch | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName))) | |||
return | |||
} | |||
message := ctx.Tr("repo.editor.rename", ctx.Repo.TreePath, form.TreePath) | |||
if err := repofiles.RenameRepoFile(ctx.Repo.Repository, ctx.User, &repofiles.RenameRepoFileOptions{ | |||
LastCommitID: form.LastCommit, | |||
BranchName: branchName, | |||
FromTreePath: ctx.Repo.TreePath, | |||
TreePath: form.TreePath, | |||
Message: message, | |||
}); err != nil { | |||
// This is where we handle all the errors thrown by repofiles.CreateOrUpdateRepoFile | |||
if git.IsErrNotExist(err) { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.file_editing_no_longer_exists", ctx.Repo.TreePath))) | |||
} else if models.IsErrLFSFileLocked(err) { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.file_editing_no_longer_exists", ctx.Tr("repo.editor.upload_file_is_locked", err.(models.ErrLFSFileLocked).Path, err.(models.ErrLFSFileLocked).UserName)))) | |||
} else if models.IsErrFilenameInvalid(err) { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath))) | |||
} else if models.IsErrFilePathInvalid(err) { | |||
if fileErr, ok := err.(models.ErrFilePathInvalid); ok { | |||
switch fileErr.Type { | |||
case git.EntryModeSymlink: | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path))) | |||
case git.EntryModeTree: | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path))) | |||
case git.EntryModeBlob: | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path))) | |||
default: | |||
ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
} | |||
} else { | |||
ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
} | |||
} else if models.IsErrRepoFileAlreadyExists(err) { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.file_already_exists", form.TreePath))) | |||
} else if git.IsErrBranchNotExist(err) { | |||
// For when a user adds/updates a file to a branch that no longer exists | |||
if branchErr, ok := err.(git.ErrBranchNotExist); ok { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name))) | |||
} else { | |||
ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
} | |||
} else if models.IsErrBranchAlreadyExists(err) { | |||
// For when a user specifies a new branch that already exists | |||
ctx.Data["Err_NewBranchName"] = true | |||
if branchErr, ok := err.(models.ErrBranchAlreadyExists); ok { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName))) | |||
} else { | |||
ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
ctx.Error(500, err.Error()) | |||
} | |||
} else if models.IsErrCommitIDDoesNotMatch(err) { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.file_changed_while_renaming"))) | |||
} else if git.IsErrPushOutOfDate(err) { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.file_changed_while_renaming"))) | |||
} else if git.IsErrPushRejected(err) { | |||
errPushRej := err.(*git.ErrPushRejected) | |||
if len(errPushRej.Message) == 0 { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.push_rejected_no_message"))) | |||
} else { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.push_rejected", utils.SanitizeFlashErrorString(errPushRej.Message)))) | |||
} | |||
} else { | |||
ctx.JSON(http.StatusOK, response.ServerError(ctx.Tr("repo.editor.fail_to_update_file", form.TreePath, utils.SanitizeFlashErrorString(err.Error())))) | |||
} | |||
return | |||
} | |||
ctx.JSON(http.StatusOK, response.Success()) | |||
} |
@@ -60,6 +60,12 @@ func SummaryStatisticDaily(date string) { | |||
} | |||
selfRepositoryNumber := repositoryNumer - mirrorRepositoryNumber - forkRepositoryNumber | |||
organizationRepoNumber, err := models.GetAllOrgRepositoriesCount() | |||
if err != nil { | |||
log.Error("can not get org repository number", err) | |||
organizationRepoNumber = 0 | |||
} | |||
//repository size | |||
repositorySize, err := models.GetAllRepositoriesSize() | |||
if err != nil { | |||
@@ -99,6 +105,7 @@ func SummaryStatisticDaily(date string) { | |||
NumRepoPrivate: privateRepositoryNumer, | |||
NumRepoPublic: publicRepositoryNumer, | |||
NumRepoSelf: selfRepositoryNumber, | |||
NumRepoOrg: organizationRepoNumber, | |||
NumRepoBigModel: topicsCount[0], | |||
NumRepoAI: topicsCount[1], | |||
NumRepoVision: topicsCount[2], | |||
@@ -19,6 +19,57 @@ const ( | |||
PAGE_SIZE = 2000 | |||
) | |||
func getUserMetricsExcelHeader(ctx *context.Context) map[string]string { | |||
excelHeader := make([]string, 0) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.date")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.newregistuser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.newregistandactiveuser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.hasactivateuser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.newregistnotactiveuser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.averageuser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.newuseractiveindex")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.totalregistuser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.totalactiveduser")) | |||
excelHeader = append(excelHeader, ctx.Tr("user.metrics.totalhasactivityuser")) | |||
excelHeaderMap := make(map[string]string, 0) | |||
var i byte | |||
i = 0 | |||
for _, value := range excelHeader { | |||
excelColumn := getColumn(i) + fmt.Sprint(1) | |||
excelHeaderMap[excelColumn] = value | |||
i++ | |||
} | |||
return excelHeaderMap | |||
} | |||
func writeUserMetricsExcel(row int, xlsx *excelize.File, sheetName string, userMetrics *models.UserMetrics) { | |||
rows := fmt.Sprint(row) | |||
var tmp byte | |||
tmp = 0 | |||
dateTime := time.Unix(userMetrics.CountDate, 0) | |||
//dateTime.Format("2006-01-02 15:04:05") | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, dateTime.Format("2006-01-02 15:04:05")) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.ActivateRegistUser+userMetrics.NotActivateRegistUser) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.ActivateRegistUser) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.RegistActivityUser) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.NotActivateRegistUser) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, "") | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, fmt.Sprintf("%.2f", userMetrics.ActivateIndex)) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.TotalUser) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.TotalActivateRegistUser) | |||
tmp = tmp + 1 | |||
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userMetrics.TotalHasActivityUser) | |||
} | |||
func getExcelHeader(ctx *context.Context) map[string]string { | |||
excelHeader := make([]string, 0) | |||
excelHeader = append(excelHeader, ctx.Tr("user.static.id")) | |||
@@ -200,16 +251,61 @@ func queryUserDataPage(ctx *context.Context, tableName string, queryObj interfac | |||
} | |||
} | |||
func QueryMetrics(ctx *context.Context) { | |||
startDate := ctx.Query("startDate") | |||
endDate := ctx.Query("endDate") | |||
startTime, _ := time.ParseInLocation("2006-01-02", startDate, time.Local) | |||
endTime, _ := time.ParseInLocation("2006-01-02", endDate, time.Local) | |||
result, count := models.QueryMetrics(startTime.Unix(), endTime.Unix()) | |||
mapInterface := make(map[string]interface{}) | |||
mapInterface["data"] = result | |||
mapInterface["count"] = count | |||
ctx.JSON(http.StatusOK, mapInterface) | |||
func queryMetrics(ctx *context.Context, tableName string, startTime time.Time, endTime time.Time) { | |||
page := ctx.QueryInt("page") | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
pageSize := ctx.QueryInt("pageSize") | |||
if pageSize <= 0 { | |||
pageSize = setting.UI.IssuePagingNum | |||
} | |||
IsReturnFile := ctx.QueryBool("IsReturnFile") | |||
var count int64 | |||
result := make([]*models.UserMetrics, 0) | |||
if tableName == "public.user_business_analysis_current_year" { | |||
result = models.QueryMetricsForYear() | |||
count = int64(len(result)) | |||
} else if tableName == "public.user_business_analysis_all" { | |||
result = models.QueryMetricsForAll() | |||
count = int64(len(result)) | |||
} else { | |||
result, count = models.QueryMetricsPage(startTime.Unix(), endTime.Unix(), page, pageSize) | |||
} | |||
if IsReturnFile { | |||
//writer exec file. | |||
xlsx := excelize.NewFile() | |||
sheetName := ctx.Tr("user.metrics.sheetname") | |||
index := xlsx.NewSheet(sheetName) | |||
xlsx.DeleteSheet("Sheet1") | |||
dataHeader := getUserMetricsExcelHeader(ctx) | |||
for k, v := range dataHeader { | |||
//设置单元格的值 | |||
xlsx.SetCellValue(sheetName, k, v) | |||
} | |||
row := 1 | |||
log.Info("return count=" + fmt.Sprint(count)) | |||
for _, userRecord := range result { | |||
row++ | |||
writeUserMetricsExcel(row, xlsx, sheetName, userRecord) | |||
} | |||
//设置默认打开的表单 | |||
xlsx.SetActiveSheet(index) | |||
filename := sheetName + "_" + ctx.Tr("user.static."+tableName) + ".xlsx" | |||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename)) | |||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
if _, err := xlsx.WriteTo(ctx.Resp); err != nil { | |||
log.Info("writer exel error." + err.Error()) | |||
} | |||
} else { | |||
mapInterface := make(map[string]interface{}) | |||
mapInterface["data"] = result | |||
mapInterface["count"] = count | |||
ctx.JSON(http.StatusOK, mapInterface) | |||
} | |||
} | |||
func QueryRankingList(ctx *context.Context) { | |||
@@ -224,34 +320,97 @@ func QueryRankingList(ctx *context.Context) { | |||
ctx.JSON(http.StatusOK, mapInterface) | |||
} | |||
func QueryUserMetricsCurrentMonth(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 0, 0, 0, 0, currentTimeNow.Location()) | |||
queryMetrics(ctx, "public.user_business_analysis_current_month", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticCurrentMonth(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_current_month", new(models.UserBusinessAnalysisCurrentMonth)) | |||
} | |||
func QueryUserMetricsCurrentWeek(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
offset := int(time.Monday - currentTimeNow.Weekday()) | |||
if offset > 0 { | |||
offset = -6 | |||
} | |||
pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset) | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
queryMetrics(ctx, "public.user_business_analysis_current_week", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticCurrentWeek(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_current_week", new(models.UserBusinessAnalysisCurrentWeek)) | |||
} | |||
func QueryUserMetricsCurrentYear(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
pageStartTime := time.Date(currentTimeNow.Year(), 1, 1, 0, 0, 0, 0, currentTimeNow.Location()) | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
queryMetrics(ctx, "public.user_business_analysis_current_year", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticCurrentYear(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_current_year", new(models.UserBusinessAnalysisCurrentYear)) | |||
} | |||
func QueryUserMetricsLast30Day(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -30) | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
queryMetrics(ctx, "public.user_business_analysis_last30_day", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticLast30Day(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_last30_day", new(models.UserBusinessAnalysisLast30Day)) | |||
} | |||
func QueryUserMetricsLastMonth(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
thisMonth := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 0, 0, 0, 0, currentTimeNow.Location()) | |||
pageStartTime := thisMonth.AddDate(0, -1, 0) | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 23, 59, 59, 0, currentTimeNow.Location()).AddDate(0, 0, -1) | |||
queryMetrics(ctx, "public.user_business_analysis_last_month", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticLastMonth(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_last_month", new(models.UserBusinessAnalysisLastMonth)) | |||
} | |||
func QueryUserMetricsYesterday(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local) | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
queryMetrics(ctx, "public.user_business_analysis_yesterday", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticYesterday(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_yesterday", new(models.UserBusinessAnalysisYesterday)) | |||
} | |||
func QueryUserMetricsAll(ctx *context.Context) { | |||
currentTimeNow := time.Now() | |||
pageStartTime := time.Date(2022, 4, 5, 0, 0, 0, 0, currentTimeNow.Location()) | |||
pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||
queryMetrics(ctx, "public.user_business_analysis_all", pageStartTime, pageEndTime) | |||
} | |||
func QueryUserStaticAll(ctx *context.Context) { | |||
queryUserDataPage(ctx, "public.user_business_analysis_all", new(models.UserBusinessAnalysisAll)) | |||
} | |||
func QueryUserMetricDataPage(ctx *context.Context) { | |||
startDate := ctx.Query("startDate") | |||
endDate := ctx.Query("endDate") | |||
startTime, _ := time.ParseInLocation("2006-01-02", startDate, time.Local) | |||
endTime, _ := time.ParseInLocation("2006-01-02", endDate, time.Local) | |||
page := ctx.QueryInt("page") | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
pageSize := ctx.QueryInt("pageSize") | |||
if pageSize <= 0 { | |||
pageSize = setting.UI.IssuePagingNum | |||
} | |||
result, count := models.QueryMetricsPage(startTime.Unix(), endTime.Unix(), page, pageSize) | |||
mapInterface := make(map[string]interface{}) | |||
mapInterface["data"] = result | |||
mapInterface["count"] = count | |||
ctx.JSON(http.StatusOK, mapInterface) | |||
} | |||
func QueryUserStaticDataPage(ctx *context.Context) { | |||
startDate := ctx.Query("startDate") | |||
endDate := ctx.Query("endDate") | |||
@@ -608,6 +608,11 @@ func getContributorInfo(contributorInfos []*ContributorInfo, email string) *Cont | |||
// Home render repository home page | |||
func Home(ctx *context.Context) { | |||
if ctx.Repo.CanEnableEditor() { | |||
ctx.Data["CanEditFile"] = true | |||
} else { | |||
ctx.Data["CanEditFile"] = false | |||
} | |||
if len(ctx.Repo.Units) > 0 { | |||
//get repo contributors info | |||
contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath(), ctx.Repo.BranchName) | |||
@@ -0,0 +1,32 @@ | |||
package response | |||
const ( | |||
RESPONSE_CODE_SUCCESS = 0 | |||
RESPONSE_MSG_SUCCESS = "ok" | |||
RESPONSE_CODE_ERROR_DEFAULT = 99 | |||
) | |||
type AiforgeResponse struct { | |||
Code int | |||
Msg string | |||
Data interface{} | |||
} | |||
func Success() *AiforgeResponse { | |||
return &AiforgeResponse{Code: RESPONSE_CODE_SUCCESS, Msg: RESPONSE_MSG_SUCCESS} | |||
} | |||
func Error(code int, msg string) *AiforgeResponse { | |||
return &AiforgeResponse{Code: code, Msg: msg} | |||
} | |||
func ServerError(msg string) *AiforgeResponse { | |||
return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: msg} | |||
} | |||
func SuccessWithData(data interface{}) *AiforgeResponse { | |||
return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: RESPONSE_MSG_SUCCESS, Data: data} | |||
} | |||
func ErrorWithData(code int, msg string, data interface{}) *AiforgeResponse { | |||
return &AiforgeResponse{Code: code, Msg: msg, Data: data} | |||
} |
@@ -346,6 +346,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/code", routers.ExploreCode) | |||
m.Get("/images", routers.ExploreImages) | |||
m.Get("/data_analysis", routers.ExploreDataAnalysis) | |||
m.Get("/data_analysis/UserTrend", routers.ExploreDataAnalysisUserTrend) | |||
m.Get("/data_analysis/UserAnalysis", routers.ExploreDataAnalysisUserAnalysis) | |||
m.Get("/data_analysis/ProAnalysis", routers.ExploreDataAnalysisProAnalysis) | |||
m.Get("/data_analysis/ProTrend", routers.ExploreDataAnalysisProTrend) | |||
m.Get("/data_analysis/Overview", routers.ExploreDataAnalysisOverview) | |||
m.Get("/data_analysis/BrainAnalysis", routers.ExploreDataAnalysisBrainAnalysis) | |||
}, ignSignIn) | |||
m.Combo("/install", routers.InstallInit).Get(routers.Install). | |||
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) | |||
@@ -935,6 +942,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Combo("/_upload/*", repo.MustBeAbleToUpload). | |||
Get(repo.UploadFile). | |||
Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost) | |||
m.Post("/_rename/*", bindIgnErr(auth.RenameRepoFileForm{}), repo.RenameFilePost) | |||
}, context.RepoRefByType(context.RepoRefBranch), repo.MustBeEditable) | |||
m.Group("", func() { | |||
m.Post("/upload-file", repo.UploadFileToServer) | |||
@@ -1,15 +1,21 @@ | |||
{{template "base/head_fluid" .}} | |||
<input id="url_params" type="hidden" value={{.url_params}}/> | |||
<div id="data_analysis" style="height: 100%;"> | |||
</div> | |||
{{template "base/footer_fluid" .}} | |||
<!-- <script> | |||
localStorage.setItem("dataAnalysisURL","{{.url_params}}") | |||
</script> --> | |||
<style> | |||
.full.height { | |||
display: flex; | |||
flex-flow: column wrap; | |||
padding-bottom:0px; | |||
/* flex-grow: 1; */ | |||
padding-bottom: 53px; | |||
/* padding-bottom: 53px; */ | |||
} | |||
</style> |
@@ -20,8 +20,9 @@ | |||
<span class="time-since poping up">{{.ModTime}}</span> | |||
</td> | |||
</tr> | |||
{{end}} | |||
</tbody> | |||
</table> | |||
{{end}} | |||
{{end}} |
@@ -1,102 +1,114 @@ | |||
{{template "base/head" .}} | |||
<style> | |||
.repository.file.list #repo-desc { | |||
font-size: 1.0em; | |||
margin-bottom: 1.0rem; | |||
} | |||
#contributorInfo > a:nth-child(n+26){ | |||
display:none; | |||
} | |||
#contributorInfo > a{ | |||
width: 2.0em; | |||
float: left; | |||
margin: .25em; | |||
} | |||
.edit-link{ | |||
vertical-align: top; | |||
display: inline-block; | |||
overflow: hidden; | |||
word-break: keep-all; | |||
white-space: nowrap; | |||
text-overflow: ellipsis; | |||
width: 16.5em; | |||
} | |||
#contributorInfo > a.circular{ | |||
height: 2.0em; | |||
padding: 0; | |||
overflow: hidden; | |||
letter-spacing:1.0em; | |||
text-indent: 0.6em; | |||
line-height: 2.0em; | |||
text-transform:capitalize; | |||
color: #FFF; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+1){ | |||
background-color: #4ccdec; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+2){ | |||
background-color: #e0b265; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+3){ | |||
background-color: #d884b7; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+4){ | |||
background-color: #8c6bdc; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+5){ | |||
background-color: #3cb99f; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+6){ | |||
background-color: #6995b9; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+7){ | |||
background-color: #ab91a7; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+8){ | |||
background-color: #bfd0aa; | |||
} | |||
.vue_menu { | |||
cursor: auto; | |||
position: absolute; | |||
outline: none; | |||
top: 100%; | |||
margin: 0em; | |||
padding: 0em 0em; | |||
background: #fff; | |||
font-size: 1em; | |||
text-shadow: none; | |||
text-align: left; | |||
/* -webkit-box-shadow: 0px 2px 3px 0px rgb(34 36 38 / 15%); */ | |||
box-shadow: 0px 2px 3px 0px rgba(34, 36, 38, 0.15); | |||
border: 1px solid rgba(34,36,38,0.15); | |||
border-radius: 0.28571429rem; | |||
-webkit-transition: opacity 0.1s ease; | |||
transition: opacity 0.1s ease; | |||
z-index: 11; | |||
will-change: transform, opacity; | |||
width: 100% !important; | |||
-webkit-animation-iteration-count: 1; | |||
animation-iteration-count: 1; | |||
-webkit-animation-duration: 300ms; | |||
animation-duration: 300ms; | |||
-webkit-animation-timing-function: ease; | |||
animation-timing-function: ease; | |||
-webkit-animation-fill-mode: both; | |||
animation-fill-mode: both; | |||
} | |||
.repository.file.list #repo-desc { | |||
font-size: 1.0em; | |||
margin-bottom: 1.0rem; | |||
} | |||
#contributorInfo>a:nth-child(n+26) { | |||
display: none; | |||
} | |||
#contributorInfo>a { | |||
width: 2.0em; | |||
float: left; | |||
margin: .25em; | |||
} | |||
.edit-link { | |||
vertical-align: top; | |||
display: inline-block; | |||
overflow: hidden; | |||
word-break: keep-all; | |||
white-space: nowrap; | |||
text-overflow: ellipsis; | |||
width: 16.5em; | |||
} | |||
#contributorInfo>a.circular { | |||
height: 2.0em; | |||
padding: 0; | |||
overflow: hidden; | |||
letter-spacing: 1.0em; | |||
text-indent: 0.6em; | |||
line-height: 2.0em; | |||
text-transform: capitalize; | |||
color: #FFF; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+1) { | |||
background-color: #4ccdec; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+2) { | |||
background-color: #e0b265; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+3) { | |||
background-color: #d884b7; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+4) { | |||
background-color: #8c6bdc; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+5) { | |||
background-color: #3cb99f; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+6) { | |||
background-color: #6995b9; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+7) { | |||
background-color: #ab91a7; | |||
} | |||
#contributorInfo>a.circular:nth-child(9n+8) { | |||
background-color: #bfd0aa; | |||
} | |||
.vue_menu { | |||
cursor: auto; | |||
position: absolute; | |||
outline: none; | |||
top: 100%; | |||
margin: 0em; | |||
padding: 0em 0em; | |||
background: #fff; | |||
font-size: 1em; | |||
text-shadow: none; | |||
text-align: left; | |||
/* -webkit-box-shadow: 0px 2px 3px 0px rgb(34 36 38 / 15%); */ | |||
box-shadow: 0px 2px 3px 0px rgba(34, 36, 38, 0.15); | |||
border: 1px solid rgba(34, 36, 38, 0.15); | |||
border-radius: 0.28571429rem; | |||
-webkit-transition: opacity 0.1s ease; | |||
transition: opacity 0.1s ease; | |||
z-index: 11; | |||
will-change: transform, opacity; | |||
width: 100% !important; | |||
-webkit-animation-iteration-count: 1; | |||
animation-iteration-count: 1; | |||
-webkit-animation-duration: 300ms; | |||
animation-duration: 300ms; | |||
-webkit-animation-timing-function: ease; | |||
animation-timing-function: ease; | |||
-webkit-animation-fill-mode: both; | |||
animation-fill-mode: both; | |||
} | |||
</style> | |||
<div class="repository file list"> | |||
{{template "repo/header" .}} | |||
<div class="ui container"> | |||
<div class="ui negative message" style="display: none;"> | |||
</div> | |||
{{template "base/alert" .}} | |||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}} | |||
<!-- <div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none"> | |||
<!-- <div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none"> | |||
<div class="fourteen wide column"> | |||
<div class="field"> | |||
<div class="ui fluid multiple search selection dropdown"> | |||
@@ -114,33 +126,33 @@ | |||
</div> | |||
</div> --> | |||
{{end}} | |||
<div class="hide" id="validate_prompt"> | |||
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span> | |||
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span> | |||
</div> | |||
{{end}} | |||
<div class="hide" id="validate_prompt"> | |||
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span> | |||
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span> | |||
</div> | |||
<div class="ui repo-description stackable grid"> | |||
{{if .RepoSearchEnabled}} | |||
<div class="ui repo-search four wide column"> | |||
<form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get"> | |||
<div class="field"> | |||
<div class="ui action input"> | |||
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "repo.search.search_repo"}}"> | |||
<button class="ui icon button" type="submit"> | |||
<i class="search icon"></i> | |||
</button> | |||
</div> | |||
<div class="ui repo-search four wide column"> | |||
<form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get"> | |||
<div class="field"> | |||
<div class="ui action input"> | |||
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "repo.search.search_repo"}}"> | |||
<button class="ui icon button" type="submit"> | |||
<i class="search icon"></i> | |||
</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
{{end}} | |||
</div> | |||
{{if .Repository.IsArchived}} | |||
<div class="ui warning message"> | |||
{{.i18n.Tr "repo.archive.title"}} | |||
</div> | |||
<div class="ui warning message"> | |||
{{.i18n.Tr "repo.archive.title"}} | |||
</div> | |||
{{end}} | |||
{{template "repo/sub_menu" .}} | |||
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins"> | |||
@@ -149,97 +161,122 @@ | |||
{{ $l := Subtract $n 1}} | |||
<!-- If home page, show new PR. If not, show breadcrumb --> | |||
{{if eq $n 0}} | |||
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} | |||
<div class="fitted item"> | |||
<a href="{{.BaseRepo.Link}}/compare/{{.BaseRepo.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.BranchName | EscapePound}}"> | |||
<button id="new-pull-request" class="ui compact basic button">{{if .PullRequestCtx.Allowed}}{{.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{.i18n.Tr "action.compare_branch"}}{{end}}</button> | |||
</a> | |||
{{if and .Repository.IsFork .PullRequestCtx.Allowed}} | |||
{{if gt .FetchUpstreamCnt 0 }} | |||
<a href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{if .UpstreamSameBranchName}}{{.BranchName | EscapePound}}{{else}}{{.BaseRepo.DefaultBranch | EscapePound}}{{end}}"> | |||
<button id="new-pull-request" class="ui compact basic button" title="{{$.i18n.Tr (TrN $.i18n.Lang .FetchUpstreamCnt "repo.pulls.commits_count_1" "repo.pulls.commits_count_n") .FetchUpstreamCnt}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button> | |||
</a> | |||
{{else if lt .FetchUpstreamCnt 0}} | |||
<a href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{.BaseRepo.DefaultBranch | EscapePound}}"> | |||
<button id="new-pull-request" class="ui compact basic button" title="{{.i18n.Tr "repo.pulls.upstream_error"}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button> | |||
</a> | |||
{{else}} | |||
<a href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{if .UpstreamSameBranchName}}{{.BranchName | EscapePound}}{{else}}{{.BaseRepo.DefaultBranch | EscapePound}}{{end}}"> | |||
<button id="new-pull-request" class="ui compact basic button" title="{{.i18n.Tr "repo.pulls.upstream_up_to_date"}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button> | |||
</a> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} | |||
<div class="fitted item"> | |||
<a | |||
href="{{.BaseRepo.Link}}/compare/{{.BaseRepo.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.BranchName | EscapePound}}"> | |||
<button id="new-pull-request" | |||
class="ui compact basic button">{{if .PullRequestCtx.Allowed}}{{.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{.i18n.Tr "action.compare_branch"}}{{end}}</button> | |||
</a> | |||
{{if and .Repository.IsFork .PullRequestCtx.Allowed}} | |||
{{if gt .FetchUpstreamCnt 0 }} | |||
<a | |||
href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{if .UpstreamSameBranchName}}{{.BranchName | EscapePound}}{{else}}{{.BaseRepo.DefaultBranch | EscapePound}}{{end}}"> | |||
<button id="new-pull-request" class="ui compact basic button" | |||
title="{{$.i18n.Tr (TrN $.i18n.Lang .FetchUpstreamCnt "repo.pulls.commits_count_1" "repo.pulls.commits_count_n") .FetchUpstreamCnt}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button> | |||
</a> | |||
{{else if lt .FetchUpstreamCnt 0}} | |||
<a | |||
href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{.BaseRepo.DefaultBranch | EscapePound}}"> | |||
<button id="new-pull-request" class="ui compact basic button" | |||
title="{{.i18n.Tr "repo.pulls.upstream_error"}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button> | |||
</a> | |||
{{else}} | |||
<a | |||
href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{if .UpstreamSameBranchName}}{{.BranchName | EscapePound}}{{else}}{{.BaseRepo.DefaultBranch | EscapePound}}{{end}}"> | |||
<button id="new-pull-request" class="ui compact basic button" | |||
title="{{.i18n.Tr "repo.pulls.upstream_up_to_date"}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button> | |||
</a> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
{{end}} | |||
{{else}} | |||
<div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}" title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section" title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | |||
<div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" | |||
href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}" | |||
title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span | |||
class="divider">/</span>{{if eq $i $l}}<span class="active section" | |||
title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span | |||
class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" | |||
title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | |||
{{end}} | |||
<div class="right fitted item" id="file-buttons"> | |||
<div class="ui tiny blue buttons"> | |||
{{if .Repository.CanEnableEditor}} | |||
{{if .CanAddFile}} | |||
<a href="{{.RepoLink}}/_new/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button"> | |||
{{.i18n.Tr "repo.editor.new_file"}} | |||
</a> | |||
{{end}} | |||
{{if .CanUploadFile}} | |||
<a href="{{.RepoLink}}/_upload/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button"> | |||
{{.i18n.Tr "repo.editor.upload_file"}} | |||
</a> | |||
{{end}} | |||
{{if .CanAddFile}} | |||
<a href="{{.RepoLink}}/_new/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" | |||
class="ui button"> | |||
{{.i18n.Tr "repo.editor.new_file"}} | |||
</a> | |||
{{end}} | |||
{{if .CanUploadFile}} | |||
<a href="{{.RepoLink}}/_upload/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" | |||
class="ui button"> | |||
{{.i18n.Tr "repo.editor.upload_file"}} | |||
</a> | |||
{{end}} | |||
{{end}} | |||
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }} | |||
<a href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}" class="ui button"> | |||
{{.i18n.Tr "repo.file_history"}} | |||
</a> | |||
<a href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}" | |||
class="ui button"> | |||
{{.i18n.Tr "repo.file_history"}} | |||
</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
<div class="fitted item"> | |||
{{if eq $n 0}} | |||
{{if .Repository.IsTemplate}} | |||
<div class="ui tiny blue buttons"> | |||
<a href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}" class="ui button"> | |||
{{.i18n.Tr "repo.use_template"}} | |||
</a> | |||
</div> | |||
{{end}} | |||
{{if .Repository.IsTemplate}} | |||
<div class="ui tiny blue buttons"> | |||
<a href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}" class="ui button"> | |||
{{.i18n.Tr "repo.use_template"}} | |||
</a> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
<div class="fitted item"> | |||
<!-- Only show clone panel in repository home page --> | |||
{{if eq $n 0}} | |||
<div class="ui action tiny input" id="clone-panel"> | |||
{{if not $.DisableHTTP}} | |||
<button class="ui basic clone button" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}"> | |||
{{if UseHTTPS}}HTTPS{{else}}HTTP{{end}} | |||
</button> | |||
{{end}} | |||
{{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}} | |||
<button class="ui basic clone button" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}"> | |||
SSH | |||
</button> | |||
{{end}} | |||
{{if not $.DisableHTTP}} | |||
<input id="repo-clone-url" value="{{$.CloneLink.HTTPS}}" readonly> | |||
{{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}} | |||
<input id="repo-clone-url" value="{{$.CloneLink.SSH}}" readonly> | |||
{{end}} | |||
{{if or (not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH))}} | |||
<button class="ui basic icon button poping up clipboard" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}" data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-target="#repo-clone-url"> | |||
{{svg "octicon-clippy" 16}} | |||
</button> | |||
{{end}} | |||
<div class="ui basic jump dropdown icon button poping up" data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted" data-position="top right"> | |||
<i class="download icon"></i> | |||
<div class="menu"> | |||
<a class="item" href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.zip">{{svg "octicon-file-zip" 16}} ZIP</a> | |||
<a class="item" href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.tar.gz">{{svg "octicon-file-zip" 16}} TAR.GZ</a> | |||
</div> | |||
<div class="ui action tiny input" id="clone-panel"> | |||
{{if not $.DisableHTTP}} | |||
<button class="ui basic clone button" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}"> | |||
{{if UseHTTPS}}HTTPS{{else}}HTTP{{end}} | |||
</button> | |||
{{end}} | |||
{{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}} | |||
<button class="ui basic clone button" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}"> | |||
SSH | |||
</button> | |||
{{end}} | |||
{{if not $.DisableHTTP}} | |||
<input id="repo-clone-url" value="{{$.CloneLink.HTTPS}}" readonly> | |||
{{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}} | |||
<input id="repo-clone-url" value="{{$.CloneLink.SSH}}" readonly> | |||
{{end}} | |||
{{if or (not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH))}} | |||
<button class="ui basic icon button poping up clipboard" id="clipboard-btn" | |||
data-original="{{.i18n.Tr "repo.copy_link"}}" | |||
data-success="{{.i18n.Tr "repo.copy_link_success"}}" | |||
data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" | |||
data-variation="inverted tiny" data-clipboard-target="#repo-clone-url"> | |||
{{svg "octicon-clippy" 16}} | |||
</button> | |||
{{end}} | |||
<div class="ui basic jump dropdown icon button poping up" | |||
data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted" | |||
data-position="top right"> | |||
<i class="download icon"></i> | |||
<div class="menu"> | |||
<a class="item" | |||
href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.zip">{{svg "octicon-file-zip" 16}} ZIP</a> | |||
<a class="item" | |||
href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.tar.gz">{{svg "octicon-file-zip" 16}} TAR.GZ</a> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
@@ -247,22 +284,24 @@ | |||
<div class="ui mobile reversed stackable grid"> | |||
<div class="ui ten wide tablet twelve wide computer column"> | |||
{{if .IsViewFile}} | |||
{{template "repo/view_file" .}} | |||
{{template "repo/view_file" .}} | |||
{{else if .IsBlame}} | |||
{{template "repo/blame" .}} | |||
{{template "repo/blame" .}} | |||
{{else}} | |||
{{template "repo/view_list" .}} | |||
{{template "repo/view_list" .}} | |||
{{end}} | |||
</div> | |||
<div class="ui six wide tablet four wide computer column"> | |||
<div id="repo-desc" data-IsAdmin= "{{.Permission.IsAdmin}}" data-IsArchived="{{.Repository.IsArchived}}" > | |||
<h4 id="about-desc" class="ui header">简介</h4> | |||
<input type="hidden" id="edit-alias" value="{{.Repository.Alias}}"> | |||
<div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}" | |||
data-IsArchived="{{.Repository.IsArchived}}"> | |||
<h4 id="about-desc" class="ui header">简介</h4> | |||
<input type="hidden" id="edit-alias" value="{{.Repository.Alias}}"> | |||
<p> | |||
{{if .Repository.DescriptionHTML}} | |||
<span class="description" style="word-break:break-all">{{.Repository.DescriptionHTML}}</span> | |||
<span class="description" | |||
style="word-break:break-all">{{.Repository.DescriptionHTML}}</span> | |||
{{else}} | |||
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||
{{end}} | |||
</p> | |||
@@ -274,7 +313,8 @@ | |||
{{if .Repository.Website}} | |||
<p class="ui"> | |||
<i class="gray linkify icon"></i> | |||
<a class="link edit-link" target="_blank" title="{{.Repository.Website}}" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||
<a class="link edit-link" target="_blank" title="{{.Repository.Website}}" | |||
href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||
</p> | |||
{{end}} | |||
@@ -284,11 +324,13 @@ | |||
<div id="repo-topics1" style="flex: 1;"> | |||
{{range .Topics}} | |||
<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a> | |||
<a class="ui repo-topic small label topic" | |||
href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a> | |||
{{end}} | |||
</div> | |||
<div> | |||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i id="manage_topic" style="cursor: pointer;" class="plus icon"></i>{{end}} | |||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i id="manage_topic" | |||
style="cursor: pointer;" class="plus icon"></i>{{end}} | |||
</div> | |||
<div id="topic_edit" class="vue_menu" style="display:none"> | |||
<div id="topic_edit1"> | |||
@@ -302,7 +344,7 @@ | |||
<p class="ui"> | |||
<i class="grey code icon"></i> | |||
{{range .LanguageStats}} | |||
{{.Language}} | |||
{{.Language}} | |||
{{end}} | |||
</p> | |||
@@ -311,7 +353,7 @@ | |||
{{if .LICENSE}} | |||
<p class="ui"> | |||
<i class="grey clone icon"></i> | |||
{{.LICENSE}} | |||
{{.LICENSE}} | |||
</p> | |||
{{end}} | |||
@@ -326,19 +368,22 @@ | |||
{{else}} | |||
<strong>贡献者 ({{len .ContributorInfo}}+)</strong> | |||
{{end}} | |||
<div class="ui right"> | |||
<!-- <a class="membersmore text grey" href="{{.RepoLink}}/contributors">全部 {{svg "octicon-chevron-right" 16}}</a> --> | |||
<a class="membersmore text grey" href="{{.RepoLink}}/contributors?type={{if .IsViewBranch}}branch{{else}}tag{{end}}&name={{.BranchName}}">全部 {{svg "octicon-chevron-right" 16}}</a> | |||
<a class="membersmore text grey" | |||
href="{{.RepoLink}}/contributors?type={{if .IsViewBranch}}branch{{else}}tag{{end}}&name={{.BranchName}}">全部 | |||
{{svg "octicon-chevron-right" 16}}</a> | |||
</div> | |||
</h4> | |||
<div class="ui members" id="contributorInfo"> | |||
{{range .ContributorInfo}} | |||
{{if .UserInfo}} | |||
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}"></a> | |||
{{else if .Email}} | |||
<a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a> | |||
{{end}} | |||
{{if .UserInfo}} | |||
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" | |||
src="{{.UserInfo.RelAvatarLink}}"></a> | |||
{{else if .Email}} | |||
<a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
</div> | |||
@@ -357,4 +402,4 @@ | |||
// }); | |||
// }); | |||
</script> | |||
{{template "base/footer" .}} | |||
{{template "base/footer" .}} |
@@ -1,109 +1,200 @@ | |||
<table id="repo-files-table" class="ui single line table"> | |||
<style> | |||
.context-menu { | |||
z-index: 99; | |||
position: absolute; | |||
padding: 0; | |||
border-radius: 4px; | |||
border: 1px solid #e3e9ed; | |||
-webkit-box-shadow: none; | |||
box-shadow: none; | |||
background: #fff; | |||
display: none !important; | |||
} | |||
.context-menu.active { | |||
display: block !important; | |||
} | |||
.context-menu-operation { | |||
padding: 5px !important; | |||
line-height: 1.78 !important; | |||
} | |||
.context-menu-icon { | |||
float: left !important; | |||
margin: 0px 5px 0px 0px !important; | |||
} | |||
</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> | |||
<table id="repo-files-table" class="ui single line table can-context-menu" data-can-editfile="{{.CanEditFile}}"> | |||
{{.CsrfTokenHtml}} | |||
<thead> | |||
<tr class="commit-list"> | |||
<th colspan="2"> | |||
{{if .LatestCommitUser}} | |||
<img class="ui avatar image img-12" src="{{.LatestCommitUser.RelAvatarLink}}" /> | |||
{{if .LatestCommitUser.FullName}} | |||
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> | |||
{{else}} | |||
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a> | |||
{{end}} | |||
<img class="ui avatar image img-12" src="{{.LatestCommitUser.RelAvatarLink}}" /> | |||
{{if .LatestCommitUser.FullName}} | |||
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> | |||
{{else}} | |||
{{if .LatestCommit.Author}} | |||
<img class="ui avatar image img-12" src="{{AvatarLink .LatestCommit.Author.Email}}" /> | |||
<strong>{{.LatestCommit.Author.Name}}</strong> | |||
{{end}} | |||
<a | |||
href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a> | |||
{{end}} | |||
<a rel="nofollow" class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified }} isVerified{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}Untrusted{{else}}Unmatched{{end}}{{else if .LatestCommitVerification.Warning}} isWarning{{end}}{{end}}" href="{{.RepoLink}}/commit/{{.LatestCommit.ID}}"> | |||
{{else}} | |||
{{if .LatestCommit.Author}} | |||
<img class="ui avatar image img-12" src="{{AvatarLink .LatestCommit.Author.Email}}" /> | |||
<strong>{{.LatestCommit.Author.Name}}</strong> | |||
{{end}} | |||
{{end}} | |||
<a rel="nofollow" | |||
class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified }} isVerified{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}Untrusted{{else}}Unmatched{{end}}{{else if .LatestCommitVerification.Warning}} isWarning{{end}}{{end}}" | |||
href="{{.RepoLink}}/commit/{{.LatestCommit.ID}}"> | |||
<span class="shortsha">{{ShortSha .LatestCommit.ID.String}}</span> | |||
{{if .LatestCommit.Signature}} | |||
<div class="ui detail icon button"> | |||
{{if .LatestCommitVerification.Verified}} | |||
<div title="{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}{{.i18n.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.LatestCommitVerification.Reason}}"> | |||
{{if ne .LatestCommitVerification.SigningUser.ID 0}} | |||
<i class="lock icon"></i> | |||
<img class="ui signature avatar image" src="{{.LatestCommitVerification.SigningUser.RelAvatarLink}}" /> | |||
{{else}} | |||
<i title="{{.LatestCommitVerification.Reason}}" class="icons"> | |||
<i class="lock icon"></i> | |||
<i class="tiny inverted cog icon centerlock"></i> | |||
</i> | |||
<img class="ui signature avatar image" src="{{AvatarLink .LatestCommitVerification.SigningEmail}}" /> | |||
{{end}} | |||
</div> | |||
<div class="ui detail icon button"> | |||
{{if .LatestCommitVerification.Verified}} | |||
<div | |||
title="{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}{{.i18n.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.LatestCommitVerification.Reason}}"> | |||
{{if ne .LatestCommitVerification.SigningUser.ID 0}} | |||
<i class="lock icon"></i> | |||
<img class="ui signature avatar image" | |||
src="{{.LatestCommitVerification.SigningUser.RelAvatarLink}}" /> | |||
{{else}} | |||
<i title="{{$.i18n.Tr .LatestCommitVerification.Reason}}" class="unlock icon"></i> | |||
<i title="{{.LatestCommitVerification.Reason}}" class="icons"> | |||
<i class="lock icon"></i> | |||
<i class="tiny inverted cog icon centerlock"></i> | |||
</i> | |||
<img class="ui signature avatar image" | |||
src="{{AvatarLink .LatestCommitVerification.SigningEmail}}" /> | |||
{{end}} | |||
</div> | |||
{{else}} | |||
<i title="{{$.i18n.Tr .LatestCommitVerification.Reason}}" class="unlock icon"></i> | |||
{{end}} | |||
</div> | |||
{{end}} | |||
</a> | |||
{{template "repo/commit_status" .LatestCommitStatus}} | |||
{{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }} | |||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> | |||
{{if IsMultilineCommitMessage .LatestCommit.Message}} | |||
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> | |||
<pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> | |||
{{end}} | |||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span | |||
class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> | |||
{{if IsMultilineCommitMessage .LatestCommit.Message}} | |||
<button class="basic compact mini ui icon button commit-button"><i | |||
class="ellipsis horizontal icon"></i></button> | |||
<pre class="commit-body" | |||
style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> | |||
{{end}} | |||
</span> | |||
</th> | |||
<th class="text grey right age">{{if .LatestCommit.Author}}{{TimeSince .LatestCommit.Author.When $.Lang}}{{end}}</th> | |||
<th class="text grey right age"> | |||
{{if .LatestCommit.Author}}{{TimeSince .LatestCommit.Author.When $.Lang}}{{end}}</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{{if .HasParentPath}} | |||
<tr class="has-parent"> | |||
<td colspan="3">{{svg "octicon-mail-reply" 16}}<a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a></td> | |||
</tr> | |||
<tr class="has-parent"> | |||
<td colspan="3">{{svg "octicon-mail-reply" 16}}<a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a> | |||
</td> | |||
</tr> | |||
{{end}} | |||
{{range $item := .Files}} | |||
{{$entry := index $item 0}} | |||
{{$commit := index $item 1}} | |||
<tr> | |||
{{if $entry.IsSubModule}} | |||
<td> | |||
<span class="truncate"> | |||
{{svg "octicon-inbox" 16}} | |||
{{$refURL := $commit.RefURL AppUrl $.Repository.FullName}} | |||
{{if $refURL}} | |||
<a href="{{$refURL}}">{{$entry.Name}}</a> @ <a href="{{$refURL}}/commit/{{$commit.RefID}}">{{ShortSha $commit.RefID}}</a> | |||
{{else}} | |||
{{$entry.Name}} @ {{ShortSha $commit.RefID}} | |||
{{end}} | |||
</span> | |||
</td> | |||
{{else}} | |||
<td class="name four wide"> | |||
<span class="truncate"> | |||
{{if $entry.IsDir}} | |||
{{$subJumpablePathName := $entry.GetSubJumpablePathName}} | |||
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}} | |||
{{svg "octicon-file-directory" 16}} | |||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $subJumpablePathName}}" title="{{$subJumpablePathName}}"> | |||
{{if eq (len $subJumpablePath) 2}} | |||
<span class="jumpable-path">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}} | |||
{{else}} | |||
{{index $subJumpablePath 0}} | |||
{{end}} | |||
</a> | |||
{{else}} | |||
{{svg (printf "octicon-%s" (EntryIcon $entry)) 16}} | |||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}" title="{{$entry.Name}}">{{$entry.Name}}</a> | |||
{{end}} | |||
</span> | |||
</td> | |||
{{end}} | |||
<td class="message nine wide"> | |||
<span class="truncate"> | |||
<a href="{{$.RepoLink}}/commit/{{$commit.ID}}" title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a> | |||
</span> | |||
</td> | |||
<td class="text right age three wide">{{TimeSince $commit.Committer.When $.Lang}}</td> | |||
</tr> | |||
{{$entry := index $item 0}} | |||
{{$commit := index $item 1}} | |||
<tr> | |||
{{if $entry.IsSubModule}} | |||
<td> | |||
<span class="truncate"> | |||
{{svg "octicon-inbox" 16}} | |||
{{$refURL := $commit.RefURL AppUrl $.Repository.FullName}} | |||
{{if $refURL}} | |||
<a href="{{$refURL}}">{{$entry.Name}}</a> @ <a | |||
href="{{$refURL}}/commit/{{$commit.RefID}}">{{ShortSha $commit.RefID}}</a> | |||
{{else}} | |||
{{$entry.Name}} @ {{ShortSha $commit.RefID}} | |||
{{end}} | |||
</span> | |||
</td> | |||
{{else}} | |||
<td class="name four wide modified-contextmenu"> | |||
<span class="truncate"> | |||
{{if $entry.IsDir}} | |||
{{$subJumpablePathName := $entry.GetSubJumpablePathName}} | |||
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}} | |||
{{svg "octicon-file-directory" 16}} | |||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $subJumpablePathName}}" | |||
title="{{$subJumpablePathName}}"> | |||
{{if eq (len $subJumpablePath) 2}} | |||
<span class="jumpable-path">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}} | |||
{{else}} | |||
{{index $subJumpablePath 0}} | |||
{{end}} | |||
</a> | |||
{{else}} | |||
{{svg (printf "octicon-%s" (EntryIcon $entry)) 16}} | |||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}" | |||
title="{{$entry.Name}}">{{$entry.Name}}</a> | |||
{{end}} | |||
</span> | |||
</td> | |||
{{end}} | |||
<td class="message nine wide"> | |||
<span class="truncate"> | |||
<a href="{{$.RepoLink}}/commit/{{$commit.ID}}" | |||
title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a> | |||
</span> | |||
</td> | |||
<td class="text right age three wide">{{TimeSince $commit.Committer.When $.Lang}}</td> | |||
</tr> | |||
<tr style="display: none !important;" class="context-menu-one"> | |||
<td colspan="12"> | |||
<div class="ui column form" method="POST"> | |||
<div class="two fields" style="margin: 0;"> | |||
<div class="five wide field"> | |||
<input class="ui input" name="new_filename" type="text" value=""> | |||
</div> | |||
<div class="five wide field"> | |||
<button class="ui blue button popup-save" type="button" | |||
data-postBasePath="{{$.RepoLink}}/_rename/{{EscapePound $.BranchName}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}/{{$entry.Name}}" | |||
data-commit="{{$.LatestCommit.ID}}" | |||
data-treepath="{{if $.TreePath}}{{EscapePound $.TreePath}}/{{end}}">保存</button> | |||
<button class="ui basic button popup-close" type="button">取消</button> | |||
</div> | |||
</div> | |||
</div> | |||
</td> | |||
</tr> | |||
{{end}} | |||
</tbody> | |||
</table> | |||
{{if .ReadmeExist}} | |||
{{template "repo/view_file" .}} | |||
{{template "repo/view_file" .}} | |||
{{end}} | |||
<!-- 确认模态框 --> | |||
<div id="deletemodel"> | |||
<div class="ui basic modal context-menu-delete"> | |||
<div class="ui icon header"> | |||
<i class="trash icon"></i> {{.i18n.Tr "cloudbrain.delete_task"}} | |||
</div> | |||
<div class="content"> | |||
<p>{{.i18n.Tr "cloudbrain.task_delete_confirm"}}</p> | |||
</div> | |||
<div class="actions"> | |||
<div class="ui red basic inverted cancel button"> | |||
<i class="remove icon"></i> {{.i18n.Tr "cloudbrain.operate_cancel"}} | |||
</div> | |||
<div class="ui green basic inverted ok button"> | |||
<i class="checkmark icon"></i> {{.i18n.Tr "cloudbrain.operate_confirm"}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -1,108 +1,61 @@ | |||
<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_rgb.svg"> | |||
</el-image> | |||
概览 | |||
</span> | |||
<div >暂无内容.......</div> | |||
<el-row style="height:100%;width: 100%; flex:1" > | |||
<el-col :span="3" style="height:100%;padding-right:15px;"> | |||
<el-menu | |||
:default-active="this.$router.path" | |||
class="el-menu-vertical-demo" | |||
:router="true" style="height:100%; background-color: #F5F5F6;" > | |||
<el-menu-item index="/Overview" > | |||
<i class="ri-home-4-line"></i> | |||
<span slot="title">概览</span> | |||
</el-menu-item> | |||
<el-submenu index="/"> | |||
<template slot="title"> | |||
<i class="ri-numbers-line"></i> | |||
<span>项目分析</span> | |||
</template> | |||
<el-menu-item index="/ProTrend">增长趋势分析</el-menu-item> | |||
<el-menu-item index="/ProAnalysis">详细数据</el-menu-item> | |||
</el-submenu> | |||
<el-submenu index="2"> | |||
<template slot="title"> | |||
<i class="ri-contacts-line"></i> | |||
<span>用户分析</span> | |||
</template> | |||
<el-menu-item index="/UserTrend">增长趋势分析</el-menu-item> | |||
<el-menu-item index="/UserAnalysis">活动分析</el-menu-item> | |||
</el-submenu> | |||
<el-menu-item index="/BrainAnalysis"> | |||
<i class="ri-server-fill"></i> | |||
<span slot="title">云脑分析(建设中..)</span> | |||
</el-menu-item> | |||
</el-menu> | |||
</el-col> | |||
<router-view> </router-view> | |||
</el-row> | |||
</template> | |||
</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_rgb.svg"> | |||
</el-image> | |||
项目分析 | |||
</span> | |||
</el-tab-pane> | |||
<el-tab-pane name="third" id='third' > | |||
<span slot='label'> | |||
<el-image style="width: 13px; height: 13px" src="/img/user_rgb.svg"> | |||
</el-image> | |||
用户分析 | |||
</span> | |||
<UserAnalysis ref='UserAnalysis' v-if="isRouterAlive1" id ="usr"></UserAnalysis> | |||
</el-tab-pane> | |||
<el-tab-pane name="four" id='four' > | |||
<BrainAnalysis ref='BrainAnalysis'id="brain" v-if="isRouterAlive"></BrainAnalysis> | |||
<span slot="label"> | |||
<el-image style="width: 13px; height: 13px" src="/img/pro_rgb.svg"> | |||
</el-image> | |||
云脑分析(建设中..) | |||
</span> | |||
</el-tab-pane> | |||
</el-tabs> | |||
</div> | |||
</template> | |||
<script> | |||
import ProAnalysis from './ProAnalysis.vue' | |||
import UserAnalysis from './UserAnalysis.vue' | |||
import BrainAnalysis from './BrainAnalysis.vue' | |||
export default { | |||
components:{ | |||
'ProAnalysis':ProAnalysis, | |||
'UserAnalysis':UserAnalysis, | |||
'BrainAnalysis':BrainAnalysis, | |||
}, | |||
data() { | |||
return { | |||
activeName:"second", | |||
loading:true, | |||
loading1:true, | |||
isRouterAlive: true, | |||
isRouterAlive1: true, | |||
isSecond:true, | |||
isThird:false, | |||
} | |||
}, | |||
methods:{ | |||
handleClick(tab, event){ | |||
if(tab.name=="second"){ | |||
this.reload() | |||
this.isSecond = true | |||
this.isThird = false | |||
this.$refs.ProAnalysis.getAllProList("all",7) | |||
} | |||
if(tab.name=="third"){ | |||
this.reload1() | |||
this.isSecond = false | |||
this.isThird = true | |||
this.$refs.UserAnalysis.getUpdateTime() | |||
this.$refs.UserAnalysis.getUserList("all_usr",7) | |||
} | |||
}, | |||
reload () { | |||
this.isRouterAlive = false | |||
this.$nextTick(() => (this.isRouterAlive = true)) | |||
}, | |||
reload1 () { | |||
this.isRouterAlive1 = false | |||
this.$nextTick(() => (this.isRouterAlive1 = true)) | |||
} | |||
}, | |||
} | |||
</script> | |||
<style scoped> | |||
<script> | |||
export default{ | |||
data(){ | |||
return { | |||
Path_router:'/' | |||
} | |||
}, | |||
created(){ | |||
var url_params = document.getElementById("url_params").value; | |||
if (url_params!='' && url_params!=undefined && url_params!='/'){ | |||
this.$router.path = '/'+url_params.split('/')[0] | |||
}else{ | |||
this.$router.path = '/ProAnalysis' | |||
} | |||
}, | |||
} | |||
</script> | |||
<style scoped> | |||
/deep/ .is-active{ | |||
color: #238BFC ; | |||
background-color: #FFFF ; | |||
color: #238BFC ; | |||
} | |||
/deep/ .ui-container{ | |||
background-color: #FFFF; | |||
@@ -137,8 +90,26 @@ | |||
/deep/ .el-tabs__item:hover .el-image{ | |||
filter:none | |||
} | |||
/deep/ .el-image{ | |||
filter:grayscale(100%) | |||
.bk{ | |||
background-color: #F5F5F6; | |||
} | |||
.el-menu-item.is-active { | |||
color: #409eff; | |||
background-color: #FFFFFF !important; | |||
} | |||
/deep/ .el-submenu.is-active .el-submenu__title { | |||
color: #409eff | |||
} | |||
/deep/ .el-submenu.is-active .el-submenu__title i{ | |||
color: #409eff | |||
} | |||
/deep/ .el-menu, .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .el-menu--horizontal>.el-submenu .el-submenu__title:hover { | |||
background-color: #F5F5F6; | |||
} | |||
/deep/ .el-pagination { | |||
padding-bottom: 30px; | |||
} | |||
</style> | |||
</style> |
@@ -0,0 +1,5 @@ | |||
<template> | |||
<div> | |||
暂无内容 | |||
</div> | |||
</template> |
@@ -1,8 +1,8 @@ | |||
<template> | |||
<div style="width: 100%;"> | |||
<div class="el-col el-col-21" style="padding-right:10px " > | |||
<div id = "pro_main"> | |||
<div style="margin-top: 10px;"> | |||
<b class="pro_item">项目分析</b> <span class="update_time">数据更新时间:</span> <span style="font-size: 12px;">{{lastUpdatedTime}} / 从{{recordBeginTime}}开始统计</span> | |||
<b class="pro_item">详细数据</b> <span class="update_time">数据更新时间:</span> <span style="font-size: 12px;">{{lastUpdatedTime}} / 从{{recordBeginTime}}开始统计</span> | |||
</div> | |||
<bar-label :width="'95%'" :height="'500px'"></bar-label> | |||
@@ -29,11 +29,11 @@ | |||
</span> | |||
<span style="float:right; margin-right: 20px;"> | |||
<div style="display:inline-block;margin-left: 20px; "> | |||
<a class="el-icon-download" v-if="tableData!=''" :href= "'../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'" ></a> | |||
<a class="el-icon-download" v-if="tableData!=''" :href= "'../../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'" ></a> | |||
<i class="el-icon-download" v-else="tableData=''" href="#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'></i> | |||
<!-- <span ><a id = "download_file" :href= "'../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'" >下载报告</a> </span> --> | |||
<span > | |||
<a id = "download_file" v-if="tableData!=''" :href= "'../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'">下载报告</a> | |||
<a id = "download_file" v-if="tableData!=''" :href= "'../../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'">下载报告</a> | |||
<a id = "download_file" v-else="tableData=''" href= "#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'>下载报告</a> | |||
</span> | |||
</div> | |||
@@ -59,6 +59,7 @@ | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
fixed | |||
label="项目名称中文" | |||
align="left" | |||
prop="name" | |||
@@ -404,8 +405,7 @@ | |||
value_time: '', | |||
search:'', | |||
dynamic:7, | |||
download_a:"", | |||
downLoadSrc:'', | |||
//单个项目参数 | |||
@@ -652,7 +652,7 @@ | |||
getAllProList(type_val,index){ | |||
console.log("类型:"+type_val) | |||
// console.log("类型:"+type_val) | |||
this.dynamic = index | |||
if (typeof type_val=="undefined" || type_val=="null" || type_val==""){ | |||
this.params.type='' | |||
@@ -665,7 +665,7 @@ | |||
this.value_time=[] | |||
} | |||
this.$axios.get('../api/v1/projectboard/project',{ | |||
this.$axios.get('../../api/v1/projectboard/project',{ | |||
params:this.params | |||
}).then((res)=>{ | |||
@@ -720,7 +720,7 @@ | |||
}, | |||
getOneProData(pro_id){ | |||
this.$axios.get('../api/v1/projectboard/project/'+pro_id,{ | |||
this.$axios.get('../../api/v1/projectboard/project/'+pro_id,{ | |||
}).then((res)=>{ | |||
this.tableDataIDTotal = res.data | |||
this.tableDataContTop10=res.data.top10 | |||
@@ -731,7 +731,7 @@ | |||
}, | |||
getOneProList(pro_id,type_val,bool_val,index){ | |||
this.dynamic_pro=index | |||
console.log("日期类型:"+type_val) | |||
// 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()) | |||
@@ -743,7 +743,7 @@ | |||
this.paramsID.endTime='' | |||
} | |||
this.paramsID.openi=bool_val | |||
this.$axios.get('../api/v1/projectboard/project/'+pro_id+"/period",{ | |||
this.$axios.get('../../api/v1/projectboard/project/'+pro_id+"/period",{ | |||
params:this.paramsID | |||
}).then((res)=>{ | |||
if (bool_val){ | |||
@@ -923,12 +923,24 @@ | |||
type : 'category', | |||
boundaryGap: false, | |||
data : xdata_openI, | |||
axisLine: { | |||
show: false, //x轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
yAxis : [ | |||
{ | |||
type : 'value', | |||
axisLine: { | |||
show: false, //x轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
@@ -1011,6 +1023,12 @@ | |||
}, | |||
legend: { | |||
data:['浏览量','下载量','commit'], | |||
selected:{ | |||
// '浏览量':true, | |||
// '下载量':true, | |||
// 'commit':true, | |||
} | |||
// orient: 'vertical', | |||
// top:'top', | |||
}, | |||
@@ -1029,12 +1047,24 @@ | |||
{ | |||
type : 'category', | |||
data : xdata, | |||
axisLine: { | |||
show: false, //x轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
yAxis : [ | |||
{ | |||
type : 'value', | |||
type : 'value', | |||
axisLine: { | |||
show: false, //x轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
series : [ | |||
@@ -1059,6 +1089,16 @@ | |||
] | |||
}; | |||
// this.echartsSelectData.resize() | |||
var checkboxs=document.getElementsByName('checkboxchart'); | |||
for(var i=0; i<checkboxs.length; i++){ | |||
// console.log("selectArr[i]:",this.option.legend.data[i]) | |||
if(checkboxs[i].checked){ | |||
this.option.legend.selected[this.option.legend.data[i]]=true; | |||
}else{ | |||
this.option.legend.selected[this.option.legend.data[i]]=false; | |||
} | |||
} | |||
this.echartsSelectData.setOption(this.option) | |||
// setTimeout(function (){ | |||
// window.onresize = function () { | |||
@@ -1299,6 +1339,9 @@ | |||
/deep/ .el-range-separator{ | |||
width: 20% !important; | |||
} | |||
/deep/ .el-pagination { | |||
padding-bottom: 30px; | |||
} | |||
.colorChange { | |||
background-color: #1684FC; | |||
@@ -0,0 +1,903 @@ | |||
<template> | |||
<div class="el-col el-col-21" style="padding-right:10px "> | |||
<div id='pro_tend' > | |||
<div style="margin-top: 10px;"> | |||
<b class="pro_item">增长趋势分析</b> <span class="update_time">数据更新时间:</span> <span style="font-size: 12px;">{{lastUpdatedTime}} / 从{{recordBeginTime}}开始统计</span> | |||
</div> | |||
<div style="margin-top:20px"> | |||
<el-row> | |||
<el-col :span='1' class ='item_list_first'> | |||
<el-row class="item_title_h"> | |||
| |||
</el-row> | |||
<el-row class="item_h"> | |||
昨天 | |||
</el-row> | |||
<el-row class="item_h"> | |||
累计 | |||
</el-row> | |||
</el-col> | |||
<el-col span='23' > | |||
<el-col :span='3' class ='item_list'> | |||
<el-row class="item_title_h"> | |||
项目 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numReposAdd}} | |||
</el-row> | |||
<el-row class="item_h"> | |||
{{ tableDataSummary.numRepos}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='3' > | |||
<el-row class ='item_list_p item_title_h'> | |||
公开 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoPublicAdd}} | |||
</el-row > | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoPublic}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='3' class ='item_list'> | |||
<el-row class="item_title_h"> | |||
私有 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoPrivateAdd}} | |||
</el-row> | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoPrivate}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='3'> | |||
<el-row class ='item_list_p item_title_h'> | |||
自建 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoSelfAdd}} | |||
</el-row > | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoSelf}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='3' > | |||
<el-row class ='item_list_p item_title_h'> | |||
派生 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoForkAdd}} | |||
</el-row > | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoFork}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='3' class ='item_list '> | |||
<el-row class="item_title_h"> | |||
镜像 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoMirrorAdd}} | |||
</el-row> | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoMirror}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='3'> | |||
<el-row class ='item_list_p item_title_h'> | |||
组织 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoOrgAdd}} | |||
</el-row> | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoOrg}} | |||
</el-row> | |||
</el-col> | |||
<el-col :span='2'> | |||
<el-row class="item_title_h"> | |||
个人 | |||
</el-row> | |||
<el-row class="item_h num_color"> | |||
{{tableDataSummary.numRepoNotOrgAdd}} | |||
</el-row> | |||
<el-row class="item_h"> | |||
{{tableDataSummary.numRepoNotOrg}} | |||
</el-row> | |||
</el-col> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
<div style="margin-top: 20px;"> | |||
<span class="sta_iterm">统计周期:</span> | |||
<!-- <button type="button" class='btnFirst' id ="yesterday" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getPeriodProList('yesterday',1)">昨天</button> --> | |||
<button type="button" class='btn' id = "current_week" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getPeriodProList('current_week',1)">本周</button> | |||
<button type="button" class='btn' id = "current_month" v-bind:class="{colorChange:2==dynamic}" @click="resetPage(),getPeriodProList('current_month',2)">本月</button> | |||
<button type="button" class='btn' id = "last_month" v-bind:class="{colorChange:3==dynamic}" @click="resetPage(),getPeriodProList('last_month',3)">上月</button> | |||
<button type="button" class='btn' id = "monthly" v-bind:class="{colorChange:4==dynamic}" @click="resetPage(),getPeriodProList('monthly',4)">近30天</button> | |||
<button type="button" class='btn' id = "current_year" v-bind:class="{colorChange:5==dynamic}" @click="resetPage(),getPeriodProList('current_year',5)">今年</button> | |||
<button type="button" class='btnLast' id = "all" v-bind:class="{colorChange:6==dynamic}" @click="resetPage(),getPeriodProList('all',6)">所有</button> | |||
<span style="margin-left: 20px;"> | |||
<el-date-picker | |||
v-model="value_time" | |||
prefix-icon="el-icon-time" | |||
@change="resetPage(),getPeriodProList('',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; "> | |||
<a class="el-icon-download" v-if="tableData!=''" :href= "'../../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'" ></a> | |||
<i class="el-icon-download" v-else="tableData=''" href="#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'></i> | |||
<!-- <span ><a id = "download_file" :href= "'../api/v1/projectboard/downloadAll/?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime+'&q='+this.params.q+'&sort=openi'" >下载报告</a> </span> --> | |||
<span > | |||
<a id = "download_file" v-if="tableData!=''" :href= "'../../api/v1/projectboard/summary/download?type='+this.params.type+'&beginTime='+this.params.beginTime+'&endTime='+this.params.endTime">下载报告</a> | |||
<a id = "download_file" v-else="tableData=''" href= "#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'>下载报告</a> | |||
</span> | |||
</div> | |||
</span> | |||
</div> | |||
<div class="item_echart" id ='linecharts'> | |||
<div style="margin: 15px 10px 30px;"> | |||
<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="新增私有项目"/>新增私有项目 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="新增自建项目"/>新增自建项目 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="新增派生项目"/>新增派生项目 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="新增镜像项目"/>新增镜像项目 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="累计项目"/>累计项目 | |||
</label> | |||
</div> | |||
<div id ="selectData" style="height: 300px;"> | |||
</div> | |||
</div> | |||
<div style="margin-top: 30px;"> | |||
<el-table | |||
:data="tableData.slice((page-1)*pageSize,page*pageSize)" | |||
style="width: 100%" | |||
:header-cell-style="tableHeaderStyle" | |||
:cell-style='cellStyle'> | |||
<el-table-column | |||
label="日期" | |||
align="left" | |||
prop="creatTime" | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
label="新增项目" | |||
align="center" | |||
prop="numReposAdd" | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
label="累计项目" | |||
align="center" | |||
prop="numRepos" | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
prop="numRepoPublicAdd" | |||
label="新增公开项目" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="numRepoPrivateAdd" | |||
label="新增私有项目" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="numRepoSelfAdd" | |||
label="新增自建项目" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="numRepoForkAdd" | |||
label="新增派生项目" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="numRepoMirrorAdd" | |||
label="新增镜像项目" | |||
align="center"> | |||
</el-table-column> | |||
</el-table> | |||
</div> | |||
<div style="margin-top:50px;text-align:center"> | |||
<el-pagination | |||
background | |||
@size-change="handleSizeChange" | |||
@current-change="handleCurrentChange" | |||
:current-page="page" | |||
:page-size="pageSize" | |||
:page-sizes="[5,10,20]" | |||
layout="total, sizes,prev, pager, next,jumper" | |||
:total="tableData.length"> | |||
</el-pagination> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script> | |||
// import barLabel from './basic/barLabel.vue'; | |||
const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; | |||
import { export2Excel } from '../excel/util.js' | |||
export default{ | |||
name:'ProAnalysis', | |||
components: { | |||
// barLabel, | |||
}, | |||
data() { | |||
return { | |||
recordBeginTime:'', | |||
lastUpdatedTime:'', | |||
page:1, | |||
pageSize:10, | |||
params:{type:'monthly',page:1,pagesize:10,beginTime:'',endTime:''}, | |||
tableData: [], | |||
tableDataSummary:{}, | |||
totalPage:0, | |||
totalNum:0, | |||
pickerOptions: { | |||
}, | |||
value_time: '', | |||
dynamic:4, | |||
echartsSelectData:'', | |||
option:'', | |||
}; | |||
}, | |||
methods: { | |||
// download_file(){ | |||
// this.params.type='all' | |||
// }, | |||
popMark(){ | |||
alert("数据为空时,不能下载!") | |||
}, | |||
exportData(){ | |||
// this.getOneProList(this.pro_id,'all',true,7) | |||
// this.getOneProList(this.pro_id,'all',false,7) | |||
// this.fileName() | |||
if (this.tableData!=''){ | |||
this.page=1 | |||
var saveFileName = this.getFileName() | |||
export2Excel(this.columns,this.tableData,saveFileName) | |||
}else{ | |||
alert("数据为空时,不能下载!") | |||
} | |||
}, | |||
getFileName(){ | |||
var now = new Date(); // 当前日期 | |||
var nowDayOfWeek = now.getDay(); // 今天本周的第几天 | |||
var nowDay = now.getDate(); // 当前日 | |||
var nowMonth = now.getMonth(); // 当前月 | |||
var nowYear = now.getFullYear(); // 当前年 | |||
var today = this.saveFormatDate(nowYear,nowMonth+1,nowDay); | |||
var tmp = new Date(now.setTime(now.getTime()-24*60*60*1000)); | |||
var yesterday = this.saveFormatDate(tmp.getFullYear(),tmp.getMonth()+1,tmp.getDate()); | |||
var yesterday_tmp = this.formatDate(tmp.getFullYear(),tmp.getMonth()+1,tmp.getDate()) | |||
var startDate='' | |||
var endDate='' | |||
var saveFileName = '' | |||
if (typeof this.paramsID.type=="undefined" || this.paramsID.type=="null" || this.paramsID.type==""){ | |||
// startDate= this.saveFormatDate(this.create_time_pro[0].getFullYear(),this.create_time_pro[0].getMonth() + 1,this.create_time_pro[0].getDate()); | |||
endDate = this.saveFormatDate(this.create_time_pro[1].getFullYear(),this.create_time_pro[1].getMonth() + 1,this.create_time_pro[1].getDate()); | |||
var tmp = this.formatDate(this.create_time_pro[0].getFullYear(),this.create_time_pro[0].getMonth() + 1,this.create_time_pro[0].getDate()) | |||
startDate = this.comparedate(tmp,this.recordBeginTime) | |||
console.log("comparedate:"+startDate) | |||
saveFileName = this.alias+"_"+startDate+'_'+endDate | |||
}else{ | |||
switch(this.paramsID.type){ | |||
case "yesterday":{ | |||
startDate = this.comparedate(yesterday_tmp,this.recordBeginTime) | |||
endDate = startDate | |||
saveFileName = this.alias+"_"+startDate+'_'+ endDate | |||
break | |||
} | |||
case "current_week":{ | |||
var now = new Date(); // 当前日期 | |||
var nowDayOfWeek = now.getDay(); // 今天本周的第几天 | |||
var day = nowDayOfWeek || 7; | |||
startDate = this.formatDate(now.getFullYear(), nowMonth+1, nowDay + 1 - day); | |||
startDate = this.comparedate(startDate,this.recordBeginTime) | |||
endDate = yesterday | |||
saveFileName = this.alias+"_"+startDate+'_'+ endDate | |||
break | |||
} | |||
case "current_month":{ | |||
startDate = this.formatDate(nowYear,nowMonth+1,1); | |||
startDate = this.comparedate(startDate,this.recordBeginTime) | |||
endDate = yesterday | |||
saveFileName = this.alias+"_"+startDate+'_'+ endDate | |||
break | |||
} | |||
case "last_month":{ | |||
let lastMonthDate = new Date(); // 上月日期 | |||
lastMonthDate.setDate(1); | |||
lastMonthDate.setMonth(lastMonthDate.getMonth()-1); | |||
let lastYear = lastMonthDate.getFullYear(); | |||
let lastMonth = lastMonthDate.getMonth(); | |||
startDate=this.formatDate(lastYear, lastMonth+1, 1); | |||
startDate = this.comparedate(startDate,this.recordBeginTime) | |||
var monthStartDate = new Date(lastYear, lastMonth, 1); | |||
var monthEndDate = new Date(lastYear, lastMonth+1, 1); | |||
var days = (monthEndDate - monthStartDate) / (1000 * 60 * 60 * 24) | |||
endDate=this.saveFormatDate(lastYear, lastMonth+1, days); //月份从0开始,所以+1保存月份 | |||
saveFileName = this.alias+"_"+startDate+'_'+ endDate | |||
break | |||
} | |||
case "monthly":{ | |||
var temp=new Date(now - 1000 * 60 * 60 * 24 * 30) | |||
startDate = this.formatDate(temp.getFullYear(),temp.getMonth()+1,temp.getDate()); | |||
startDate = this.comparedate(startDate,this.recordBeginTime) | |||
endDate = yesterday | |||
saveFileName = this.alias+"_"+startDate+'_'+ endDate | |||
break | |||
} | |||
case "current_year":{ | |||
startDate = this.formatDate(now.getFullYear(), 1, 1); | |||
startDate = this.comparedate(startDate,this.recordBeginTime) | |||
endDate = yesterday | |||
saveFileName = this.alias+"_"+startDate+'_'+ endDate | |||
break | |||
} | |||
case "all":{ | |||
console.log("e:"+today) | |||
startDate = 'all' | |||
endDate = yesterday | |||
saveFileName = this.alias+'_所有' | |||
break | |||
} | |||
} | |||
} | |||
return saveFileName | |||
}, | |||
resetPage(){ | |||
this.page=1 | |||
this.params.page = 1 | |||
}, | |||
resetCurrentPage(){ | |||
this.page=1 | |||
}, | |||
handleSizeChange(val){ | |||
this.pageSize = val | |||
}, | |||
handleCurrentChange(val){ | |||
this.page = val; | |||
}, | |||
saveFormatDate(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; | |||
} | |||
console.log((myyear +''+ mymonth +''+ myweekday)) | |||
return (myyear +''+ mymonth +''+ myweekday); | |||
}, | |||
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); | |||
}, | |||
//获得某月的天数 | |||
getPeriodProList(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=[] | |||
} | |||
// console.log("params:",this.params) | |||
this.$axios.get('../../api/v1/projectboard/summary/period',{ | |||
params:this.params | |||
}).then((res)=>{ | |||
this.recordBeginTime=res.data.recordBeginTime | |||
// this.lastUpdatedTime=res.data.creatTime | |||
this.tableData = res.data.pageRecords | |||
this.totalPage=res.data.totalPage | |||
// this.totalNum = res.data.totalCount//this.totalPage*this.params.pagesize | |||
// console.log("res.data:"+res.data) | |||
this.drawSelectData() | |||
}) | |||
}, | |||
getSummaryPro(){ | |||
this.$axios.get('../../api/v1/projectboard/summary',{ | |||
}).then((res)=>{ | |||
this.tableDataSummary = res.data | |||
this.lastUpdatedTime = res.data.creatTime | |||
}) | |||
}, | |||
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' | |||
} | |||
}, | |||
drawSelectData(){ | |||
// $("#selectData").removeAttr("selectData").empty(); | |||
var xdata=[] | |||
var ydata_add_pro=[] | |||
var ydata_add_public_pro=[] | |||
var ydata_add_private_pro=[] | |||
var ydata_add_self=[] | |||
var ydata_add_fork=[] | |||
var ydata_add_mirror=[] | |||
var ydata_cumulative_pro=[] | |||
// if () | |||
for(var i =0;i<this.tableData.length;i++){ | |||
xdata.push(this.tableData[this.tableData.length-1-i].creatTime); | |||
ydata_add_pro.push(this.tableData[this.tableData.length-1-i].numReposAdd) | |||
ydata_add_public_pro.push(this.tableData[this.tableData.length-1-i].numRepoPublicAdd) | |||
ydata_add_private_pro.push(this.tableData[this.tableData.length-1-i].numRepoPrivateAdd) | |||
ydata_add_self.push(this.tableData[this.tableData.length-1-i].numRepoSelfAdd) | |||
ydata_add_fork.push(this.tableData[this.tableData.length-1-i].numRepoForkAdd) | |||
ydata_add_mirror.push(this.tableData[this.tableData.length-1-i].numRepoMirrorAdd) | |||
ydata_cumulative_pro.push(this.tableData[this.tableData.length-1-i].numRepos) | |||
} | |||
// console.log("ydata_openI:"+ydata_add_pro) | |||
// 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:['新增项目','新增公开项目','新增私有项目','新增自建项目','新增派生项目','新增镜像项目','累计项目'], | |||
selected:{ | |||
// '新增项目':true, | |||
// '新增公开项目':true, | |||
// '新增私有项目':true, | |||
// '新增自建项目':false, | |||
// '新增派生项目':false, | |||
// '新增镜像项目':false, | |||
// '累计项目':false | |||
} | |||
// 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, | |||
axisLine: { | |||
show: false, //x轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
yAxis : [ | |||
{ | |||
type : 'value', | |||
axisLine: { | |||
show: false, //y轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
series : [ | |||
{ name:"新增项目", | |||
data: ydata_add_pro, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#3894FF", | |||
}, | |||
color:"#3894FF", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增公开项目", | |||
data: ydata_add_public_pro, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#67B3BB", | |||
}, | |||
color:"#67B3BB", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增私有项目", | |||
data: ydata_add_private_pro, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#58A55C", | |||
}, | |||
color:"#58A55C", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增自建项目", | |||
data: ydata_add_self, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#F2BD42", | |||
}, | |||
color:"#F2BD42", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增派生项目", | |||
data: ydata_add_fork, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#DAA67B", | |||
}, | |||
color:"#DAA67B", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增镜像项目", | |||
data: ydata_add_mirror, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#2E4552", | |||
}, | |||
color:"#2E4552", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"累计项目", | |||
data: ydata_cumulative_pro, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#4786B4", | |||
}, | |||
color:"#4786B4", | |||
} | |||
}, | |||
}, | |||
] | |||
}; | |||
// this.echartsSelectData.resize() | |||
var checkboxs=document.getElementsByName('checkboxchart'); | |||
for(var i=0; i<checkboxs.length; i++){ | |||
// console.log("selectArr[i]:",this.option.legend.data[i]) | |||
if(checkboxs[i].checked){ | |||
this.option.legend.selected[this.option.legend.data[i]]=true; | |||
}else{ | |||
this.option.legend.selected[this.option.legend.data[i]]=false; | |||
} | |||
} | |||
this.echartsSelectData.setOption(this.option) | |||
// setTimeout(function (){ | |||
// window.onresize = function () { | |||
// this.echartsSelectData.resize; | |||
// } | |||
// },200) | |||
// // 使用刚指定的选择项数据显示图表。 | |||
// 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); | |||
// }); | |||
}, | |||
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); | |||
// }); | |||
}, | |||
comparedate(date1,date2){ | |||
// console.log("date1:"+date1) | |||
// console.log("date1:"+date2) | |||
var oDate1 = new Date(date1); | |||
var oDate2 = new Date(date2); | |||
if(oDate1.getTime() < oDate2.getTime()){ | |||
var data = date2.split('-') | |||
return data[0]+''+data[1]+''+data[2] | |||
} else { | |||
var data = date1.split('-') | |||
return data[0]+''+data[1]+''+data[2] | |||
} | |||
}, | |||
}, | |||
filters:{ | |||
}, | |||
mounted() { | |||
this.getPeriodProList("monthly",4); | |||
this.getSummaryPro(); | |||
document.getElementById('selectData').style.width = document.getElementById('pro_tend').offsetWidth*0.8+'px' | |||
this.echartsSelectData = this.$echarts.init(document.getElementById('selectData')) | |||
}, | |||
watch:{ | |||
}, | |||
created() { | |||
this.getSummaryPro(); | |||
this.getPeriodProList("monthly",4); | |||
}, | |||
updated(){ | |||
if(document.querySelectorAll('img[avatar]').length!==0){ | |||
window.LetterAvatar.transform() | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped> | |||
.item_list_first{ | |||
border-right: 1px solid rgba(219,219,219,100); | |||
padding-right: 10px; | |||
} | |||
.item_list{ | |||
border-right: 1px solid rgba(219,219,219,100); | |||
padding:0px 10px; | |||
} | |||
.item_list_p{ | |||
border-right: 1px solid rgba(219,219,219,100); | |||
padding:0px 10px; | |||
} | |||
.item_h{ | |||
line-height: 40px; | |||
text-align: center; | |||
} | |||
.item_title_h{ | |||
line-height: 28px; | |||
text-align: center; | |||
} | |||
.num_color{ | |||
color: #0366D6; | |||
font-weight: bold; | |||
} | |||
.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; | |||
} | |||
.btnFirst{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px 0px 0px 4px; | |||
} | |||
.btn{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
} | |||
.btnLast{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
/* border-right: none; */ | |||
background: #FFFF; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:0px 4px 4px 0px; | |||
} | |||
.btnFirst, .btn, .btnLast { | |||
cursor: pointer; | |||
} | |||
/deep/ .el-table tbody tr:hover>td { | |||
background-color:#D3D3D3!important; | |||
opacity:1 | |||
} | |||
/deep/ .el-table { | |||
font-size: 12px; | |||
} | |||
/deep/ .el-range-separator{ | |||
width: 20% !important; | |||
} | |||
/deep/ .el-pagination { | |||
padding-bottom: 30px; | |||
} | |||
.colorChange { | |||
background-color: #1684FC; | |||
color: #FFFF; | |||
cursor: default; | |||
} | |||
.items{ | |||
text-align: center; | |||
border-right:1px 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; | |||
overflow:auto | |||
} | |||
.item_echart{ | |||
margin-top: 10px; | |||
margin-right: 5px; | |||
border:1px solid rgba(219, 219, 219, 100); | |||
height: 350px; | |||
width: 100%; | |||
} | |||
.item_content{ | |||
color:#0366D6; | |||
margin-top: 10px; | |||
font-weight:bold; | |||
} | |||
</style> |
@@ -1,7 +1,7 @@ | |||
<template> | |||
<div> | |||
<div class="el-col el-col-21" style="padding-right:10px"> | |||
<div style="margin-top: 10px;"> | |||
<b class="pro_item">用户分析</b> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{lastUpdatedTime}}  / 从{{recordBeginTime}}开始统计</span> | |||
<b class="pro_item">活动分析</b> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{lastUpdatedTime}}  / 从{{recordBeginTime}}开始统计</span> | |||
</div> | |||
<div style="margin-top: 20px;"> | |||
<span class="sta_iterm">统计周期:</span> | |||
@@ -54,6 +54,7 @@ | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
fixed | |||
label="用户名" | |||
align="left" | |||
prop="Name" | |||
@@ -259,7 +260,7 @@ | |||
params:{startDate:'',endDate:'',page:1,pageSize:10,userName:''}, | |||
tableData: [], | |||
totalNum:0, | |||
dataUrl:'../api/v1/query_user_static_page', | |||
dataUrl:'../../api/v1/query_user_static_page', | |||
pickerOptions: { | |||
}, | |||
value_time: '', | |||
@@ -334,7 +335,7 @@ | |||
return days; | |||
}, | |||
getUpdateTime(){ | |||
this.$axios.get('../api/v1/projectboard/project',{ | |||
this.$axios.get('../../api/v1/projectboard/project',{ | |||
params:this.params_pro | |||
}).then((res)=>{ | |||
this.recordBeginTime=res.data.recordBeginTime | |||
@@ -357,7 +358,7 @@ | |||
let lastYear = lastMonthDate.getYear(); | |||
let lastMonth = lastMonthDate.getMonth(); | |||
this.dataUrl = '../api/v1/query_user_static_page'; | |||
this.dataUrl = '../../api/v1/query_user_static_page'; | |||
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()); | |||
@@ -366,37 +367,37 @@ | |||
switch(type_val){ | |||
case "yesterday_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_yesterday'; | |||
this.dataUrl = '../../api/v1/query_user_yesterday'; | |||
break | |||
} | |||
case "current_week_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_current_week'; | |||
this.dataUrl = '../../api/v1/query_user_current_week'; | |||
break | |||
} | |||
case "current_month_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_current_month'; | |||
this.dataUrl = '../../api/v1/query_user_current_month'; | |||
break | |||
} | |||
case "last_month_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_last_month'; | |||
this.dataUrl = '../../api/v1/query_user_last_month'; | |||
break | |||
} | |||
case "monthly_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_last30_day'; | |||
this.dataUrl = '../../api/v1/query_user_last30_day'; | |||
break | |||
} | |||
case "current_year_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_current_year'; | |||
this.dataUrl = '../../api/v1/query_user_current_year'; | |||
break | |||
} | |||
case "all_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../api/v1/query_user_all'; | |||
this.dataUrl = '../../api/v1/query_user_all'; | |||
break | |||
} | |||
} | |||
@@ -561,7 +562,9 @@ | |||
/deep/ .el-range-separator{ | |||
width: 20% !important; | |||
} | |||
/deep/ .el-pagination { | |||
padding-bottom: 30px; | |||
} | |||
.colorChange { | |||
background-color: #1684FC; | |||
color: #FFFF; | |||
@@ -0,0 +1,942 @@ | |||
<template> | |||
<div class="el-col el-col-21" style="padding-right:10px"> | |||
<div id='user_tend'> | |||
<div style="margin-top: 10px;"> | |||
<b class="pro_item">增长趋势分析</b> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{lastUpdatedTime}}  / 从{{recordBeginTime}}开始统计</span> | |||
</div> | |||
<div id = 'isShow'> | |||
<el-col :span="11"> | |||
<el-col id="ys_add_user" class="draw_region"> | |||
</el-col> | |||
<el-col :span="8" :style="{ height: '180px'}" v-if="ys_count>0"> | |||
<span class="yesterday_blk yesterday_title" >昨日新增注册用户数 </span> | |||
<span class="yesterday_blk yesterday_color1 yesterday_pdrt yesterday_text">未激活:<span class="bold_num">{{ tableDataYesterday.NotActivateRegistUser }}</span> 人 </span> | |||
<span class="yesterday_blk yesterday_color2 yesterday_pdrt yesterday_text">已激活: <span class="bold_num">{{ tableDataYesterday.ActivateRegistUser }} </span>人</span> | |||
<span class="yesterday_blk yesterday_pdrt yesterday_text">有贡献活动: <span class="bold_num">{{ tableDataYesterday.RegistActivityUser }} </span>人</span> | |||
</el-col> | |||
</el-col> | |||
<el-col :span="13"> | |||
<el-col id="ys_all_user" class="draw_region"> | |||
</el-col> | |||
<el-col :span="8" :style="{ height: '180px'}" v-if="ys_count>0"> | |||
<span class="yesterday_blk yesterday_title" >注册用户数 </span> | |||
<span class="yesterday_blk yesterday_color1 yesterday_pdrt yesterday_text">未激活:<span class="bold_num">{{ tableDataYesterday.TotalActivateRegistUser}} </span>人 </span> | |||
<span class="yesterday_blk yesterday_color2 yesterday_pdrt yesterday_text">已激活:<span class="bold_num">{{ tableDataYesterday.TotalNotActivateRegistUser }} </span>人</span> | |||
<span class="yesterday_blk yesterday_pdrt yesterday_text">有贡献活动:<span class="bold_num"> {{ tableDataYesterday.TotalHasActivityUser}} </span>人</span> | |||
</el-col> | |||
</el-col> | |||
</div> | |||
<div style="margin-top: 20px;"> | |||
<span class="sta_iterm">统计周期:</span> | |||
<button type="button" class='btn' id = "current_week_usr" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getUserList('current_week_usr',1)">本周</button> | |||
<button type="button" class='btn' id = "current_month_usr" v-bind:class="{colorChange:2==dynamic}" @click="resetPage(),getUserList('current_month_usr',2)">本月</button> | |||
<button type="button" class='btn' id = "last_month_usr" v-bind:class="{colorChange:3==dynamic}" @click="resetPage(),getUserList('last_month_usr',3)">上月</button> | |||
<button type="button" class='btn' id = "monthly_usr" v-bind:class="{colorChange:4==dynamic}" @click="resetPage(),getUserList('monthly_usr',4)">近30天</button> | |||
<button type="button" class='btn' id = "current_year_usr" v-bind:class="{colorChange:5==dynamic}" @click="resetPage(),getUserList('current_year_usr',5)">今年</button> | |||
<button type="button" class='btnLast' id = "all_usr" v-bind:class="{colorChange:6==dynamic}" @click="resetPage(),getUserList('all_usr',6)">所有</button> | |||
<span style="margin-left: 20px;"> | |||
<el-date-picker | |||
v-model="value_time" | |||
prefix-icon="el-icon-time" | |||
@change="resetPage(),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'> | |||
<a class="el-icon-download" v-if="tableData!=''" :href= "this.dataUrl + '?startDate='+this.params.startDate+'&endDate='+this.params.endDate+'&IsReturnFile=true' " ></a> | |||
<i class="el-icon-download" v-else="tableData=''" href="#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'></i> | |||
<span > | |||
<a v-if="tableData!=''" :href= "this.dataUrl + '?startDate='+this.params.startDate+'&endDate='+this.params.endDate+'&IsReturnFile=true' " >下载报告</a> | |||
<a v-else="tableData=''" href= "#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'>下载报告</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" @click="searchName() "></i> | |||
</el-input> | |||
</span> --> | |||
</span> | |||
</div> | |||
<div class="item_echart" id ='linecharts'> | |||
<div style="margin: 15px 10px 30px;"> | |||
<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="新增私有项目"/>新增有贡献活动 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="新增自建项目"/>新增未激活 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="新增派生项目"/>累计注册用户 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="新增镜像项目"/>累计已激活 | |||
<input type="checkbox" class="checkboxchart" name="checkboxchart" value="累计项目"/>累计有贡献活动 | |||
</label> | |||
</div> | |||
<div id ="selectData" style="height: 300px;"> | |||
</div> | |||
</div> | |||
<div style="margin-top: 30px;"> | |||
<el-table | |||
:data="tableData.slice((page-1)*pageSize,page*pageSize)" | |||
style="width: 100%" | |||
:header-cell-style="tableHeaderStyle" | |||
:cell-style='cellStyle'> | |||
<el-table-column | |||
label="日期" | |||
prop="DisplayDate" | |||
align="center" | |||
stripe | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
label="新增注册用户" | |||
prop="TotalRegistUser" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="ActivateRegistUser" | |||
label="新增已激活" | |||
width="120px" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="RegistActivityUser" | |||
label="新增有贡献活动" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="NotActivateRegistUser" | |||
label="新增未激活" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="ActivateIndex" | |||
label="新增用户激活率" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="TotalUser" | |||
label="累计注册用户" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="TotalActivateRegistUser" | |||
label="累计已激活" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
prop="TotalHasActivityUser" | |||
label="累计有贡献活动" | |||
align="center"> | |||
</el-table-column> | |||
</el-table> | |||
</div> | |||
<div style="margin-top:50px;text-align:center"> | |||
<el-pagination | |||
background | |||
@size-change="handleSizeChange" | |||
@current-change="handleCurrentChange" | |||
:current-page="page" | |||
:page-size="pageSize" | |||
:page-sizes="[5,10,20]" | |||
layout="total, sizes, prev, pager, next,jumper" | |||
:total="tableData.length"> | |||
</el-pagination> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script> | |||
import { export2Excel } from '../excel/util.js' | |||
export default{ | |||
name:'UserAnalysis', | |||
data() { | |||
return { | |||
type_val:'', | |||
recordBeginTime:'', | |||
lastUpdatedTime:'', | |||
page:1, | |||
pageSize:10, | |||
params:{startDate:'',endDate:''}, | |||
tableData: [], | |||
totalNum:0, | |||
dataUrl:'../../api/v1/query_user_metrics_page', | |||
dataYesterdayUrl:'../../api/v1/query_metrics_yesterday', | |||
ys_count:0, | |||
tableDataYesterday: {}, | |||
option:'', | |||
optionYesterdayUser:'', | |||
optionYesterdaAllUser:'', | |||
echartsSelectData:'', | |||
echartsYsAddUser:'', | |||
echartsYsAllUser:'', | |||
pickerOptions: { | |||
}, | |||
value_time: '', | |||
search:'', | |||
data:'', | |||
// columns: [{title: 'ID',key: 'ID'},{title: '用户名',key: 'Name'},{title: 'PR数',key: 'CodeMergeCount'},{title: 'commit数',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'},{title:'云脑任务数',key:'CloudBrainTaskNum'},{title:'云脑运行时间(小时)',key:'CloudBrainRunTime'},{title:'上传(提交)数据集文件数',key:'CommitDatasetNum'},{title:'提交模型数',key:'CommitModelCount'},{title:'用户指数',key:'UserIndex'},{title:'系统统计时间',key:'CountDate'}], | |||
blob:'', | |||
fileName:'', | |||
dynamic:4, | |||
params_pro:{type:'all',page:1,pagesize:10,beginTime:'',endTime:'',q:'',sort:'openi'}, | |||
}; | |||
}, | |||
methods: { | |||
popMark(){ | |||
alert("数据为空时,不能下载!") | |||
}, | |||
// exportData(){ | |||
// // this.getUserList('all_usr',7) | |||
// var saveFileName = this.getFileName() | |||
// export2Excel(this.columns,this.tableData,saveFileName) | |||
// }, | |||
// getFileName(){ | |||
// var saveFileName='' | |||
// var Date=(this.params.startDate).split('-') | |||
// var startDate=Date[0]+''+Date[1]+''+Date[2] | |||
// Date=(this.params.endDate).split('-') | |||
// var endDate=Date[0]+Date[1]+Date[2] | |||
// saveFileName = '用户分析_'+this.search+''+startDate+'_'+endDate | |||
// if (this.type_val=='all_usr'){ | |||
// saveFileName = '用户分析_'+this.search+'_all' | |||
// } | |||
// return saveFileName | |||
// }, | |||
handleCurrentChange(val){ | |||
this.page = val | |||
}, | |||
handleSizeChange(val){ | |||
this.pageSize = val | |||
}, | |||
resetPage(){ | |||
this.page=1 | |||
}, | |||
addUser(val1, val2){ | |||
return (val1+val2) | |||
}, | |||
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; | |||
}, | |||
getUpdateTime(){ | |||
this.$axios.get('../../api/v1/projectboard/project',{ | |||
params:this.params_pro | |||
}).then((res)=>{ | |||
this.recordBeginTime=res.data.recordBeginTime | |||
this.lastUpdatedTime=res.data.lastUpdatedTime | |||
}) | |||
}, | |||
getUserList(type_val,index){ | |||
this.type_val = type_val | |||
this.dynamic = index; | |||
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(); | |||
this.dataUrl = '../../api/v1/query_user_metrics_page'; | |||
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":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_user_yesterday'; | |||
break | |||
} | |||
case "current_week_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_metrics_current_week'; | |||
break | |||
} | |||
case "current_month_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_metrics_current_month'; | |||
break | |||
} | |||
case "last_month_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_metrics_last_month'; | |||
break | |||
} | |||
case "monthly_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_metrics_last30_day'; | |||
break | |||
} | |||
case "current_year_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_metrics_current_year'; | |||
break | |||
} | |||
case "all_usr":{ | |||
this.value_time=[] | |||
this.dataUrl = '../../api/v1/query_metrics_all'; | |||
break | |||
} | |||
} | |||
}; | |||
this.$axios.get(this.dataUrl,{ | |||
params:this.params | |||
}).then((res)=>{ | |||
this.tableData = res.data.data | |||
// console.log("res.data:"+res.data.data) | |||
this.totalNum = res.data.count | |||
this.drawSelectData() | |||
}) | |||
}, | |||
getYesterdayUser(){ | |||
this.$axios.get(this.dataYesterdayUrl,{ | |||
}).then((res)=>{ | |||
this.ys_count = res.data.count | |||
this.tableDataYesterday = res.data.data[0] | |||
if(this.ys_count>0){ | |||
this.drawYesterdayUser() | |||
this.drawSumUser() | |||
}else{ | |||
document.getElementById("isShow").style.display='none' | |||
} | |||
}) | |||
}, | |||
drawYesterdayUser(){ | |||
this.optionYesterdayUser = { | |||
tooltip: { | |||
trigger: 'item', | |||
show:false | |||
}, | |||
legend: { | |||
top: '5%', | |||
left: 'center', | |||
show:false | |||
}, | |||
// graphic:{ | |||
// type:'text', | |||
// left:'center', | |||
// top:'center', | |||
// style:{ | |||
// text:this.tableDataYesterday.TotalRegistUser, | |||
// fontSize:18, | |||
// fontWeight:'bold', | |||
// color:'#101010' | |||
// } | |||
// }, | |||
color:['#5087Ec','#DBDBDB'], | |||
series: [ | |||
{ | |||
name: '', | |||
type: 'pie', | |||
radius: ['65%', '70%'], | |||
center:['50%','50%'], | |||
avoidLabelOverlap: false, | |||
label: { | |||
normal:{ | |||
show: true, | |||
position: 'center', | |||
formatter:''+this.tableDataYesterday.TotalRegistUser, | |||
fontSize:18, | |||
fontWeight:'bold', | |||
color:'#101010' | |||
} | |||
}, | |||
emphasis: { | |||
label: { | |||
show: false, | |||
fontSize: '40', | |||
fontWeight: 'bold' | |||
} | |||
}, | |||
labelLine: { | |||
normal:{ | |||
show:false | |||
} | |||
}, | |||
data: [ | |||
{ value: this.tableDataYesterday.ActivateRegistUser, name: '已激活' }, | |||
{ value: this.tableDataYesterday.NotActivateRegistUser, name: '未激活'}, | |||
], | |||
hoverAnimation:false, | |||
} | |||
] | |||
}; | |||
this.echartsYsAddUser.setOption(this.optionYesterdayUser) | |||
}, | |||
drawSumUser(){ | |||
this.optionYesterdaAllUser = { | |||
tooltip: { | |||
trigger: 'item', | |||
show:false | |||
}, | |||
legend: { | |||
top: '5%', | |||
left: 'center', | |||
show:false | |||
}, | |||
// graphic:{ | |||
// type:'text', | |||
// left:'center', | |||
// top:'center', | |||
// style:{ | |||
// text:this.tableDataYesterday.TotalRegistUser, | |||
// fontSize:18, | |||
// fontWeight:'bold', | |||
// color:'#101010' | |||
// } | |||
// }, | |||
color:['#5087Ec','#DBDBDB'], | |||
series: [ | |||
{ | |||
name: '', | |||
type: 'pie', | |||
radius: ['65%', '70%'], | |||
center:['50%','50%'], | |||
avoidLabelOverlap: false, | |||
label: { | |||
normal:{ | |||
show: true, | |||
position: 'center', | |||
formatter:''+this.tableDataYesterday.TotalUser, | |||
fontSize:18, | |||
fontWeight:'bold', | |||
color:'#101010' | |||
} | |||
}, | |||
emphasis: { | |||
label: { | |||
show: false, | |||
fontSize: '40', | |||
fontWeight: 'bold' | |||
} | |||
}, | |||
labelLine: { | |||
normal:{ | |||
show:false | |||
} | |||
}, | |||
data: [ | |||
{ value: this.tableDataYesterday.TotalActivateRegistUser, name: '已激活' }, | |||
{ value: this.tableDataYesterday.TotalNotActivateRegistUser, name: '未激活'}, | |||
], | |||
hoverAnimation:false, | |||
} | |||
] | |||
}; | |||
this.echartsYsAllUser.setOption(this.optionYesterdaAllUser) | |||
}, | |||
// searchName(){ | |||
// this.params.userName = this.search | |||
// this.params.page = 1 | |||
// this.page=1 | |||
// this.getUserList(this.type_val, this.dynamic) | |||
// }, | |||
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' | |||
} | |||
}, | |||
drawSelectData(){ | |||
// $("#selectData").removeAttr("selectData").empty(); | |||
var xdata=[] | |||
var ydata_TotalRegistUser=[] | |||
var ydata_ActivateRegistUser=[] | |||
var ydata_NotActivateRegistUser=[] | |||
var ydata_RegistActivityUser=[] | |||
var ydata_TotalUser=[] | |||
var ydata_TotalActivateRegistUser=[] | |||
var ydata_TotalHasActivityUser=[] | |||
// if () | |||
for(var i =0;i<this.tableData.length;i++){ | |||
xdata.push(this.tableData[this.tableData.length-1-i].DisplayDate); | |||
ydata_TotalRegistUser.push(this.tableData[this.tableData.length-1-i].TotalRegistUser) | |||
ydata_ActivateRegistUser.push(this.tableData[this.tableData.length-1-i].ActivateRegistUser) | |||
ydata_RegistActivityUser.push(this.tableData[this.tableData.length-1-i].RegistActivityUser) | |||
ydata_NotActivateRegistUser.push(this.tableData[this.tableData.length-1-i].NotActivateRegistUser) | |||
ydata_TotalUser.push(this.tableData[this.tableData.length-1-i].TotalUser) | |||
ydata_TotalActivateRegistUser.push(this.tableData[this.tableData.length-1-i].TotalActivateRegistUser) | |||
ydata_TotalHasActivityUser.push(this.tableData[this.tableData.length-1-i].TotalHasActivityUser) | |||
} | |||
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:['新增注册用户','新增已激活','新增有贡献活动','新增未激活','累计注册用户','累计已激活','累计有贡献活动'], | |||
selected:{ | |||
// '新增注册用户':true, | |||
// '新增已激活':true, | |||
// '新增有贡献活动':true, | |||
// '新增未激活':false, | |||
// '累计注册用户':false, | |||
// '累计已激活':false, | |||
// '累计有贡献活动':false | |||
} | |||
// 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, | |||
axisLine: { | |||
show: false, //x轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
yAxis : [ | |||
{ | |||
type : 'value', | |||
axisLine: { | |||
show: false, //y轴线消失 | |||
}, | |||
axisTick:{ | |||
show:false//刻度隐藏 | |||
} | |||
} | |||
], | |||
series : [ | |||
{ name:"新增注册用户", | |||
data: ydata_TotalRegistUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#3894FF ", | |||
}, | |||
color:"#3894FF ", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增已激活", | |||
data: ydata_ActivateRegistUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#67B3BB", | |||
}, | |||
color:"#67B3BB", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增有贡献活动", | |||
data: ydata_RegistActivityUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#58A55C", | |||
}, | |||
color:"#58A55C", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"新增未激活", | |||
data: ydata_NotActivateRegistUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#F2BD42", | |||
}, | |||
color:"#F2BD42", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"累计注册用户", | |||
data: ydata_TotalUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#2E4552", | |||
}, | |||
color:"#2E4552", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"累计已激活", | |||
data: ydata_TotalActivateRegistUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#4786B4", | |||
}, | |||
color:"#4786B4", | |||
} | |||
}, | |||
}, | |||
{ | |||
name:"累计有贡献活动", | |||
data: ydata_TotalHasActivityUser, | |||
type: 'line', | |||
areaStyle: {}, | |||
itemStyle:{ | |||
normal:{ | |||
lineStyle:{ | |||
color:"#4E9C8F", | |||
}, | |||
color:"#4E9C8F", | |||
} | |||
}, | |||
}, | |||
] | |||
}; | |||
// this.echartsSelectData.resize() | |||
var checkboxs=document.getElementsByName('checkboxchart'); | |||
// $(".checkboxchart").click(function(){ | |||
for(var i=0; i<checkboxs.length; i++){ | |||
// console.log("selectArr[i]:",this.option.legend.data[i]) | |||
if(checkboxs[i].checked){ | |||
this.option.legend.selected[this.option.legend.data[i]]=true; | |||
}else{ | |||
this.option.legend.selected[this.option.legend.data[i]]=false; | |||
} | |||
} | |||
this.echartsSelectData.setOption(this.option) | |||
// this.clickCheckBox() | |||
// setTimeout(function (){ | |||
// window.onresize = function () { | |||
// this.echartsSelectData.resize; | |||
// } | |||
// },200) | |||
// // 使用刚指定的选择项数据显示图表。 | |||
// 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); | |||
// }); | |||
}, | |||
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; | |||
} | |||
} | |||
// console.log("obj:",obj) | |||
this.option.legend.selected = obj; | |||
this.echartsSelectData.setOption(this.option); | |||
// }); | |||
}, | |||
}, | |||
filters:{ | |||
rounding (value) { | |||
return Number(value).toFixed(2) | |||
}, | |||
roundingToHour (value) { | |||
return (Number(value)/3600).toFixed(2) | |||
}, | |||
transformTimestamp(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; | |||
return dateString; | |||
}, | |||
addUser(val1,val2){ | |||
return val1+val2 | |||
} | |||
}, | |||
mounted() { | |||
// document.getElementById("all_usr").style.outline="none" | |||
// document.getElementById("all_usr").focus() | |||
this.getUpdateTime() | |||
this.getUserList("monthly_usr",4) | |||
this.getYesterdayUser() | |||
document.getElementById('selectData').style.width = document.getElementById('user_tend').offsetWidth*0.8+'px' | |||
this.echartsSelectData = this.$echarts.init(document.getElementById('selectData')) | |||
this.echartsYsAddUser = this.$echarts.init(document.getElementById('ys_add_user')) | |||
this.echartsYsAllUser = this.$echarts.init(document.getElementById('ys_all_user')) | |||
}, | |||
created() { | |||
this.getUserList("monthly_usr",4) | |||
this.getYesterdayUser() | |||
}, | |||
watch:{ | |||
// search(val){ | |||
// if(!val){ | |||
// this.params.userName = this.search | |||
// this.params.page = 1 | |||
// this.page=1 | |||
// this.getUserList(this.type_val, this.dynamic) | |||
// } | |||
// } | |||
}, | |||
} | |||
</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 ; | |||
} */ | |||
.btnFirst{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px 0px 0px 4px; | |||
} | |||
.btn{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
} | |||
.btnLast{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
/* border-right: none; */ | |||
background: #FFFF; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:0px 4px 4px 0px; | |||
} | |||
.btnFirst,.btn,.btnLast { | |||
cursor: pointer; | |||
} | |||
/* .btn:focus, | |||
.btn:active{ | |||
background-color:#409effd6 ; | |||
} */ | |||
/* /deep/ .el-date-picker { | |||
width: 220px; | |||
} */ | |||
/deep/ .el-table { | |||
font-size: 12px; | |||
} | |||
/deep/ .el-table tbody tr:hover>td { | |||
background-color:#D3D3D3!important; | |||
opacity:1 | |||
} | |||
/deep/ .el-range-separator{ | |||
width: 20% !important; | |||
} | |||
/deep/ .el-pagination { | |||
padding-bottom: 30px; | |||
} | |||
.colorChange { | |||
background-color: #1684FC; | |||
color: #FFFF; | |||
cursor: default; | |||
} | |||
.item_echart{ | |||
margin-top: 10px; | |||
margin-right: 5px; | |||
border:1px solid rgba(219, 219, 219, 100); | |||
height: 350px; | |||
width: 100%; | |||
} | |||
.yesterday_blk{ | |||
display: block; | |||
margin-top:40px | |||
} | |||
.yesterday_pdrt{ | |||
padding-left: 10px; | |||
} | |||
.yesterday_color1{ | |||
border-left: 3px solid #DBDBDB; | |||
} | |||
.yesterday_color2{ | |||
border-left: 3px solid #5087Ec; | |||
} | |||
.yesterday_title{ | |||
font-size: 14px; | |||
font-weight: bold; | |||
} | |||
.yesterday_text{ | |||
font-size: 12px; | |||
line-height: 12px; | |||
color: #888888; | |||
margin-top: 10px; | |||
} | |||
.bold_num{ | |||
font-weight: bold; | |||
} | |||
.draw_region{ | |||
width: 180px; | |||
height: 180px; | |||
} | |||
</style> |
@@ -0,0 +1,153 @@ | |||
export default async function initContextMenu() { | |||
$('.popup-close').on('click', function (e) { | |||
$(this).parents('tr').prev().css('display', 'table-row') | |||
$(this).closest('tr').css('cssText', 'display:none !important') | |||
}) | |||
function contextMenu() { | |||
let canContextMenu = $('.ui.single.line.table.can-context-menu').data('can-editfile') | |||
if (canContextMenu) { | |||
$('.name.four.wide').on('contextmenu', function (e) { | |||
let ev = window.event || e; | |||
ev.preventDefault(); | |||
menu.show(e) | |||
}) | |||
} else { | |||
return | |||
} | |||
} | |||
contextMenu() | |||
const menu = new Menu({ | |||
data: [ | |||
{ | |||
label: '新标签打开', | |||
icon: "file outline icon context-menu-icon", | |||
active: (e, a) => { | |||
window.open(a.currentTarget.getElementsByTagName("a")[0].getAttribute('href')) | |||
} | |||
}, | |||
{ | |||
label: '重命名', | |||
icon: "edit icon context-menu-icon", | |||
active: (e, a) => { | |||
document.querySelectorAll(".context-menu-one").forEach((ele) => { | |||
if (ele.style.display === 'table-row') { | |||
ele.style.display = 'none' | |||
ele.previousElementSibling.style.display = 'table-row' | |||
} | |||
}) | |||
if (a.currentTarget.parentNode.nextElementSibling) { | |||
a.currentTarget.parentNode.style.setProperty('display', 'none', 'important') | |||
a.currentTarget.parentNode.nextElementSibling.style.display = 'table-row' | |||
a.currentTarget.parentNode.nextElementSibling.getElementsByTagName("input")[0].setAttribute("value", a.currentTarget.getElementsByTagName("a")[0].getAttribute('title')) | |||
} | |||
let btn = a.currentTarget.parentNode.nextElementSibling.getElementsByTagName("button")[0] | |||
btn.addEventListener('click', function (e) { | |||
let postUrl = btn.getAttribute('data-postbasepath') | |||
let last_commit = btn.getAttribute('data-commit') | |||
let tree_path = btn.getAttribute('data-treepath') + e.target.parentNode.previousElementSibling.getElementsByTagName("input")[0].value | |||
let csrf = $("input[name='_csrf']").val() | |||
$.ajax({ | |||
url: postUrl, | |||
type: "POST", | |||
contentType: "application/x-www-form-urlencoded", | |||
data: { last_commit: last_commit, tree_path: tree_path, _csrf: csrf }, | |||
success: function (res) { | |||
console.log("--------------") | |||
console.log(res.Code) | |||
console.log(res.Message) | |||
if (res.Code === 0) { | |||
document.getElementById("mask").style.display = "block" | |||
location.reload() | |||
} | |||
else { | |||
$('.ui.negative.message').text(res.Msg).show().delay(10000).fadeOut(); | |||
} | |||
} | |||
}) | |||
}) | |||
}, | |||
}, | |||
// { | |||
// label: '删除', | |||
// icon: "trash icon context-menu-icon", | |||
// active: (e, a) => { | |||
// console.dir(a) | |||
// $('.context-menu-delete.modal') | |||
// .modal({ | |||
// onApprove() { | |||
// } | |||
// }) | |||
// .modal('show'); | |||
// } | |||
// }, | |||
] | |||
}) | |||
} | |||
class Menu { | |||
constructor(param) { | |||
this.target = document.createElement('div') | |||
this.target.classList.add("ui", "menu", "compact", "vertical", "context-menu") | |||
this.data = param.data | |||
this.active = false | |||
this.clickZ = this.click.bind(this) | |||
this.closeZ = this.close2.bind(this) | |||
document.addEventListener('click', this.closeZ) | |||
for (let i = 0; i < this.data.length; i++) { | |||
let div = document.createElement('a') | |||
div.classList.add('item', 'context-menu-operation') | |||
if (this.data[i].disabled) { | |||
div.classList.add('disabled') | |||
} | |||
div.dataset.index = i.toString() | |||
div.innerHTML = `<i class="${this.data[i].icon}"></i>${this.data[i].label}` | |||
div.addEventListener('click', this.clickZ) | |||
this.target.append(div) | |||
} | |||
document.body.append(this.target) | |||
} | |||
click(e) { | |||
let index = parseInt(e.target.dataset.index) | |||
if (this.data[index].active) { | |||
this.data[index].active(e, this.acEvent) | |||
} | |||
this.close() | |||
} | |||
show(e) { | |||
this.active = true | |||
this.nodeList = this.target.querySelectorAll('.item') | |||
for (let i = 0; i < this.data.length; i++) { | |||
if (this.data[i].beforeDisabled) { | |||
let t = this.data[i].beforeDisabled(e) | |||
this.data[i].disabled = t | |||
if (t) { | |||
this.nodeList[i].classList.add('disabled') | |||
} else { | |||
this.nodeList[i].classList.remove('disabled') | |||
} | |||
} | |||
} | |||
this.acEvent = e | |||
this.target.style.top = `${e.pageY}px` | |||
this.target.style.left = `${e.pageX}px` | |||
this.target.style.minWidth = '90px' | |||
this.target.classList.add('active') | |||
} | |||
close() { | |||
this.active = false | |||
this.target.classList.remove('active') | |||
} | |||
close2(e) { | |||
if (!this.target.contains(e.target) && this.active) { | |||
this.active = false | |||
this.target.classList.remove('active') | |||
} | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
import Vue from 'vue' | |||
import Router from 'vue-router' | |||
import DataAnalysis from '../components/DataAnalysis.vue' | |||
import ProAnalysis from '../components/ProAnalysis.vue' | |||
import ProTrend from '../components/ProTrend.vue' | |||
import UserTrend from '../components/UserTrend.vue' | |||
import UserAnalysis from '../components/UserAnalysis.vue' | |||
import BrainAnalysis from '../components/BrainAnalysis.vue' | |||
import Overview from '../components/Overview.vue' | |||
const originalPush = Router.prototype.push | |||
Router.prototype.push = function push(location) { | |||
return originalPush.call(this, location).catch(err => err) | |||
} | |||
Vue.use(Router) | |||
export default new Router({ | |||
mode: 'history', | |||
base: '/explore/data_analysis/', //添加根目录 | |||
scrollBehavior: () => ({ y: 0 }), | |||
routes:[ | |||
{ | |||
path:'/',redirect:'/ProAnalysis', | |||
name:'ProAnalysis', | |||
component:ProAnalysis, | |||
}, | |||
{ | |||
path:'/Overview', | |||
name:'Overview', | |||
component:Overview, | |||
}, | |||
{ | |||
path:'/ProTrend', | |||
name:'ProTrend', | |||
component:ProTrend, | |||
}, | |||
{ | |||
path:'/ProAnalysis', | |||
name:'ProAnalysis', | |||
component:ProAnalysis, | |||
}, | |||
{ | |||
path:'/UserAnalysis', | |||
name:'UserAnalysis', | |||
component:UserAnalysis, | |||
}, | |||
{ | |||
path:'/UserTrend', | |||
name:'UserTrend', | |||
component:UserTrend, | |||
}, | |||
{ | |||
path:'/BrainAnalysis', | |||
name:'BrainAnalysis', | |||
component:BrainAnalysis, | |||
}, | |||
], | |||
}) |