Browse Source

Merge branch 'V202108' into fixs-249

tags/v1.21.12.1
zhoupzh 4 years ago
parent
commit
dbc9b49303
14 changed files with 578 additions and 63 deletions
  1. +3
    -2
      models/cloudbrain.go
  2. +41
    -38
      modules/cloudbrain/resty.go
  3. +0
    -0
      modules/timeutil/since.go
  4. +1
    -0
      options/locale/locale_en-US.ini
  5. +3
    -2
      options/locale/locale_zh-CN.ini
  6. +6
    -1
      routers/home.go
  7. +68
    -16
      routers/repo/cloudbrain.go
  8. +5
    -1
      routers/routes/routes.go
  9. +2
    -2
      services/mailer/mail.go
  10. +2
    -0
      templates/base/head_navbar.tmpl
  11. +2
    -0
      templates/base/head_navbar_home.tmpl
  12. +7
    -0
      templates/explore/images.tmpl
  13. +420
    -0
      web_src/js/components/Images.vue
  14. +18
    -1
      web_src/js/index.js

+ 3
- 2
models/cloudbrain.go View File

@@ -123,8 +123,9 @@ type GetImagesResult struct {
}

type GetImagesPayload struct {
Count int `json:"count"`
ImageInfo []*ImageInfo `json:"rows"`
Count int `json:"count"`
TotalPages int `json:"totalPages,omitempty"`
ImageInfo []*ImageInfo `json:"rows"`
}

type CloudbrainsOptions struct {


+ 41
- 38
modules/cloudbrain/resty.go View File

@@ -1,9 +1,11 @@
package cloudbrain

import (
"code.gitea.io/gitea/modules/log"
"encoding/json"
"fmt"
"strings"

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
@@ -11,13 +13,16 @@ import (
)

var (
restyClient *resty.Client
HOST string
TOKEN string
restyClient *resty.Client
HOST string
TOKEN string
ImagesUrlMap = map[string]string{Public: "/rest-server/api/v1/image/public/list/", Custom: "/rest-server/api/v1/image/list/"}
)

const (
JobHasBeenStopped = "S410"
Public = "public"
Custom = "custom"
)

func getRestyClient() *resty.Client {
@@ -77,6 +82,12 @@ sendjob:
Post(HOST + "/rest-server/api/v1/jobs/")

if err != nil {
if res != nil {
var response models.CloudBrainResult
json.Unmarshal(res.Body(), &response)
log.Error("code(%s), msg(%s)", response.Code, response.Msg)
return nil, fmt.Errorf(response.Msg)
}
return nil, fmt.Errorf("resty create job: %s", err)
}

@@ -126,6 +137,16 @@ sendjob:
}

func GetImages() (*models.GetImagesResult, error) {

return GetImagesPageable(1, 100, Custom, "")

}

func GetPublicImages() (*models.GetImagesResult, error) {
return GetImagesPageable(1, 100, Public, "")
}

func GetImagesPageable(page int, size int, imageType string, name string) (*models.GetImagesResult, error) {
checkSetting()
client := getRestyClient()
var getImagesResult models.GetImagesResult
@@ -136,9 +157,9 @@ sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetQueryString("pageIndex=1&pageSize=100").
SetQueryString(getQueryString(page, size, name)).
SetResult(&getImagesResult).
Get(HOST + "/rest-server/api/v1/image/list/")
Get(HOST + ImagesUrlMap[imageType])

if err != nil {
return nil, fmt.Errorf("resty GetImages: %v", err)
@@ -157,48 +178,30 @@ sendjob:
goto sendjob
}

if len(response.Code) != 0 {
log.Error("getImagesResult failed(%s): %s", response.Code, response.Msg)
return &getImagesResult, fmt.Errorf("getImagesResult failed(%s): %s", response.Code, response.Msg)
}

if getImagesResult.Code != Success {
return &getImagesResult, fmt.Errorf("getImagesResult err: %s", res.String())
}

getImagesResult.Payload.TotalPages = getTotalPages(getImagesResult, size)
return &getImagesResult, nil
}

func GetPublicImages() (*models.GetImagesResult, error) {
checkSetting()
client := getRestyClient()
var getImagesResult models.GetImagesResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetQueryString("pageIndex=1&pageSize=100").
SetResult(&getImagesResult).
Get(HOST + "/rest-server/api/v1/image/public/list/")

if err != nil {
return nil, fmt.Errorf("resty GetPublicImages: %v", err)
}

if getImagesResult.Code == "S401" && retry < 1 {
retry++
_ = loginCloudbrain()
goto sendjob
func getTotalPages(getImagesResult models.GetImagesResult, size int) int {
totalCount := getImagesResult.Payload.Count
var totalPages int
if totalCount%size != 0 {
totalPages = totalCount/size + 1
} else {
totalPages = totalCount / size
}
return totalPages
}

if getImagesResult.Code != Success {
return &getImagesResult, fmt.Errorf("getImgesResult err: %s", res.String())
func getQueryString(page int, size int, name string) string {
if strings.TrimSpace(name) == "" {
return fmt.Sprintf("pageIndex=%d&pageSize=%d", page, size)
}

return &getImagesResult, nil
return fmt.Sprintf("pageIndex=%d&pageSize=%d&name=%s", page, size, name)
}

func CommitImage(jobID string, params models.CommitImageParams) error {


+ 0
- 0
modules/timeutil/since.go View File


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

@@ -223,6 +223,7 @@ issues.in_your_repos = In your repositories
repos = Repositories
users = Users
organizations = Organizations
images = CloudImages
search = Search
code = Code
repo_no_results = No matching repositories found.


+ 3
- 2
options/locale/locale_zh-CN.ini View File

@@ -224,6 +224,7 @@ issues.in_your_repos=属于该用户项目的
repos=项目
users=用户
organizations=组织
images = 云脑镜像
search=搜索
code=代码
repo_no_results=未找到匹配的项目。
@@ -478,7 +479,7 @@ add_new_email=添加新的邮箱地址
add_new_openid=添加新的 OpenID URI
add_email=增加电子邮件地址
add_openid=添加 OpenID URI
add_email_confirmation_sent=一封新的确认邮件已经被发送至 <b>%s</b>,请检查您的收件箱并在 %s 内完成确认注册操作。
add_email_confirmation_sent=一封新的确认邮件已经被发送至 <b>%s</b>,请检查您的收件箱并在 %s 内完成确认操作。
add_email_success=新的电子邮件地址已添加。
email_preference_set_success=电子邮件首选项已成功设置。
add_openid_success=新的 OpenID 地址已添加。
@@ -2436,7 +2437,7 @@ future=将来
1y=1年
seconds=%d 秒
minutes=%d 分钟
hours=%d hours
hours=%d 小时
days=%d 天
weeks=%d 周
months=%d 个月


+ 6
- 1
routers/home.go View File

@@ -32,7 +32,8 @@ const (
// tplExploreOrganizations explore organizations page template
tplExploreOrganizations base.TplName = "explore/organizations"
// tplExploreCode explore code page template
tplExploreCode base.TplName = "explore/code"
tplExploreCode base.TplName = "explore/code"
tplExploreImages base.TplName = "explore/images"
)

// Home render home page
@@ -475,6 +476,10 @@ func ExploreCode(ctx *context.Context) {
ctx.HTML(200, tplExploreCode)
}

func ExploreImages(ctx *context.Context) {
ctx.HTML(200, tplExploreImages)
}

// NotFound render 404 page
func NotFound(ctx *context.Context) {
ctx.Data["Title"] = "Page Not Found"


+ 68
- 16
routers/repo/cloudbrain.go View File

@@ -1,8 +1,6 @@
package repo

import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/storage"
"encoding/json"
"errors"
"net/http"
@@ -13,6 +11,9 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/storage"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
@@ -23,10 +24,10 @@ import (
)

const (
tplCloudBrainIndex base.TplName = "repo/cloudbrain/index"
tplCloudBrainNew base.TplName = "repo/cloudbrain/new"
tplCloudBrainShow base.TplName = "repo/cloudbrain/show"
tplCloudBrainShowModels base.TplName = "repo/cloudbrain/models/index"
tplCloudBrainIndex base.TplName = "repo/cloudbrain/index"
tplCloudBrainNew base.TplName = "repo/cloudbrain/new"
tplCloudBrainShow base.TplName = "repo/cloudbrain/show"
tplCloudBrainShowModels base.TplName = "repo/cloudbrain/models/index"
)

var (
@@ -89,13 +90,17 @@ func cutString(str string, lens int) string {

func jobNamePrefixValid(s string) string {
lowStr := strings.ToLower(s)
re := regexp.MustCompile(`[^a-z0-9\\.\\-]+`)
return re.ReplaceAllString(lowStr, "")
re := regexp.MustCompile(`[^a-z0-9_\\-]+`)
//去掉非法字符
removeSpecial := re.ReplaceAllString(lowStr, "")
//任务名以数字字母开头,去掉非法前缀
re = regexp.MustCompile(`^[_\\-]+`)
return re.ReplaceAllString(removeSpecial, "")

}

func CloudBrainNew(ctx *context.Context) {
func cloudBrainNewDataPrepare(ctx *context.Context) error{
ctx.Data["PageIsCloudBrain"] = true

t := time.Now()
var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
ctx.Data["job_name"] = jobName
@@ -103,7 +108,7 @@ func CloudBrainNew(ctx *context.Context) {
result, err := cloudbrain.GetImages()
if err != nil {
ctx.Data["error"] = err.Error()
log.Error("cloudbrain.GetImages failed:", err.Error(), ctx.Data["msgID"])
log.Error("cloudbrain.GetImages failed:", err.Error(), ctx.Data["MsgID"])
}

for i, payload := range result.Payload.ImageInfo {
@@ -119,7 +124,7 @@ func CloudBrainNew(ctx *context.Context) {
resultPublic, err := cloudbrain.GetPublicImages()
if err != nil {
ctx.Data["error"] = err.Error()
log.Error("cloudbrain.GetPublicImages failed:", err.Error(), ctx.Data["msgID"])
log.Error("cloudbrain.GetPublicImages failed:", err.Error(), ctx.Data["MsgID"])
}

for i, payload := range resultPublic.Payload.ImageInfo {
@@ -134,8 +139,8 @@ func CloudBrainNew(ctx *context.Context) {

attachs, err := models.GetAllUserAttachments(ctx.User.ID)
if err != nil {
ctx.ServerError("GetAllUserAttachments failed:", err)
return
log.Error("GetAllUserAttachments failed: %v", err, ctx.Data["MsgID"])
return err
}

ctx.Data["attachments"] = attachs
@@ -162,6 +167,16 @@ func CloudBrainNew(ctx *context.Context) {
ctx.Data["resource_specs"] = cloudbrain.ResourceSpecs.ResourceSpec
ctx.Data["snn4imagenet_path"] = cloudbrain.Snn4imagenetMountPath
ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled

return nil
}

func CloudBrainNew(ctx *context.Context) {
err := cloudBrainNewDataPrepare(ctx)
if err != nil {
ctx.ServerError("get new cloudbrain info failed", err)
return
}
ctx.HTML(200, tplCloudBrainNew)
}

@@ -177,7 +192,8 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
resourceSpecId := form.ResourceSpecId

if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) {
log.Error("jobtype error:", jobType, ctx.Data["msgID"])
log.Error("jobtype error:", jobType, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form)
return
}
@@ -185,11 +201,13 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
_, err := models.GetCloudbrainByName(jobName)
if err == nil {
log.Error("the job name did already exist", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("the job name did already exist", tplCloudBrainNew, &form)
return
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainNew, &form)
return
}
@@ -200,6 +218,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath
err = os.MkdirAll(modelPath, os.ModePerm)
if err != nil {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form)
return
}
@@ -223,6 +242,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {

err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue, resourceSpecId)
if err != nil {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form)
return
}
@@ -397,6 +417,38 @@ func CloudBrainShowModels(ctx *context.Context) {
ctx.HTML(200, tplCloudBrainShowModels)
}

func GetPublicImages(ctx *context.Context) {

getImages(ctx, cloudbrain.Public)

}

func GetCustomImages(ctx *context.Context) {

getImages(ctx, cloudbrain.Custom)

}

func getImages(ctx *context.Context, imageType string) {
log.Info("Get images begin")

page := ctx.QueryInt("page")
size := ctx.QueryInt("size")
name := ctx.Query("name")
getImagesResult, err := cloudbrain.GetImagesPageable(page, size, imageType, name)
if err != nil {
log.Error("Can not get images:%v", err)
ctx.JSON(http.StatusOK, models.GetImagesPayload{
Count: 0,
TotalPages: 0,
ImageInfo: []*models.ImageInfo{},
})
} else {
ctx.JSON(http.StatusOK, getImagesResult.Payload)
}
log.Info("Get images end")
}

func getModelDirs(jobName string, parentDir string) (string, error) {
var req string
modelActualPath := setting.JobPath + jobName + "/model/"
@@ -413,7 +465,7 @@ func CloudBrainDownloadModel(ctx *context.Context) {
parentDir := ctx.Query("parentDir")
fileName := ctx.Query("fileName")
jobName := ctx.Query("jobName")
filePath := "jobs/" +jobName + "/model/" + parentDir
filePath := "jobs/" + jobName + "/model/" + parentDir
url, err := storage.Attachments.PresignedGetURL(filePath, fileName)
if err != nil {
log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"])


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

@@ -6,13 +6,14 @@ package routes

import (
"bytes"
"code.gitea.io/gitea/routers/secure"
"encoding/gob"
"net/http"
"path"
"text/template"
"time"

"code.gitea.io/gitea/routers/secure"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context"
@@ -313,11 +314,14 @@ 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("/repos", routers.ExploreRepos)
m.Get("/datasets", routers.ExploreDatasets)
m.Get("/users", routers.ExploreUsers)
m.Get("/organizations", routers.ExploreOrganizations)
m.Get("/code", routers.ExploreCode)
m.Get("/images", routers.ExploreImages)
}, ignSignIn)
m.Combo("/install", routers.InstallInit).Get(routers.Install).
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)


+ 2
- 2
services/mailer/mail.go View File

@@ -59,7 +59,7 @@ func SendTestMail(email string) error {
func SendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) {
data := map[string]interface{}{
"DisplayName": u.DisplayName(),
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, language),
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, "en-US"),
"ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, language),
"Code": code,
}
@@ -97,7 +97,7 @@ func SendResetPasswordMail(locale Locale, u *models.User) {
func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAddress) {
data := map[string]interface{}{
"DisplayName": u.DisplayName(),
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale.Language()),
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, "en-US"),
"Code": u.GenerateEmailActivateCode(email.Email),
"Email": email.Email,
}


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

@@ -28,6 +28,7 @@
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}
@@ -42,6 +43,7 @@
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
</div>
</div>
{{else if .IsLandingPageExplore}}


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

@@ -28,6 +28,7 @@
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}
@@ -42,6 +43,7 @@
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
</div>
</div>
{{else if .IsLandingPageExplore}}


+ 7
- 0
templates/explore/images.tmpl View File

@@ -0,0 +1,7 @@
{{template "base/head" .}}
<div id="images">

</div>

{{template "base/footer" .}}

+ 420
- 0
web_src/js/components/Images.vue View File

@@ -0,0 +1,420 @@
<template>
<div>
<div class="header-wrapper">
<div class="ui container">
<el-row class="image_text">
<h1>云脑镜像</h1>
</el-row>
</div>
</div>
<div class="ui container" id="header">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="公共镜像(云脑1)" name="first" v-loading="loading">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入内容" v-model="search" class="input-with-select">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-row style="margin-top:15px;">

<el-table
:data="tableData"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:default-sort="{prop:'createtime',order:'descending'}">
<el-table-column
label="镜像名称"
width="350"
align="center"
prop="name"
sortable
>
<template slot-scope="scope">
<a class="text-over" style="cursor:default;color:#426290" :title="scope.row.name">{{ scope.row.name }}</a>
</template>
</el-table-column>
<el-table-column
label="文件路径/镜像描述"
width="450"
align="center"
>
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="点击复制文件路径" placement="top">
<a class="text-over" style="display:block;" @click="copyUrl(scope.row.place)">{{ scope.row.place }}</a>
</el-tooltip>
<span class="text-over" :title="scope.row.description">{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column
prop="provider"
label="提供者"
width="120"
align="center"
sortable>
</el-table-column>
<el-table-column
prop="createtime"
label="创建时间"
align="center"
sortable>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="[5,10,20]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNum">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="自定义镜像(云脑1)" name="second" v-loading="loading1">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入内容" v-model="search" class="input-with-select">
<el-button slot="append" id="success" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-row style="margin-top:15px;">

<el-table
:data="tableData1"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:default-sort="{prop:'createtime',order:'descending'}">
<el-table-column
label="镜像名称"
width="350"
align="center"
prop="name"
sortable
>
<template slot-scope="scope">
<a class="text-over" :title="scope.row.name" style="cursor:default;color:#426290">{{ scope.row.name }}</a>
</template>
</el-table-column>
<el-table-column
label="文件路径/镜像描述"
width="450"
align="center"
>
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="点击复制文件路径" placement="top">
<a class="text-over" style="display:block;" @click="copyUrl(scope.row.place)">{{ scope.row.place }}</a>
</el-tooltip>
<span class="text-over" :title="scope.row.description">{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column
prop="provider"
label="提供者"
width="120"
align="center"
sortable>
</el-table-column>
<el-table-column
prop="createtime"
label="创建时间"
align="center"
sortable>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChange1"
@current-change="handleCurrentChange1"
:current-page="currentPage1"
:page-size="pageSize1"
:page-sizes="[5,10,20]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNum1">
</el-pagination>
</div>

</el-tab-pane>
<el-tab-pane label="公共镜像(云脑2)" name="third">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入内容" v-model="search" class="input-with-select">
<el-button slot="append" id="success" icon="el-icon-search">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-empty :image-size="200"></el-empty>
</el-tab-pane>
<el-tab-pane label="自定义镜像(云脑2)" name="fourth">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入内容" v-model="search" class="input-with-select">
<el-button slot="append" id="success" icon="el-icon-search">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-empty :image-size="200"></el-empty>

</el-tab-pane>
</el-tabs>
</div>
</div>

</template>

<script>

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;




export default {
components: {
},
data() {
return {
activeName: 'first',
search:'',
currentPage:1,
pageSize:10,
totalNum:0,
params:{page:1,size:10,name:''},
tableData: [],
loading:false,

currentPage1:1,
pageSize1:10,
totalNum1:0,
params1:{page:1,size:10,name:''},
tableData1: [],
loading1:false
};
},
methods: {
handleClick(tab, event) {
if(tab.name=="first"){
this.getImageList()
}
if(tab.name=="second"){
this.getImageList1()
}
},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
handleSizeChange(val){
this.params.size = val
this.getImageList()


},
handleCurrentChange(val){
console.log(val)
this.params.page = val
this.getImageList()

},
handleSizeChange1(val){
this.params1.size = val
this.getImageList1()


},
handleCurrentChange1(){
this.params1.page = val
this.getImageList1()

},
getImageList(){
this.loading = true
this.$axios.get('/explore/images/public',{
params:this.params
}).then((res)=>{
this.totalNum = res.data.count
this.tableData = res.data.rows
this.loading = false
})
},

getImageList1(){
this.loading1 = true
this.$axios.get('/explore/images/custom',{
params:this.params1
}).then((res)=>{
this.totalNum1 = res.data.count
this.tableData1 = res.data.rows
this.loading1 = false
})
},
copyUrl(url){
console.log(url)
const cInput = document.createElement('input')
cInput.value = url
document.body.appendChild(cInput)
cInput.select()
document.execCommand('Copy')
cInput.remove()

},
searchName(){
if(this.activeName=='first'){
this.params.name = this.search
this.getImageList()
}
if(this.activeName=='second'){
this.params1.name = this.search
this.getImageList1()
}
}

},
watch:{
search(val){
if(!val && this.activeName=='first'){
this.params.name = val
this.getImageList()
}
if(!val && this.activeName=='second'){
this.params1.name = val
this.getImageList1()
}
}

},
mounted() {
this.getImageList()
},
created() {
}

};
</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;
}
#success{
background-color: #4093ff;
color: white;
}
.text-over{
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
</style>

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

@@ -35,6 +35,7 @@ import {createCodeEditor} from './features/codeeditor.js';
import MinioUploader from './components/MinioUploader.vue';
import ObsUploader from './components/ObsUploader.vue';
import EditAboutInfo from './components/EditAboutInfo.vue';
import Images from './components/Images.vue'

Vue.use(ElementUI);
Vue.prototype.$axios = axios;
@@ -2966,6 +2967,7 @@ $(document).ready(async () => {
initVueUploader();
initObsUploader();
initVueEditAbout();
initVueImages();
initTeamSettings();
initCtrlEnterSubmit();
initNavbarContentToggle();
@@ -3653,7 +3655,7 @@ function initVueUploader() {

function initVueEditAbout() {
const el = document.getElementById('about-desc');
console.log(el)
if (!el) {
return;
}
@@ -3664,6 +3666,21 @@ function initVueEditAbout() {
});
}


function initVueImages() {
const el = document.getElementById('images');
console.log("el",el)
if (!el) {
return;
}

new Vue({
el: '#images',
render: h => h(Images)
});
}

// 新增
function initObsUploader() {
const el = document.getElementById('obsUploader');


Loading…
Cancel
Save