Browse Source

Merge branch 'V20220926' into point-v2

pull/2975/head^2
chenyifan01 2 years ago
parent
commit
932ac68f26
45 changed files with 2257 additions and 564 deletions
  1. +24
    -0
      models/ai_model_manage.go
  2. +1
    -0
      models/models.go
  3. +72
    -5
      models/user_business_analysis.go
  4. +14
    -7
      models/user_business_struct.go
  5. +102
    -0
      models/user_invitation.go
  6. +1
    -1
      modules/auth/user_form.go
  7. +6
    -5
      modules/grampus/resty.go
  8. +6
    -5
      modules/setting/setting.go
  9. +9
    -0
      options/locale/locale_en-US.ini
  10. +9
    -0
      options/locale/locale_zh-CN.ini
  11. +14
    -0
      routers/api/v1/api.go
  12. +5
    -1
      routers/api/v1/repo/cloudbrain.go
  13. +15
    -0
      routers/home.go
  14. +49
    -24
      routers/repo/ai_model_manage.go
  15. +30
    -3
      routers/repo/grampus.go
  16. +2
    -1
      routers/repo/modelarts.go
  17. +440
    -0
      routers/repo/user_invitation.go
  18. +3
    -0
      routers/routes/routes.go
  19. +107
    -0
      routers/user/Invitation.go
  20. +28
    -24
      routers/user/auth.go
  21. +22
    -0
      services/repository/repository.go
  22. +11
    -1
      templates/base/footer.tmpl
  23. +4
    -0
      templates/base/head_navbar.tmpl
  24. +4
    -0
      templates/base/head_navbar_fluid.tmpl
  25. +4
    -0
      templates/base/head_navbar_home.tmpl
  26. +4
    -0
      templates/base/head_navbar_pro.tmpl
  27. +1
    -2
      templates/repo/cloudbrain/trainjob/show.tmpl
  28. +10
    -1
      templates/repo/grampus/trainjob/show.tmpl
  29. +9
    -2
      templates/repo/modelmanage/convertIndex.tmpl
  30. +2
    -2
      templates/repo/modelmanage/index.tmpl
  31. +38
    -0
      templates/user/auth/signup_inner.tmpl
  32. +5
    -0
      templates/user/dashboard/repolist.tmpl
  33. +34
    -1
      templates/user/profile.tmpl
  34. +7
    -0
      templates/user/settings/invite.tmpl
  35. +624
    -473
      web_src/js/components/Model.vue
  36. +140
    -0
      web_src/js/features/ad.js
  37. +8
    -0
      web_src/js/features/i18nVue.js
  38. +1
    -0
      web_src/js/index.js
  39. +15
    -5
      web_src/js/standalone/phoneverify.js
  40. +2
    -1
      web_src/less/standalone/_phoneverify.less
  41. +11
    -0
      web_src/vuepages/apis/modules/userinvite.js
  42. +14
    -0
      web_src/vuepages/langs/config/en-US.js
  43. +14
    -0
      web_src/vuepages/langs/config/zh-CN.js
  44. +319
    -0
      web_src/vuepages/pages/user/invite/index.vue
  45. +17
    -0
      web_src/vuepages/pages/user/invite/vp-user-invite.js

+ 24
- 0
models/ai_model_manage.go View File

@@ -25,6 +25,7 @@ type AiModelManage struct {
DownloadCount int `xorm:"NOT NULL DEFAULT 0"`
Engine int64 `xorm:"NOT NULL DEFAULT 0"`
Status int `xorm:"NOT NULL DEFAULT 0"`
StatusDesc string `xorm:"varchar(500)"`
Accuracy string `xorm:"varchar(1000)"`
AttachmentId string `xorm:"NULL"`
RepoId int64 `xorm:"INDEX NULL"`
@@ -286,6 +287,23 @@ func ModifyModelDescription(id string, description string) error {
return nil
}

func ModifyModelStatus(id string, modelSize int64, status int, modelPath string, statusDesc string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("size", "status", "path", "status_desc").Update(&AiModelManage{
Size: modelSize,
Status: status,
Path: modelPath,
StatusDesc: statusDesc,
})
if err != nil {
return err
}
log.Info("success to update ModelStatus from db.re=" + fmt.Sprint((re)))
return nil
}

func ModifyModelNewProperty(id string, new int, versioncount int) error {
var sess *xorm.Session
sess = x.ID(id)
@@ -356,6 +374,12 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
)
}

if (opts.Status) >= 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.status": opts.Status},
)
}

count, err := sess.Where(cond).Count(new(AiModelManage))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)


+ 1
- 0
models/models.go View File

@@ -178,6 +178,7 @@ func init() {
new(UserLoginLog),
new(UserMetrics),
new(UserAnalysisPara),
new(Invitation),
)

gonicNames := []string{"SSL", "UID"}


+ 72
- 5
models/user_business_analysis.go View File

@@ -106,7 +106,8 @@ type UserBusinessAnalysisAll struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysis struct {
@@ -193,7 +194,8 @@ type UserBusinessAnalysis struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisQueryOptions struct {
@@ -354,6 +356,33 @@ func QueryRankList(key string, tableName string, limit int) ([]*UserBusinessAnal
return userBusinessAnalysisAllList, int64(len(userBusinessAnalysisAllList))
}

func QueryUserInvitationDataByTableName(start int, pageSize int, tableName string, queryObj interface{}, userName string, invitationNum int) ([]*UserBusinessAnalysisAll, int64) {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
var cond = builder.NewCond()
if len(userName) > 0 {
cond = cond.And(
builder.Like{"lower(name)", strings.ToLower(userName)},
)
}
cond = cond.And(
builder.Gte{"invitation_user_num": invitationNum},
)

allCount, err := statictisSess.Where(cond).Count(queryObj)
if err != nil {
log.Info("query error." + err.Error())
return nil, 0
}
log.Info("query return total:" + fmt.Sprint(allCount))
userBusinessAnalysisAllList := make([]*UserBusinessAnalysisAll, 0)
if err := statictisSess.Table(tableName).Where(cond).OrderBy("invitation_user_num desc,id asc").Limit(pageSize, start).
Find(&userBusinessAnalysisAllList); err != nil {
return nil, 0
}
return userBusinessAnalysisAllList, allCount
}

func QueryUserStaticDataByTableName(start int, pageSize int, tableName string, queryObj interface{}, userName string) ([]*UserBusinessAnalysisAll, int64) {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
@@ -363,6 +392,7 @@ func QueryUserStaticDataByTableName(start int, pageSize int, tableName string, q
builder.Like{"lower(name)", strings.ToLower(userName)},
)
}

allCount, err := statictisSess.Where(cond).Count(queryObj)
if err != nil {
log.Info("query error." + err.Error())
@@ -752,6 +782,8 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
CollectImage, CollectedImage := queryImageStars(start_unix, end_unix)
RecommendImage := queryRecommedImage(start_unix, end_unix)

InvitationMap := queryUserInvitationCount(start_unix, end_unix)

DataDate := currentTimeNow.Format("2006-01-02") + " 00:01"

cond := "type != 1 and is_active=true"
@@ -825,7 +857,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
dateRecordAll.CollectImage = getMapValue(dateRecordAll.ID, CollectImage)
dateRecordAll.CollectedImage = getMapValue(dateRecordAll.ID, CollectedImage)
dateRecordAll.RecommendImage = getMapValue(dateRecordAll.ID, RecommendImage)
dateRecordAll.InvitationUserNum = getMapValue(dateRecordAll.ID, InvitationMap)
dateRecordAll.UserIndexPrimitive = getUserIndexFromAnalysisAll(dateRecordAll, ParaWeight)
userIndexMap[dateRecordAll.ID] = dateRecordAll.UserIndexPrimitive
if maxUserIndex < dateRecordAll.UserIndexPrimitive {
@@ -888,7 +920,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static

insertBatchSql := "INSERT INTO public." + tableName +
"(id, count_date, code_merge_count, commit_count, issue_count, comment_count, focus_repo_count, star_repo_count, watched_count, gitea_age_month, commit_code_size, commit_dataset_size, " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone) " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone,invitation_user_num) " +
"VALUES"

for i, record := range dateRecords {
@@ -897,7 +929,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static
", " + fmt.Sprint(record.WatchedCount) + ", " + fmt.Sprint(record.GiteaAgeMonth) + ", " + fmt.Sprint(record.CommitCodeSize) + ", " + fmt.Sprint(record.CommitDatasetSize) +
", " + fmt.Sprint(record.CommitModelCount) + ", " + fmt.Sprint(record.SolveIssueCount) + ", " + fmt.Sprint(record.EncyclopediasCount) + ", " + fmt.Sprint(record.RegistDate) +
", " + fmt.Sprint(record.CreateRepoCount) + ", " + fmt.Sprint(record.LoginCount) + ", " + fmt.Sprint(record.OpenIIndex) + ", '" + record.Email + "', '" + record.Name + "', '" + record.DataDate + "'," + fmt.Sprint(record.CloudBrainTaskNum) + "," + fmt.Sprint(record.GpuDebugJob) + "," + fmt.Sprint(record.NpuDebugJob) + "," + fmt.Sprint(record.GpuTrainJob) + "," + fmt.Sprint(record.NpuTrainJob) + "," + fmt.Sprint(record.NpuInferenceJob) + "," + fmt.Sprint(record.GpuBenchMarkJob) + "," + fmt.Sprint(record.CloudBrainRunTime) + "," + fmt.Sprint(record.CommitDatasetNum) + "," + fmt.Sprint(record.UserIndex) + ",'" + record.UserLocation + "'," +
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "')"
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "'" + "," + fmt.Sprint(record.InvitationUserNum) + ")"
if i < (len(dateRecords) - 1) {
insertBatchSql += ","
}
@@ -2173,6 +2205,41 @@ func queryCloudBrainTask(start_unix int64, end_unix int64) (map[int64]int, map[s

return resultMap, resultItemMap
}

func queryUserInvitationCount(start_unix int64, end_unix int64) map[int64]int {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()

resultMap := make(map[int64]int)
cond := "created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)
count, err := statictisSess.Where(cond).Count(new(Invitation))
if err != nil {
log.Info("query queryUserInvitationCount error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
statictisSess.Select("id,src_user_id,user_id").Table("invitation").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
invitationList := make([]*Invitation, 0)
statictisSess.Find(&invitationList)
log.Info("query invitationList size=" + fmt.Sprint(len(invitationList)))
for _, invitationRecord := range invitationList {
if _, ok := resultMap[invitationRecord.SrcUserID]; !ok {
resultMap[invitationRecord.SrcUserID] = 1
} else {
resultMap[invitationRecord.SrcUserID] += 1
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
log.Info("invitationList size=" + fmt.Sprint(len(resultMap)))
return resultMap
}

func setMapKey(key string, userId int64, value int, resultItemMap map[string]int) {
newKey := fmt.Sprint(userId) + "_" + key
if _, ok := resultItemMap[newKey]; !ok {


+ 14
- 7
models/user_business_struct.go View File

@@ -66,7 +66,8 @@ type UserBusinessAnalysisCurrentYear struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisLast30Day struct {
@@ -133,7 +134,8 @@ type UserBusinessAnalysisLast30Day struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisLastMonth struct {
@@ -200,7 +202,8 @@ type UserBusinessAnalysisLastMonth struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisCurrentMonth struct {
@@ -267,7 +270,8 @@ type UserBusinessAnalysisCurrentMonth struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisCurrentWeek struct {
@@ -335,7 +339,8 @@ type UserBusinessAnalysisCurrentWeek struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisYesterday struct {
@@ -403,7 +408,8 @@ type UserBusinessAnalysisYesterday struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisLastWeek struct {
@@ -471,7 +477,8 @@ type UserBusinessAnalysisLastWeek struct {
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
}

type UserAnalysisPara struct {


+ 102
- 0
models/user_invitation.go View File

@@ -0,0 +1,102 @@
package models

import (
"fmt"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
)

// Follow represents relations of user and his/her followers.
type Invitation struct {
ID int64 `xorm:"pk autoincr"`
SrcUserID int64 `xorm:"NOT NULL DEFAULT 0"`
UserID int64 `xorm:"NOT NULL DEFAULT 0"`
Phone string `xorm:"INDEX"`
Avatar string `xorm:"-"`
Name string `xorm:"-"`
InvitationUserNum int `xorm:"-"`
IsActive bool `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

func QueryInvitaionByPhone(phone string) []*Invitation {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
cond := "phone ='" + phone + "'"
invitationList := make([]*Invitation, 0)
if err := statictisSess.Table(new(Invitation)).Where(cond).
Find(&invitationList); err != nil {
return nil
} else {
return invitationList
}
}

func GetAllUserName() map[int64]string {
sess := x.NewSession()
defer sess.Close()
sess.Select("id,name").Table("user")
userList := make([]*User, 0)
reMap := make(map[int64]string)
sess.Find(&userList)
for _, user := range userList {
reMap[user.ID] = user.Name
}
return reMap
}

func QueryInvitaionPage(start int, pageSize int) ([]*Invitation, int64) {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
//cond := "created_unix >=" + fmt.Sprint(startTime) + " and created_unix <=" + fmt.Sprint(endTime)

allCount, err := statictisSess.Count(new(Invitation))
if err != nil {
log.Info("query error." + err.Error())
return nil, 0
}
invitationList := make([]*Invitation, 0)
if err := statictisSess.Table(new(Invitation)).OrderBy("created_unix desc").Limit(pageSize, start).
Find(&invitationList); err != nil {
return nil, 0
}
return invitationList, allCount
}

func QueryInvitaionByTime(startTime int64, endTime int64) []*Invitation {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
cond := "created_unix >=" + fmt.Sprint(startTime) + " and created_unix <=" + fmt.Sprint(endTime)
invitationList := make([]*Invitation, 0)
if err := statictisSess.Table(new(Invitation)).Where(cond).OrderBy("created_unix desc").
Find(&invitationList); err != nil {
return nil
}
return invitationList
}

func InsertInvitaion(invitationUser *Invitation) error {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
_, err := statictisSess.Insert(invitationUser)
return err
}

func QueryInvitaionBySrcUserId(srcUserId int64, start int, pageSize int) ([]*Invitation, int64) {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
cond := "src_user_id =" + fmt.Sprint(srcUserId)
allCount, err := statictisSess.Where(cond).Count(new(Invitation))
if err != nil {
log.Info("query error." + err.Error())
return nil, 0
}
invitationList := make([]*Invitation, 0)

if err := statictisSess.Table(new(Invitation)).Where(cond).OrderBy("created_unix desc").Limit(pageSize, start).
Find(&invitationList); err != nil {
return nil, 0
}
return invitationList, allCount
}

+ 1
- 1
modules/auth/user_form.go View File

@@ -372,7 +372,7 @@ func (f *U2FDeleteForm) Validate(ctx *macaron.Context, errs binding.Errors) bind

type PhoneNumberForm struct {
PhoneNumber string `binding:"Required;MaxSize(20)"`
Mode int `binding:"Required"`
Mode int `binding:"Required"`
SlideID string `binding:"Required;MaxSize(100)"`
}



+ 6
- 5
modules/grampus/resty.go View File

@@ -1,14 +1,15 @@
package grampus

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/go-resty/resty/v2"
"net/http"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-resty/resty/v2"
)

var (
@@ -236,7 +237,7 @@ func GetTrainJobLog(jobID string) (string, error) {
return logContent, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return logContent, fmt.Errorf("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return logContent, fmt.Errorf("GetTrainJobLog failed(%d):%d(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

logContent = res.String()


+ 6
- 5
modules/setting/setting.go View File

@@ -66,9 +66,10 @@ const (
)

type C2NetSequenceInfo struct {
ID int `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
ID int `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
ContentEN string `json:"content_en"`
}

type C2NetSqInfos struct {
@@ -1475,7 +1476,7 @@ func NewContext() {
FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("")
TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("")
ModelArtsSpecialPools = sec.Key("SPECIAL_POOL").MustString("")
ModelArtsMultiNode=sec.Key("MULTI_NODE").MustString("")
ModelArtsMultiNode = sec.Key("MULTI_NODE").MustString("")

sec = Cfg.Section("elk")
ElkUrl = sec.Key("ELKURL").MustString("")
@@ -1570,7 +1571,7 @@ func getGrampusConfig() {
Grampus.UserName = sec.Key("USERNAME").MustString("")
Grampus.Password = sec.Key("PASSWORD").MustString("")
Grampus.SpecialPools = sec.Key("SPECIAL_POOL").MustString("")
Grampus.C2NetSequence = sec.Key("C2NET_SEQUENCE").MustString("{\"sequence\":[{\"id\":1,\"name\":\"cloudbrain_one\",\"content\":\"鹏城云脑一号\"},{\"id\":2,\"name\":\"cloudbrain_two\",\"content\":\"鹏城云脑二号\"},{\"id\":3,\"name\":\"beida\",\"content\":\"北大人工智能集群系统\"},{\"id\":4,\"name\":\"hefei\",\"content\":\"合肥类脑智能开放平台\"},{\"id\":5,\"name\":\"wuhan\",\"content\":\"武汉人工智能计算中心\"},{\"id\":6,\"name\":\"xian\",\"content\":\"西安未来人工智能计算中心\"},{\"id\":7,\"pclcci\":\"more\",\"content\":\"鹏城云计算所\"},{\"id\":8,\"name\":\"xuchang\",\"content\":\"中原人工智能计算中心\"},{\"id\":9,\"name\":\"chengdu\",\"content\":\"成都人工智能计算中心\"},{\"id\":10,\"name\":\"more\",\"content\":\"横琴先进智能计算中心\"},{\"id\":11,\"name\":\"more\",\"content\":\"国家超级计算济南中心\"}]}")
Grampus.C2NetSequence = sec.Key("C2NET_SEQUENCE").MustString("{\"sequence\":[{\"id\":1,\"name\":\"cloudbrain_one\",\"content\":\"鹏城云脑一号\",\"content_en\":\"Pencheng Cloudbrain Ⅰ\"},{\"id\":2,\"name\":\"cloudbrain_two\",\"content\":\"鹏城云脑二号\",\"content_en\":\"Pencheng Cloudbrain Ⅱ\"},{\"id\":3,\"name\":\"beida\",\"content\":\"北大人工智能集群系统\",\"content_en\":\"Peking University AI Center\"},{\"id\":4,\"name\":\"hefei\",\"content\":\"合肥类脑智能开放平台\",\"content_en\":\"Hefei AI Center\"},{\"id\":5,\"name\":\"wuhan\",\"content\":\"武汉人工智能计算中心\",\"content_en\":\"Wuhan AI Center\"},{\"id\":6,\"name\":\"xian\",\"content\":\"西安未来人工智能计算中心\",\"content_en\":\"Xi'an AI Center\"},{\"id\":7,\"pclcci\":\"more\",\"content\":\"鹏城云计算所\",\"content_en\":\"Pengcheng Cloud Computing Institute\"},{\"id\":8,\"name\":\"xuchang\",\"content\":\"中原人工智能计算中心\",\"content_en\":\"Zhongyuan AI Center\"},{\"id\":9,\"name\":\"chengdu\",\"content\":\"成都人工智能计算中心\",\"content_en\":\"Chengdu AI Center\"},{\"id\":10,\"name\":\"more\",\"content\":\"横琴先进智能计算中心\",\"content_en\":\"Hengqin AI Center\"},{\"id\":11,\"name\":\"more\",\"content\":\"国家超级计算济南中心\",\"content_en\":\"HPC & AI Center\"}]}")
if Grampus.C2NetSequence != "" {
if err := json.Unmarshal([]byte(Grampus.C2NetSequence), &C2NetInfos); err != nil {
log.Error("Unmarshal(C2NetSequence) failed:%v", err)


+ 9
- 0
options/locale/locale_en-US.ini View File

@@ -70,6 +70,10 @@ your_dashboard = Dashboard
your_profile = Profile
your_starred = Starred
your_settings = Settings
invite_friends = Invite Friends
your_friend=Your friend
invite_you_to_join_the_OpenI_AI_Collaboration_Platform_and_enjoy_abundant_free_computing_resources=invite you to join the OpenI AI Collaboration Platform and enjoy abundant free computing resources!
recommender=Recommender

all = All
sources = Sources
@@ -532,6 +536,10 @@ form.name_reserved = The username '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username.
form.name_chars_not_allowed = User name '%s' contains invalid characters.

static.invitationdetailsheetname=User Invitation Detail
static.invitationNum=User Invitation Count
static.invitationsheetname=User Invitation
static.srcUserId=Recommended User ID
static.sheetname=User Analysis
static.id=ID
static.name=User Name
@@ -1293,6 +1301,7 @@ modelconvert.taskurlname=Model transformation task
log_scroll_start=Scroll to top
log_scroll_end=Scroll to bottom
modelconvert.tasknameempty=Please enter a task name.
modelconvert.modelfileempty=Please choose a model file.
modelconvert.inputshapeerror=Format input error, please input such as: 1,1,32,32, corresponding to the input data format.

modelconvert.manage.create_error1=A model transformation task with the same name already exists.


+ 9
- 0
options/locale/locale_zh-CN.ini View File

@@ -70,6 +70,10 @@ your_dashboard=个人中心
your_profile=个人信息
your_starred=已点赞
your_settings=设置
invite_friends=邀请好友
your_friend=您的好友
invite_you_to_join_the_OpenI_AI_Collaboration_Platform_and_enjoy_abundant_free_computing_resources=邀请您加入启智社区AI协作平台,畅享充沛的免费算力资源!
recommender=推荐人

all=所有
sources=自建
@@ -537,7 +541,11 @@ form.name_reserved='%s' 用户名被保留。
form.name_pattern_not_allowed=用户名中不允许使用 "%s"。
form.name_chars_not_allowed=用户名 '%s' 包含无效字符。

static.invitationdetailsheetname=用户邀请详细数据
static.invitationNum=邀请用户数
static.sheetname=用户分析
static.srcUserId=推荐用户ID
static.invitationsheetname=用户邀请分析
static.id=ID
static.name=用户名
static.codemergecount=PR数
@@ -1309,6 +1317,7 @@ log_scroll_start=滚动到顶部
log_scroll_end=滚动到底部
modelconvert.tasknameempty=请输入任务名称。
modelconvert.inputshapeerror=格式输入错误,请输入如:1,1,32,32,与输入数据格式对应。
modelconvert.modelfileempty=请选择模型文件。

modelconvert.manage.create_error1=相同的名称模型转换任务已经存在。
modelconvert.manage.create_error2=只能创建一个正在运行的模型转换任务。


+ 14
- 0
routers/api/v1/api.go View File

@@ -572,6 +572,19 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/query_user_all", operationReq, repo_ext.QueryUserStaticAll)
m.Get("/query_user_activity", operationReq, repo_ext.QueryUserActivity)
m.Get("/query_user_login", operationReq, repo_ext.QueryUserLoginInfo)

m.Get("/query_invitation_current_month", operationReq, repo_ext.QueryInvitationCurrentMonth)
m.Get("/query_invitation_current_week", operationReq, repo_ext.QueryInvitationCurrentWeek)
m.Get("/query_invitation_last_week", operationReq, repo_ext.QueryInvitationLastWeek)
m.Get("/query_invitation_current_year", operationReq, repo_ext.QueryInvitationCurrentYear)
m.Get("/query_invitation_last30_day", operationReq, repo_ext.QueryInvitationLast30Day)
m.Get("/query_invitation_last_month", operationReq, repo_ext.QueryInvitationLastMonth)
m.Get("/query_invitation_yesterday", operationReq, repo_ext.QueryInvitationYesterday)
m.Get("/query_invitation_all", operationReq, repo_ext.QueryInvitationAll)
m.Get("/query_invitation_userdefine", operationReq, repo_ext.QueryUserDefineInvitationPage)

m.Get("/download_invitation_detail", operationReq, repo_ext.DownloadInvitationDetail)

//cloudbrain board
m.Group("/cloudbrainboard", func() {
m.Get("/downloadAll", repo.DownloadCloudBrainBoard)
@@ -969,6 +982,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", repo.GetModelArtsTrainJobVersion)
m.Post("/stop_version", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo_ext.GrampusStopJob)
m.Get("/log", repo_ext.GrampusGetLog)
m.Get("/download_log", cloudbrain.AdminOrJobCreaterRightForTrain, repo_ext.GrampusDownloadLog)
})
})
}, reqRepoReader(models.UnitTypeCloudBrain))


+ 5
- 1
routers/api/v1/repo/cloudbrain.go View File

@@ -379,7 +379,11 @@ func CloudbrainDownloadLogFile(ctx *context.Context) {
return
}

prefix := "/" + setting.CBCodePathPrefix + job.JobName + "/model"
logDir := "/model"
if job.JobType == string(models.JobTypeInference) {
logDir = cloudbrain.ResultPath
}
prefix := "/" + setting.CBCodePathPrefix + job.JobName + logDir
files, err := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "")
if err != nil {
log.Error("query cloudbrain model failed: %v", err)


+ 15
- 0
routers/home.go View File

@@ -106,6 +106,11 @@ func Dashboard(ctx *context.Context) {
log.Info("set image info=" + pictureInfo[0]["url"])
ctx.Data["image_url"] = pictureInfo[0]["url"]
ctx.Data["image_link"] = pictureInfo[0]["image_link"]

if len(pictureInfo) > 1 {
ctx.Data["invite_image_url"] = pictureInfo[1]["url"]
ctx.Data["invite_image_link"] = pictureInfo[1]["image_link"]
}
}
if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
@@ -728,6 +733,16 @@ func getImageInfo(filename string) ([]map[string]string, error) {
return imageInfo, nil
}

func GetMapInfo(ctx *context.Context) {
filename := ctx.Query("filename")
url := setting.RecommentRepoAddr + filename
result, err := repository.RecommendContentFromPromote(url)
if err != nil {
log.Info("get file error:" + err.Error())
}
ctx.JSON(http.StatusOK, result)
}

func GetRankUser(index string) ([]map[string]interface{}, error) {
url := setting.RecommentRepoAddr + "user_rank_" + index
result, err := repository.RecommendFromPromote(url)


+ 49
- 24
routers/repo/ai_model_manage.go View File

@@ -27,6 +27,9 @@ const (
MODEL_LATEST = 1
MODEL_NOT_LATEST = 0
MODEL_MAX_SIZE = 1024 * 1024 * 1024
STATUS_COPY_MODEL = 1
STATUS_FINISHED = 0
STATUS_ERROR = 2
)

func saveModelByParameters(jobId string, versionName string, name string, version string, label string, description string, engine int, ctx *context.Context) error {
@@ -62,13 +65,9 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
modelSelectedFile := ctx.Query("modelSelectedFile")
//download model zip //train type
if aiTask.ComputeResource == models.NPUResource {
modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
if err != nil {
log.Info("download model from CloudBrainTwo faild." + err.Error())
return err
}
cloudType = models.TypeCloudBrainTwo
} else if aiTask.ComputeResource == models.GPUResource {
cloudType = models.TypeCloudBrainOne
var ResourceSpecs *models.ResourceSpecs
json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs)
for _, tmp := range ResourceSpecs.ResourceSpec {
@@ -77,24 +76,8 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
aiTask.FlavorName = flaverName
}
}
modelPath, modelSize, err = downloadModelFromCloudBrainOne(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
if err != nil {
log.Info("download model from CloudBrainOne faild." + err.Error())
return err
}
cloudType = models.TypeCloudBrainOne
}
// else if cloudType == models.TypeC2Net {
// if aiTask.ComputeResource == models.NPUResource {
// modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
// if err != nil {
// log.Info("download model from CloudBrainTwo faild." + err.Error())
// return err
// }
// } else if aiTask.ComputeResource == models.GPUResource {

// }
// }

accuracy := make(map[string]string)
accuracy["F1"] = ""
accuracy["Recall"] = ""
@@ -123,6 +106,7 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
Engine: int64(engine),
TrainTaskInfo: string(aiTaskJson),
Accuracy: string(accuracyJson),
Status: STATUS_COPY_MODEL,
}

err = models.SaveModelToDb(model)
@@ -146,11 +130,44 @@ func saveModelByParameters(jobId string, versionName string, name string, versio

models.UpdateRepositoryUnits(ctx.Repo.Repository, units, deleteUnitTypes)

go asyncToCopyModel(aiTask, id, modelSelectedFile)

log.Info("save model end.")
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, name, models.ActionCreateNewModelTask)
return nil
}

func asyncToCopyModel(aiTask *models.Cloudbrain, id string, modelSelectedFile string) {
if aiTask.ComputeResource == models.NPUResource {
modelPath, modelSize, err := downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
if err != nil {
updateStatus(id, 0, STATUS_ERROR, modelPath, err.Error())
log.Info("download model from CloudBrainTwo faild." + err.Error())
} else {
updateStatus(id, modelSize, STATUS_FINISHED, modelPath, "")
}
} else if aiTask.ComputeResource == models.GPUResource {

modelPath, modelSize, err := downloadModelFromCloudBrainOne(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
if err != nil {
updateStatus(id, 0, STATUS_ERROR, modelPath, err.Error())
log.Info("download model from CloudBrainOne faild." + err.Error())
} else {
updateStatus(id, modelSize, STATUS_FINISHED, modelPath, "")
}
}
}

func updateStatus(id string, modelSize int64, status int, modelPath string, statusDesc string) {
if len(statusDesc) > 400 {
statusDesc = statusDesc[0:400]
}
err := models.ModifyModelStatus(id, modelSize, status, modelPath, statusDesc)
if err != nil {
log.Info("update status error." + err.Error())
}
}

func SaveNewNameModel(ctx *context.Context) {
if !ctx.Repo.CanWrite(models.UnitTypeModelManage) {
ctx.Error(403, ctx.Tr("repo.model_noright"))
@@ -331,6 +348,7 @@ func QueryModelByParameters(repoId int64, page int) ([]*models.AiModelManage, in
RepoID: repoId,
Type: -1,
New: MODEL_LATEST,
Status: -1,
})
}

@@ -642,7 +660,6 @@ func queryUserName(intSlice []int64) map[int64]*models.User {
result[user.ID] = user
}
}

return result
}

@@ -685,6 +702,7 @@ func SetModelCount(ctx *context.Context) {
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: -1,
})
ctx.Data["MODEL_COUNT"] = count
}
@@ -758,6 +776,7 @@ func ShowModelPageInfo(ctx *context.Context) {
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: -1,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -835,6 +854,7 @@ func QueryModelListForPredict(ctx *context.Context) {
RepoID: repoId,
Type: ctx.QueryInt("type"),
New: -1,
Status: 0,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -896,12 +916,17 @@ func QueryOneLevelModelFile(ctx *context.Context) {
log.Info("TypeCloudBrainTwo list model file.")
prefix := model.Path[len(setting.Bucket)+1:]
fileinfos, _ := storage.GetOneLevelAllObjectUnderDir(setting.Bucket, prefix, parentDir)
if fileinfos == nil {
fileinfos = make([]storage.FileInfo, 0)
}
ctx.JSON(http.StatusOK, fileinfos)
} else if model.Type == models.TypeCloudBrainOne {
log.Info("TypeCloudBrainOne list model file.")
prefix := model.Path[len(setting.Attachment.Minio.Bucket)+1:]
fileinfos, _ := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, parentDir)
if fileinfos == nil {
fileinfos = make([]storage.FileInfo, 0)
}
ctx.JSON(http.StatusOK, fileinfos)
}

}

+ 30
- 3
routers/repo/grampus.go View File

@@ -914,7 +914,7 @@ func GrampusTrainJobShow(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplGrampusTrainJobShow)
}

func GrampusGetLog(ctx *context.Context) {
func GrampusDownloadLog(ctx *context.Context) {
jobID := ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
@@ -926,13 +926,40 @@ func GrampusGetLog(ctx *context.Context) {
content, err := grampus.GetTrainJobLog(job.JobID)
if err != nil {
log.Error("GetTrainJobLog failed: %v", err, ctx.Data["MsgID"])
content = ""
}
fileName := job.JobName + "-log.txt"
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+fileName)
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
var b []byte = []byte(content)
ctx.Resp.Write(b)
}

func GrampusGetLog(ctx *context.Context) {
jobID := ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
log.Error("GetCloudbrainByJobID failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}

content, err := grampus.GetTrainJobLog(job.JobID)
if err != nil {
log.Error("GetTrainJobLog failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}
var canLogDownload bool
if err != nil {
canLogDownload = false
} else {
canLogDownload = true
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"JobName": job.JobName,
"Content": content,
"JobName": job.JobName,
"Content": content,
"CanLogDownload": canLogDownload,
})

return


+ 2
- 1
routers/repo/modelarts.go View File

@@ -1551,7 +1551,6 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
})
}


task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, PreVersionName)
if err != nil {
log.Error("GetCloudbrainByJobIDAndVersionName(%s) failed:%v", jobID, err.Error())
@@ -2322,6 +2321,7 @@ func InferenceJobIndex(ctx *context.Context) {
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: 0,
})
ctx.Data["MODEL_COUNT"] = model_count

@@ -2402,6 +2402,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error {
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: 0,
})
ctx.Data["MODEL_COUNT"] = model_count
ctx.Data["datasetType"] = models.TypeCloudBrainTwo


+ 440
- 0
routers/repo/user_invitation.go View File

@@ -0,0 +1,440 @@
package repo

import (
"fmt"
"net/http"
"net/url"
"sort"
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/360EntSecGroup-Skylar/excelize/v2"
)

func QueryInvitationCurrentMonth(ctx *context.Context) {
// userName := ctx.Query("userName")
// currentTimeNow := time.Now()
// pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location())
// pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 0, 0, 0, 0, currentTimeNow.Location())

//queryUserDataPage(ctx, "public.user_business_analysis_current_month", new(models.UserBusinessAnalysisCurrentMonth))
//_, count := models.QueryUserStaticDataByTableName(1, 1, "public.user_business_analysis_current_month", new(models.UserBusinessAnalysisCurrentMonth), userName, 1)

queryDataFromStaticTable(ctx, "public.user_business_analysis_current_month", new(models.UserBusinessAnalysisCurrentMonth))
}

func getInvitationExcelHeader(ctx *context.Context) map[string]string {
excelHeader := make([]string, 0)
excelHeader = append(excelHeader, ctx.Tr("user.static.id"))
excelHeader = append(excelHeader, ctx.Tr("user.static.name"))
excelHeader = append(excelHeader, ctx.Tr("user.static.invitationNum"))
excelHeader = append(excelHeader, ctx.Tr("user.static.phone"))
excelHeader = append(excelHeader, ctx.Tr("user.static.registdate"))

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 getInvitationDetailExcelHeader(ctx *context.Context) map[string]string {
excelHeader := make([]string, 0)
excelHeader = append(excelHeader, ctx.Tr("user.static.id"))
excelHeader = append(excelHeader, ctx.Tr("user.static.name"))
excelHeader = append(excelHeader, ctx.Tr("user.static.srcUserId"))
excelHeader = append(excelHeader, ctx.Tr("user.static.phone"))
excelHeader = append(excelHeader, ctx.Tr("user.static.registdate"))

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 writeInvitationExcel(row int, xlsx *excelize.File, sheetName string, userRecord *models.UserBusinessAnalysisAll) {
rows := fmt.Sprint(row)
var tmp byte
tmp = 0
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.ID)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Name)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.InvitationUserNum)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone)
tmp = tmp + 1

formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05")
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime[0:len(formatTime)-3])

}

func writeInvitationDetailExcel(row int, xlsx *excelize.File, sheetName string, userRecord *models.Invitation) {
rows := fmt.Sprint(row)
var tmp byte
tmp = 0
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.UserID)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Name)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.SrcUserID)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone)
tmp = tmp + 1

formatTime := userRecord.CreatedUnix.Format("2006-01-02 15:04:05")
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime[0:len(formatTime)-3])

}

func DownloadInvitationDetail(ctx *context.Context) {
xlsx := excelize.NewFile()
sheetName := ctx.Tr("user.static.invitationdetailsheetname")
index := xlsx.NewSheet(sheetName)
xlsx.DeleteSheet("Sheet1")
excelHeader := getInvitationDetailExcelHeader(ctx)
for k, v := range excelHeader {
//设置单元格的值
xlsx.SetCellValue(sheetName, k, v)
}
userNameMap := models.GetAllUserName()
_, count := models.QueryInvitaionPage(1, 1)
var indexTotal int64
indexTotal = 0
row := 1
for {
re, _ := models.QueryInvitaionPage(int(indexTotal), PAGE_SIZE)
log.Info("return count=" + fmt.Sprint(count))
for _, userRecord := range re {
row++
userRecord.Name = userNameMap[userRecord.UserID]
if userRecord.Name == "" {
userRecord.Name = "已注销"
}
writeInvitationDetailExcel(row, xlsx, sheetName, userRecord)
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
//设置默认打开的表单
xlsx.SetActiveSheet(index)
filename := sheetName + ".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())
}
}

func queryDataFromStaticTable(ctx *context.Context, tableName string, queryObj interface{}) {
page, pageSize := getPageInfo(ctx)
userName := ctx.Query("userName")
IsReturnFile := ctx.QueryBool("IsReturnFile")

if IsReturnFile {
//writer exec file.
xlsx := excelize.NewFile()
sheetName := ctx.Tr("user.static.invitationsheetname")
index := xlsx.NewSheet(sheetName)
xlsx.DeleteSheet("Sheet1")
excelHeader := getInvitationExcelHeader(ctx)
for k, v := range excelHeader {
//设置单元格的值
xlsx.SetCellValue(sheetName, k, v)
}
_, count := models.QueryUserInvitationDataByTableName(1, 1, tableName, queryObj, "", 1)
var indexTotal int64
indexTotal = 0
row := 1
for {
re, _ := models.QueryUserInvitationDataByTableName(int(indexTotal), PAGE_SIZE, tableName, queryObj, "", 1)
log.Info("return count=" + fmt.Sprint(count))
for _, userRecord := range re {
row++
writeInvitationExcel(row, xlsx, sheetName, userRecord)
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
//设置默认打开的表单
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 {
resultRecord, count := models.QueryUserInvitationDataByTableName((page-1)*pageSize, pageSize, tableName, queryObj, userName, 1)
result := make([]models.Invitation, 0)
for _, record := range resultRecord {
invi := models.Invitation{
SrcUserID: record.ID,
Name: record.Name,
InvitationUserNum: record.InvitationUserNum,
Phone: record.Phone,
CreatedUnix: record.RegistDate,
}
result = append(result, invi)
}
mapInterface := make(map[string]interface{})
mapInterface["data"] = result
mapInterface["count"] = count
ctx.JSON(http.StatusOK, mapInterface)
}
}

func QueryInvitationCurrentWeek(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(), 0, 0, 0, 0, currentTimeNow.Location())
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())
queryDataFromStaticTable(ctx, "public.user_business_analysis_current_week", new(models.UserBusinessAnalysisCurrentWeek))
}

func QueryInvitationLastWeek(ctx *context.Context) {
// currentTimeNow := time.Now()
// offset := int(time.Monday - currentTimeNow.Weekday())
// if offset > 0 {
// offset = -6
// }
// pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset)
// pageStartTime := pageEndTime.AddDate(0, 0, -7)
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())

queryDataFromStaticTable(ctx, "public.user_business_analysis_last_week", new(models.UserBusinessAnalysisLastWeek))
}

func QueryInvitationCurrentYear(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(), 0, 0, 0, 0, currentTimeNow.Location())
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())

queryDataFromStaticTable(ctx, "public.user_business_analysis_current_year", new(models.UserBusinessAnalysisCurrentYear))
}

func QueryInvitationLast30Day(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(), 0, 0, 0, 0, currentTimeNow.Location())
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())

queryDataFromStaticTable(ctx, "public.user_business_analysis_last30_day", new(models.UserBusinessAnalysisLast30Day))
}

func QueryInvitationLastMonth(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)
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())

queryDataFromStaticTable(ctx, "public.user_business_analysis_last_month", new(models.UserBusinessAnalysisLastMonth))
}

func QueryInvitationYesterday(ctx *context.Context) {
// currentTimeNow := time.Now().AddDate(0, 0, -1)
// 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())
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())

queryDataFromStaticTable(ctx, "public.user_business_analysis_yesterday", new(models.UserBusinessAnalysisYesterday))
}

func QueryInvitationAll(ctx *context.Context) {
// currentTimeNow := time.Now()
// pageStartTime := time.Date(2022, 8, 5, 0, 0, 0, 0, currentTimeNow.Location())
// pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location())
// queryData(ctx, pageStartTime.Unix(), pageEndTime.Unix())

queryDataFromStaticTable(ctx, "public.user_business_analysis_all", new(models.UserBusinessAnalysisAll))
}

func QueryUserDefineInvitationPage(ctx *context.Context) {
startDate := ctx.Query("startDate")
endDate := ctx.Query("endDate")
startTime, _ := time.ParseInLocation("2006-01-02", startDate, time.Local)
//startTime = startTime.UTC()
endTime, _ := time.ParseInLocation("2006-01-02", endDate, time.Local)

queryData(ctx, startTime, endTime)
}

func queryData(ctx *context.Context, startTime time.Time, endTime time.Time) {
page, pageSize := getPageInfo(ctx)
IsReturnFile := ctx.QueryBool("IsReturnFile")

dbResult := models.QueryInvitaionByTime(startTime.Unix(), endTime.Unix())

invitaionNumMap := make(map[int64]int, 0)
allUserIds := make([]int64, 0)
for _, record := range dbResult {
if _, ok := invitaionNumMap[record.SrcUserID]; !ok {
invitaionNumMap[record.SrcUserID] = 1
} else {
invitaionNumMap[record.SrcUserID] = invitaionNumMap[record.SrcUserID] + 1
}
}
invitaionNumList := make([]models.Invitation, 0)
for key, value := range invitaionNumMap {
invi := models.Invitation{
SrcUserID: key,
InvitationUserNum: value,
}
invitaionNumList = append(invitaionNumList, invi)
allUserIds = append(allUserIds, key)
}
sort.Slice(invitaionNumList, func(i, j int) bool {
return invitaionNumList[i].InvitationUserNum > invitaionNumList[j].InvitationUserNum
})
if IsReturnFile {
xlsx := excelize.NewFile()
sheetName := ctx.Tr("user.static.invitationsheetname")
index := xlsx.NewSheet(sheetName)
xlsx.DeleteSheet("Sheet1")
excelHeader := getInvitationExcelHeader(ctx)
for k, v := range excelHeader {
//设置单元格的值
xlsx.SetCellValue(sheetName, k, v)
}
end := 100
userMap := make(map[int64]*models.User, 0)
log.Info("len(allUserIds)=" + fmt.Sprint(len(allUserIds)))
for i := 0; i < len(allUserIds); i += 100 {
if end >= len(allUserIds) {
end = len(allUserIds)
}
log.Info("i=" + fmt.Sprint(i) + " end=" + fmt.Sprint(end))
if i == end {
break
}
userList, err := models.GetUsersByIDs(allUserIds[i:end])
if err == nil {
for _, tmp := range userList {
userMap[tmp.ID] = tmp
}
} else {

}
end = end + 100
}
row := 1
log.Info("len(userMap)=" + fmt.Sprint(len(userMap)))
for _, userRecord := range invitaionNumList {
row++
rows := fmt.Sprint(row)
var tmp byte
tmp = 0

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.SrcUserID)
tmp = tmp + 1
name := "已注销"
if userMap[userRecord.SrcUserID] != nil {
name = userMap[userRecord.SrcUserID].Name
}
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, name)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.InvitationUserNum)
tmp = tmp + 1
Phone := ""
if userMap[userRecord.SrcUserID] != nil {
Phone = userMap[userRecord.SrcUserID].PhoneNumber
}
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, Phone)
tmp = tmp + 1

formatTime := ""
if userMap[userRecord.SrcUserID] != nil {
formatTime = userMap[userRecord.SrcUserID].CreatedUnix.Format("2006-01-02 15:04:05")
formatTime = formatTime[0 : len(formatTime)-3]
}
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime)
}
//设置默认打开的表单
xlsx.SetActiveSheet(index)
filename := sheetName + "_" + getTimeFileName(startTime) + "_" + getTimeFileName(endTime) + ".xlsx"
//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 {
result := make([]*models.Invitation, 0)
userIds := make([]int64, 0)
end := len(invitaionNumList) - 1
for start := (page - 1) * pageSize; start <= end; start++ {
invi := invitaionNumList[start]
//todo name phone,createunix
result = append(result, &invi)
userIds = append(userIds, invi.SrcUserID)
if len(result) == pageSize {
break
}
}
userList, err := models.GetUsersByIDs(userIds)
if err == nil {
for _, invi := range result {
tmpUser := userList[0]
for _, tmp := range userList {
if tmp.ID == invi.SrcUserID {
tmpUser = tmp
break
}
}
if invi.SrcUserID == tmpUser.ID {
invi.Name = tmpUser.Name
invi.Phone = tmpUser.PhoneNumber
invi.CreatedUnix = tmpUser.CreatedUnix
} else {
invi.Name = "已注销"
}
}
} else {
log.Info("query user error." + err.Error())
}
mapInterface := make(map[string]interface{})
mapInterface["data"] = result
mapInterface["count"] = len(invitaionNumList)
ctx.JSON(http.StatusOK, mapInterface)
}
}

func getPageInfo(ctx *context.Context) (int, int) {
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = setting.UI.IssuePagingNum
}
return page, pageSize
}

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

@@ -335,6 +335,7 @@ func RegisterRoutes(m *macaron.Macaron) {
go reward.AcceptStatusChangeAction()
m.Get("/action/notification", routers.ActionNotification)
m.Get("/recommend/home", routers.RecommendHomeInfo)
m.Get("/dashboard/invitation", routers.GetMapInfo)
//m.Get("/recommend/org", routers.RecommendOrgFromPromote)
//m.Get("/recommend/repo", routers.RecommendRepoFromPromote)
m.Get("/recommend/userrank/:index", routers.GetUserRankFromPromote)
@@ -509,6 +510,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/forgot_password", user.ForgotPasswd)
m.Post("/forgot_password", user.ForgotPasswdPost)
m.Post("/logout", user.SignOut)
m.Get("/invitation_code", reqSignIn, user.GetInvitaionCode)
m.Get("/invitation_tpl", reqSignIn, user.InviationTpl)
})
// ***** END: User *****



+ 107
- 0
routers/user/Invitation.go View File

@@ -0,0 +1,107 @@
package user

import (
"errors"
"strconv"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/repository"
)

const (
tplInvitation base.TplName = "user/settings/invite"
)

func GetInvitaionCode(ctx *context.Context) {
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = setting.UI.IssuePagingNum
}

url := setting.RecommentRepoAddr + "invitaion_page"
result, err := repository.RecommendFromPromote(url)
resultJsonMap := make(map[string]interface{}, 0)
if err == nil {
for _, strLine := range result {
tmpIndex := strings.Index(strLine, "=")
if tmpIndex != -1 {
key := strLine[0:tmpIndex]
value := strLine[tmpIndex+1:]
resultJsonMap[key] = value
}
}
}

if ctx.IsSigned {
resultJsonMap["invitation_code"] = getInvitaionCode(ctx)
re, count := models.QueryInvitaionBySrcUserId(ctx.User.ID, (page-1)*pageSize, pageSize)
for _, record := range re {
tmpUser, err := models.GetUserByID(record.UserID)
if err == nil {
record.Avatar = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + tmpUser.Name + "/" + strconv.Itoa(-1)
record.IsActive = tmpUser.IsActive
record.Name = tmpUser.Name
}
}
resultJsonMap["invitation_users"] = re
resultJsonMap["invitation_users_count"] = count
}

ctx.JSON(200, resultJsonMap)
}

func InviationTpl(ctx *context.Context) {
ctx.HTML(200, tplInvitation)
}

func RegisteUserByInvitaionCode(invitationcode string, newUserId int64, newPhoneNumber string) error {
user := parseInvitaionCode(invitationcode)
if user == nil {
return errors.New("The invitated user not existed.")
}

if newPhoneNumber != "" {
re := models.QueryInvitaionByPhone(newPhoneNumber)
if re != nil {
if len(re) > 0 {
log.Info("The phone has been invitated. so ingore it.")
return errors.New("The phone has been invitated.")
}
}
} else {
log.Info("the phone number is null. user name=" + user.Name)
}

invitation := &models.Invitation{
SrcUserID: user.ID,
UserID: newUserId,
Phone: newPhoneNumber,
}

err := models.InsertInvitaion(invitation)
if err != nil {
log.Info("insert error," + err.Error())
}
return err
}

func getInvitaionCode(ctx *context.Context) string {
return ctx.User.Name
}

func parseInvitaionCode(invitationcode string) *models.User {
user, err := models.GetUserByName(invitationcode)
if err == nil {
return user
}
return nil
}

+ 28
- 24
routers/user/auth.go View File

@@ -8,11 +8,12 @@ package user
import (
"errors"
"fmt"
"github.com/gomodule/redigo/redis"
"net/http"
"strconv"
"strings"

"github.com/gomodule/redigo/redis"

"code.gitea.io/gitea/modules/slideimage"

phoneService "code.gitea.io/gitea/services/phone"
@@ -352,18 +353,17 @@ func SignInPostCommon(ctx *context.Context, form auth.SignInForm) {
ctx.Redirect(setting.AppSubURL + "/user/two_factor")
}


func SignInCloudBrainPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["PageIsCloudBrainLogin"] = true
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login/cloud_brain"
SignInPostCommon(ctx,form)
SignInPostCommon(ctx, form)
}

// SignInPost response for sign in request
func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["PageIsLogin"] = true
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
SignInPostCommon(ctx,form)
SignInPostCommon(ctx, form)
}

// TwoFactor shows the user a two-factor authentication page.
@@ -1264,9 +1264,9 @@ func SignUp(ctx *context.Context) {
// SignUpPost response for sign up information submission
func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) {
ctx.Data["Title"] = ctx.Tr("sign_up")
invitationCode := ctx.Query("invitation_code")
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
ctx.Data["invitationCode"] = invitationCode
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
@@ -1366,6 +1366,11 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
}
log.Trace("Account created: %s", u.Name, ctx.Data["MsgID"])

log.Info("enter here, and form.InvitaionCode =" + invitationCode)
if invitationCode != "" {
RegisteUserByInvitaionCode(invitationCode, u.ID, u.PhoneNumber)
}

err := models.AddEmailAddress(&models.EmailAddress{
UID: u.ID,
Email: form.Email,
@@ -1919,7 +1924,7 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for
return
}

if form.Mode==0 { //注册
if form.Mode == 0 { //注册

if has {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register")))
@@ -1935,32 +1940,31 @@ func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, for

} else {
//修改手机号 mode=2 绑定手机
u, err := models.GetUserByPhoneNumber(phoneNumber)
if err != nil && !models.IsErrUserNotExist(err) {
log.Warn("sql err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return
}

if u != nil {
u, err := models.GetUserByPhoneNumber(phoneNumber)
if err != nil && !models.IsErrUserNotExist(err) {
log.Warn("sql err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return
}

if u.ID == ctx.User.ID { //没有修改手机号
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_modify")))
return
} else { //修改的手机已经被别的用户注册
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register")))
return
}
if u != nil {

if u.ID == ctx.User.ID { //没有修改手机号
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_modify")))
return
} else { //修改的手机已经被别的用户注册
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register")))
return
}
}

}
}

redisConn := labelmsg.Get()
defer redisConn.Close()

sendTimes, err := phoneService.GetPhoneNumberSendTimes(redisConn, phoneNumber)
if err != nil && err!=redis.ErrNil {
if err != nil && err != redis.ErrNil {
log.Warn("redis err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return


+ 22
- 0
services/repository/repository.go View File

@@ -148,6 +148,28 @@ func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, err
return resultRepo, nil
}

func RecommendContentFromPromote(url string) (string, error) {
defer func() {
if err := recover(); err != nil {
log.Info("not error.", err)
return
}
}()
resp, err := http.Get(url)
if err != nil || resp.StatusCode != 200 {
log.Info("Get organizations url error=" + err.Error())
return "", err
}
bytes, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
log.Info("Get organizations url error=" + err.Error())
return "", err
}
allLineStr := string(bytes)
return allLineStr, nil
}

func RecommendFromPromote(url string) ([]string, error) {
defer func() {
if err := recover(); err != nil {


+ 11
- 1
templates/base/footer.tmpl View File

@@ -52,6 +52,12 @@
<script src="/rotation3D/rotation3D.js?v={{MD5 AppVer}}"></script>
<script>
var jobTask={};
var html =document.documentElement;
var lang = html.attributes["lang"]
var isZh = true;
if(lang != null && lang.nodeValue =="en-US" ){
isZh=false;
}
function queryAiCenterInfo(){
$.ajax({
type:"GET",
@@ -96,7 +102,11 @@
tmp["type"]="green";
}
tmp["icon"]="";
tmp["content"]=json[i].content;
if(isZh){
tmp["content"]=json[i].content;
}else{
tmp["content"]=json[i].content_en;
}
serverItemList.push(tmp);
}
}


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

@@ -182,6 +182,10 @@
</i>
{{.i18n.Tr "custom.Platform_Tutorial"}}
</a>
<a class="item" href="{{AppSubUrl}}/user/invitation_tpl">
<i class="icon users"></i>
{{.i18n.Tr "invite_friends"}}
</a>
{{if .IsAdmin}}
<div class="divider"></div>



+ 4
- 0
templates/base/head_navbar_fluid.tmpl View File

@@ -179,6 +179,10 @@
</svg>
</i>
{{.i18n.Tr "custom.Platform_Tutorial"}}
</a>
<a class="item" href="{{AppSubUrl}}/user/invitation_tpl">
<i class="icon users"></i>
{{.i18n.Tr "invite_friends"}}
</a>
{{if .IsAdmin}}
<div class="divider"></div>


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

@@ -162,6 +162,10 @@
</i>
{{.i18n.Tr "custom.Platform_Tutorial"}}
</a>
<a class="item" href="{{AppSubUrl}}/user/invitation_tpl">
<i class="icon users"></i>
{{.i18n.Tr "invite_friends"}}
</a>
{{if .IsAdmin}}
<div class="divider"></div>



+ 4
- 0
templates/base/head_navbar_pro.tmpl View File

@@ -183,6 +183,10 @@
</i>
{{.i18n.Tr "custom.Platform_Tutorial"}}
</a>
<a class="item" href="{{AppSubUrl}}/user/invitation_tpl">
<i class="icon users"></i>
{{.i18n.Tr "invite_friends"}}
</a>
{{if .IsAdmin}}
<div class="divider"></div>



+ 1
- 2
templates/repo/cloudbrain/trainjob/show.tmpl View File

@@ -533,8 +533,7 @@
</a>

</div>
<div
style="position: relative;border: 1px solid rgba(0,0,0,.2);padding: 0 10px;margin-top: 10px;">
<div style="position: relative;border: 1px solid rgba(0,0,0,.2);padding: 0 10px;margin-top: 10px;">
<span>
<a title="滚动到顶部" style="position: absolute; right: -32px;cursor: pointer;"
class="log_top" data-version="{{.VersionName}}"><i class="icon-to-top"></i></a>


+ 10
- 1
templates/repo/grampus/trainjob/show.tmpl View File

@@ -499,7 +499,16 @@
</div>
</div>
<div class="ui tab" data-tab="second{{$k}}">
<div style="position: relative;">
<div>
<a id="{{.VersionName}}-log-down"
class='{{if $.canDownload}}ti-download-file{{else}}disabled{{end}}'
href="/api/v1/repos/{{$.RepoRelPath}}/grampus/train-job/{{.JobID}}/download_log">
<i class="ri-download-cloud-2-line"></i>
<span style="margin-left: 0.3rem;">{{$.i18n.Tr "repo.modelarts.download_log"}}</span>
</a>

</div>
<div style="position: relative;border: 1px solid rgba(0,0,0,.2);padding: 0 10px;margin-top: 10px;">
<span>
<a title="{{$.i18n.Tr "repo.log_scroll_start"}}" style="position: absolute; right: -32px;cursor: pointer;"
class="log_top" data-version="{{.VersionName}}"><i class="icon-to-top"></i></a>


+ 9
- 2
templates/repo/modelmanage/convertIndex.tmpl View File

@@ -241,7 +241,7 @@
</div>
</div>
</div>
<div class="unite min_title inline fields required">
<div class="unite min_title inline fields required" id="ModelFile_Div">
<div class="three wide field right aligned">
<label for="choice_file">{{$.i18n.Tr "repo.model.manage.modelfile"}}</label>
</div>
@@ -382,7 +382,14 @@
data['DestFormat'] = $('#DestFormat').val();
data['NetOutputFormat']= $('#NetOutputFormat').val();
data['ModelFile'] = $('#ModelFile').val();

if(data['ModelFile']==""){
$('.ui.error.message').text("{{.i18n.Tr "repo.modelconvert.modelfileempty"}}")
$('.ui.error.message').css('display','block')
$("#ModelFile_Div").addClass("error")
return false
}else{
$("#ModelFile_Div").removeClass("error")
}
$.post(`${repolink}/modelmanage/create_model_convert`,data,(result) => {
console.log("result=" + result);
if(result.result_code ==0){


+ 2
- 2
templates/repo/modelmanage/index.tmpl View File

@@ -46,9 +46,9 @@
<div class="repository release dataset-list view">
{{template "repo/header" .}}
<!-- 列表容器 -->
<div class="ui container {{if ne $.MODEL_COUNT 0}}active loader {{end}}" id="loadContainer">
<div class="ui container {{if ne $.MODEL_COUNT 0}}active loader {{end}}" id="loadContainer" >
{{template "base/alert" .}}
<div class="ui two column stackable grid">
<div class="ui two column stackable grid" style="display: none;">
<div class="column">
<div class="ui blue small menu compact selectcloudbrain">
<a class="active item" href="{{.RepoLink}}/modelmanage/show_model">{{$.i18n.Tr "repo.model.list"}}</a>


+ 38
- 0
templates/user/auth/signup_inner.tmpl View File

@@ -35,6 +35,9 @@
{{if .DisableRegistration}}
<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p>
{{else}}
<div class="field invitation_tips" style="font-weight:400;font-size:14px;color:rgba(250,140,22,1);{{if not .invitationCode}}display:none;{{end}}">
<span>{{.i18n.Tr "your_friend"}} <span class="__invitation_code__">{{.invitationCode}}</span> {{.i18n.Tr "invite_you_to_join_the_OpenI_AI_Collaboration_Platform_and_enjoy_abundant_free_computing_resources"}}</span>
</div>
<div class="field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<input id="user_name" name="user_name" value="{{.user_name}}" placeholder="{{.i18n.Tr "username"}}" autofocus required>
</div>
@@ -71,6 +74,16 @@
{{template "user/auth/phone_verify" .}}
</div>
{{end}}
<div class="field">
<div style="display:flex;">
<div style="display:flex;align-items:center;">
<span>{{.i18n.Tr "recommender"}}</span>
</div>
<input style="flex:1;margin-left:12px;" id="invitation_code" name="invitation_code" value="{{.invitationCode}}" {{if .invitationCode}}readonly="true"{{end}} autocomplete="off" />
</div>
</div>

<div class="field">
<div class="ui checkbox">
<input name="agree" type="checkbox" tabindex="0" class="hidden" {{if .agree}}checked{{end}}><label>{{.i18n.Tr "use_and_privacy_agree" "/home/term" "/home/privacy" | Safe}}</label>
@@ -94,3 +107,28 @@
</div>
</div>
</div>
<script>
; (function() {
var getUrlParams = function() {
var url = window.location.search;
var index = url.indexOf('?');
var obj = {};
if (index !== -1) {
var str = url.substr(1);
var arr = str.split('&');
for (var i = 0, iLen = arr.length; i < iLen; i++) {
var list = arr[i].split('=');
obj[list[0]] = list[1];
}
}
return obj;
};
var sharedUser = getUrlParams()['sharedUser'];
if (sharedUser) {
setTimeout(function() {
$('.invitation_tips').show().find('.__invitation_code__').text(sharedUser);
$('input#invitation_code').val(sharedUser).attr('readonly', true);
}, 20);
}
})();
</script>

+ 5
- 0
templates/user/dashboard/repolist.tmpl View File

@@ -18,6 +18,11 @@
v-cloak
>
<div>
{{if .invite_image_url}}
<div style="height:60px;">
<a href="{{.invite_image_link}}" target="_blank"><img src="{{.invite_image_url}}" style="width:100%;height:100%" /></a>
</div>
{{end}}
<div v-if="!isOrganization" class="ui two item tabable menu">
<a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{.i18n.Tr "repository"}}</a>
<a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{.i18n.Tr "organization"}}</a>


+ 34
- 1
templates/user/profile.tmpl View File

@@ -48,7 +48,40 @@
</li>
{{end}}
{{end}}
<li>{{svg "octicon-clock" 16}} {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li>
<li>
{{svg "octicon-clock" 16}} {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}
{{if and .IsSigned (eq .SignedUserName .Owner.Name)}}
<div class=__ad_profile_c__ style="margin-top:6px;height:50px;display:none;">
<a class="__ad_profile__" href="" target="_blank"><img src="" style="width:100%;height:100%" /></a>
</div>
<script>
;(function(){
document.addEventListener("DOMContentLoaded", function() {
$.ajax({
type: "GET",
url: "/dashboard/invitation",
dataType: "json",
data: { filename: 'ad-profile.json' },
success: function (res) {
try {
var data = JSON.parse(res);
$('.__ad_profile__').attr('href', data.url).find('img').attr('src', data.src);
$('.__ad_profile_c__').show();
} catch (err) {
console.log(err);
}
},
error: function (err) {
console.log(err);
}
});
});
})();
</script>
{{end}}
</li>
{{if and .Orgs .HasOrgsVisible}}
<li style="border-bottom: none;padding-bottom: 0;"><div style="border-bottom: 1px solid #eaeaea;padding-top: 5px;padding-bottom:5px"> <b>{{.i18n.Tr "organization"}} </b></div></li>


+ 7
- 0
templates/user/settings/invite.tmpl View File

@@ -0,0 +1,7 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-user-invite.css?v={{MD5 AppVer}}" />
<div class="user settings invite">
<div id="__vue-root"></div>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-user-invite.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 624
- 473
web_src/js/components/Model.vue
File diff suppressed because it is too large
View File


+ 140
- 0
web_src/js/features/ad.js View File

@@ -0,0 +1,140 @@
; (function () {
/*const adList = [
{
"width": 144,
"height": 108,
"pos": {
"left": 50,
"bottom": 50
},
"src": "https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/imgs/invitation/pic-01.png",
"url": "/user/invitation_tpl",
"show": true
},
{
"width": 144,
"height": 108,
"pos": {
"right": 50,
"bottom": 50
},
"src": "https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/imgs/invitation/pic-01.png",
"url": "/user/invitation_tpl",
"show": false
}
];*/
const exceptPages = [
// '/user/invitation_tpl'
];

function initAd() {
$.ajax({
type: "GET",
url: "/dashboard/invitation",
dataType: "json",
data: { filename: 'ad-pop-up.json' },
success: function (res) {
try {
var data = JSON.parse(res);
createAd(data);
} catch (err) {
console.log(err);
}
},
error: function (err) {
console.log(err);
}
});
}

function createAd(adList) {
const adInfoStr = window.localStorage.getItem('ads') || '{}';
let adInfoObj = JSON.parse(adInfoStr);
const today = new Date();
const timeTodayEnd = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1).getTime();
const now = Date.now();
const expTime = now + 4 * 60 * 60 * 1000;
if (!adInfoObj.expires || adInfoObj.expires <= now) {
adInfoObj = {
expires: Math.min(timeTodayEnd, expTime),
};
}
for (var i = 0, iLen = adList.length; i < iLen; i++) {
var adI = adList[i];
if (adI.show === false) continue;
var showOr = adInfoObj[i] === false ? false : true;
adInfoObj[i] = showOr;
if (!showOr) continue;
var adEl = $(`<div class="__ad_c__" _id="${i}" style="position:fixed;z-index:99999999;
width:${adI.width}px;height:${adI.height}px;
left:${adI.pos.left !== undefined ? adI.pos.left + 'px' : ''};
top:${adI.pos.top !== undefined ? adI.pos.top + 'px' : ''};
right:${adI.pos.right !== undefined ? adI.pos.right + 'px' : ''};
bottom:${adI.pos.bottom !== undefined ? adI.pos.bottom + 'px' : ''};">
<a style="" href="${adI.url}" target="_blank">
<img style="height:100%;width:100%;" src="${adI.src}" />
</a>
<div class="__ad_close_c__" style="position:absolute;top:6px;right:6px;">
<i class="ri-close-circle-line __ad_close__" style="color:white;font-size:18px;cursor:pointer;"></i>
</div>
</div>`);
adEl.data('data', adI);
$('body').append(adEl);
}
window.localStorage.setItem('ads', JSON.stringify(adInfoObj));
}

function initAdEvent() {
$('body').on('click', '.__ad_c__ .__ad_close__', function () {
var self = $(this);
var adEl = self.closest('.__ad_c__');
var adId = adEl.attr('_id');
const adInfoStr = window.localStorage.getItem('ads') || '{}';
const adInfoObj = JSON.parse(adInfoStr);
adInfoObj[adId] = false;
window.localStorage.setItem('ads', JSON.stringify(adInfoObj));
adEl.remove();
});
var scrollTopOld = $(document).scrollTop();
var timeHandler = null;
$(window).scroll(function (e) {
var scrollTop = $(document).scrollTop();
var offSet = scrollTop - scrollTopOld;
scrollTopOld = scrollTop;
timeHandler && clearTimeout(timeHandler);
$('.__ad_c__').each(function (_, item) {
var self = $(item);
var adData = self.data('data');
if (adData.pos.bottom !== undefined) {
self.animate({ bottom: adData.pos.bottom + offSet + 'px' }, 0);
}
if (adData.pos.top !== undefined) {
self.animate({ top: adData.pos.top - offSet + 'px' }, 0);
}
})
timeHandler = setTimeout(function () {
$('.__ad_c__').each(function (_, item) {
var self = $(item);
var adData = self.data('data');
if (adData.pos.bottom !== undefined) {
self.animate({ bottom: adData.pos.bottom + 'px' }, 0);
}
if (adData.pos.top !== undefined) {
self.animate({ top: adData.pos.top + 'px' }, 0);
}
})
}, 20);
});
}

setTimeout(function () {
if (!$('meta[name="_uid"]').length) { // 未登录,不显示
window.localStorage.removeItem('ads');
return;
}
var pathName = window.location.pathname;
if (exceptPages.indexOf(pathName) > -1) return; // 排除页,不显示
initAd();
initAdEvent();
}, 0);
})();

+ 8
- 0
web_src/js/features/i18nVue.js View File

@@ -97,6 +97,10 @@ export const i18nVue = {
model_delete: "删除",
model_create_title: "导入新模型",
model_create_version_title: "创建模型新版本",
model_status:"状态",
model_wait:"模型加载中",
model_success:"模型加载成功",
model_failed:"模型加载失败",
},
US: {
computer_vision: "computer vision",
@@ -200,5 +204,9 @@ export const i18nVue = {
model_delete: "Delete",
model_create_title: "Import new model",
model_create_version_title: "Create a new version of the model",
model_status: "Status",
model_wait:"Loading",
model_success:"Success",
model_failed:"Failed",
},
};

+ 1
- 0
web_src/js/index.js View File

@@ -52,6 +52,7 @@ import router from "./router/index.js";
import { Message } from "element-ui";

import { i18nVue } from "./features/i18nVue.js";
import './features/ad.js';

Vue.use(ElementUI);
Vue.prototype.$axios = axios;


+ 15
- 5
web_src/js/standalone/phoneverify.js View File

@@ -77,7 +77,7 @@
});

function mouseMove(e) {
var _clientX = e.clientX;
var _clientX = e.clientX !== undefined ? e.clientX : e.targetTouches[0].clientX;
var offset = _clientX - clientX;
var triggerEl = self.dom.find('.slide-trigger');
var triggerWidth = triggerEl.width();
@@ -99,6 +99,8 @@
function mouseUp(e) {
$(document).off('mousemove', mouseMove);
$(document).off('mouseup', mouseUp);
$(document).off('touchmove', mouseMove);
$(document).off('touchend', mouseUp);
self.isMoving = false;
$.ajax({
url: '/verifySlideImage',
@@ -106,7 +108,7 @@
dataType: 'json',
data: {
slide_id: self.imgID,
x: parseInt(self.dom.find('.slide-image-small').position().left)
x: parseInt(self.dom.find('.slide-image-small').position().left / self.dom.find('.slide-image-big').attr('scale'))
},
success: function (res) {
if (res && res.Code === 0) {
@@ -138,13 +140,18 @@
});
}

this.dom.find('.slide-trigger').on('mousedown', function (e) {
function mouseDown(e) {
if (self.verifySucess) return;
clientX = e.clientX;
clientX = e.clientX !== undefined ? e.clientX : e.targetTouches[0].clientX;
oLeft = $(this).position().left;
$(document).on('mousemove', mouseMove);
$(document).on('mouseup', mouseUp);
});
$(document).on('touchmove', mouseMove);
$(document).on('touchend', mouseUp);
}

this.dom.find('.slide-trigger').on('mousedown', mouseDown);
this.dom.find('.slide-trigger').on('touchstart', mouseDown);

this.dom.find('.verify-code-send-btn').on('click', function () {
if (!self.canSendCode) return;
@@ -199,6 +206,7 @@
self.dom.find('.slide-bar-wrap').css('display', 'flex');
self.dom.find('.verify-code-c').css('display', 'flex');
self.dom.find('.modify-phone-number').hide();
self.refreshImages();
});
};

@@ -210,6 +218,8 @@
this.imgID = '';
this.dom.find('.slide-bar').removeClass('sucess error').css('width', '30px');
this.dom.find('.slide-trigger').removeClass('sucess error').css('left', '0px');
var scale = this.dom.find('.slide-bar-bg').width() / 391;
this.dom.find('.slide-image-big').css('transform', `scale(${scale})`).attr('scale', scale);
this.dom.find('.slide-trigger .icon').hide();
this.dom.find('.slide-trigger .icon.arrow').show();
this.dom.find('.slide-txt').show();


+ 2
- 1
web_src/less/standalone/_phoneverify.less View File

@@ -174,10 +174,11 @@
width: 391px;
height: 196px;
top: 36px;
left: 0;
left: 1px;
border-radius: 2px;
z-index: 100;
display: none;
transform-origin: 0 0;
}

.__phone-verify-code .slide-bar-c .slide-image-small {


+ 11
- 0
web_src/vuepages/apis/modules/userinvite.js View File

@@ -0,0 +1,11 @@
import service from '../service';

// 邀请好友页面数据
export const getUserInvitationCode = (params) => { // page pageSize
return service({
url: '/user/invitation_code',
method: 'get',
params: params,
data: {},
});
}

+ 14
- 0
web_src/vuepages/langs/config/en-US.js View File

@@ -162,6 +162,20 @@ const en = {
available: 'Available',
notAvailable: 'Not Available',
},
user: {
inviteFriends: 'Invite Friends',
inviteFriendsTips: 'Copy QR code or invite registration link to share with friends',
clickToViewTheEventDetails: 'Click to view the event details',
copyRegistrationInvitationLink: 'Copy registration invitation link',
registrationAdress: 'Registration Adress: ',
recommender: 'Recommender: ',
invitedFriends: 'Invited friends',
registrationTime: 'Registration time',
theSharedContentHasBeenCopiedToTheClipboard: 'The shared content has been copied to the clipboard',
copyError: 'Copy error',
Activated: 'Activated',
notActive: 'Not active',
},
}

export default en;

+ 14
- 0
web_src/vuepages/langs/config/zh-CN.js View File

@@ -162,6 +162,20 @@ const zh = {
available: '可用',
notAvailable: '不可用',
},
user: {
inviteFriends: '邀请好友',
inviteFriendsTips: '复制二维码或者注册邀请链接分享给好友',
clickToViewTheEventDetails: '点击查看活动详情',
copyRegistrationInvitationLink: '复制注册邀请链接',
registrationAdress: '注册地址:',
recommender: '推荐人:',
invitedFriends: '已邀请好友',
registrationTime: '注册时间',
theSharedContentHasBeenCopiedToTheClipboard: '分享内容已复制到剪切板',
copyError: '复制错误',
Activated: '已激活',
notActive: '未激活',
},
}

export default zh;

+ 319
- 0
web_src/vuepages/pages/user/invite/index.vue View File

@@ -0,0 +1,319 @@
<template>
<div class="ui container">
<div class="title">
<div class="title-1"><span>{{ $t('user.inviteFriends') }}</span></div>
<div class="title-2"><span>{{ $t('user.inviteFriendsTips') }}</span></div>
</div>
<div class="content-1">
<div class="img-c">
<img class="img" :src="bannerImg" />
<div class="txt">{{ bannerTitle }}</div>
</div>
<div class="descr">
<span>{{ pageLinkDesc }}</span>
<a :href="pageLink" target="_blank">{{ $t('user.clickToViewTheEventDetails') }}</a>
</div>
</div>
<div class="content-2">
<div class="txt-c">
<div class="txt-1">
<span>{{ pageOpeniDesc }}</span>
</div>
<div class="txt-2"><span>{{ $t('user.registrationAdress') }}</span><span>{{ invitationLink + invitationCode
}}</span></div>
<div class="txt-3"><span>{{ $t('user.recommender') }}</span><span>{{ invitationCode }}</span></div>
<el-button class="__copy_link_btn__" type="primary">{{ $t('user.copyRegistrationInvitationLink') }}</el-button>
</div>
<div class="qr-code">
<div id="__qr-code__" style="width:120px;height:120px;"></div>
</div>
</div>
<div class="table-container">
<div>
<el-table border :data="tableData" style="width:100%" v-loading="loading" stripe>
<el-table-column prop="ID" :label="$t('user.invitedFriends')" align="left" header-align="center">
<template slot-scope="scope">
<div style="display:flex;align-items:center;padding-left:20px;">
<img :src="scope.row.avatarSrc" alt="" style="height:45px;width:45px;margin-right:10px;" />
<a :href="scope.row.userLink" style="font-weight:500;font-size:15px;">{{ scope.row.userName }}</a>
</div>
</template>
</el-table-column>
<el-table-column prop="statusStr" :label="$t('status')" align="center" header-align="center">
<template slot-scope="scope">
<span :style="{ color: scope.row.statusColor }">{{ scope.row.statusStr }}</span>
</template>
</el-table-column>
<el-table-column prop="regTime" :label="$t('user.registrationTime')" align="center" header-align="center">
</el-table-column>
<template slot="empty">
<span>{{
loading ? $t('loading') : $t('noData')
}}</span>
</template>
</el-table>
</div>
<div class="__r_p_pagination">
<div style="margin-top: 2rem">
<div class="center">
<el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage"
:page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total">
</el-pagination>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import Clipboard from 'clipboard';
import QRCode from 'qrcodejs2';
import { formatDate } from 'element-ui/lib/utils/date-util';
import { getUserInvitationCode } from '~/apis/modules/userinvite';

export default {
data() {
return {
bannerImg: '',
bannerTitle: '',
pageLink: '',
pageLinkDesc: '',
invitationLink: window.origin + '/user/sign_up?sharedUser=',
invitationCode: '',
pageOpeniDesc: '',

loading: false,
tableData: [],
pageInfo: {
curpage: 1,
pageSize: 10,
pageSizes: [10],
total: 0,
},
};
},
components: {},
methods: {
initCopy() {
const clipboard = new Clipboard('.__copy_link_btn__', {
text: () => {
return `${this.pageOpeniDesc}\n${this.$t('user.registrationAdress')}${this.invitationLink + this.invitationCode}\n${this.$t('user.recommender')}${this.invitationCode}`;
},
});
clipboard.on('success', (e) => {
this.$message({
type: 'success',
message: this.$t('user.theSharedContentHasBeenCopiedToTheClipboard')
});
});
clipboard.on('error', (e) => {
this.$message({
type: 'error',
message: this.$t('user.copyError')
});
});
},
transRowData(item) {
return {
userName: item.Name,
avatarSrc: item.Avatar,
userLink: window.origin + '/' + item.Name,
statusStr: item.IsActive ? this.$t('user.Activated') : this.$t('user.notActive'),
statusColor: item.IsActive ? 'rgb(82, 196, 26)' : 'rgb(245, 34, 45)',
regTime: formatDate(new Date(item.CreatedUnix * 1000), 'yyyy-MM-dd HH:mm:ss'),
}
},
initData() {
getUserInvitationCode({ page: this.pageInfo.curpage, pageSize: this.pageInfo.pageSize }).then(res => {
res = res.data;
if (res) {
this.bannerImg = res.page_banner_img;
this.bannerTitle = res.page_banner_title;
this.pageLink = res.page_link;
this.pageLinkDesc = res.page_link_desc;
this.invitationCode = res.invitation_code;
this.pageOpeniDesc = res.page_openi_desc;
this.tableData = (res.invitation_users || []).map((item, index) => {
return this.transRowData(item);
});
this.pageInfo.total = res.invitation_users_count;
const qrCode = new QRCode("__qr-code__", {
text: this.invitationLink + this.invitationCode,
width: 120,
height: 120,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
}
}).catch(err => {
console.log(err);
});
},
getTableData() {
const params = {
page: this.pageInfo.curpage,
pageSize: this.pageInfo.pageSize,
};
this.loading = true;
getUserInvitationCode(params).then(res => {
this.loading = false;
res = res.data;
const data = (res.invitation_users || []).map((item, index) => {
return this.transRowData(item);
});
this.tableData = data;
this.pageInfo.total = res.invitation_users_count;
}).catch(err => {
console.log(err);
this.loading = false;
});
},
currentChange(val) {
this.pageInfo.curpage = val;
this.getTableData();
},
},
mounted() {
this.initData();
this.initCopy();
},
beforeDestroy() {
},
};
</script>

<style scoped lang="less">
.title {
margin-top: 15px;
margin-bottom: 15px;

.title-1 {
font-weight: 500;
font-size: 20px;
color: rgba(16, 16, 16, 1);
margin-bottom: 10px;
}

.title-2 {
font-weight: 400;
font-size: 14px;
color: rgba(136, 136, 136, 1);
}
}

.content-1 {
margin-bottom: 32px;

.img-c {
height: 80px;
position: relative;

.img {
width: 100%;
height: 100%;
}

.txt {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
line-height: 80px;
padding-left: 25px;
font-weight: 500;
font-size: 24px;
color: rgb(255, 255, 255);
}
}

.descr {
font-weight: 300;
font-size: 16px;
color: rgb(16, 16, 16);
padding: 25px;
border-left: 1px solid rgba(0, 0, 0, 0.1);
border-right: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 0px 0px 4px 4px;
}
}

.content-2 {
display: flex;
background-color: rgb(228, 242, 255);
border-color: rgb(228, 242, 255);
border-width: 1px;
border-style: solid;
border-radius: 5px;
padding: 25px;
margin-bottom: 32px;

.txt-c {
flex: 1;
font-weight: 300;
font-size: 16px;
color: rgb(16, 16, 16);

span {
line-height: 24px;
}

div {
margin-bottom: 6px;
}

.txt-3 {
margin-bottom: 15px;
}
}

.__copy_link_btn__ {
font-size: 14px;
padding: 11px 15px;
background: rgb(21, 114, 255);
border-radius: 0;
}

.qr-code {
width: 150px;
display: flex;
flex-direction: column;
align-items: end;
}
}

.table-container {
margin-bottom: 16px;

/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
font-size: 14px;
color: rgb(36, 36, 36);
font-weight: 400;
}
}

/deep/ .el-table__body {
td {
font-size: 14px;
}
}

.op-btn {
cursor: pointer;
font-size: 12px;
color: rgb(25, 103, 252);
margin: 0 5px;
}
}

.center {
display: flex;
justify-content: center;
}
</style>

+ 17
- 0
web_src/vuepages/pages/user/invite/vp-user-invite.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

new Vue({
i18n,
render: (h) => h(App),
}).$mount('#__vue-root');

Loading…
Cancel
Save