| @@ -92,6 +92,7 @@ func NewFuncMap() []template.FuncMap { | |||
| "Safe": Safe, | |||
| "SafeJS": SafeJS, | |||
| "Str2html": Str2html, | |||
| "subOne": subOne, | |||
| "TimeSince": timeutil.TimeSince, | |||
| "TimeSinceUnix": timeutil.TimeSinceUnix, | |||
| "TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||
| @@ -443,7 +444,10 @@ func SafeJS(raw string) template.JS { | |||
| func Str2html(raw string) template.HTML { | |||
| return template.HTML(markup.Sanitize(raw)) | |||
| } | |||
| // | |||
| func subOne(length int)int{ | |||
| return length-1 | |||
| } | |||
| // Escape escapes a HTML string | |||
| func Escape(raw string) string { | |||
| return html.EscapeString(raw) | |||
| @@ -0,0 +1,22 @@ | |||
| {{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="alert"></div> | |||
| <div class="admin user"> | |||
| {{template "admin/navbar" .}} | |||
| <div id="images-admin"> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -18,7 +18,7 @@ | |||
| <div class="ui grid" > | |||
| <div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;"> | |||
| {{template "admin/cloudbrain/search" .}} | |||
| <div class="ui ten wide column right aligned" style="margin: 1rem 0;"> | |||
| <div class="ui six wide column right aligned" style="margin: 1rem 0;"> | |||
| <a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;" href="/admin/cloudbrains/download"><i class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a> | |||
| </div> | |||
| <div class="ui sixteen wide column"> | |||
| @@ -6,7 +6,7 @@ | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <div class="ui six wide column" style="margin: 1rem 0;" id="adminCloud"> | |||
| <div class="ui ten wide column" style="margin: 1rem 0;" id="adminCloud"> | |||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
| <div class="default text" style="color: rgba(0,0,0,.87);">{{.i18n.Tr "admin.cloudbrain.all_task_types"}}</div> | |||
| <i class="dropdown icon"></i> | |||
| @@ -17,6 +17,9 @@ | |||
| <a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains"> | |||
| 云脑任务 | |||
| </a> | |||
| <a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/images"> | |||
| 云脑镜像 | |||
| </a> | |||
| <a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks"> | |||
| {{.i18n.Tr "admin.hooks"}} | |||
| </a> | |||
| @@ -1,120 +1,121 @@ | |||
| <style> | |||
| .label_color{ | |||
| color:#505559 !important; | |||
| width: 6% !important; | |||
| text-align: center; | |||
| } | |||
| </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> | |||
| .label_color{ | |||
| color:#505559 !important; | |||
| width: 6% !important; | |||
| text-align: center; | |||
| } | |||
| </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 class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="alert"></div> | |||
| <div class="ui container"> | |||
| <div> | |||
| <h4 class="ui top attached header"> | |||
| {{.i18n.Tr "repo.submit_image"}} | |||
| </h4> | |||
| <div class="submit-image-tmplvalue" style="display: none;" data-link="/image/{{$.Image.ID}}"></div> | |||
| <div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;"> | |||
| <form class="ui form" id="form_image"> | |||
| {{.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> | |||
| CPU/GPU | |||
| </div> | |||
| <input type="hidden" value="{{.Type}}" 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: 0.5rem;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</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="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> | |||
| </div> | |||
| <div class="inline field" style="display: flex;align-items: center;"> | |||
| {{$x := (len .Image.Topics) -1}} | |||
| <label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label> {{$x}} | |||
| <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 ((len $.Image.Topics))}},{{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: 0.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"> | |||
| <div class="ui radio checkbox"> | |||
| <input type="radio" name="isPrivate" checked="{{if not .Image.IsPrivate}} checked {{end}}" value="false"> | |||
| <label>{{.i18n.Tr "org.settings.visibility.public"}}</label> | |||
| </div> | |||
| </div> | |||
| <div class="field" style="flex: 0.15;"> | |||
| <div class="ui radio checkbox" > | |||
| <input type="radio" name="isPrivate" checked="{{if .Image.IsPrivate}} checked {{end}}" value="true"> | |||
| <label>{{.i18n.Tr "home.show_private"}}</label> | |||
| </div> | |||
| </div> | |||
| <div class="field"> | |||
| <span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span> | |||
| </div> | |||
| </div> | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="alert"></div> | |||
| <div class="ui container"> | |||
| <div> | |||
| <h4 class="ui top attached header"> | |||
| {{.i18n.Tr "repo.submit_image"}} | |||
| </h4> | |||
| <div class="submit-image-tmplvalue" style="display: none;" data-link="/image/{{$.Image.ID}}"></div> | |||
| <div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;"> | |||
| <div class="ui form" id="form_image"> | |||
| {{.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> | |||
| CPU/GPU | |||
| </div> | |||
| <input type="hidden" value="{{.Type}}" 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: 0.5rem;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</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="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> | |||
| </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> | |||
| <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: 0.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"> | |||
| <div class="ui radio checkbox"> | |||
| <input type="radio" name="isPrivate" {{if not .Image.IsPrivate}} checked {{end}} value="false"> | |||
| <label>{{.i18n.Tr "org.settings.visibility.public"}}</label> | |||
| </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"> | |||
| {{.i18n.Tr "repo.cloudbrain.commit_image"}} | |||
| </button> | |||
| <a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
| </div> | |||
| <div class="field" style="flex: 0.15;"> | |||
| <div class="ui radio checkbox" > | |||
| <input type="radio" name="isPrivate" {{if .Image.IsPrivate}} checked {{end}} value="true"> | |||
| <label>{{.i18n.Tr "home.show_private"}}</label> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <div class="field"> | |||
| <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"> | |||
| {{.i18n.Tr "repo.cloudbrain.commit_image"}} | |||
| </button> | |||
| <a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| console.log({{$.Link}},{{$.PageFrom}},{{$.Image}}) | |||
| console.log({{.Image.Description}}) | |||
| let url_href = {{$.Link}} | |||
| function submitImage(){ | |||
| console.log($('#form_image').serialize()) | |||
| $.ajax({ | |||
| url:url_href, | |||
| type:'POST', | |||
| data:$('#form_image').serialize(), | |||
| success:function(res){ | |||
| console.log("res",res) | |||
| }, | |||
| error: function(xhr){ | |||
| // 隐藏 loading | |||
| // 只有请求不正常(状态码不为200)才会执行 | |||
| // $('.ui.error.message').text(xhr.responseText) | |||
| // $('.ui.error.message').css('display','block') | |||
| }, | |||
| complete:function(xhr){ | |||
| // $("#mask").css({"display":"none","z-index":"1"}) | |||
| } | |||
| }) | |||
| } | |||
| </script> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| console.log({{$.Link}},{{$.PageFrom}},{{$.Image}}) | |||
| console.log({{.Image.IsPrivate}}) | |||
| let url_href = {{$.Link}} | |||
| function submitImage(){ | |||
| console.log($('#form_image').serialize()) | |||
| $.ajax({ | |||
| url:url_href, | |||
| type:'POST', | |||
| data:$('#form_image').serialize(), | |||
| success:function(res){ | |||
| console.log("res",res) | |||
| }, | |||
| error: function(xhr){ | |||
| // 隐藏 loading | |||
| // 只有请求不正常(状态码不为200)才会执行 | |||
| // $('.ui.error.message').text(xhr.responseText) | |||
| // $('.ui.error.message').css('display','block') | |||
| }, | |||
| complete:function(xhr){ | |||
| // $("#mask").css({"display":"none","z-index":"1"}) | |||
| } | |||
| }) | |||
| } | |||
| </script> | |||
| @@ -25,7 +25,7 @@ | |||
| </h4> | |||
| <div class="submit-image-tmplvalue" style="display: none;" data-link="{{$.Link}}"></div> | |||
| <div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;"> | |||
| <form class="ui form" id="form_image"> | |||
| <div class="ui form" id="form_image"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="inline field"> | |||
| <label class="label_color" for="">{{$.i18n.Tr "dataset.dataset_available_clusters"}}</label> | |||
| @@ -78,7 +78,7 @@ | |||
| </button> | |||
| <a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -11,7 +11,7 @@ | |||
| <el-tabs v-model="activeName" @tab-click="handleClick"> | |||
| <el-tab-pane label="公开镜像" name="first" v-loading="loadingPublic"> | |||
| <template v-if="tableDataPublic.length!==0"> | |||
| <div class="ui sixteen wide column"> | |||
| <!-- <div class="ui two column stackable grid"> | |||
| <div class="column"> | |||
| @@ -22,16 +22,21 @@ | |||
| </el-input> | |||
| </div> | |||
| </div> --> | |||
| <div class="ui eight wide column"> | |||
| </div> | |||
| <div class="ui three wide column"> | |||
| </div> | |||
| <div class="ui five wide column"> | |||
| </div> | |||
| </div> | |||
| <el-row style="align-items: center;display: flex;"> | |||
| <el-col :span="12"> | |||
| <div> | |||
| <el-checkbox v-model="checked">仅显示平台推荐</el-checkbox> | |||
| </div> | |||
| </el-col> | |||
| <el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col> | |||
| <el-col :span="8"> | |||
| <div> | |||
| <el-input placeholder="搜镜像名称/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| </el-input> | |||
| </div> | |||
| </el-col> | |||
| </el-row> | |||
| <el-row style="margin-top:15px;"> | |||
| <el-table | |||
| @@ -67,13 +72,13 @@ | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="type" | |||
| prop="cloudbrainType" | |||
| label="可用集群" | |||
| min-width="10%" | |||
| align="center" | |||
| > | |||
| <template slot-scope="scope"> | |||
| {{scope.row.type | transformType}} | |||
| {{scope.row.cloudbrainType | transformType}} | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| @@ -128,20 +133,43 @@ | |||
| </el-pagination> | |||
| </div> | |||
| </template> | |||
| <el-empty v-else :image-size="200"></el-empty> | |||
| <template v-else> | |||
| <el-row style="align-items: center;display: flex;"> | |||
| <el-col :span="12"> | |||
| <div> | |||
| <el-checkbox v-model="checked">备选项</el-checkbox> | |||
| </div> | |||
| </el-col> | |||
| <el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col> | |||
| <el-col :span="8"> | |||
| <div> | |||
| <el-input placeholder="搜镜像名称/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| </el-input> | |||
| </div> | |||
| </el-col> | |||
| </el-row> | |||
| <el-empty :image-size="200"></el-empty> | |||
| </template> | |||
| </el-tab-pane> | |||
| <el-tab-pane label="我的镜像" name="second" v-loading="loadingCustom"> | |||
| <template v-if="tableDataCustom.length!==0"> | |||
| <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" @keyup.enter.native="searchName()"> | |||
| <el-button slot="append" id="success" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| <el-row style="align-items: center;display: flex;"> | |||
| <el-col :span="12"> | |||
| <div style="visibility: hidden;"> | |||
| TODO | |||
| </div> | |||
| </el-col> | |||
| <el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col> | |||
| <el-col :span="8"> | |||
| <div> | |||
| <el-input placeholder="搜镜像名称/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </el-col> | |||
| </el-row> | |||
| <el-row style="margin-top:15px;"> | |||
| <el-table | |||
| :data="tableDataCustom" | |||
| @@ -176,13 +204,13 @@ | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="type" | |||
| prop="cloudbrainType" | |||
| label="可用集群" | |||
| min-width="10%" | |||
| align="center" | |||
| > | |||
| <template slot-scope="scope"> | |||
| {{scope.row.type | transformType}} | |||
| {{scope.row.cloudbrainType | transformType}} | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| @@ -246,19 +274,43 @@ | |||
| </el-pagination> | |||
| </div> | |||
| </template> | |||
| <el-empty v-else :image-size="200"></el-empty> | |||
| <template v-else> | |||
| <el-row style="align-items: center;display: flex;"> | |||
| <el-col :span="12"> | |||
| <div style="visibility: hidden;"> | |||
| TODO | |||
| </div> | |||
| </el-col> | |||
| <el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col> | |||
| <el-col :span="8"> | |||
| <div> | |||
| <el-input placeholder="搜镜像名称/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| </el-input> | |||
| </div> | |||
| </el-col> | |||
| </el-row> | |||
| <el-empty :image-size="200"></el-empty> | |||
| </template> | |||
| </el-tab-pane> | |||
| <el-tab-pane label="我收藏的镜像" name="third"> | |||
| <template v-if="tableDataStar.length!==0"> | |||
| <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-row style="align-items: center;display: flex;"> | |||
| <el-col :span="12"> | |||
| <div style="visibility: hidden;"> | |||
| TODO | |||
| </div> | |||
| </el-col> | |||
| <el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col> | |||
| <el-col :span="8"> | |||
| <div> | |||
| <el-input placeholder="搜镜像名称/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </el-col> | |||
| </el-row> | |||
| <el-row style="margin-top:15px;"> | |||
| <el-table | |||
| @@ -294,13 +346,13 @@ | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="type" | |||
| prop="cloudbrainType" | |||
| label="可用集群" | |||
| min-width="10%" | |||
| align="center" | |||
| > | |||
| <template slot-scope="scope"> | |||
| {{scope.row.type | transformType}} | |||
| {{scope.row.cloudbrainType | transformType}} | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| @@ -356,7 +408,24 @@ | |||
| </div> | |||
| </template> | |||
| <el-empty v-else :image-size="200"></el-empty> | |||
| <template v-else> | |||
| <el-row style="align-items: center;display: flex;"> | |||
| <el-col :span="12"> | |||
| <div style="visibility: hidden;"> | |||
| TODO | |||
| </div> | |||
| </el-col> | |||
| <el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col> | |||
| <el-col :span="8"> | |||
| <div> | |||
| <el-input placeholder="搜镜像名称/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||
| </el-input> | |||
| </div> | |||
| </el-col> | |||
| </el-row> | |||
| <el-empty :image-size="200"></el-empty> | |||
| </template> | |||
| </el-tab-pane> | |||
| </el-tabs> | |||
| </div> | |||
| @@ -380,6 +449,7 @@ export default { | |||
| return { | |||
| activeName: 'first', | |||
| search:'', | |||
| checked:false, | |||
| currentPagePublic:1, | |||
| pageSizePublic:15, | |||
| totalNumPublic:0, | |||
| @@ -405,7 +475,9 @@ export default { | |||
| }, | |||
| methods: { | |||
| handleClick(tab, event) { | |||
| this.search = '' | |||
| if(tab.name=="first"){ | |||
| this.paramsPublic.q = '' | |||
| this.getImageListPublic() | |||
| } | |||
| if(tab.name=="second"){ | |||
| @@ -584,15 +656,15 @@ export default { | |||
| }, | |||
| watch:{ | |||
| search(val){ | |||
| if(!val && this.activeName=='first'){ | |||
| if(this.activeName=='first'){ | |||
| this.paramsPublic.q = val | |||
| this.getImageListPublic() | |||
| } | |||
| if(!val && this.activeName=='second'){ | |||
| if(this.activeName=='second'){ | |||
| this.paramsCustom.q = val | |||
| this.getImageListCustom() | |||
| } | |||
| if(!val && this.activeName=='third'){ | |||
| if(this.activeName=='third'){ | |||
| this.paramsStar.q = val | |||
| this.getImageListStar() | |||
| } | |||
| @@ -0,0 +1,408 @@ | |||
| <template> | |||
| <div> | |||
| <div class="ui container" style="width: 80%;"> | |||
| <div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;"> | |||
| <div class="ui attached segment"> | |||
| <div class="ui form ignore-dirty"> | |||
| <div class="ui fluid action input"> | |||
| <input type="text" v-model="search" @keyup.enter="searchName()"> | |||
| <button class="ui blue button" @click="searchName()">搜索</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="ui ten wide column" style="padding-top: 1rem;padding-left: 1rem"> | |||
| <el-checkbox v-model="checked">仅显示平台推荐</el-checkbox> | |||
| </div> | |||
| <el-row style="margin-top:15px;"> | |||
| <el-table | |||
| :data="tableDataCustom" | |||
| style="width: 100%" | |||
| :header-cell-style="tableHeaderStyle" | |||
| > | |||
| <el-table-column | |||
| label="镜像名称" | |||
| 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> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| label="镜像描述" | |||
| min-width="28%" | |||
| 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">{{topic}}</span> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="cloudbrainType" | |||
| label="可用集群" | |||
| min-width="10%" | |||
| align="center" | |||
| > | |||
| <template slot-scope="scope"> | |||
| {{scope.row.cloudbrainType | transformType}} | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="isPrivate" | |||
| label="状态" | |||
| min-width="8%" | |||
| align="center" | |||
| > | |||
| <template slot-scope="scope"> | |||
| {{scope.row.isPrivate | transformPravite}} | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="creator" | |||
| label="创建者" | |||
| min-width="8%" | |||
| align="center" | |||
| > | |||
| <template slot-scope="scope"> | |||
| <a :href="'/' + scope.row.userName" :title="scope.row.userName"> | |||
| <img :src="scope.row.relAvatarLink" class="ui avatar image"> | |||
| </a> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| prop="createdUnix" | |||
| label="创建时间" | |||
| align="center" | |||
| min-width="14%" | |||
| > | |||
| <template slot-scope="scope"> | |||
| {{scope.row.createdUnix | transformTimestamp}} | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column | |||
| align="center" | |||
| min-width="21%" | |||
| label="操作" | |||
| > | |||
| <template slot-scope="scope"> | |||
| <div style="display: flex;justify-content: flex-end;align-items: center;"> | |||
| <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:#0366d6;cursor:pointer;" v-if="scope.row.type==5" @click="unSetRecommend(scope.$index,scope.row.id)">取消推荐</span> | |||
| <span style="padding: 0 1rem;color:#0366d6;cursor:pointer;" v-else @click="setRecommend(scope.$index,scope.row.id)">设为推荐</span> | |||
| <span style="padding: 0 1rem;color:#0366d6;cursor:pointer;" @click="copyUrl(scope.row.place)">复制地址</span> | |||
| <div style="padding-left:1rem;cursor:pointer;"> | |||
| <el-dropdown size="medium"> | |||
| <span class="el-dropdown-link"> | |||
| 更多<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)">编辑</el-dropdown-item> | |||
| <el-dropdown-item style="color: red;" @click.native="deleteImage(scope.row.id)">删除</el-dropdown-item> | |||
| </el-dropdown-menu> | |||
| </el-dropdown> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| </el-table> | |||
| </el-row> | |||
| <div class="ui container" style="padding:2rem 0;text-align:center"> | |||
| <el-pagination | |||
| background | |||
| @size-change="handleSizeChangeCustom" | |||
| @current-change="handleCurrentChangeCustom" | |||
| :current-page="currentPageCustom" | |||
| :page-size="pageSizeCustom" | |||
| :page-sizes="[5,15,20]" | |||
| layout="total, sizes, prev, pager, next, jumper" | |||
| :total="totalNumCustom"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; | |||
| export default { | |||
| components: { | |||
| }, | |||
| data() { | |||
| return { | |||
| search:'', | |||
| checked:false, | |||
| currentPageCustom:1, | |||
| pageSizeCustom:15, | |||
| totalNumCustom:0, | |||
| paramsCustom:{page:1,pageSize:15,q:''}, | |||
| tableDataCustom: [], | |||
| starCustom:[], | |||
| loadingCustom:false, | |||
| }; | |||
| }, | |||
| 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 | |||
| this.$axios.get('/admin/images/data',{ | |||
| params:this.paramsCustom | |||
| }).then((res)=>{ | |||
| console.log("res",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){ | |||
| this.$axios.delete('/image/'+id).then((res)=>{ | |||
| console.log(res) | |||
| this.getImageListCustom() | |||
| }) | |||
| }, | |||
| eidtImage(id){ | |||
| location.href = `/image/${id}/imageSquare` | |||
| }, | |||
| imageStar(index,id,isStar){ | |||
| if(isStar){ | |||
| this.$axios.put(`/image/${id}/action/unstar`).then((res)=>{ | |||
| console.log(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)=>{ | |||
| console.log(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/images/${id}/action/recommend`).then((res)=>{ | |||
| console.log(res) | |||
| this.tableDataCustom[index].type = 5 | |||
| }) | |||
| }, | |||
| unSetRecommend(index,id){ | |||
| this.$axios.put(`/admin/images/${id}/action/unrecommend`).then((res)=>{ | |||
| console.log(res) | |||
| this.tableDataCustom[index].type = 0 | |||
| }) | |||
| } | |||
| }, | |||
| filters:{ | |||
| clearP(value){ | |||
| if(!value) return '' | |||
| const reg = /\<\/?p\>/g; | |||
| value = value.replace(reg,'') | |||
| return value | |||
| }, | |||
| transformType(val){ | |||
| if(val==0){ | |||
| return "GPU" | |||
| } | |||
| }, | |||
| transformPravite(val){ | |||
| if(val){ | |||
| return "私有" | |||
| }else{ | |||
| return "公开" | |||
| } | |||
| }, | |||
| transformTimestamp(timestamp){ | |||
| // let a = new Date(timestamp).getTime(); | |||
| 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; | |||
| // console.log('dateString', dateString); // > dateString 2021-07-06 14:23 | |||
| return dateString; | |||
| }, | |||
| }, | |||
| watch:{ | |||
| search(val){ | |||
| this.paramsCustom.q = val | |||
| this.getImageListCustom() | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.getImageListCustom() | |||
| }, | |||
| 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; | |||
| } | |||
| /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 | |||
| } | |||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active { | |||
| background-color: #5bb973; | |||
| color: #FFF; | |||
| } | |||
| /deep/ .el-pagination.is-background .el-pager li.active { | |||
| color: #fff; | |||
| cursor: default; | |||
| } | |||
| /deep/ .el-pagination.is-background .el-pager li:hover { | |||
| color: #5bb973; | |||
| } | |||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover { | |||
| color: #5bb973; | |||
| } | |||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover { | |||
| background-color: #5bb973; | |||
| color: #FFF; | |||
| } | |||
| /deep/ .el-pager li.active { | |||
| color: #08C0B9; | |||
| cursor: default; | |||
| } | |||
| /deep/ .el-pagination .el-pager li:hover { | |||
| color: #08C0B9; | |||
| } | |||
| /deep/ .el-pagination .el-pager li:not(.disabled):hover { | |||
| color: #08C0B9; | |||
| } | |||
| /* /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active{ | |||
| background-color: #5bb973; | |||
| color: #000; | |||
| } */ | |||
| /* /deep/ .el-pager li:hover{ | |||
| 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: #666; | |||
| stroke-width: 2; | |||
| fill: #fff | |||
| } | |||
| .stars_active{ | |||
| fill: #FA8C16 !important; | |||
| stroke:#FA8C16 !important | |||
| } | |||
| </style> | |||
| @@ -1,4 +1,5 @@ | |||
| import Images from '../components/Images.vue'; | |||
| import adminImages from '../components/adminImages.vue'; | |||
| import Vue from 'vue'; | |||
| export default async function initImage(){ | |||
| function validate() { | |||
| @@ -27,16 +28,14 @@ export default async function initImage(){ | |||
| } | |||
| }) | |||
| } | |||
| function $params(obj) { | |||
| var str = []; | |||
| for (var p in obj) { | |||
| str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); | |||
| } | |||
| return str.join("&"); | |||
| } | |||
| function initDropdown(){ | |||
| // const editLable = [] | |||
| // $('#dropdown_image').find('.ui.label.transition.visible').each((index,item)=>{ | |||
| // console.log(index,item) | |||
| // console.log(item.getAttribute("data-value")) | |||
| // editLable.push(item.getAttribute("data-value")) | |||
| // console.log(editLable.join(',')) | |||
| // $("input[name='topics']").val(editLable.join(',')) | |||
| // }) | |||
| $('#dropdown_image') | |||
| .dropdown({ | |||
| allowAdditions: true, | |||
| @@ -50,7 +49,7 @@ export default async function initImage(){ | |||
| if(!query){ | |||
| $('#course_label_item').empty() | |||
| }else{ | |||
| $.get(`/api/v1/topics/search?q=${query}`,(data)=>{ | |||
| $.get(`/api/v1/image/topics/search?q=${query}`,(data)=>{ | |||
| if(data.topics.length!==0){ | |||
| let html='' | |||
| $('#course_label_item').empty() | |||
| @@ -68,15 +67,40 @@ export default async function initImage(){ | |||
| initDropdown() | |||
| let link = $('.submit-image-tmplvalue').data('link') | |||
| $('.ui.create_image.green.button').click(()=>{ | |||
| console.log($('#form_image').serialize()) | |||
| let pattenTag = new RegExp(/^[A-Za-z0-9_.-]{1,100}[A-Za-z0-9_.]$/) | |||
| if(!pattenTag.test($("input[name='tag']").val())){ | |||
| $("input[name='tag']").parent().addClass('error') | |||
| return false | |||
| } | |||
| if(!$("textarea[name='description']").val()){ | |||
| $("textarea[name='description']").parent().addClass('error') | |||
| return false | |||
| } | |||
| $("#mask").css({"display":"block","z-index":"999"}) | |||
| const postData = { | |||
| _csrf:$("input[name='_csrf']").val(), | |||
| tag:$("input[name='tag']").val(), | |||
| description:$("textarea[name='description']").val(), | |||
| type:$("input[name='type']").val(), | |||
| isPrivate:$("input[name='isPrivate']").val(), | |||
| topics:$("input[name='topics']").val(), | |||
| id:$("input[name='id']").val() | |||
| } | |||
| console.log(postData) | |||
| let formData = $params(postData) | |||
| console.log(formData) | |||
| $.ajax({ | |||
| url:link, | |||
| type:'POST', | |||
| data:$('#form_image').serialize(), | |||
| data:formData, | |||
| success:function(res){ | |||
| console.log("res",res) | |||
| if(res.Code===1){ | |||
| $('.alert').html(res.Message).removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
| }else if(res.Code==0){ | |||
| location.href = `${window.config.AppSubUrl}/explore/images` | |||
| } | |||
| }, | |||
| error: function(xhr){ | |||
| @@ -112,5 +136,19 @@ export default async function initImage(){ | |||
| render: h => h(Images) | |||
| }); | |||
| } | |||
| function initVueAdminImages() { | |||
| const el = document.getElementById('images-admin'); | |||
| console.log(el) | |||
| if (!el) { | |||
| return; | |||
| } | |||
| new Vue({ | |||
| el:el, | |||
| render: h => h(adminImages) | |||
| }); | |||
| } | |||
| initVueImages() | |||
| initVueAdminImages() | |||
| } | |||