#4712 fix-4606

Merged
zouap merged 31 commits from fix-4606 into V20230912 8 months ago
  1. +1
    -1
      README.md
  2. +20
    -6
      models/cloudbrain_image.go
  3. +4
    -1
      modules/auth/cloudbrain.go
  4. +4
    -1
      modules/cloudbrain/resty.go
  5. +1
    -0
      modules/grampus/resty.go
  6. +13
    -0
      options/locale/locale_en-US.ini
  7. +14
    -0
      options/locale/locale_zh-CN.ini
  8. +6
    -6
      routers/api/v1/api.go
  9. +15
    -0
      routers/api/v1/repo/images.go
  10. +60
    -9
      routers/image/image.go
  11. +76
    -52
      routers/repo/cloudbrain.go
  12. +2
    -2
      routers/repo/grampus.go
  13. +4
    -5
      routers/routes/routes.go
  14. +7
    -3
      templates/admin/cloudbrain/imagecommit.tmpl
  15. +126
    -0
      templates/repo/cloudbrain/image/apply.tmpl
  16. +10
    -2
      templates/repo/cloudbrain/image/edit.tmpl
  17. +3
    -1
      templates/repo/cloudbrain/image/submit.tmpl
  18. +372
    -565
      web_src/js/components/images/Images.vue
  19. +632
    -447
      web_src/js/components/images/adminImages.vue
  20. +35
    -2
      web_src/js/features/i18nVue.js
  21. +1
    -1
      web_src/js/features/images.js
  22. +10
    -0
      web_src/less/openi.less
  23. +3
    -3
      web_src/vuepages/apis/modules/images.js
  24. +2
    -2
      web_src/vuepages/components/cloudbrain/ImageSelectV1.vue
  25. +1
    -0
      web_src/vuepages/langs/config/en-US.js
  26. +1
    -0
      web_src/vuepages/langs/config/zh-CN.js

+ 1
- 1
README.md View File

@@ -50,7 +50,7 @@

- node版本 >= v10.13.0

- golang版本 >= 1.13.3
- golang版本 >= 1.18.10

[从源代码安装说明](https://docs.gitea.io/zh-cn/install-from-source/)



+ 20
- 6
models/cloudbrain_image.go View File

@@ -16,6 +16,13 @@ const IMAGE_STATUS_COMMIT = 0
const IMAGE_STATUS_SUCCESS = 1
const IMAGE_STATUS_Failed = 2

type ApplyStatus int

const NoneApply ApplyStatus = 1
const Applying ApplyStatus = 2
const OKApply ApplyStatus = 3
const FailApply ApplyStatus = 4

type Image struct {
ID int64 `xorm:"pk autoincr" json:"id"`
ImageID string `json:"image_id"`
@@ -24,7 +31,7 @@ type Image struct {
UID int64 `xorm:"INDEX NOT NULL" json:"uid"`
IsPrivate bool `xorm:"INDEX NOT NULL" json:"isPrivate"`
Tag string `xorm:"varchar(100) UNIQUE" json:"tag"`
Description string `xorm:"varchar(765)" json:"description"`
Description string `xorm:"varchar(1000)" json:"description"`
Topics []string `xorm:"TEXT JSON" json:"topics"`
Place string `xorm:"varchar(300)" json:"place"`
NumStars int `xorm:"NOT NULL DEFAULT 0" json:"numStars"`
@@ -33,6 +40,8 @@ type Image struct {
UserName string `xorm:"-" json:"userName"`
RelAvatarLink string `xorm:"-" json:"relAvatarLink"`
Status int `xorm:"INDEX NOT NULL DEFAULT 0" json:"status"` //0代表正在提交,1提交完成,2提交失败
ApplyStatus ApplyStatus `xorm:"DEFAULT 1" json:"apply_status"`
Message string `xorm:"varchar(500)" json:"message"`
UseCount int64 `xorm:"NOT NULL DEFAULT 0" json:"useCount"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
@@ -71,6 +80,7 @@ type SearchImageOptions struct {
IncludeCustom bool
IncludeOwnerOnly bool
Topics string
ApplyStatus int
CloudbrainType int
ListOptions
SearchOrderBy
@@ -428,6 +438,10 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
}
}

if opts.ApplyStatus != 0 {
cond = cond.And(builder.Eq{"apply_status": opts.ApplyStatus})
}

if opts.IncludePublicOnly {
cond = cond.And(builder.Eq{"is_private": false})
}
@@ -552,7 +566,7 @@ func CreateLocalImage(image *Image) error {

func UpdateLocalImage(image *Image) error {

_, err := x.ID(image.ID).Cols("description", "is_private", "status").Update(image)
_, err := x.ID(image.ID).Cols("description", "is_private", "status", "message", "apply_status").Update(image)
return err
}

@@ -578,7 +592,7 @@ func DeleteLocalImage(id int64) error {
return err
}

//star or unstar Image
// star or unstar Image
func StarImage(userID, imageID int64, star bool) error {
sess := x.NewSession()
defer sess.Close()
@@ -629,10 +643,10 @@ func isImageStaring(e Engine, userID, imageID int64) bool {
has, _ := e.Get(&ImageStar{0, userID, imageID, 0})
return has
}
func RecommendImage(imageId int64, recommond bool) error {
func RecommendImage(imageId int64, recommend bool, applyStatus ApplyStatus, message string) error {

image := Image{Type: GetRecommondType(recommond)}
_, err := x.ID(imageId).Cols("type").Update(image)
image := Image{Type: GetRecommondType(recommend), ApplyStatus: applyStatus, Message: message}
_, err := x.ID(imageId).Cols("type", "apply_status", "message").Update(image)
return err
}



+ 4
- 1
modules/auth/cloudbrain.go View File

@@ -63,10 +63,13 @@ type CommitAdminImageCloudBrainForm struct {
type EditImageCloudBrainForm struct {
ID int64 `form:"id" binding:"Required"`
Description string `form:"description" binding:"Required"`
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
}

type ReviewImageForm struct {
Message string `form:"message" binding:"Required"`
}

type CreateCloudBrainInferencForm struct {
JobName string `form:"job_name" binding:"Required"`
DisplayJobName string `form:"display_job_name" binding:"Required"`


+ 4
- 1
modules/cloudbrain/resty.go View File

@@ -1,7 +1,6 @@
package cloudbrain

import (
"code.gitea.io/gitea/modules/notification"
"encoding/json"
"errors"
"fmt"
@@ -10,6 +9,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/notification"

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

"code.gitea.io/gitea/models"
@@ -347,6 +348,7 @@ sendjob:
Description: params.ImageDescription,
Place: setting.Cloudbrain.ImageURLPrefix + imageTag,
Status: models.IMAGE_STATUS_COMMIT,
ApplyStatus: models.NoneApply,
}

err = models.WithTx(func(ctx models.DBContext) error {
@@ -402,6 +404,7 @@ func CommitAdminImage(params models.CommitImageParams, doer *models.User) error
Place: params.Place,
Status: models.IMAGE_STATUS_SUCCESS,
Type: params.Type,
ApplyStatus: models.NoneApply,
}

err = models.WithTx(func(ctx models.DBContext) error {


+ 1
- 0
modules/grampus/resty.go View File

@@ -659,6 +659,7 @@ sendjob:
Description: params.Description,
ImageID: result.ImageId,
Status: models.IMAGE_STATUS_COMMIT,
ApplyStatus: models.NoneApply,
}

err = models.WithTx(func(ctx models.DBContext) error {


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

@@ -1111,6 +1111,10 @@ image_not_exist=Image does not exits.
image_edit_fail=Failed to edit image, please try again later.
image_delete_fail=Failed to delete image, please try again later.
image_overwrite=You had submitted the same name image before, are you sure to overwrite the original image?
image_apply_duplicate=You had a apply for the image before, can not apply again.
failApplyCannotRecommend=Can not recommend the image, because the image has been reviewed failed.
reviewCannotBeEmpty=The review message can not be empty.

download=Download
score=Score
wait_count_start = Your current queue position is
@@ -1118,6 +1122,7 @@ wait_count_end =
file_limit_100 = Display up to 100 files or folders in a single directory
images.name = Image Tag
images.name_placerholder = Please enter the image name
images.descr_placerholder = The description should not exceed 1000 characters
image.label_tooltips = Example Python 3.7, Tensorflow 2.0, cuda 10, pytorch 1.6
images.public_tooltips = After the image is set to public, it can be seen by other users.
images.name_format_err=The format of image tag is wrong.
@@ -2815,6 +2820,14 @@ repos.no=No

images.recommend = Recommend
images.unrecommend = Unrecommend
images.recommendImage = Recommend Images
images.applyrecommendImage = Apply to become a platform recommended image
images.descrTip = Please add the framework and version of the image, CUDA and version, Python version, operating system and version, installed software package and version in the description field, such as:
images.framework = Framework
images.pythonVersion = Python Version
images.operatingSystem = Operating System
images.installedSoftwarePackage = Installed Software Package
images.submitApply = Submit Apply

datasets.dataset_manage_panel= Dataset Manage
datasets.owner=Owner


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

@@ -1110,6 +1110,11 @@ image_not_exist=镜像不存在。
image_edit_fail=编辑镜像失败,请稍后再试。
image_delete_fail=删除镜像失败,请稍后再试。
image_overwrite=您已经提交过相同名称的镜像,您确定要覆盖原来提交的镜像吗?
image_apply_duplicate=您已经提交过申请,不需要重复提交。
failApplyCannotRecommend=不能推荐审核不通过的镜像。
reviewCannotBeEmpty=请输入评审信息。


download=模型下载
score=评分
wait_count_start = 您当前排队位置是第
@@ -1117,6 +1122,7 @@ wait_count_end = 位
file_limit_100 = 单目录下最多显示100个文件或文件夹
images.name = 镜像Tag
images.name_placerholder = 请输入镜像Tag
images.descr_placerholder = 描述字数不超过1000个字符
image.label_tooltips = 如Python 3.7, Tensorflow 2.0, cuda 10, pytorch 1.6
images.public_tooltips = 镜像设置为公开后,可被其他用户看到。
images.name_format_err=镜像Tag格式错误。
@@ -2833,6 +2839,14 @@ repos.no=否

images.recommend = 推荐
images.unrecommend = 不推荐
images.recommendImage = 平台推荐镜像
images.applyrecommendImage = 申请成为平台推荐镜像
images.descrTip = 请在描述字段补充镜像的框架及版本、CUDA及版本、Python版本、操作系统及版本、安装的软件包及版本,如:
images.framework = 框架
images.pythonVersion = Python版本
images.operatingSystem = 操作系统
images.installedSoftwarePackage = 安装的软件包
images.submitApply = 提交申请

datasets.dataset_manage_panel=数据集管理
datasets.owner=所有者


+ 6
- 6
routers/api/v1/api.go View File

@@ -690,20 +690,20 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", HasRole(models.MonitorAdmin), ai_task.GetAITaskInfo)
m.Get("/log/download", HasRole(models.MonitorAdmin), reqAITaskInRepo(), ai_task.DownloadAITaskLog)
m.Get("/resource_usage", HasRole(models.MonitorAdmin), reqAITaskInRepo(), ai_task.GetAITaskResourceUsage)
}, repoAssignment())
})

})
m.Get("/ai_tasks", HasRole(models.MonitorAdmin), ai_task.GetAllMonitorAITask)
})

m.Group("/images", func() {
m.Get("/recommend", repo.GetRecommendImages)
m.Get("/public", repo.GetPublicImages)
m.Get("/custom", repo.GetCustomImages)
m.Get("/star", repo.GetStarImages)
m.Get("/npu", repo.GetNpuImages)
m.Get("/custom", reqToken(), repo.GetCustomImages)
m.Get("/star", reqToken(), repo.GetStarImages)
m.Get("/npu", reqToken(), repo.GetNpuImages)

}, reqToken())
})

m.Group("/tech", func() {
m.Get("", tech.FindTech)


+ 15
- 0
routers/api/v1/repo/images.go View File

@@ -19,6 +19,21 @@ type NPUImageINFO struct {
Value string `json:"value"`
}

func GetRecommendImages(ctx *context.APIContext) {
uid := getUID(ctx)
opts := models.SearchImageOptions{
UID: uid,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: true,
Status: models.IMAGE_STATUS_SUCCESS,
CloudbrainType: ctx.QueryInt("cloudbrainType"),
}

routeRepo.GetImages(ctx.Context, &opts)

}

func GetPublicImages(ctx *context.APIContext) {
uid := getUID(ctx)
opts := models.SearchImageOptions{


+ 60
- 9
routers/image/image.go View File

@@ -1,35 +1,86 @@
package image

import (
"code.gitea.io/gitea/modules/notification"
"net/http"
"strconv"
"strings"

"code.gitea.io/gitea/modules/auth"

"code.gitea.io/gitea/modules/notification"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
)

func Action(ctx *context.Context) {
var err error
func UserAction(ctx *context.Context) {
imageId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)
image, err := models.GetImageByID(imageId)
if err != nil {
ctx.Error(http.StatusNotFound)
return
}
switch ctx.Params(":action") {

case "star":
err = models.StarImage(ctx.User.ID, imageId, true)
case "unstar":
err = models.StarImage(ctx.User.ID, imageId, false)
}
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.star_fail", ctx.Params(":action"))))
} else {
notification.NotifyImageRecommend(ctx.User, image, ctx.Params(":action"))
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}
}
func Action(ctx *context.Context, form auth.ReviewImageForm) {
imageId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)
image, err := models.GetImageByID(imageId)
if err != nil {
ctx.Error(http.StatusNotFound)
return
}
switch ctx.Params(":action") {
case "recommend":
err = models.RecommendImage(imageId, true)
if !ctx.User.IsAdmin {
ctx.Error(http.StatusForbidden)
return
}
if image.ApplyStatus == models.FailApply {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.failApplyCannotRecommend")))
return
}
if image.ApplyStatus == models.Applying {
image.ApplyStatus = models.OKApply
}

err = models.RecommendImage(imageId, true, image.ApplyStatus, "")
case "unrecommend":
err = models.RecommendImage(imageId, false)
if !ctx.User.IsAdmin {
ctx.Error(http.StatusForbidden)
return
}
if strings.TrimSpace(form.Message) == "" {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.reviewCannotBeEmpty")))
return
}
oldStatus := image.ApplyStatus
if oldStatus == models.OKApply {
image.ApplyStatus = models.NoneApply
}
if oldStatus == models.Applying {
image.ApplyStatus = models.FailApply
}

err = models.RecommendImage(imageId, false, image.ApplyStatus, strings.TrimSpace(form.Message))
}
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.star_fail", ctx.Params(":action"))))
} else {
image, err := models.GetImageByID(imageId)
if err == nil {
notification.NotifyImageRecommend(ctx.User, image, ctx.Params(":action"))
}

notification.NotifyImageRecommend(ctx.User, image, ctx.Params(":action"))

ctx.JSON(http.StatusOK, models.BaseOKMessage)
}
}

+ 76
- 52
routers/repo/cloudbrain.go View File

@@ -61,6 +61,7 @@ const (

tplCloudBrainImageSubmit base.TplName = "repo/cloudbrain/image/submit"
tplCloudBrainImageEdit base.TplName = "repo/cloudbrain/image/edit"
tplCloudBrainImageApply base.TplName = "repo/cloudbrain/image/apply"

tplCloudBrainTrainJobNew base.TplName = "repo/cloudbrain/trainjob/new"
tplCloudBrainTrainJobShow base.TplName = "repo/cloudbrain/trainjob/show"
@@ -1140,10 +1141,29 @@ func CloudBrainImageEdit(ctx *context.Context) {

}

func CloudBrainImageEditPost(ctx *context.Context, form auth.EditImageCloudBrainForm) {
func CloudBrainImageApplyRecommend(ctx *context.Context) {
ctx.Data["PageIsImageEdit"] = true
ctx.Data["PageFrom"] = "apply"
var ID = ctx.Params(":id")
id, err := strconv.ParseInt(ID, 10, 64)
if err != nil {
log.Error("GetImageByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}
image, err := models.GetImageByID(id)
if err != nil {
log.Error("GetImageByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}
ctx.Data["Image"] = image
ctx.HTML(http.StatusOK, tplCloudBrainImageApply)

}

func CloudBrainImageRecommendApplyPost(ctx *context.Context, form auth.EditImageCloudBrainForm) {

if utf8.RuneCountInString(form.Description) > 255 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 255)))
if utf8.RuneCountInString(form.Description) > 1000 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 1000)))
return
}

@@ -1155,10 +1175,57 @@ func CloudBrainImageEditPost(ctx *context.Context, form auth.EditImageCloudBrain
image, err := models.GetImageByID(form.ID)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))
return
}

image.Description = form.Description

if image.ApplyStatus == models.Applying || image.ApplyStatus == models.OKApply {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_apply_duplicate")))
return
}

image.ApplyStatus = models.Applying
image.Message = ""

err = models.WithTx(func(ctx models.DBContext) error {
if err := models.UpdateLocalImage(image); err != nil {
return err
}
if err := models.SaveImageTopics(image.ID, validTopics...); err != nil {
return err
}
return nil

})

if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))

} else {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}

}

func CloudBrainImageEditPost(ctx *context.Context, form auth.EditImageCloudBrainForm) {

if utf8.RuneCountInString(form.Description) > 1000 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 1000)))
return
}

validTopics, errMessage := checkTopics(form.Topics)
if errMessage != "" {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr(errMessage)))
return
}
image, err := models.GetImageByID(form.ID)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))
return
}

image.IsPrivate = form.IsPrivate
image.Description = form.Description

err = models.WithTx(func(ctx models.DBContext) error {
@@ -1215,8 +1282,8 @@ func CloudBrainAdminCommitImage(ctx *context.Context, form auth.CommitAdminImage
return
}

if utf8.RuneCountInString(form.Description) > 255 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 255)))
if utf8.RuneCountInString(form.Description) > 1000 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", "1000")))
return
}

@@ -1262,8 +1329,8 @@ func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrain
return
}

if utf8.RuneCountInString(form.Description) > 255 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 255)))
if utf8.RuneCountInString(form.Description) > 1000 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", "1000")))
return
}

@@ -1641,50 +1708,6 @@ func CloudBrainShowModels(ctx *context.Context) {
ctx.HTML(200, tplCloudBrainShowModels)
}

func GetPublicImages(ctx *context.Context) {
uid := getUID(ctx)
opts := models.SearchImageOptions{
IncludePublicOnly: true,
UID: uid,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: ctx.QueryBool("recommend"),
Status: models.IMAGE_STATUS_SUCCESS,
CloudbrainType: ctx.QueryInt("cloudbrainType"),
}

GetImages(ctx, &opts)

}

func GetCustomImages(ctx *context.Context) {
uid := getUID(ctx)
opts := models.SearchImageOptions{
UID: uid,
IncludeOwnerOnly: true,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
Status: -1,
CloudbrainType: ctx.QueryInt("cloudbrainType"),
}
GetImages(ctx, &opts)

}
func GetStarImages(ctx *context.Context) {

uid := getUID(ctx)
opts := models.SearchImageOptions{
UID: uid,
IncludeStarByMe: true,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
Status: models.IMAGE_STATUS_SUCCESS,
CloudbrainType: ctx.QueryInt("cloudbrainType"),
}
GetImages(ctx, &opts)

}

func GetImages(ctx *context.Context, opts *models.SearchImageOptions) {
page := ctx.QueryInt("page")
if page <= 0 {
@@ -1743,6 +1766,7 @@ func GetAllImages(ctx *context.Context) {
opts := models.SearchImageOptions{
UID: uid,
Keyword: ctx.Query("q"),
ApplyStatus: ctx.QueryInt("apply"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: ctx.QueryBool("recommend"),
CloudbrainType: ctx.QueryInt("cloudbrainType"),


+ 2
- 2
routers/repo/grampus.go View File

@@ -1992,8 +1992,8 @@ func GrampusCommitImage(ctx *context.Context, form auth.CommitImageGrampusForm)
return
}

if utf8.RuneCountInString(form.Description) > 255 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 255)))
if utf8.RuneCountInString(form.Description) > 1000 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", "1000")))
return
}



+ 4
- 5
routers/routes/routes.go View File

@@ -399,9 +399,6 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", func(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/explore/repos")
})
m.Get("/images/public", repo.GetPublicImages)
m.Get("/images/custom", repo.GetCustomImages)
m.Get("/images/star", repo.GetStarImages)

m.Group("/repos", func() {
//m.Get("", routers.ExploreRepos)
@@ -649,7 +646,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/commit_image", admin.CloudBrainCommitImageShow)
m.Post("/commit_image", bindIgnErr(auth.CommitAdminImageCloudBrainForm{}), repo.CloudBrainAdminCommitImage)
})
m.Put("/image/:id/action/:action", image.Action)
m.Put("/image/:id/action/:action", bindIgnErr(auth.ReviewImageForm{}), image.Action)

m.Group("/^:configType(hooks|system-hooks)$", func() {
m.Get("", admin.DefaultOrSystemWebhooks)
@@ -1194,10 +1191,12 @@ func RegisterRoutes(m *macaron.Macaron) {

m.Group("/image/:id", func() {
m.Get("", repo.GetImage)
m.Get("/apply", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageApplyRecommend)
m.Get("/:from", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageEdit)
m.Post("", cloudbrain.AdminOrImageCreaterRight, bindIgnErr(auth.EditImageCloudBrainForm{}), repo.CloudBrainImageEditPost)
m.Post("/apply", cloudbrain.AdminOrImageCreaterRight, bindIgnErr(auth.EditImageCloudBrainForm{}), repo.CloudBrainImageRecommendApplyPost)
m.Delete("", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageDelete)
m.Put("/action/:action", reqSignIn, image.Action)
m.Put("/action/:action", reqSignIn, image.UserAction)
})
m.Group("/:username/:reponame", func() {



+ 7
- 3
templates/admin/cloudbrain/imagecommit.tmpl View File

@@ -61,7 +61,9 @@
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images.name"}}</label>
<input type="text" name="tag" required placeholder="{{$.i18n.Tr "repo.images.name_placerholder"}}" style="width: 80%;" maxlength="100">
<span class="tooltips" style="display: block;padding-left: 0.5rem;">{{.i18n.Tr "repo.images.name_rule"}}</span>
<span class="tooltips" style="display: block;padding-left: 1.5rem;">
{{.i18n.Tr "repo.images.name_rule50"}}
</span>
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images"}}</label>
@@ -70,7 +72,7 @@
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea>
<textarea style="width: 80%;" required id="description" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)"></textarea>
</div>
<div class="inline field" style="display: flex;align-items: center;">
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
@@ -80,7 +82,7 @@
<div class="menu" id="course_label_item"></div>
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 0.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
@@ -97,6 +99,7 @@
</div>
</div>
<!--
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
@@ -115,6 +118,7 @@
<span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span>
</div>
</div>
-->
<div class="inline required field" style="padding-top: 2rem;">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">


+ 126
- 0
templates/repo/cloudbrain/image/apply.tmpl View File

@@ -0,0 +1,126 @@
<style>
.label_color{
color:#505559 !important;
width: 6% !important;
text-align: center;
}
.descr-tip-box {
display: inline-block;
border: 1px solid #f2711c;
background-color: rgba(242,113,28,0.05);
width: 80%;
padding: 10px;
}
.descr-tip-head {
color: #888888;
font-size: 14px;
margin-bottom: 4px;
}
.descr-tip-item {
margin: 2px 0;
padding-left: 10px;
color: rgb(242 113 28);
font-size: 12px;
}
</style>
{{template "base/head" .}}
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository">
{{template "repo/header" .}}
<div class="alert"></div>
<div class="ui container">
<div>
<div class="ui negative message" style="display: none;">
</div>
<div class="ui info message" style="display: none;">
</div>
<div class="ui positive message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{$.i18n.Tr "admin.images.applyrecommendImage"}}
</h4>
<div class="submit-image-tmplvalue" style="display: none;" data-link="/image/{{$.Image.ID}}/apply" data-edit-page="{{.PageFrom}}"></div>
<div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;">
<div class="ui form" id="form_image">
<input type="hidden" name="edit" value="edit">
{{.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{.Image.ID}}">
<div class="inline field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.dataset_available_clusters"}}</label>
<div class="ui basic label" style="border: none !important;color:#3291f8;">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14"><path fill="none" d="M0 0h24v24H0z"></path><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"></path></svg>
{{if eq .Image.CloudbrainType 2}}
{{$.i18n.Tr "cloudbrain.resource_cluster_c2net_simple"}} GPU
{{else}}
{{$.i18n.Tr "cloudbrain.resource_cluster_openi_simple"}} GPU
{{end}}
</div>
<input type="hidden" value="{{.Image.CloudbrainType}}" name="type">
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images.name"}}</label>
<input type="hidden" name="tag" value="{{.Image.Tag}}" >
<input disabled value="{{.Image.Tag}}" style="width: 80%;">
<span class="tooltips" style="display: block;padding-left: 1.5rem;">
{{if eq .Image.CloudbrainType 2}}
{{.i18n.Tr "repo.images.name_rule50"}}
{{else}}
{{.i18n.Tr "repo.images.name_rule100"}}
{{end}}
</span>
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" value="{{.Image.Description}}" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)">{{.Image.Description}}</textarea>
</div>
<div class="inline field">
<label class="label_color" style="color:transparent !important;">x</label>
<div class="descr-tip-box">
<div class="descr-tip-head">{{$.i18n.Tr "admin.images.descrTip"}}</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.framework"}}:pytorch1.9.1;</div>
<div class="descr-tip-item">CUDA:cuda11;</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.pythonVersion"}}:python 3.7.11;</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.operatingSystem"}}:Ubuntu 20.02;</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.installedSoftwarePackage"}}:numpy1.21.2</div>
</div>
</div>
<div class="inline field" style="display: flex;align-items: center;">
{{$lenTopics := len .Image.Topics}}
{{$subTopics := subOne $lenTopics}}
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
<div class="ui multiple search selection dropdown" id="dropdown_image" style="width: 80%;">
<input type="hidden" name="topics" value="{{range $k,$v := .Image.Topics}}{{$v}}{{if ne $k $subTopics}},{{end}}{{end}}" required>
{{range .Image.Topics}}
<a class="ui label transition visible" data-value="{{.}}" style="display: inline-block !important;">{{.}}<i class="delete icon"></i></a>
{{end}}
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div>
<div class="menu" id="course_label_item"></div>
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<div class="inline required field" style="padding-top: 2rem;">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">
{{$.i18n.Tr "admin.images.submitApply"}}
</button>
<a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}
<script>
;(function(){
var Images = {{.Image}};
})();
</script>

+ 10
- 2
templates/repo/cloudbrain/image/edit.tmpl View File

@@ -19,7 +19,13 @@
{{template "repo/header" .}}
<div class="alert"></div>
<div class="ui container">
<div>
<div>
<div class="ui negative message" style="display: none;">
</div>
<div class="ui info message" style="display: none;">
</div>
<div class="ui positive message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "repo.modify_image"}}
</h4>
@@ -55,7 +61,7 @@
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" value="{{.Image.Description}}" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)">{{.Image.Description}}</textarea>
<textarea style="width: 80%;" required id="description" value="{{.Image.Description}}" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)">{{.Image.Description}}</textarea>
</div>
<div class="inline field" style="display: flex;align-items: center;">
{{$lenTopics := len .Image.Topics}}
@@ -71,6 +77,7 @@
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<!--
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
@@ -89,6 +96,7 @@
<span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span>
</div>
</div>
-->
<div class="inline required field" style="padding-top: 2rem;">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">


+ 3
- 1
templates/repo/cloudbrain/image/submit.tmpl View File

@@ -58,7 +58,7 @@
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea>
<textarea style="width: 80%;" required id="description" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)"></textarea>
</div>
<div class="inline field" style="display: flex;align-items: center;">
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
@@ -69,6 +69,7 @@
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<!--
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
@@ -87,6 +88,7 @@
<span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span>
</div>
</div>
-->
<div class="inline required field" style="padding-top: 2rem;">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">


+ 372
- 565
web_src/js/components/images/Images.vue View File

@@ -3,211 +3,140 @@
<div class="header-wrapper">
<div class="ui container">
<el-row class="image_text">
<h1>{{$i18n['cloudeBrainMirror']['cloud_brain_mirror']}}</h1>
<h1>{{ $i18n['cloudeBrainMirror']['cloud_brain_mirror'] }}</h1>
</el-row>
</div>
</div>
<div class="ui container" id="header">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="$i18n['cloudeBrainMirror']['public_mirror']" name="first" v-loading="loadingPublic">
<template v-if="tableDataPublic.length !== 0">
<el-tab-pane :label="$i18n['cloudeBrainMirror']['recommendImages']" name="first" v-loading="loadingRecommend">
<template v-if="tableDataRecommend.length !== 0">
<el-row style="align-items: center; display: flex">
<el-col :span="12">
<div>
<el-checkbox v-model="checked">{{$i18n['cloudeBrainMirror']['platform_recommendations']}}</el-checkbox>
<!-- <el-checkbox v-model="checked">{{$i18n['cloudeBrainMirror']['platform_recommendations']}}</el-checkbox> -->
</div>
</el-col>
<el-col :span="10">
<div>
<el-input
:placeholder="$i18n['cloudeBrainMirror']['placeholder']"
v-model="search"
class="input-with-select"
@keyup.enter.native="searchName()"
>
<el-button
id="success"
slot="append"
icon="el-icon-search"
@click="searchName()"
>{{$i18n['cloudeBrainMirror']['search']}}</el-button
>
<el-input :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">{{
$i18n['cloudeBrainMirror']['search'] }}</el-button>
</el-input>
</div>
</el-col>
<el-col :span="2">
<el-dropdown @command="handleCommandPublic" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{sortPublic | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'defaultsort',sort:''}">{{$i18n['cloudeBrainMirror']['defaultsort']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown @command="handleCommandRecommend" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{ sortRecommend | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'defaultsort', sort: '' }">{{
$i18n['cloudeBrainMirror']['defaultsort'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{
$i18n['cloudeBrainMirror']['newest'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-row style="margin-top: 15px">
<el-table
:data="tableDataPublic"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
:label="$i18n['cloudeBrainMirror']['mirror_tag']"
min-width="19%"
align="left"
prop="tag"
>
<el-table :data="tableDataRecommend" style="width: 100%" :header-cell-style="tableHeaderStyle">
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_tag']" min-width="19%" align="left"
prop="tag">
<template slot-scope="scope">
<div style="display: flex; align-items: center">
<a class="text-over image_title" :title="scope.row.tag">{{
scope.row.tag
}}</a>
<img
v-if="scope.row.type == 5"
src="/img/jian.svg"
style="margin-left: 0.5rem"
/>
<img v-if="scope.row.type == 5" src="/img/jian.svg" style="margin-left: 0.5rem" />
</div>
</template>
</el-table-column>
<el-table-column
:label="$i18n['cloudeBrainMirror']['mirror_description']"
min-width="26%"
align="left"
prop="description"
>
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" min-width="26%" align="left"
prop="description">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">
{{ scope.row.description }}
</div>
<div v-if="!!scope.row.topics">
<span
v-for="(topic, index) in scope.row.topics"
class="ui repo-topic label topic"
style="cursor: default"
>{{ topic }}</span
>
<span v-for="(topic, index) in scope.row.topics" class="ui repo-topic label topic" :key="index"
style="cursor: default">{{ topic }}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
:label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="15%"
align="center"
>
<el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="15%" align="center">
<template slot-scope="scope">
{{ scope.row.cloudbrainType | transformType(vm) }}
</template>
</el-table-column>
<el-table-column
prop="creator"
:label="$i18n['cloudeBrainMirror']['creator']"
min-width="8%"
align="center"
>
<el-table-column prop="creator" :label="$i18n['cloudeBrainMirror']['creator']" min-width="8%"
align="center">
<template slot-scope="scope">
<a
v-if="scope.row.userName || scope.row.relAvatarLink"
:href="'/' + scope.row.userName"
:title="scope.row.userName"
>
<img
:src="scope.row.relAvatarLink"
class="ui avatar image"
/>
<a v-if="scope.row.userName || scope.row.relAvatarLink" :href="'/' + scope.row.userName"
:title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image" />
</a>
<a v-else
><img
class="ui avatar image"
title="Ghost"
src="/user/avatar/ghost/-1"
/></a>
<a v-else><img class="ui avatar image" title="Ghost" src="/user/avatar/ghost/-1" /></a>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
:label="$i18n['cloudeBrainMirror']['creation_time']"
align="center"
min-width="14%"
>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center"
min-width="14%">
<template slot-scope="scope">
{{ scope.row.createdUnix | transformTimestamp }}
</template>
</el-table-column>
<el-table-column align="center" min-width="18%" :label="$i18n['cloudeBrainMirror']['operation']">
<template slot-scope="scope">
<div
style="
<div style="
display: flex;
justify-content: flex-end;
align-items: center;
"
>
">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
<i class="ri-links-line" style="font-size: 16px;"></i>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount}}</span>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount }}</span>
</div>
<div
style="
<div style="
display: flex;
align-items: center;
cursor: pointer;
padding: 0 1rem;
"
@click="
" @click="
imageStar(
scope.$index,
scope.row.id,
scope.row.isStar
)
"
>
<svg
width="1.4em"
height="1.4em"
viewBox="0 0 32 32"
class="heart-stroke"
:class="{ stars_active: scope.row.isStar }"
>
">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke"
:class="{ stars_active: scope.row.isStar }">
<path
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"
></path>
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z">
</path>
</svg>
<span style="line-height: 2; margin-left: 0.3rem">{{
scope.row.numStars
}}</span>
</div>
<span
:class="scope.row.place?'copy-adress':'copy-adress-no'"
@click="copyUrl(scope.row.place)"
>{{$i18n['cloudeBrainMirror']['copy_address']}}</span
>
<span :class="scope.row.place ? 'copy-adress' : 'copy-adress-no'"
@click="copyUrl(scope.row.place)">{{ $i18n['cloudeBrainMirror']['copy_address'] }}</span>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<div
class="ui container"
style="margin-top: 50px; text-align: center"
>
<el-pagination
background
@size-change="handleSizeChangePublic"
@current-change="handleCurrentChangePublic"
:current-page="currentPagePublic"
:page-size="pageSizePublic"
:page-sizes="[5, 10, 15]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumPublic"
>
<div class="ui container" style="margin-top: 50px; text-align: center">
<el-pagination background @size-change="handleSizeChangeRecommend"
@current-change="handleCurrentChangeRecommend" :current-page="currentPageRecommend"
:page-size="pageSizeRecommend" :page-sizes="[5, 10, 15]" layout="total, sizes, prev, pager, next, jumper"
:total="totalNumRecommend">
</el-pagination>
</div>
</template>
@@ -215,41 +144,36 @@
<el-row style="align-items: center; display: flex">
<el-col :span="12">
<div>
<el-checkbox v-model="checked">{{$i18n['cloudeBrainMirror']['platform_recommendations']}}</el-checkbox>
<!-- <el-checkbox v-model="checked">{{ $i18n['cloudeBrainMirror']['platform_recommendations']
}}</el-checkbox> -->
</div>
</el-col>
<el-col :span="10">
<div>
<el-input
:placeholder="$i18n['cloudeBrainMirror']['placeholder']"
v-model="search"
class="input-with-select"
@keyup.enter.native="searchName()"
>
<el-button
id="success"
slot="append"
icon="el-icon-search"
@click="searchName()"
>{{$i18n['cloudeBrainMirror']['search']}}</el-button
>
<el-input :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">{{
$i18n['cloudeBrainMirror']['search'] }}</el-button>
</el-input>
</div>
</el-col>
<el-col :span="2">
<el-dropdown @command="handleCommandPublic" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{sortPublic | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'defaultsort',sort:''}">{{$i18n['cloudeBrainMirror']['defaultsort']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown @command="handleCommandRecommend" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{ sortRecommend | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'defaultsort', sort: '' }">{{
$i18n['cloudeBrainMirror']['defaultsort'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{
$i18n['cloudeBrainMirror']['newest'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-empty :image-size="200"></el-empty>
@@ -263,216 +187,171 @@
</el-col>
<el-col :span="10">
<div>
<el-input
:placeholder="$i18n['cloudeBrainMirror']['placeholder']"
v-model="search"
class="input-with-select"
@keyup.enter.native="searchName()"
>
<el-button
id="success"
slot="append"
icon="el-icon-search"
@click="searchName()"
>{{$i18n['cloudeBrainMirror']['search']}}</el-button
>
<el-input :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">{{
$i18n['cloudeBrainMirror']['search'] }}</el-button>
</el-input>
</div>
</el-col>
<el-col :span="2">
<el-dropdown @command="handleCommandCustom" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{sortCustom | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{ sortCustom | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{
$i18n['cloudeBrainMirror']['newest'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-row style="margin-top: 15px">
<el-table
:data="tableDataCustom"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
:label="$i18n['cloudeBrainMirror']['mirror_tag']"
min-width="19%"
align="left"
prop="tag"
>
<el-table :data="tableDataCustom" style="width: 100%" :header-cell-style="tableHeaderStyle">
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_tag']" min-width="19%" align="left"
prop="tag">
<template slot-scope="scope">
<div style="display: flex; align-items: center">
<a class="text-over image_title" :title="scope.row.tag">{{
scope.row.tag
}}</a
>&nbsp;&nbsp;&nbsp;
<i
class="ri-lock-2-line"
style="color: #fa8c16"
v-if="scope.row.isPrivate"
></i>
<img
v-if="scope.row.type == 5"
src="/img/jian.svg"
style="margin-left: 0.5rem"
/>
}}</a>&nbsp;&nbsp;&nbsp;
<!-- <i class="ri-lock-2-line" style="color: #fa8c16" v-if="scope.row.isPrivate"></i> -->
<img v-if="scope.row.type == 5" src="/img/jian.svg" style="margin-left: 0.5rem" />
</div>
</template>
</el-table-column>
<el-table-column
:label="$i18n['cloudeBrainMirror']['mirror_description']"
min-width="25%"
align="left"
prop="description"
>
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" min-width="25%" align="left"
prop="description">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">
{{ scope.row.description }}
</div>
<div v-if="!!scope.row.topics">
<span
v-for="(topic, index) in scope.row.topics"
class="ui repo-topic label topic"
style="cursor: default"
>{{ topic }}</span
>
<span v-for="(topic, index) in scope.row.topics" class="ui repo-topic label topic" :key="index"
style="cursor: default">{{ topic }}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
:label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="12%"
align="center"
>
<el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="12%" align="center">
<template slot-scope="scope">
{{ scope.row.cloudbrainType | transformType(vm) }}
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
:label="$i18n['cloudeBrainMirror']['state']"
min-width="9%"
align="center"
>
<el-table-column prop="sumbimtState" :label="$i18n['cloudeBrainMirror']['commit_status']" min-width="9%"
align="center">
<template slot-scope="scope">
<div
style="
<div style="
display: flex;
align-items: center;
justify-content: center;
"
>
<span
v-if="scope.row.isPrivate"
style="color: rgb(250, 140, 22)"
>{{$i18n['cloudeBrainMirror']['private']}}</span
>
<span v-else style="color: rgb(19, 194, 141)">{{$i18n['cloudeBrainMirror']['public']}}</span>
<el-tooltip
v-if="scope.row.status === 0"
class="item"
effect="dark"
:content="$i18n['cloudeBrainMirror']['mirror_committed']"
placement="top"
>
">
<span v-if="scope.row.status === 0">{{ $i18n['cloudeBrainMirror']['commiting'] }}</span>
<span v-if="scope.row.status === 1">{{ $i18n['cloudeBrainMirror']['commit_success'] }}</span>
<span v-if="scope.row.status === 2">{{ $i18n['cloudeBrainMirror']['commit_failed'] }}</span>
<el-tooltip v-if="scope.row.status === 0" class="item" effect="dark"
:content="$i18n['cloudeBrainMirror']['mirror_committed']" placement="top">
<i class="CREATING" style="margin-left: 0.3rem"></i>
</el-tooltip>

<el-tooltip
v-if="scope.row.status === 2"
class="item"
effect="dark"
:content="$i18n['cloudeBrainMirror']['check_exceeds_20g']"
placement="top"
>
<i class="FAILED" style="margin-left: 0.3rem"></i>
</el-tooltip>

<el-tooltip
v-if="scope.row.status === 1"
class="item"
effect="dark"
:content="$i18n['cloudeBrainMirror']['mirror_submitted']"
placement="top"
>
<el-tooltip v-if="scope.row.status === 1" class="item" effect="dark"
:content="$i18n['cloudeBrainMirror']['mirror_submitted']" placement="top">
<i class="SUCCEEDED" style="margin-left: 0.3rem"></i>
</el-tooltip>
<el-tooltip v-if="scope.row.status === 2" class="item" effect="dark"
:content="$i18n['cloudeBrainMirror']['check_exceeds_20g']" placement="top">
<i class="FAILED" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
:label="$i18n['cloudeBrainMirror']['creation_time']"
align="center"
min-width="13%"
>
<el-table-column prop="sumbimtState" :label="$i18n['cloudeBrainMirror']['recommend_by_plateform']"
width="140" align="center">
<template slot-scope="scope">
<div v-if="scope.row.status === 1" style="">
<div v-if="scope.row.apply_status === 2"
style="display: flex;align-items:center;justify-content:center;">
<span style="color: rgb(250, 140, 22);">{{ $i18n['cloudeBrainMirror']['pending_approval']
}}</span>
<el-tooltip class="item" effect="dark" :content="$i18n['cloudeBrainMirror']['pending_approval']"
placement="top">
<i class="CLOCK" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
<div v-if="scope.row.apply_status === 3"
style="display: flex;align-items:center;justify-content:center;">
<span style="color: rgb(19, 194, 141);">{{ $i18n['cloudeBrainMirror']['approved'] }}</span>
<el-tooltip class="item" effect="dark" :content="$i18n['cloudeBrainMirror']['approved']"
placement="top">
<i class="SUCCEEDED" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
<div v-if="scope.row.apply_status === 4"
style="display: flex;align-items:center;justify-content:center;">
<span style="color: red">{{ $i18n['cloudeBrainMirror']['not_approved'] }}</span>
<el-tooltip class="item" effect="dark" :content="scope.row.message" placement="top">
<i class="FAILED" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
<div
v-if="scope.row.apply_status == 0 || scope.row.apply_status == 1 || scope.row.apply_status === 4">
<span class="apply-btn" v-if="scope.row.type != 5" @click="applyImage(scope.row.id)">
{{ $i18n['cloudeBrainMirror']['apply'] }}
</span>
<div v-else>--</div>
</div>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center"
min-width="13%">
<template slot-scope="scope">
{{ scope.row.createdUnix | transformTimestamp }}
</template>
</el-table-column>
<el-table-column align="center" min-width="22%" :label="$i18n['cloudeBrainMirror']['operation']">
<template slot-scope="scope">
<div
style="
<div style="
display: flex;
justify-content: flex-end;
align-items: center;
"
>
">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
<i class="ri-links-line" style="font-size: 16px;"></i>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount}}</span>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount }}</span>
</div>
<div
style="
<div style="
display: flex;
align-items: center;
cursor: default;
padding: 0 1rem;
"
>
<svg
width="1.4em"
height="1.4em"
viewBox="0 0 32 32"
class="heart-stroke"
>
">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke">
<path
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"
></path>
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z">
</path>
</svg>
<span style="line-height: 2; margin-left: 0.3rem">{{
scope.row.numStars
}}</span>
</div>
<span
:class="scope.row.place?'copy-adress':'copy-adress-no'"
@click="copyUrl(scope.row.place)"
>{{$i18n['cloudeBrainMirror']['copy_address']}}</span
>
<span :class="scope.row.place ? 'copy-adress' : 'copy-adress-no'"
@click="copyUrl(scope.row.place)">{{ $i18n['cloudeBrainMirror']['copy_address'] }}</span>
<div style="padding-left: 1rem; cursor: pointer">
<el-dropdown size="medium">
<span class="el-dropdown-link">
{{$i18n['cloudeBrainMirror']['more']}}<i
class="el-icon-arrow-down el-icon--right"
></i>
{{ $i18n['cloudeBrainMirror']['more'] }}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
@click.native="eidtImage(scope.row.id)"
>{{$i18n['cloudeBrainMirror']['edit']}}</el-dropdown-item
>
<el-dropdown-item
style="color: red"
@click.native="deleteImage(scope.row.id)"
>{{$i18n['cloudeBrainMirror']['delete']}}</el-dropdown-item
>
<el-dropdown-item @click.native="eidtImage(scope.row.id)">{{
$i18n['cloudeBrainMirror']['edit'] }}</el-dropdown-item>
<el-dropdown-item style="color: red" @click.native="deleteImage(scope.row.id)">{{
$i18n['cloudeBrainMirror']['delete'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
@@ -481,20 +360,10 @@
</el-table-column>
</el-table>
</el-row>
<div
class="ui container"
style="margin-top: 50px; text-align: center"
>
<el-pagination
background
@size-change="handleSizeChangeCustom"
@current-change="handleCurrentChangeCustom"
:current-page="currentPageCustom"
:page-size="pageSizeCustom"
:page-sizes="[5, 10, 15]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumCustom"
>
<div class="ui container" style="margin-top: 50px; text-align: center">
<el-pagination background @size-change="handleSizeChangeCustom" @current-change="handleCurrentChangeCustom"
:current-page="currentPageCustom" :page-size="pageSizeCustom" :page-sizes="[5, 10, 15]"
layout="total, sizes, prev, pager, next, jumper" :total="totalNumCustom">
</el-pagination>
</div>
</template>
@@ -503,37 +372,30 @@
<el-col :span="12">
<div style="visibility: hidden"></div>
</el-col>
<el-col :span="10">
<div>
<el-input
:placeholder="$i18n['cloudeBrainMirror']['placeholder']"
v-model="search"
class="input-with-select"
@keyup.enter.native="searchName()"
>
<el-button
id="success"
slot="append"
icon="el-icon-search"
@click="searchName()"
>{{$i18n['cloudeBrainMirror']['search']}}</el-button
>
<el-input :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">{{
$i18n['cloudeBrainMirror']['search'] }}</el-button>
</el-input>
</div>
</el-col>
<el-col :span="2">
<el-dropdown @command="handleCommandCustom" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{sortCustom | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{ sortCustom | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{
$i18n['cloudeBrainMirror']['newest'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-empty :image-size="200"></el-empty>
@@ -545,231 +407,152 @@
<el-col :span="12">
<div style="visibility: hidden"></div>
</el-col>
<el-col :span="10">
<div>
<el-input
:placeholder="$i18n['cloudeBrainMirror']['placeholder']"
v-model="search"
class="input-with-select"
@keyup.enter.native="searchName()"
>
<el-button
id="success"
slot="append"
icon="el-icon-search"
@click="searchName()"
>{{$i18n['cloudeBrainMirror']['search']}}</el-button
>
<el-input :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">{{
$i18n['cloudeBrainMirror']['search'] }}</el-button>
</el-input>
</div>
</el-col>
<el-col :span="2">
<el-dropdown @command="handleCommandStar" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{sortStar | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'defaultsort',sort:''}">{{$i18n['cloudeBrainMirror']['defaultsort']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{ sortStar | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'defaultsort', sort: '' }">{{
$i18n['cloudeBrainMirror']['defaultsort'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{
$i18n['cloudeBrainMirror']['newest'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-row style="margin-top: 15px">
<el-table
:data="tableDataStar"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
:label="$i18n['cloudeBrainMirror']['mirror_tag']"
min-width="19%"
align="left"
prop="tag"
>
<el-table :data="tableDataStar" style="width: 100%" :header-cell-style="tableHeaderStyle">
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_tag']" min-width="19%" align="left"
prop="tag">
<template slot-scope="scope">
<div style="display: flex; align-items: center">
<a class="text-over image_title" :title="scope.row.tag">{{
scope.row.tag
}}</a>
<img
v-if="scope.row.type == 5"
src="/img/jian.svg"
style="margin-left: 0.5rem"
/>
<img v-if="scope.row.type == 5" src="/img/jian.svg" style="margin-left: 0.5rem" />
</div>
</template>
</el-table-column>
<el-table-column
:label="$i18n['cloudeBrainMirror']['mirror_description']"
min-width="26%"
align="left"
prop="description"
>
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" min-width="26%" align="left"
prop="description">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">
{{ scope.row.description }}
</div>
<div v-if="!!scope.row.topics">
<span
v-for="(topic, index) in scope.row.topics"
class="ui repo-topic label topic"
style="cursor: default"
>{{ topic }}</span
>
<span v-for="(topic, index) in scope.row.topics" class="ui repo-topic label topic" :key="index"
style="cursor: default">{{ topic }}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
:label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="14%"
align="center"
>
<el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="14%" align="center">
<template slot-scope="scope">
{{ scope.row.cloudbrainType | transformType(vm) }}
</template>
</el-table-column>
<el-table-column
prop="creator"
:label="$i18n['cloudeBrainMirror']['creator']"
min-width="8%"
align="center"
>
<el-table-column prop="creator" :label="$i18n['cloudeBrainMirror']['creator']" min-width="8%"
align="center">
<template slot-scope="scope">
<a
v-if="scope.row.userName || scope.row.relAvatarLink"
:href="'/' + scope.row.userName"
:title="scope.row.userName"
>
<img
:src="scope.row.relAvatarLink"
class="ui avatar image"
/>
<a v-if="scope.row.userName || scope.row.relAvatarLink" :href="'/' + scope.row.userName"
:title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image" />
</a>
<a v-else
><img
class="ui avatar image"
title="Ghost"
src="/user/avatar/ghost/-1"
/></a>
<a v-else><img class="ui avatar image" title="Ghost" src="/user/avatar/ghost/-1" /></a>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
:label="$i18n['cloudeBrainMirror']['creation_time']"
align="center"
min-width="14%"
>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center"
min-width="14%">
<template slot-scope="scope">
{{ scope.row.createdUnix | transformTimestamp }}
</template>
</el-table-column>
<el-table-column align="center" min-width="18%" :label="$i18n['cloudeBrainMirror']['operation']">
<template slot-scope="scope">
<div
style="
<div style="
display: flex;
justify-content: flex-end;
align-items: center;
"
>
">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
<i class="ri-links-line" style="font-size: 16px;"></i>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount}}</span>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount }}</span>
</div>
<div
style="
<div style="
display: flex;
align-items: center;
cursor: pointer;
padding: 0 1rem;
"
@click="imageUnstar(scope.row.id)"
>
<svg
width="1.4em"
height="1.4em"
viewBox="0 0 32 32"
class="heart-stroke stars_active"
>
" @click="imageUnstar(scope.row.id)">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke stars_active">
<path
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"
></path>
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z">
</path>
</svg>
<span style="line-height: 2; margin-left: 0.3rem">{{
scope.row.numStars
}}</span>
</div>
<span
:class="scope.row.place?'copy-adress':'copy-adress-no'"
@click="copyUrl(scope.row.place)"
>{{$i18n['cloudeBrainMirror']['copy_address']}}</span
>
<span :class="scope.row.place ? 'copy-adress' : 'copy-adress-no'"
@click="copyUrl(scope.row.place)">{{ $i18n['cloudeBrainMirror']['copy_address'] }}</span>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<div
class="ui container"
style="margin-top: 50px; text-align: center"
>
<el-pagination
background
@size-change="handleSizeChangeStar"
@current-change="handleCurrentChangeStar"
:current-page="currentPageStar"
:page-size="pageSizeStar"
:page-sizes="[5, 10, 15]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumStar"
>
<div class="ui container" style="margin-top: 50px; text-align: center">
<el-pagination background @size-change="handleSizeChangeStar" @current-change="handleCurrentChangeStar"
:current-page="currentPageStar" :page-size="pageSizeStar" :page-sizes="[5, 10, 15]"
layout="total, sizes, prev, pager, next, jumper" :total="totalNumStar">
</el-pagination>
</div>
</template>

<template v-else>
<el-row style="align-items: center; display: flex">
<el-col :span="12">
<div style="visibility: hidden"></div>
</el-col>
<el-col :span="10">
<div>
<el-input
:placeholder="$i18n['cloudeBrainMirror']['placeholder']"
v-model="search"
class="input-with-select"
@keyup.enter.native="searchName()"
>
<el-button
id="success"
slot="append"
icon="el-icon-search"
@click="searchName()"
>{{$i18n['cloudeBrainMirror']['search']}}</el-button
>
<el-input :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">{{
$i18n['cloudeBrainMirror']['search'] }}</el-button>
</el-input>
</div>
</el-col>
<el-col :span="2">
<el-dropdown @command="handleCommandStar" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{sortStar | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'defaultsort',sort:''}">{{$i18n['cloudeBrainMirror']['defaultsort']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;float: right;">
<span class="el-dropdown-link">
{{ sortStar | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'defaultsort', sort: '' }">{{
$i18n['cloudeBrainMirror']['defaultsort'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{
$i18n['cloudeBrainMirror']['newest'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-empty :image-size="200"></el-empty>
@@ -790,32 +573,32 @@ export default {
activeName: "first",
search: "",
checked: false,
currentPagePublic: 1,
pageSizePublic: 10,
totalNumPublic: 0,
paramsPublic: { page: 1, pageSize: 10, q: "", recommend: false,cloudbrainType:-1,sort:"" },
tableDataPublic: [],
loadingPublic: false,
sortPublic:'defaultsort',
currentPageRecommend: 1,
pageSizeRecommend: 10,
totalNumRecommend: 0,
paramsRecommend: { page: 1, pageSize: 10, q: "", recommend: false, cloudbrainType: -1, sort: "", _csrf: csrf, },
tableDataRecommend: [],
loadingRecommend: false,
sortRecommend: 'defaultsort',

currentPageCustom: 1,
pageSizeCustom: 10,
totalNumCustom: 0,
paramsCustom: { page: 1, pageSize: 10, q: "",cloudbrainType:-1,sort:"newest" },
paramsCustom: { page: 1, pageSize: 10, q: "", cloudbrainType: -1, sort: "newest", _csrf: csrf },
tableDataCustom: [],
starCustom: [],
loadingCustom: false,
refreshCustomTimer: null,
sortCustom:'newest',
sortCustom: 'newest',

currentPageStar: 1,
pageSizeStar: 10,
totalNumStar: 0,
paramsStar: { page: 1, pageSize: 10, q: "",cloudbrainType:-1 ,sort:""},
paramsStar: { page: 1, pageSize: 10, q: "", cloudbrainType: -1, sort: "", _csrf: csrf },
tableDataStar: [],
loadingStar: false,
sortStar:'defaultsort',
vm:this,
sortStar: 'defaultsort',
vm: this,
};
},
methods: {
@@ -823,8 +606,8 @@ export default {
this.search = "";
this.stopImageListCustomRefresh();
if (tab.name == "first") {
this.paramsPublic.q = "";
this.getImageListPublic();
this.paramsRecommend.q = "";
this.getImageListRecommend();
}
if (tab.name == "second") {
this.getImageListCustom();
@@ -833,34 +616,34 @@ export default {
this.getImageListStar();
}
},
handleCommandPublic(command) {
this.sortPublic = command.label
this.paramsPublic.sort = command.sort
this.getImageListPublic();
handleCommandRecommend(command) {
this.sortRecommend = command.label
this.paramsRecommend.sort = command.sort
this.getImageListRecommend();
},
handleCommandCustom(command) {
this.sortCustom = command.label
this.paramsCustom.sort = command.sort
this.getImageListCustom()
this.sortCustom = command.label
this.paramsCustom.sort = command.sort
this.getImageListCustom()
},
handleCommandStar(command) {
this.sortStar = command.label
this.paramsStar.sort = command.sort
this.getImageListStar();
this.sortStar = command.label
this.paramsStar.sort = command.sort
this.getImageListStar();
},
tableHeaderStyle({ row, column, rowIndex, columnIndex }) {
if (rowIndex === 0) {
return "background:#f5f5f6;color:#606266";
}
},
handleSizeChangePublic(val) {
this.paramsPublic.pageSize = val;
this.getImageListPublic();
handleSizeChangeRecommend(val) {
this.paramsRecommend.pageSize = val;
this.getImageListRecommend();
},
handleCurrentChangePublic(val) {
this.currentPagePublic = val;
this.paramsPublic.page = val;
this.getImageListPublic();
handleCurrentChangeRecommend(val) {
this.currentPageRecommend = val;
this.paramsRecommend.page = val;
this.getImageListRecommend();
},
handleSizeChangeCustom(val) {
this.paramsCustom.pageSize = val;
@@ -878,23 +661,22 @@ export default {
this.paramsStar.page = val;
this.getImageListStar();
},
getImageListPublic() {
this.loadingPublic = true;
getImageListRecommend() {
this.loadingRecommend = true;
this.$axios
.get("/explore/images/public", {
params: this.paramsPublic,
.get("/api/v1/images/recommend", {
params: this.paramsRecommend,
})
.then((res) => {
this.totalNumPublic = res.data.count;
this.tableDataPublic = res.data.images;
this.loadingPublic = false;
this.totalNumRecommend = res.data.count;
this.tableDataRecommend = res.data.images;
this.loadingRecommend = false;
});
},

getImageListCustom() {
this.loadingCustom = true;
this.$axios
.get("/explore/images/custom", {
.get("/api/v1/images/custom", {
params: this.paramsCustom,
})
.then((res) => {
@@ -905,9 +687,13 @@ export default {
});
this.loadingCustom = false;
this.getImageListCustomRefresh();
}).catch(err => {
console.log(err);
if (err.response.status == 401) {
window.location.href = `/user/login?redirect_to=${encodeURIComponent(window.location.href)}`;
}
});
},

getImageListCustomRefresh() {
this.stopImageListCustomRefresh();
this.refreshCustomTimer = setInterval(() => {
@@ -926,23 +712,26 @@ export default {
});
}, 5000);
},

stopImageListCustomRefresh() {
this.refreshCustomTimer && clearInterval(this.refreshCustomTimer);
},
getImageListStar() {
this.loadingStar = true;
this.$axios
.get("/explore/images/star", {
.get("/api/v1/images/star", {
params: this.paramsStar,
})
.then((res) => {
this.totalNumStar = res.data.count;
this.tableDataStar = res.data.images;
this.loadingStar = false;
}).catch(err => {
console.log(err);
if (err.response.status == 401) {
window.location.href = `/user/login?redirect_to=${encodeURIComponent(window.location.href)}`;
}
});
},

deleteImage(id) {
let flag = 1;
let _this = this;
@@ -982,13 +771,16 @@ export default {
eidtImage(id) {
location.href = `/image/${id}/imageSquare`;
},
applyImage(id) {
location.href = `/image/${id}/apply`;
},
imageStar(index, id, isStar) {
if (isStar) {
this.$axios.put(`/image/${id}/action/unstar`).then((res) => {
if (res.data.Code == 0) {
this.tableDataPublic[index].numStars =
this.tableDataPublic[index].numStars - 1;
this.tableDataPublic[index].isStar = false;
this.tableDataRecommend[index].numStars =
this.tableDataRecommend[index].numStars - 1;
this.tableDataRecommend[index].isStar = false;
} else {
console.log(res.data.Message);
}
@@ -996,9 +788,9 @@ export default {
} else {
this.$axios.put(`/image/${id}/action/star`).then((res) => {
if (res.data.Code == 0) {
this.tableDataPublic[index].numStars =
this.tableDataPublic[index].numStars + 1;
this.tableDataPublic[index].isStar = true;
this.tableDataRecommend[index].numStars =
this.tableDataRecommend[index].numStars + 1;
this.tableDataRecommend[index].isStar = true;
} else {
console.log(res.data.Message);
}
@@ -1036,9 +828,9 @@ export default {
},
searchName() {
if (this.activeName == "first") {
this.paramsPublic.q = this.search;
this.paramsPublic.page = 1;
this.getImageListPublic();
this.paramsRecommend.q = this.search;
this.paramsRecommend.page = 1;
this.getImageListRecommend();
}
if (this.activeName == "second") {
this.paramsCustom.q = this.search;
@@ -1053,23 +845,21 @@ export default {
},
},
filters: {
transformType(val,vm) {
transformType(val, vm) {
if (val == 0) {
return `${vm.$i18n['cloudeBrainMirror']['openi']} GPU`;
}else{
} else {
return `${vm.$i18n['cloudeBrainMirror']['c2net']} GPU`;
}

},
transformSort(val,vm) {
if (val==='moststars') {
transformSort(val, vm) {
if (val === 'moststars') {
return vm.$i18n['cloudeBrainMirror']['moststars'];
} else if(val==='mostused'){
} else if (val === 'mostused') {
return vm.$i18n['cloudeBrainMirror']['mostused'];
}else if(val==='defaultsort'){
} else if (val === 'defaultsort') {
return vm.$i18n['cloudeBrainMirror']['defaultsort'];
}else{
} else {
return vm.$i18n['cloudeBrainMirror']['newest'];
}
},
@@ -1095,16 +885,16 @@ export default {
},
watch: {
checked(val) {
this.paramsPublic.page = 1;
this.paramsPublic.recommend = val;
this.currentPagePublic = 1;
this.getImageListPublic();
this.paramsRecommend.page = 1;
this.paramsRecommend.recommend = val;
this.currentPageRecommend = 1;
this.getImageListRecommend();
},
},
mounted() {
const lang = document.querySelector('html').getAttribute('lang');
if(lang!='zh-CN'){
document.getElementsByClassName('el-col-2').forEach((item)=>{item.style.width="10%"})
if (lang != 'zh-CN') {
document.getElementsByClassName('el-col-2').forEach((item) => { item.style.width = "10%" })
}
},
created() {
@@ -1114,7 +904,7 @@ export default {
this.activeName = "second";
this.getImageListCustom();
} else {
this.getImageListPublic();
this.getImageListRecommend();
}
},
beforeDestroy() {
@@ -1123,59 +913,71 @@ export default {
};
</script>

<style scoped>
<style scoped lang="less">
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}

.image_text {
padding: 25px 0 55px 0;
}

#header {
position: relative;
top: -40px;
}

.el-dropdown-menu__item--divided {
border-top: 1px solid blue;
}

.el-table thead {
background-color: #f5f5f6;
}

/deep/ .el-tabs__item:hover {
color: #000;
font-weight: 500;
}

/deep/ .el-tabs__item.is-active {
color: #000;
font-weight: 500;
}

/deep/ .el-tabs__active-bar {
background-color: #000;
}

@media screen and (max-width: 1600px) {
/deep/ .el-col-10{
/deep/ .el-col-10 {
width: 40%;
}
/deep/ .el-col-2{

/deep/ .el-col-2 {
width: 10%;
}
}

#success {
background-color: #5bb973;
color: white;
}

.text-over {
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}

.image_title {
display: inline-block;
cursor: default;
color: rgb(66, 98, 144);
}

.image_desc {
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@@ -1183,25 +985,30 @@ export default {
text-overflow: ellipsis;
overflow: hidden;
}

.heart-stroke {
stroke: #fa8c16;
stroke-width: 2;
fill: #fff;
}

.stars_active {
fill: #fa8c16 !important;
stroke: #fa8c16 !important;
}
.copy-adress{

.apply-btn,
.copy-adress {
padding: 0 1rem;
color: #0366d6;
cursor: pointer;
}
.copy-adress-no{

.copy-adress-no {
cursor: pointer;
pointer-events: none;
opacity: .45!important;
color: rgba(0,0,0,.6);
opacity: .45 !important;
color: rgba(0, 0, 0, .6);
padding: 0 1rem;
}
</style>

+ 632
- 447
web_src/js/components/images/adminImages.vue View File

@@ -1,475 +1,660 @@
<template>
<div>
<div class="ui container" style="width: 100% !important;padding-right: 0;">
<div class="ui grid" style="margin: 0 !important">
<div class="row" style="border: 1px solid #d4d4d5;margin-top:0px;padding-top: 0;">
<div class="ui attached segment">
<div class="ui form ignore-dirty">
<div class="ui fluid action input">
<input type="text" :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
@keyup.enter="searchName()">
<button class="ui blue button" @click="searchName()">{{$i18n['cloudeBrainMirror']['search']}}</button>
</div>
</div>
</div>

<div class="ui ten wide column" style="margin: 1rem 0;">
<el-checkbox v-model="checked" style="padding: 0.5rem 1rem;">{{$i18n['cloudeBrainMirror']['platform_recommendations']}}</el-checkbox>
<el-dropdown @command="handleCommand" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{dropdownPrivate}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:$i18n['all'],private:''}">{{$i18n['all']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['public'],private:false}">{{$i18n['cloudeBrainMirror']['public']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['private'],private:true}">{{$i18n['cloudeBrainMirror']['private']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown @command="handleCommandType" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{dropdownType}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:$i18n['all'],type:-1}">{{$i18n['all']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['openi'],type:0}">{{$i18n['cloudeBrainMirror']['openi']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['c2net'],type:2}">{{$i18n['cloudeBrainMirror']['c2net']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown @command="handleCommandSort" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{sortCustom | transformSort(vm)}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:'defaultsort',sort:''}">{{$i18n['cloudeBrainMirror']['defaultsort']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'moststars',sort:'moststars'}">{{$i18n['cloudeBrainMirror']['moststars']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'mostused',sort:'mostused'}">{{$i18n['cloudeBrainMirror']['mostused']}}</el-dropdown-item>
<el-dropdown-item :command="{label:'newest',sort:'newest'}">{{$i18n['cloudeBrainMirror']['newest']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<div class="ui six wide column right aligned" style="margin: 1rem 0;">
<a class="ui blue small button" href="/admin/images/commit_image">{{$i18n['cloudeBrainMirror']['create_cloud_brain_mirror']}}</a>
</div>
<div class="ui sixteen wide column" style="padding: 0;overflow-x: auto;">
<el-table :data="tableDataCustom" style="width: 100%;min-width:1700px;" :header-cell-style="tableHeaderStyle">
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_tag']" min-width="19%" align="left" prop="tag">
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a>
<i class="ri-lock-2-line" style="color: #fa8c16;padding: 0 1rem;"
v-if="scope.row.isPrivate"></i>
<img v-if="scope.row.type==5" src="/img/jian.svg" style="margin-left: 0.5rem;">
</div>
</template>
</el-table-column>
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" min-width="26%" align="left" prop="description">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description}}
</div>
<div v-if="!!scope.row.topics">
<span v-for="(topic,index) in scope.row.topics"
class="ui repo-topic label topic" style="cursor: default;">{{topic}}</span>
</div>

</template>
</el-table-column>
<el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']" min-width="12%" align="center">
<template slot-scope="scope">
{{scope.row.cloudbrainType | transformType(vm)}}
</template>
</el-table-column>
<el-table-column prop="isPrivate" :label="$i18n['cloudeBrainMirror']['state']" min-width="6%" align="center">
<div>
<div class="ui container" style="width: 100% !important;padding-right: 0;">
<div class="ui grid" style="margin: 0 !important">
<div class="row" style="border: 1px solid #d4d4d5;margin-top:0px;padding-top: 0;">
<div class="ui attached segment">
<div class="ui form ignore-dirty">
<div class="ui fluid action input">
<input type="text" :placeholder="$i18n['cloudeBrainMirror']['placeholder']" v-model="search"
@keyup.enter="searchName()">
<button class="ui blue button" @click="searchName()">{{ $i18n['cloudeBrainMirror']['search'] }}</button>
</div>
</div>
</div>
<div class="ui ten wide column" style="margin: 1rem 0;">
<el-checkbox v-model="checked" style="padding: 0.5rem 1rem;">{{
$i18n['cloudeBrainMirror']['platform_recommendations'] }}</el-checkbox>
<!-- <el-dropdown @command="handleCommand" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{dropdownPrivate}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:$i18n['all'],private:''}">{{$i18n['all']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['public'],private:false}">{{$i18n['cloudeBrainMirror']['public']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['private'],private:true}">{{$i18n['cloudeBrainMirror']['private']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> -->
<el-dropdown @command="handleCommandType" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{ dropdownType }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['all_cluster'], type: -1 }">{{
$i18n['cloudeBrainMirror']['all_cluster'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['openi'], type: 0 }">{{
$i18n['cloudeBrainMirror']['openi'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['c2net'], type: 2 }">{{
$i18n['cloudeBrainMirror']['c2net'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown @command="handleApplyState" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{ dropdownApplyState }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['all_approval_status'], type: '' }">{{
$i18n['cloudeBrainMirror']['all_approval_status'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['pending_approval'], type: 2 }">{{
$i18n['cloudeBrainMirror']['pending_approval'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['approved'], type: 3 }">{{
$i18n['cloudeBrainMirror']['approved'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['not_approved'], type: 4 }">{{
$i18n['cloudeBrainMirror']['not_approved'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['none'], type: 1 }">{{
$i18n['cloudeBrainMirror']['none'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown @command="handleCommandSort" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{ sortCustom | transformSort(vm) }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: 'defaultsort', sort: '' }">{{
$i18n['cloudeBrainMirror']['defaultsort'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'moststars', sort: 'moststars' }">{{
$i18n['cloudeBrainMirror']['moststars'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'mostused', sort: 'mostused' }">{{
$i18n['cloudeBrainMirror']['mostused'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: 'newest', sort: 'newest' }">{{ $i18n['cloudeBrainMirror']['newest']
}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<div class="ui six wide column right aligned" style="margin: 1rem 0;">
<a class="ui blue small button" href="/admin/images/commit_image">{{
$i18n['cloudeBrainMirror']['create_cloud_brain_mirror'] }}</a>
</div>
<div class="ui sixteen wide column" style="padding: 0;overflow-x: auto;">
<el-table :data="tableDataCustom" style="width: 100%;min-width:1700px;" :header-cell-style="tableHeaderStyle">
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_tag']" min-width="19%" align="left" prop="tag">
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a>
<!-- <i class="ri-lock-2-line" style="color: #fa8c16;padding: 0 1rem;" v-if="scope.row.isPrivate"></i> -->
<img v-if="scope.row.type == 5" src="/img/jian.svg" style="margin-left: 0.5rem;">
</div>
</template>
</el-table-column>
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" min-width="26%" align="left"
prop="description">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description }}
</div>
<div v-if="!!scope.row.topics">
<span v-for="(topic, index) in scope.row.topics" :key="index" class="ui repo-topic label topic"
style="cursor: default;">{{ topic }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="10%" align="center">
<template slot-scope="scope">
{{ scope.row.cloudbrainType | transformType(vm) }}
</template>
</el-table-column>
<!-- <el-table-column prop="isPrivate" :label="$i18n['cloudeBrainMirror']['state']" min-width="6%" align="center">
<template slot-scope="scope">
<span v-if="scope.row.isPrivate" style="color: rgb(250, 140, 22);">{{$i18n['cloudeBrainMirror']['private']}}</span>
<span v-else style="color: rgb(19, 194, 141);">{{$i18n['cloudeBrainMirror']['public']}}</span>
</template>
</el-table-column>
<el-table-column prop="creator" :label="$i18n['cloudeBrainMirror']['creator']" min-width="6%" align="center">
<template slot-scope="scope">
<a v-if="scope.row.userName||scope.row.relAvatarLink"
:href="'/' + scope.row.userName" :title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image">
</a>
<a v-else>
<img class="ui avatar image" title="Ghost" src="/user/avatar/ghost/-1">
</a>
</template>
</el-table-column>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center" min-width="11%">
<template slot-scope="scope">
{{scope.row.createdUnix | transformTimestamp}}
</template>
</el-table-column>
<el-table-column align="center" min-width="28%" :label="$i18n['cloudeBrainMirror']['operation']">
<template slot-scope="scope">
<div style="display: flex;justify-content: center;align-items: center;">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
<i class="ri-links-line" style="font-size: 16px;"></i>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount}}</span>
</div>
<div
style="display: flex;align-items: center;cursor: default;;padding: 0 1rem;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke">
<path
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z">
</path>
</svg>
<span
style="line-height: 2;margin-left:0.3rem;">{{scope.row.numStars}}</span>
</div>
<span style="padding: 0 1rem;color: rgb(250, 140, 22);cursor:pointer;"
v-if="scope.row.type==5"
@click="unSetRecommend(scope.$index,scope.row.id)">{{$i18n['cloudeBrainMirror']['cancel_recommendation']}}</span>
<span style="padding: 0 1rem;color: rgb(19, 194, 141);cursor:pointer;"
v-if="scope.row.type!==5 && !scope.row.isPrivate"
@click="setRecommend(scope.$index,scope.row.id)">{{$i18n['cloudeBrainMirror']['set_as_recommended']}}</span>
<span :class="scope.row.place?'copy-adress':'copy-adress-no'"
@click="copyUrl(scope.row.place)">{{$i18n['cloudeBrainMirror']['copy_address']}}</span>
<div style="padding-left:1rem;cursor:pointer;">
<el-dropdown size="medium">
<span class="el-dropdown-link">
{{$i18n['cloudeBrainMirror']['more']}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="eidtImage(scope.row.id)">{{$i18n['cloudeBrainMirror']['edit']}}
</el-dropdown-item>
<el-dropdown-item style="color: red;"
@click.native="deleteImage(scope.row.id)">{{$i18n['cloudeBrainMirror']['delete']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
</el-table-column>
</el-table>
</el-table-column> -->
<el-table-column prop="creator" :label="$i18n['cloudeBrainMirror']['creator']" min-width="6%"
align="center">
<template slot-scope="scope">
<a v-if="scope.row.userName || scope.row.relAvatarLink" :href="'/' + scope.row.userName"
:title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image">
</a>
<a v-else>
<img class="ui avatar image" title="Ghost" src="/user/avatar/ghost/-1">
</a>
</template>
</el-table-column>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center"
min-width="11%">
<template slot-scope="scope">
{{ scope.row.createdUnix | transformTimestamp }}
</template>
</el-table-column>
<el-table-column prop="apply_status" :label="$i18n['cloudeBrainMirror']['approval_status']" min-width="10%"
align="center">
<template slot-scope="scope">
<span v-if="scope.row.apply_status == 0" style="">{{ '--' }}</span>
<div v-if="scope.row.apply_status === 1"
style="display: flex;align-items:center;justify-content:center;">
<span> {{ $i18n['cloudeBrainMirror']['none'] }}</span>
<el-tooltip v-if="scope.row.message" class="item" effect="dark" :content="scope.row.message"
placement="top">
<i class="INFO" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
<div v-if="scope.row.apply_status === 2"
style="display: flex;align-items:center;justify-content:center;">
<span style="color: rgb(250, 140, 22);">{{ $i18n['cloudeBrainMirror']['pending_approval'] }}</span>
<el-tooltip class="item" effect="dark" :content="$i18n['cloudeBrainMirror']['pending_approval']"
placement="top">
<i class="CLOCK" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
<div v-if="scope.row.apply_status === 3"
style="display: flex;align-items:center;justify-content:center;">
<span style="color: rgb(19, 194, 141);">{{ $i18n['cloudeBrainMirror']['approved'] }}</span>
<el-tooltip class="item" effect="dark" :content="$i18n['cloudeBrainMirror']['approved']"
placement="top">
<i class="SUCCEEDED" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
<div v-if="scope.row.apply_status === 4"
style="display: flex;align-items:center;justify-content:center;">
<span style="color: red">{{ $i18n['cloudeBrainMirror']['not_approved'] }}</span>
<el-tooltip class="item" effect="dark" :content="scope.row.message" placement="top">
<i class="FAILED" style="margin-left: 0.3rem"></i>
</el-tooltip>
</div>
</template>
</el-table-column>
<el-table-column align="center" min-width="33%" :label="$i18n['cloudeBrainMirror']['operation']">
<template slot-scope="scope">
<div style="display: flex;justify-content: center;align-items: center;">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
<i class="ri-links-line" style="font-size: 16px;"></i>
<span style="line-height: 2;margin-left: 0.3rem;">{{ scope.row.useCount }}</span>
</div>
<div class="ui container" style="padding:2rem 0;text-align:center">
<el-pagination background @size-change="handleSizeChangeCustom"
@current-change="handleCurrentChangeCustom" :current-page="paramsCustom.page"
:page-size="paramsCustom.pageSize" :page-sizes="[5,15,20]"
layout="total, sizes, prev, pager, next, jumper" :total="totalNumCustom">
</el-pagination>
<div style="display: flex;align-items: center;cursor: default;;padding: 0 1rem;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke">
<path
d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z">
</path>
</svg>
<span style="line-height: 2;margin-left:0.3rem;">{{ scope.row.numStars }}</span>
</div>
</div>
</div>
<span style="padding: 0 1rem;color: rgb(19, 194, 141);cursor:pointer;" v-if="scope.row.type !== 5 && scope.row.status == '1'
&& (scope.row.apply_status == 0 || scope.row.apply_status == 1 || scope.row.apply_status == 2)"
@click="setRecommend(scope.$index, scope.row.id)">{{
$i18n['cloudeBrainMirror']['set_as_recommended'] }}</span>
<span style="padding: 0 1rem;color: rgb(250, 140, 22);cursor:pointer;"
v-if="scope.row.type != 5 && scope.row.apply_status == 2"
@click="unSetRecommend(scope.$index, scope.row.id, 'reject')">{{
$i18n['cloudeBrainMirror']['not_recommend'] }}</span>
<span style="padding: 0 1rem;color: rgb(250, 140, 22);cursor:pointer;" v-if="scope.row.type == 5"
@click="unSetRecommend(scope.$index, scope.row.id, 'cancel')">{{
$i18n['cloudeBrainMirror']['cancel_recommendation'] }}</span>
<span :class="scope.row.place ? 'copy-adress' : 'copy-adress-no'" @click="copyUrl(scope.row.place)">{{
$i18n['cloudeBrainMirror']['copy_address'] }}</span>
<div style="padding-left:1rem;cursor:pointer;">
<el-dropdown size="medium">
<span class="el-dropdown-link">
{{ $i18n['cloudeBrainMirror']['more'] }}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="eidtImage(scope.row.id)">{{ $i18n['cloudeBrainMirror']['edit']
}}
</el-dropdown-item>
<el-dropdown-item style="color: red;" @click.native="deleteImage(scope.row.id)">{{
$i18n['cloudeBrainMirror']['delete'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="ui container" style="padding:2rem 0;text-align:center">
<el-pagination background @size-change="handleSizeChangeCustom" @current-change="handleCurrentChangeCustom"
:current-page="paramsCustom.page" :page-size="paramsCustom.pageSize" :page-sizes="[5, 15, 20]"
layout="total, sizes, prev, pager, next, jumper" :total="totalNumCustom">
</el-pagination>
</div>
</div>
</div>
</div>


<el-dialog class="reason-dlg" width="600px" :title="reasonDialogTitle" :visible.sync="reasonDialogShow"
:before-close="reasonDialogHandleClose" @open="reasonDialogHandleOpen">
<div class="reason-content">
<el-form label-width="80px" style="margin-top:36px;padding-right:60px;">
<el-form-item :label="$i18n['cloudeBrainMirror']['reason']" required>
<el-input v-model="reasonDialogContent" maxlength="64"></el-input>
</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button class="btn" @click="reasonDialogHandleClose">{{ $i18n['cancel'] }}</el-button>
<el-button class="btn confirm-btn" type="primary" @click="reasonDialogHandleConfirm">{{ $i18n['confirm']
}}</el-button>
</span>
</el-dialog>
</div>
</template>

<script>

const { _AppSubUrl, _StaticUrlPrefix, csrf } = window.config;
import qs from 'qs'



export default {
components: {

},
data() {
return {
search: '',
dropdownPrivate: '全部',
dropdownType:'全部',
checked: false,
currentPageCustom: 1,
pageSizeCustom: 15,
totalNumCustom: 0,
paramsCustom: { page: 1, pageSize: 15, q: '',recommend: false,cloudbrainType: -1,sort:'', },
tableDataCustom: [],
starCustom: [],
loadingCustom: false,
vm:this,
firstSearch:false,
sortCustom:'defaultsort',
};
},
methods: {
tableHeaderStyle({ row, column, rowIndex, columnIndex }) {
if (rowIndex === 0) {
return 'background:#f5f5f6;color:#606266'
}
},

handleSizeChangeCustom(val) {
this.paramsCustom.pageSize = val
this.getImageListCustom()
},
handleCurrentChangeCustom(val) {
this.paramsCustom.page = val
this.getImageListCustom()
},

getImageListCustom() {
this.loadingCustom = true
if(this.firstSearch){
history.replaceState('','',location.href.split('?')[0])
}
this.$axios.get('/admin/images/data', {
params: this.paramsCustom
}).then((res) => {
this.totalNumCustom = res.data.count
this.tableDataCustom = res.data.images
this.tableDataCustom.forEach(element => {
this.starCustom.push({ id: element.id, })

});
this.loadingCustom = false
})
},
deleteImage(id) {
let flag = 1
let _this = this
$('.ui.basic.modal.images')
.modal({
onDeny: function () {
flag = false
},
onApprove: function () {
_this.$axios.delete('/image/' + id).then((res) => {
_this.getImageListCustom()
})
flag = true
},
onHidden: function () {
if (flag == false) {
$('.alert').html(_this.$i18n['canceled_operation']).removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
} else {
$('.alert').html(_this.$i18n['successfully_deleted']).removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut();
}
}
})
.modal('show')
},
eidtImage(id) {
location.href = `/image/${id}/imageAdmin?${qs.stringify(this.paramsCustom)}`
},
imageStar(index, id, isStar) {
if (isStar) {
this.$axios.put(`/image/${id}/action/unstar`).then((res) => {
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars - 1
this.tableDataPublic[index].isStar = false
})
} else {
this.$axios.put(`/image/${id}/action/star`).then((res) => {
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars + 1
this.tableDataPublic[index].isStar = true
})
}

},
copyUrl(url) {
const cInput = document.createElement('input')
cInput.value = url
document.body.appendChild(cInput)
cInput.select()
document.execCommand('Copy')
cInput.remove()
},
searchName() {
this.paramsCustom.q = this.search
this.paramsCustom.page = 1
this.getImageListCustom()

},
setRecommend(index, id) {
this.$axios.put(`/admin/image/${id}/action/recommend`).then((res) => {
this.tableDataCustom[index].type = 5
})
},
unSetRecommend(index, id) {
this.$axios.put(`/admin/image/${id}/action/unrecommend`).then((res) => {
this.tableDataCustom[index].type = 0
})
},
handleCommand(command) {
this.dropdownPrivate = command.label
this.paramsCustom.private = command.private
this.getImageListCustom()
},
handleCommandType(command){
this.dropdownType = command.label
this.paramsCustom.cloudbrainType = command.type
this.getImageListCustom()
},
handleCommandSort(command) {
this.sortCustom = command.label
this.paramsCustom.sort = command.sort
this.getImageListCustom()
},
},
filters: {
transformType(val,vm) {
if (val == 0) {
return `${vm.$i18n['cloudeBrainMirror']['openi']}/GPU`;
}else{
return `${vm.$i18n['cloudeBrainMirror']['c2net']}/GPU`;
}
},
transformSort(val,vm) {
if (val==='moststars') {
return vm.$i18n['cloudeBrainMirror']['moststars'];
} else if(val==='mostused'){
return vm.$i18n['cloudeBrainMirror']['mostused'];
}else if(val==='defaultsort'){
return vm.$i18n['cloudeBrainMirror']['defaultsort'];
}else{
return vm.$i18n['cloudeBrainMirror']['newest'];
}
},
transformTimestamp(timestamp) {
const date = new Date(parseInt(timestamp) * 1000);
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;
},
},
watch: {
checked(val) {
if(this.firstSearch){
this.firstSearch = false
return
};
this.paramsCustom.page = 1
this.paramsCustom.recommend = val
this.getImageListCustom()
}

},
mounted() {
this.getImageListCustom()
},
created() {
this.$i18n = window.i18n;
this.dropdownPrivate = this.$i18n['all'];
let params = new URLSearchParams(location.search)
this.dropdownType = this.$i18n['all'];
if(location.search){
this.firstSearch = true
this.paramsCustom = qs.parse(location.search.split('?')[1])
if(params.has('private')){
this.dropdownPrivate = !params.get('private') ? this.$i18n['cloudeBrainMirror']['private']:this.$i18n['cloudeBrainMirror']['public']
}
if(params.has('recommend')){
this.checked = params.get('recommend')==='true'?true:false
}
if(params.has('cloudbrainType')){
this.dropdownType = params.get('cloudbrainType')===0 ? this.$i18n['cloudeBrainMirror']['openi']:this.$i18n['cloudeBrainMirror']['c2net']
}
if(params.has('page')){
this.paramsCustom.page = Number(params.get('page')) || 1
}
if(params.has('pageSize')){
this.paramsCustom.pageSize = Number(params.get('pageSize')) || 15
}
}
}

const { _AppSubUrl, _StaticUrlPrefix, csrf } = window.config;
import qs from 'qs'
export default {
components: {},
data() {
return {
search: '',
dropdownPrivate: '',
dropdownType: '',
dropdownApplyState: '',
checked: false,
currentPageCustom: 1,
pageSizeCustom: 15,
totalNumCustom: 0,
paramsCustom: { page: 1, pageSize: 15, q: '', recommend: false, cloudbrainType: -1, sort: '', apply: '', },
tableDataCustom: [],
starCustom: [],
loadingCustom: false,
vm: this,
firstSearch: false,
sortCustom: 'defaultsort',

reasonDialogShow: false,
reasonDialogType: '',
reasonDialogTitle: '',
reasonDialogContent: '',
reasonDialogData: null,
};
</script>

<style scoped>
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}

.image_text {
padding: 25px 0 55px 0;
}

#header {
position: relative;
top: -40px;
}

.el-dropdown-menu__item--divided {
border-top: 1px solid blue;
}

.el-table thead {
background-color: #f5f5f6;
}

/deep/ .el-tabs__item:hover {
color: #000;
font-weight: 500;

}

/deep/ .el-tabs__item.is-active {
color: #000;
font-weight: 500;
},
methods: {
tableHeaderStyle({ row, column, rowIndex, columnIndex }) {
if (rowIndex === 0) {
return 'background:#f5f5f6;color:#606266'
}
},
handleSizeChangeCustom(val) {
this.paramsCustom.pageSize = val
this.getImageListCustom()
},
handleCurrentChangeCustom(val) {
this.paramsCustom.page = val
this.getImageListCustom()
},
getImageListCustom() {
this.loadingCustom = true
if (this.firstSearch) {
history.replaceState('', '', location.href.split('?')[0])
}
this.$axios.get('/admin/images/data', {
params: this.paramsCustom
}).then((res) => {
this.totalNumCustom = res.data.count
this.tableDataCustom = res.data.images
this.tableDataCustom.forEach(element => {
this.starCustom.push({ id: element.id, })
});
this.loadingCustom = false
})
},
deleteImage(id) {
let flag = 1
let _this = this
$('.ui.basic.modal.images')
.modal({
onDeny: function () {
flag = false
},
onApprove: function () {
_this.$axios.delete('/image/' + id).then((res) => {
_this.getImageListCustom()
})
flag = true
},
onHidden: function () {
if (flag == false) {
$('.alert').html(_this.$i18n['canceled_operation']).removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
} else {
$('.alert').html(_this.$i18n['successfully_deleted']).removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut();
}
}
})
.modal('show')
},
eidtImage(id) {
location.href = `/image/${id}/imageAdmin?${qs.stringify(this.paramsCustom)}`
},
imageStar(index, id, isStar) {
if (isStar) {
this.$axios.put(`/image/${id}/action/unstar`).then((res) => {
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars - 1
this.tableDataPublic[index].isStar = false
})
} else {
this.$axios.put(`/image/${id}/action/star`).then((res) => {
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars + 1
this.tableDataPublic[index].isStar = true
})
}
},
copyUrl(url) {
const cInput = document.createElement('input')
cInput.value = url
document.body.appendChild(cInput)
cInput.select()
document.execCommand('Copy')
cInput.remove()
},
searchName() {
this.paramsCustom.q = this.search
this.paramsCustom.page = 1
this.getImageListCustom()
},
setRecommend(index, id) {
this.$axios.put(`/admin/image/${id}/action/recommend`).then((res) => {
// this.tableDataCustom[index].type = 5
this.getImageListCustom()
})
},
unSetRecommend(index, id, type) {
// this.$axios.put(`/admin/image/${id}/action/unrecommend`).then((res) => {
// this.tableDataCustom[index].type = 0
// })
this.reasonDialogContent = '';
this.reasonDialogType = type;
this.reasonDialogData = this.tableDataCustom[index];
if (type == 'reject') {
this.reasonDialogTitle = this.$i18n['cloudeBrainMirror']['not_recommend'];
this.reasonDialogShow = true;
} else if (type == 'cancel') {
this.reasonDialogTitle = this.$i18n['cloudeBrainMirror']['cancel_recommendation'];
this.reasonDialogShow = true;
}
},
handleCommand(command) {
this.dropdownPrivate = command.label
this.paramsCustom.private = command.private
this.paramsCustom.page = 1
this.getImageListCustom()
},
handleCommandType(command) {
this.dropdownType = command.label
this.paramsCustom.cloudbrainType = command.type
this.paramsCustom.page = 1
this.getImageListCustom()
},
handleApplyState(command) {
this.dropdownApplyState = command.label
this.paramsCustom.apply = command.type
this.paramsCustom.page = 1
this.getImageListCustom()
},
handleCommandSort(command) {
this.sortCustom = command.label
this.paramsCustom.sort = command.sort
this.paramsCustom.page = 1
this.getImageListCustom()
},
reasonDialogHandleOpen() { },
reasonDialogHandleClose() {
this.reasonDialogShow = false;
},
reasonDialogHandleConfirm() {
// console.log(this.reasonDialogType, this.reasonDialogContent, this.reasonDialogData);
const reasonContent = this.reasonDialogContent.trim();
if (reasonContent == '') {
this.$message(this.$i18n['cloudeBrainMirror']['pleaseEnterReason']);
return;
}
this.$axios.put(`/admin/image/${this.reasonDialogData.id}/action/unrecommend`, {
message: reasonContent,
}).then((res) => {
this.reasonDialogShow = false;
this.getImageListCustom();
});
}

/deep/ .el-tabs__active-bar {
background-color: #000
},
filters: {
transformType(val, vm) {
if (val == 0) {
return `${vm.$i18n['cloudeBrainMirror']['openi']}/GPU`;
} else {
return `${vm.$i18n['cloudeBrainMirror']['c2net']}/GPU`;
}
},
transformSort(val, vm) {
if (val === 'moststars') {
return vm.$i18n['cloudeBrainMirror']['moststars'];
} else if (val === 'mostused') {
return vm.$i18n['cloudeBrainMirror']['mostused'];
} else if (val === 'defaultsort') {
return vm.$i18n['cloudeBrainMirror']['defaultsort'];
} else {
return vm.$i18n['cloudeBrainMirror']['newest'];
}
},
transformTimestamp(timestamp) {
const date = new Date(parseInt(timestamp) * 1000);
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;
},
},
watch: {
checked(val) {
if (this.firstSearch) {
this.firstSearch = false
return
};
this.paramsCustom.page = 1
this.paramsCustom.recommend = val
this.getImageListCustom()
}

#success {
background-color: #5bb973;
color: white;
},
mounted() {
this.getImageListCustom()
},
created() {
this.$i18n = window.i18n;
this.dropdownPrivate = this.$i18n['all'];
this.dropdownType = this.$i18n['cloudeBrainMirror']['all_cluster'];
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['all_approval_status'];
let params = new URLSearchParams(location.search)
if (location.search) {
this.firstSearch = true
this.paramsCustom = qs.parse(location.search.split('?')[1]);
if (params.has('private')) {
this.dropdownPrivate = !params.get('private') ? this.$i18n['cloudeBrainMirror']['private'] : this.$i18n['cloudeBrainMirror']['public']
}
if (params.has('recommend')) {
this.checked = params.get('recommend') === 'true' ? true : false
}
if (params.has('cloudbrainType')) {
this.dropdownType = params.get('cloudbrainType') === 0 ? this.$i18n['cloudeBrainMirror']['openi'] : this.$i18n['cloudeBrainMirror']['c2net']
}
if (params.has('apply')) {
const apply = params.get('apply');
switch (apply) {
case '1':
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['none']
break;
case '2':
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['pending_approval']
break;
case '3':
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['approved']
break;
case '4':
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['not_approved']
break;
case '':
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['all_approval_status']
default:
break;
}
}
if (params.has('page')) {
this.paramsCustom.page = Number(params.get('page')) || 1
}
if (params.has('pageSize')) {
this.paramsCustom.pageSize = Number(params.get('pageSize')) || 15
}
}
}
};
</script>

.text-over {
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
<style scoped lang="less">
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}

.image_text {
padding: 25px 0 55px 0;
}

#header {
position: relative;
top: -40px;
}

.el-dropdown-menu__item--divided {
border-top: 1px solid blue;
}

.el-table thead {
background-color: #f5f5f6;
}

/deep/ .el-tabs__item:hover {
color: #000;
font-weight: 500;

}

/deep/ .el-tabs__item.is-active {
color: #000;
font-weight: 500;
}

/deep/ .el-tabs__active-bar {
background-color: #000
}

#success {
background-color: #5bb973;
color: white;
}

.text-over {
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}

.image_title {
display: inline-block;
cursor: default;
color: rgb(66, 98, 144);
}

.image_desc {
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
}

.heart-stroke {
stroke: #FA8C16;
stroke-width: 2;
fill: #fff
}

.stars_active {
fill: #FA8C16 !important;
stroke: #FA8C16 !important
}

.header-new-drop {
width: 100%;
}

.copy-adress {
padding: 0 1rem;
color: #0366d6;
cursor: pointer;
}

.copy-adress-no {
cursor: pointer;
pointer-events: none;
opacity: .45 !important;
color: rgba(0, 0, 0, .6);
padding: 0 1rem;
}

.reason-dlg {
/deep/ .el-dialog__header {
text-align: left;
height: 45px;
background: rgb(240, 240, 240);
border-radius: 5px 5px 0px 0px;
border-bottom: 1px solid rgb(212, 212, 213);
padding: 0 15px;
display: flex;
align-items: center;
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);

.el-dialog__title {
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);
}

.image_title {
display: inline-block;

cursor: default;
color: rgb(66, 98, 144);
.el-dialog__headerbtn {
top: 15px;
right: 15px;
}
}

.image_desc {
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
}
/deep/ .el-dialog__body {
padding: 15px 15px;
}

.heart-stroke {
stroke: #FA8C16;
stroke-width: 2;
fill: #fff
.el-input {
/deep/ .el-input__inner:focus {
border-color: #85b7d9;
outline: 0;
}
}

.stars_active {
fill: #FA8C16 !important;
stroke: #FA8C16 !important
}
.btn {
color: rgb(2, 0, 4);
background-color: rgb(194, 199, 204);
border-color: rgb(194, 199, 204);

.header-new-drop {
width: 100%;
}
.copy-adress{
padding: 0 1rem;
color: #0366d6;
cursor: pointer;
}
.copy-adress-no{
cursor: pointer;
pointer-events: none;
opacity: .45!important;
color: rgba(0,0,0,.6);
padding: 0 1rem;
&.confirm-btn {
color: #fff;
background-color: rgb(56, 158, 13);
border-color: rgb(56, 158, 13);
}
}
}
</style>

+ 35
- 2
web_src/js/features/i18nVue.js View File

@@ -110,6 +110,7 @@ export const i18nVue = {
cloudeBrainMirror: {
cloud_brain_mirror: '云脑镜像',
public_mirror: '公开镜像',
recommendImages: '平台推荐镜像',
platform_recommendations:'仅显示平台推荐',
placeholder: '搜镜像Tag/描述/标签...',
search:'搜索',
@@ -140,7 +141,23 @@ export const i18nVue = {
defaultsort: '默认排序',
moststars: '最多收藏',
mostused: '最多引用',
newest:'最新创建'
newest:'最新创建',
commit_status: '提交状态',
commiting: '提交中',
commit_success: '成功',
commit_failed: '失败',
recommend_by_plateform: '平台推荐',
approved: '通过审核',
not_approved: '未通过审核',
pending_approval: '待审核',
none: '无',
apply: '申请',
all_cluster: '全部集群',
approval_status: '评审状态',
all_approval_status: '全部评审状态',
not_recommend: '不同意推荐',
reason: '原因',
pleaseEnterReason: '请输入原因',
},
modelObj: {
model_label: '选择模型',
@@ -280,6 +297,7 @@ export const i18nVue = {
cloudeBrainMirror: {
cloud_brain_mirror: 'Cloud Brain Mirror',
public_mirror: 'Public Mirror',
recommendImages: 'Recommend Mirror',
platform_recommendations:'Show platform recommendations only',
placeholder: 'Search Mirror tag / description / tag ... ',
search:'Search',
@@ -310,7 +328,22 @@ export const i18nVue = {
defaultsort: 'Default Sort',
moststars: 'Most Stars',
mostused: 'Most Quote',
newest:'Newest Create'
newest:'Newest Create',
commit_status: 'Commit Status',
commiting: 'Commiting',
commit_success: 'Success',
commit_failed: 'Failed',
recommend_by_plateform: 'Recommend',
approved: 'Approved',
not_approved: 'Not approved',
pending_approval: 'Pending approval',
apply: 'Apply',
all_cluster: 'All Cluster',
approval_status: 'Approval status',
all_approval_status: 'All Approval Status',
not_recommend: 'Not recommend',
reason: 'Reason',
pleaseEnterReason: 'Please enter the reason',
},
modelObj: {
model_label: 'Select Model',


+ 1
- 1
web_src/js/features/images.js View File

@@ -175,7 +175,7 @@ export default async function initImage(){
$('#cancel_submit_image').click(()=>{
if(pageform=='submit'){
location.href = `${window.config.AppSubUrl}${repoLink}/debugjob?debugListType=all`
}else if(pageform=='imageSquare'){
}else if(pageform=='imageSquare' || pageform=='apply'){
location.href = `${window.config.AppSubUrl}/explore/images?type=myimage`
}else if(pageform){
location.href = `${window.config.AppSubUrl}/admin/images`


+ 10
- 0
web_src/less/openi.less View File

@@ -289,6 +289,16 @@ footer .column {
.i-bg-organ {
background-position: -496px -52px;
}
i.CLOCK, i.INFO {
display: inline-block;
width: 18px;
height: 18px;
background: url("/img/icons.svg");
background-position: -359px -52px;
}
i.INFO {
background-position: -494px -52px;
}
.STOPPED,
.KILLED {
display: inline-block;


+ 3
- 3
web_src/vuepages/apis/modules/images.js View File

@@ -4,16 +4,16 @@ import service from "../service";
// params: { type-0|1|2, q, page, pageSize, cloudbrainType-0,1 }
export const getImages = (params) => {
const typeMap = {
'0': 'public',
'0': 'recommend',
'1': 'custom',
'2': 'star',
};
let type = 'public';
let type = 'recommend';
if (params.type && typeMap[params.type]) {
type = typeMap[params.type];
}
return service({
url: `/explore/images/${type}`,
url: `/api/v1/images/${type}`,
method: "get",
params: {
q: params.q || '',


+ 2
- 2
web_src/vuepages/components/cloudbrain/ImageSelectV1.vue View File

@@ -20,7 +20,7 @@
<div class="main-area" v-loading="dlgLoading">
<div class="image-tabs-c">
<el-tabs class="image-tabs" v-model="dlgActiveName" @tab-click="dlgTabClick">
<el-tab-pane :label="$t('cloudbrainObj.publicImage')" name="first"></el-tab-pane>
<el-tab-pane :label="$t('cloudbrainObj.recommendImage')" name="first"></el-tab-pane>
<el-tab-pane :label="$t('cloudbrainObj.myImage')" name="second"></el-tab-pane>
<el-tab-pane :label="$t('cloudbrainObj.myFavImage')" name="third"></el-tab-pane>
</el-tabs>
@@ -44,7 +44,7 @@
</div>
</div>
<div class="item-l-b">
<a class="item-creator" :href="`/${item.repoOwnerName}`">
<a class="item-creator" :href="`/${item.userName}`">
<img class="ui avatar mini image" style="width: 20px; height: 20px"
:src="item.relAvatarLink ? item.relAvatarLink : `/user/avatar/ghost/-1`" />
</a>


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

@@ -599,6 +599,7 @@ const en = {
modelDownload: 'Model Download',
failedReason: 'Failed reason',
publicImage: 'Public Image',
recommendImage: 'Recommend Image',
myImage: 'My Images',
myFavImage: 'My collected images',
searchImagePlaceholder: 'Search image tag/description/label...',


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

@@ -615,6 +615,7 @@ const zh = {
modelDownload: '结果下载',
failedReason: '运行失败原因',
publicImage: '公开镜像',
recommendImage: '平台推荐镜像',
myImage: '我的镜像',
myFavImage: '我收藏的镜像',
searchImagePlaceholder: '搜镜像Tag/描述/标签...',


Loading…
Cancel
Save