增加用户系统界面管理、用户可以设置关联集群

This commit is contained in:
GoEdgeLab
2020-12-16 15:49:15 +08:00
parent 041ead3d7f
commit ee549b945d
30 changed files with 411 additions and 312 deletions

View File

@@ -9,17 +9,17 @@ import (
"reflect"
)
var sharedUIConfig *systemconfigs.AdminUIConfig = nil
var sharedAdminUIConfig *systemconfigs.AdminUIConfig = nil
const (
UISettingName = "adminUIConfig"
AdminUISettingName = "adminUIConfig"
)
func LoadUIConfig() (*systemconfigs.AdminUIConfig, error) {
func LoadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
locker.Lock()
defer locker.Unlock()
config, err := loadUIConfig()
config, err := loadAdminUIConfig()
if err != nil {
return nil, err
}
@@ -28,7 +28,7 @@ func LoadUIConfig() (*systemconfigs.AdminUIConfig, error) {
return &v, nil
}
func UpdateUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
func UpdateAdminUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
locker.Lock()
defer locker.Unlock()
@@ -41,48 +41,48 @@ func UpdateUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
return err
}
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
Code: UISettingName,
Code: AdminUISettingName,
ValueJSON: valueJSON,
})
if err != nil {
return err
}
sharedUIConfig = uiConfig
sharedAdminUIConfig = uiConfig
return nil
}
func loadUIConfig() (*systemconfigs.AdminUIConfig, error) {
if sharedUIConfig != nil {
return sharedUIConfig, nil
func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
if sharedAdminUIConfig != nil {
return sharedAdminUIConfig, nil
}
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return nil, err
}
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
Code: UISettingName,
Code: AdminUISettingName,
})
if err != nil {
return nil, err
}
if len(resp.ValueJSON) == 0 {
sharedUIConfig = defaultUIConfig()
return sharedUIConfig, nil
sharedAdminUIConfig = defaultAdminUIConfig()
return sharedAdminUIConfig, nil
}
config := &systemconfigs.AdminUIConfig{}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
sharedUIConfig = defaultUIConfig()
return sharedUIConfig, nil
sharedAdminUIConfig = defaultAdminUIConfig()
return sharedAdminUIConfig, nil
}
sharedUIConfig = config
return sharedUIConfig, nil
sharedAdminUIConfig = config
return sharedAdminUIConfig, nil
}
func defaultUIConfig() *systemconfigs.AdminUIConfig {
func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
return &systemconfigs.AdminUIConfig{
ProductName: "GoEdge",
AdminSystemName: "GoEdge管理员系统",

View File

@@ -0,0 +1,92 @@
package configloaders
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/logs"
"reflect"
)
var sharedUserUIConfig *systemconfigs.UserUIConfig = nil
const (
UserUISettingName = "userUIConfig"
)
func LoadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
locker.Lock()
defer locker.Unlock()
config, err := loadUserUIConfig()
if err != nil {
return nil, err
}
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.UserUIConfig)
return &v, nil
}
func UpdateUserUIConfig(uiConfig *systemconfigs.UserUIConfig) error {
locker.Lock()
defer locker.Unlock()
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return err
}
valueJSON, err := json.Marshal(uiConfig)
if err != nil {
return err
}
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
Code: UserUISettingName,
ValueJSON: valueJSON,
})
if err != nil {
return err
}
sharedUserUIConfig = uiConfig
return nil
}
func loadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
if sharedUserUIConfig != nil {
return sharedUserUIConfig, nil
}
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return nil, err
}
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
Code: UserUISettingName,
})
if err != nil {
return nil, err
}
if len(resp.ValueJSON) == 0 {
sharedUserUIConfig = defaultUserUIConfig()
return sharedUserUIConfig, nil
}
config := &systemconfigs.UserUIConfig{}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
sharedUserUIConfig = defaultUserUIConfig()
return sharedUserUIConfig, nil
}
sharedUserUIConfig = config
return sharedUserUIConfig, nil
}
func defaultUserUIConfig() *systemconfigs.UserUIConfig {
return &systemconfigs.UserUIConfig{
ProductName: "GoEdge",
UserSystemName: "GoEdge用户系统",
ShowOpenSourceInfo: true,
ShowVersion: true,
}
}

View File

@@ -17,6 +17,12 @@ func init() {
GetPost("/create", new(CreateAction)).
Post("/sync", new(SyncAction)).
Post("/checkChange", new(CheckChangeAction)).
// 只要登录即可访问的Action
EndHelpers().
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
Post("/options", new(OptionsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,30 @@
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type OptionsAction struct {
actionutils.ParentAction
}
func (this *OptionsAction) RunPost(params struct{}) {
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClusters(this.AdminContext(), &pb.FindAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.Clusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Success()
}

View File

@@ -78,7 +78,7 @@ func (this *PricesAction) formatBits(bits int64) string {
} else if bits < 1_000_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fTBPS", float64(bits)/1000/1000/1000/1000)
} else {
sizeHuman = fmt.Sprintf("%.2fPTBPS", float64(bits)/1000/1000/1000/1000/1000)
sizeHuman = fmt.Sprintf("%.2fPBPS", float64(bits)/1000/1000/1000/1000/1000)
}
return sizeHuman
}

View File

@@ -13,6 +13,4 @@ func (this *IndexAction) Init() {
func (this *IndexAction) RunGet(params struct{}) {
// TODO 暂时先跳转到账单页将来做成Dashboard
this.RedirectURL("/finance/bills")
this.Show()
}

View File

@@ -50,7 +50,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["token"] = stringutil.Md5(TokenSalt+timestamp) + timestamp
this.Data["from"] = params.From
config, err := configloaders.LoadUIConfig()
config, err := configloaders.LoadAdminUIConfig()
if err != nil {
this.ErrorPage(err)
return

View File

@@ -30,7 +30,8 @@ func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool)
var adminId = session.GetInt64("adminId")
if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) {
tabbar.Add("Web服务", "", "/settings/server", "", this.tab == "server")
tabbar.Add("界面设置", "", "/settings/ui", "", this.tab == "ui")
tabbar.Add("管理界面设置", "", "/settings/ui", "", this.tab == "ui")
tabbar.Add("用户界面设置", "", "/settings/user-ui", "", this.tab == "userUI")
tabbar.Add("安全设置", "", "/settings/security", "", this.tab == "security")
tabbar.Add("IP库", "", "/settings/ip-library", "", this.tab == "ipLibrary")
tabbar.Add("备份", "", "/settings/backup", "", this.tab == "backup")

View File

@@ -1,4 +1,4 @@
package server
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
@@ -15,7 +15,7 @@ func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct{}) {
config, err := configloaders.LoadUIConfig()
config, err := configloaders.LoadAdminUIConfig()
if err != nil {
this.ErrorPage(err)
return
@@ -41,7 +41,7 @@ func (this *IndexAction) RunPost(params struct {
Field("adminSystemName", params.AdminSystemName).
Require("请输入管理员系统名称")
config, err := configloaders.LoadUIConfig()
config, err := configloaders.LoadAdminUIConfig()
if err != nil {
this.ErrorPage(err)
return
@@ -51,7 +51,7 @@ func (this *IndexAction) RunPost(params struct {
config.ShowOpenSourceInfo = params.ShowOpenSourceInfo
config.ShowVersion = params.ShowVersion
config.Version = params.Version
err = configloaders.UpdateUIConfig(config)
err = configloaders.UpdateAdminUIConfig(config)
if err != nil {
this.ErrorPage(err)
return

View File

@@ -1,4 +1,4 @@
package server
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"

View File

@@ -0,0 +1,61 @@
package userui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
config, err := configloaders.LoadUserUIConfig()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["config"] = config
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ProductName string
UserSystemName string
ShowOpenSourceInfo bool
ShowVersion bool
Version string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
params.Must.
Field("productName", params.ProductName).
Require("请输入产品名称").
Field("userSystemName", params.UserSystemName).
Require("请输入管理员系统名称")
config, err := configloaders.LoadUserUIConfig()
if err != nil {
this.ErrorPage(err)
return
}
config.ProductName = params.ProductName
config.UserSystemName = params.UserSystemName
config.ShowOpenSourceInfo = params.ShowOpenSourceInfo
config.ShowVersion = params.ShowVersion
config.Version = params.Version
err = configloaders.UpdateUserUIConfig(config)
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,19 @@
package userui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
Helper(settingutils.NewHelper("userUI")).
Prefix("/settings/user-ui").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -20,14 +20,15 @@ func (this *CreatePopupAction) RunGet(params struct{}) {
}
func (this *CreatePopupAction) RunPost(params struct {
Username string
Pass1 string
Pass2 string
Fullname string
Mobile string
Tel string
Email string
Remark string
Username string
Pass1 string
Pass2 string
Fullname string
Mobile string
Tel string
Email string
Remark string
ClusterId int64
Must *actions.Must
CSRF *actionutils.CSRF
@@ -60,6 +61,10 @@ func (this *CreatePopupAction) RunPost(params struct {
Field("fullname", params.Fullname).
Require("请输入全名")
if params.ClusterId <= 0 {
this.Fail("请选择关联集群")
}
if len(params.Mobile) > 0 {
params.Must.
Field("mobile", params.Mobile).
@@ -72,14 +77,15 @@ func (this *CreatePopupAction) RunPost(params struct {
}
createResp, err := this.RPC().UserRPC().CreateUser(this.AdminContext(), &pb.CreateUserRequest{
Username: params.Username,
Password: params.Pass1,
Fullname: params.Fullname,
Mobile: params.Mobile,
Tel: params.Tel,
Email: params.Email,
Remark: params.Remark,
Source: "admin:" + numberutils.FormatInt64(this.AdminId()),
Username: params.Username,
Password: params.Pass1,
Fullname: params.Fullname,
Mobile: params.Mobile,
Tel: params.Tel,
Email: params.Email,
Remark: params.Remark,
Source: "admin:" + numberutils.FormatInt64(this.AdminId()),
ClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -34,6 +34,13 @@ func (this *IndexAction) RunGet(params struct {
}
userMaps := []maps.Map{}
for _, user := range usersResp.Users {
var clusterMap maps.Map = nil
if user.Cluster != nil {
clusterMap = maps.Map{
"id": user.Cluster.Id,
"name": user.Cluster.Name,
}
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"username": user.Username,
@@ -43,6 +50,7 @@ func (this *IndexAction) RunGet(params struct {
"mobile": user.Mobile,
"tel": user.Tel,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", user.CreatedAt),
"cluster": clusterMap,
})
}
this.Data["users"] = userMaps

View File

@@ -47,20 +47,26 @@ func (this *UpdateAction) RunGet(params struct {
"isOn": user.IsOn,
}
this.Data["clusterId"] = 0
if user.Cluster != nil {
this.Data["clusterId"] = user.Cluster.Id
}
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
UserId int64
Username string
Pass1 string
Pass2 string
Fullname string
Mobile string
Tel string
Email string
Remark string
IsOn bool
UserId int64
Username string
Pass1 string
Pass2 string
Fullname string
Mobile string
Tel string
Email string
Remark string
IsOn bool
ClusterId int64
Must *actions.Must
CSRF *actionutils.CSRF
@@ -109,15 +115,16 @@ func (this *UpdateAction) RunPost(params struct {
}
_, err = this.RPC().UserRPC().UpdateUser(this.AdminContext(), &pb.UpdateUserRequest{
UserId: params.UserId,
Username: params.Username,
Password: params.Pass1,
Fullname: params.Fullname,
Mobile: params.Mobile,
Tel: params.Tel,
Email: params.Email,
Remark: params.Remark,
IsOn: params.IsOn,
UserId: params.UserId,
Username: params.Username,
Password: params.Pass1,
Fullname: params.Fullname,
Mobile: params.Mobile,
Tel: params.Tel,
Email: params.Email,
Remark: params.Remark,
IsOn: params.IsOn,
ClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -35,6 +35,14 @@ func (this *UserAction) RunGet(params struct {
return
}
var clusterMap maps.Map = nil
if user.Cluster != nil {
clusterMap = maps.Map{
"id": user.Cluster.Id,
"name": user.Cluster.Name,
}
}
this.Data["user"] = maps.Map{
"id": user.Id,
"username": user.Username,
@@ -44,6 +52,7 @@ func (this *UserAction) RunGet(params struct {
"remark": user.Remark,
"mobile": user.Mobile,
"isOn": user.IsOn,
"cluster": clusterMap,
}
this.Show()

View File

@@ -74,7 +74,7 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
return true
}
config, err := configloaders.LoadUIConfig()
config, err := configloaders.LoadAdminUIConfig()
if err != nil {
action.WriteString(err.Error())
return false

View File

@@ -86,6 +86,7 @@ import (
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ui"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/upgrade"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-nodes"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/setup"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ui"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users"

View File

@@ -0,0 +1,28 @@
Vue.component("cluster-selector", {
mounted: function () {
let that = this
Tea.action("/clusters/options")
.post()
.success(function (resp) {
that.clusters = resp.data.clusters
})
},
props: ["v-cluster-id"],
data: function () {
let clusterId = this.vClusterId
if (clusterId == null) {
clusterId = 0
}
return {
clusters: [],
clusterId: clusterId
}
},
template: `<div>
<select class="ui dropdown auto-width" name="clusterId" v-model="clusterId">
<option value="0">[选择集群]</option>
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
</select>
</div>`
})

View File

@@ -213,9 +213,6 @@ body .ui.menu .item .blink {
body.expanded .main-menu {
display: none;
}
body.expanded .sub-menu {
display: none;
}
body.expanded .main {
left: 1em;
}
@@ -226,6 +223,7 @@ body.expanded .main {
width: 100%;
z-index: 1000;
overflow-x: auto;
border: 0 !important;
background: #276ac6 !important;
}
.top-nav::-webkit-scrollbar {
@@ -362,6 +360,8 @@ body.expanded .main {
}
.main-menu .menu {
background: #276ac6 !important;
border: 0 !important;
box-shadow: none !important;
}
.main-menu::-webkit-scrollbar {
width: 2px;
@@ -401,113 +401,6 @@ body.expanded .main {
z-index: 999999;
background: white;
}
/** 子菜单 **/
.main.without-menu .sub-menu {
display: none;
}
.sub-menu {
position: fixed;
left: 8em;
width: 12.5em;
top: 3em;
bottom: 2.8em;
}
.sub-menu .menus-box {
overflow-y: auto;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
padding-right: 0.4em !important;
}
.sub-menu .menus-box::-webkit-scrollbar {
width: 4px;
height: 4px;
}
.sub-menu .menu {
max-width: 12em !important;
border-right: 0 !important;
}
@media screen and (max-width: 512px) {
.sub-menu {
position: relative;
width: 100%;
left: 0;
top: 0;
}
.sub-menu .menus-box {
position: relative !important;
}
.sub-menu .menu {
width: 100% !important;
max-width: 30em !important;
}
.sub-menu .menus-box .menu .item {
width: 100% !important;
max-width: 30em !important;
}
}
.sub-menu .menu .item.active {
font-weight: normal !important;
outline: none !important;
}
.sub-menu .menu .item:not(.header) {
padding-top: 0.7em !important;
padding-bottom: 0.7em !important;
}
.sub-menu .menu .item:not(.header) span {
font-size: 0.8em;
display: block;
margin-top: 0.6em !important;
line-height: 1.5;
}
.sub-menu .menu .item:not(.active):hover {
background: rgba(0, 0, 0, 0.05) !important;
border-top: 1px white solid !important;
border-bottom: 1px white solid !important;
margin-top: -1px !important;
margin-bottom: -1px !important;
}
.sub-menu .menu .item.active {
background: rgba(0, 0, 0, 0.05) !important;
}
.sub-menu .menu .item var {
font-style: normal;
}
.sub-menu .menu .item:not(.active) var.grey {
color: grey;
}
.sub-menu .menu .item span:not(.green) {
color: grey;
}
.sub-menu .menu .item span.red {
color: #db2828 !important;
}
.sub-menu .menus-box .menu .item.header {
padding-right: 0.2em !important;
cursor: pointer;
}
.sub-menu .menus-box .menu .item.header span {
font-weight: normal;
color: grey;
font-size: 0.8em;
}
.sub-menu .menus-box .menu a {
display: block;
word-break: break-all;
line-height: 1.6 !important;
}
.sub-menu .menus-box .menu .item .menu {
margin-top: 0 !important;
}
.sub-menu .fourth-menu {
margin-left: 1.2em;
}
.sub-menu .fourth-menu .icon,
.sub-menu .third-menu .icon {
float: left !important;
}
/** 右侧文本子菜单 **/
.text.menu {
overflow-x: auto;

File diff suppressed because one or more lines are too long

View File

@@ -171,10 +171,6 @@ body.expanded .main-menu {
display: none;
}
body.expanded .sub-menu {
display: none;
}
body.expanded .main {
left: 1em;
}
@@ -186,6 +182,7 @@ body.expanded .main {
width: 100%;
z-index: 1000;
overflow-x: auto;
border: 0 !important;
background: #276ac6 !important;
}
@@ -349,10 +346,12 @@ body.expanded .main {
overflow-y: auto;
background: #276ac6 !important;
z-index: 10;
}
.main-menu .menu {
background: #276ac6 !important;
.menu {
background: #276ac6 !important;
border: 0 !important;
box-shadow: none !important;
}
}
.main-menu::-webkit-scrollbar {
@@ -405,136 +404,6 @@ body.expanded .main {
background: white;
}
/** 子菜单 **/
.main.without-menu .sub-menu {
display: none;
}
.sub-menu {
position: fixed;
left: 8em;
width: 12.5em;
top: 3em;
bottom: 2.8em;
}
.sub-menu .menus-box {
overflow-y: auto;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
padding-right: 0.4em !important;
}
.sub-menu .menus-box::-webkit-scrollbar {
width: 4px;
height: 4px;
}
.sub-menu .menu {
max-width: 12em !important;
border-right: 0 !important;
}
@media screen and (max-width: 512px) {
.sub-menu {
position: relative;
width: 100%;
left: 0;
top: 0;
}
.sub-menu .menus-box {
position: relative !important;
}
.sub-menu .menu {
width: 100% !important;
max-width: 30em !important;
}
.sub-menu .menus-box .menu .item {
width: 100% !important;
max-width: 30em !important;
}
}
.sub-menu .menu .item.active {
font-weight: normal !important;
outline: none !important;
}
.sub-menu .menu .item:not(.header) {
padding-top: 0.7em !important;
padding-bottom: 0.7em !important;
}
.sub-menu .menu .item:not(.header) span {
font-size: 0.8em;
display: block;
margin-top: 0.6em !important;
line-height: 1.5;
}
.sub-menu .menu .item:not(.active):hover {
background: rgba(0, 0, 0, 0.05) !important;
border-top: 1px white solid !important;
border-bottom: 1px white solid !important;
margin-top: -1px !important;
margin-bottom: -1px !important;
}
.sub-menu .menu .item.active {
background: rgba(0, 0, 0, 0.05) !important;
}
.sub-menu .menu .item var {
font-style: normal;
}
.sub-menu .menu .item:not(.active) var.grey {
color: grey;
}
.sub-menu .menu .item span:not(.green) {
color: grey;
}
.sub-menu .menu .item span.red {
color: #db2828 !important;
}
.sub-menu .menus-box .menu .item.header {
padding-right: 0.2em !important;
cursor: pointer;
}
.sub-menu .menus-box .menu .item.header span {
font-weight: normal;
color: grey;
font-size: 0.8em;
}
.sub-menu .menus-box .menu a {
display: block;
word-break: break-all;
line-height: 1.6 !important;
}
.sub-menu .menus-box .menu .item .menu {
margin-top: 0 !important;
}
.sub-menu .fourth-menu {
margin-left: 1.2em;
}
.sub-menu .fourth-menu .icon, .sub-menu .third-menu .icon {
float: left !important;
}
/** 右侧文本子菜单 **/
.text.menu {
overflow-x: auto;

View File

@@ -0,0 +1,41 @@
{$layout}
<form class="ui form" method="post" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td class="title">产品名称 *</td>
<td>
<input type="text" name="productName" v-model="config.productName" maxlength="100"/>
</td>
</tr>
<tr>
<td>用户系统名称 *</td>
<td>
<input type="text" name="userSystemName" v-model="config.userSystemName" maxlength="100"/>
</td>
</tr>
<tr>
<td>是否显示底部开源信息</td>
<td>
<checkbox name="showOpenSourceInfo" v-model="config.showOpenSourceInfo"></checkbox>
</td>
</tr>
<tr>
<td>是否显示版本号</td>
<td>
<checkbox name="showVersion" v-model="config.showVersion"></checkbox>
</td>
</tr>
<tr v-show="config.showVersion">
<td>定制版本号</td>
<td>
<input type="text" name="version" v-model="config.version" maxlength="100"/>
<p class="comment">定制自己的版本号,留空表示使用系统自带的版本号。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -31,6 +31,13 @@
<p class="comment">用户姓名或者公司名称等等。</p>
</td>
</tr>
<tr>
<td>关联集群 *</td>
<td>
<cluster-selector></cluster-selector>
<p class="comment">用户发布的网站服务会自动部署到此集群。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>

View File

@@ -10,6 +10,7 @@
<tr>
<th>用户名</th>
<th>全名</th>
<th>关联集群</th>
<th>手机号</th>
<th>注册时间</th>
<th class="center width10">状态</th>
@@ -19,6 +20,10 @@
<tr v-for="user in users">
<td :class="{disabled:!user.isOn}">{{user.username}}</td>
<td :class="{disabled:!user.isOn}">{{user.fullname}}</td>
<td>
<span v-if="user.cluster != null">{{user.cluster.name}} <link-icon :href="'/clusters/cluster?clusterId=' + user.cluster.id"></link-icon></span>
<span v-else class="disabled">-</span>
</td>
<td :class="{disabled:!user.isOn}">
<span v-if="user.mobile.length > 0">{{user.mobile}}</span>
<span v-else class="disabled">-</span>

View File

@@ -1,6 +1,7 @@
Tea.context(function () {
this.createUser = function () {
teaweb.popup(Tea.url(".createPopup"), {
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()

View File

@@ -38,6 +38,13 @@
<p class="comment">用户姓名或者公司名称等等。</p>
</td>
</tr>
<tr>
<td>关联集群 *</td>
<td>
<cluster-selector :v-cluster-id="clusterId"></cluster-selector>
<p class="comment">用户发布的网站服务会自动部署到此集群。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>

View File

@@ -20,6 +20,13 @@
{{user.fullname}}
</td>
</tr>
<tr>
<td>关联集群</td>
<td>
<span v-if="user.cluster != null">{{user.cluster.name}} <link-icon :href="'/clusters/cluster?clusterId=' + user.cluster.id"></link-icon></span>
<span v-else class="disabled">-</span>
</td>
</tr>
<tr>
<td>手机号</td>
<td>