mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 05:00:25 +08:00 
			
		
		
		
	[区域]可以设置区域-价格项目之间的价格
This commit is contained in:
		@@ -75,6 +75,10 @@ func (this *RPCClient) NodeRegionRPC() pb.NodeRegionServiceClient {
 | 
			
		||||
	return pb.NewNodeRegionServiceClient(this.pickConn())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *RPCClient) NodePriceItemRPC() pb.NodePriceItemServiceClient {
 | 
			
		||||
	return pb.NewNodePriceItemServiceClient(this.pickConn())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
 | 
			
		||||
	return pb.NewNodeIPAddressServiceClient(this.pickConn())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ type IndexAction struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IndexAction) Init() {
 | 
			
		||||
	this.Nav("", "", "")
 | 
			
		||||
	this.Nav("", "", "region")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IndexAction) RunGet(params struct{}) {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,8 @@ func init() {
 | 
			
		||||
			Post("/delete", new(DeleteAction)).
 | 
			
		||||
			Post("/sort", new(SortAction)).
 | 
			
		||||
			GetPost("/selectPopup", new(SelectPopupAction)).
 | 
			
		||||
			GetPost("/prices", new(PricesAction)).
 | 
			
		||||
			GetPost("/updatePricePopup", new(UpdatePricePopupAction)).
 | 
			
		||||
			EndAll()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,50 @@
 | 
			
		||||
package items
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/actions"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type CreatePopupAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *CreatePopupAction) Init() {
 | 
			
		||||
	this.Nav("", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *CreatePopupAction) RunGet(params struct{}) {
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *CreatePopupAction) RunPost(params struct {
 | 
			
		||||
	Name     string
 | 
			
		||||
	BitsFrom int64
 | 
			
		||||
	BitsTo   int64
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
}) {
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("name", params.Name).
 | 
			
		||||
		Require("请输入名称").
 | 
			
		||||
		Field("bitsFrom", params.BitsFrom).
 | 
			
		||||
		Gte(0, "请输入不小于0的整数").
 | 
			
		||||
		Field("bitsTo", params.BitsTo).
 | 
			
		||||
		Gte(0, "请输入不小于0的整数")
 | 
			
		||||
 | 
			
		||||
	createResp, err := this.RPC().NodePriceItemRPC().CreateNodePriceItem(this.AdminContext(), &pb.CreateNodePriceItemRequest{
 | 
			
		||||
		Name:     params.Name,
 | 
			
		||||
		Type:     regionutils.PriceTypeTraffic,
 | 
			
		||||
		BitsFrom: params.BitsFrom * 1000 * 1000,
 | 
			
		||||
		BitsTo:   params.BitsTo * 1000 * 1000,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer this.CreateLogInfo("创建流量价格项目", createResp.NodePriceItemId)
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
package items
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DeleteAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *DeleteAction) RunPost(params struct {
 | 
			
		||||
	ItemId int64
 | 
			
		||||
}) {
 | 
			
		||||
	defer this.CreateLogInfo("删除流量价格项目 %d", params.ItemId)
 | 
			
		||||
 | 
			
		||||
	_, err := this.RPC().NodePriceItemRPC().DeleteNodePriceItem(this.AdminContext(), &pb.DeleteNodePriceItemRequest{NodePriceItemId: params.ItemId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								internal/web/actions/default/clusters/regions/items/init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								internal/web/actions/default/clusters/regions/items/init.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package items
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
 | 
			
		||||
	"github.com/iwind/TeaGo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	TeaGo.BeforeStart(func(server *TeaGo.Server) {
 | 
			
		||||
		server.
 | 
			
		||||
			Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
 | 
			
		||||
			Data("teaMenu", "clusters").
 | 
			
		||||
			Data("teaSubMenu", "region").
 | 
			
		||||
			Prefix("/clusters/regions/items").
 | 
			
		||||
			GetPost("/createPopup", new(CreatePopupAction)).
 | 
			
		||||
			GetPost("/updatePopup", new(UpdatePopupAction)).
 | 
			
		||||
			Post("/delete", new(DeleteAction)).
 | 
			
		||||
			EndAll()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,73 @@
 | 
			
		||||
package items
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/actions"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UpdatePopupAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdatePopupAction) Init() {
 | 
			
		||||
	this.Nav("", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdatePopupAction) RunGet(params struct {
 | 
			
		||||
	ItemId int64
 | 
			
		||||
}) {
 | 
			
		||||
	itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	item := itemResp.NodePriceItem
 | 
			
		||||
	if item == nil {
 | 
			
		||||
		this.NotFound("nodePriceItem", params.ItemId)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["item"] = maps.Map{
 | 
			
		||||
		"id":       item.Id,
 | 
			
		||||
		"name":     item.Name,
 | 
			
		||||
		"bitsFrom": item.BitsFrom,
 | 
			
		||||
		"bitsTo":   item.BitsTo,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdatePopupAction) RunPost(params struct {
 | 
			
		||||
	ItemId   int64
 | 
			
		||||
	Name     string
 | 
			
		||||
	BitsFrom int64
 | 
			
		||||
	BitsTo   int64
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
}) {
 | 
			
		||||
	defer this.CreateLogInfo("修改流量价格项目", params.ItemId)
 | 
			
		||||
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("name", params.Name).
 | 
			
		||||
		Require("请输入名称").
 | 
			
		||||
		Field("bitsFrom", params.BitsFrom).
 | 
			
		||||
		Gte(0, "请输入不小于0的整数").
 | 
			
		||||
		Field("bitsTo", params.BitsTo).
 | 
			
		||||
		Gte(0, "请输入不小于0的整数")
 | 
			
		||||
 | 
			
		||||
	_, err := this.RPC().NodePriceItemRPC().UpdateNodePriceItem(this.AdminContext(), &pb.UpdateNodePriceItemRequest{
 | 
			
		||||
		NodePriceItemId: params.ItemId,
 | 
			
		||||
		Name:            params.Name,
 | 
			
		||||
		BitsFrom:        params.BitsFrom * 1000 * 1000,
 | 
			
		||||
		BitsTo:          params.BitsTo * 1000 * 1000,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								internal/web/actions/default/clusters/regions/prices.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								internal/web/actions/default/clusters/regions/prices.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
package regions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PricesAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *PricesAction) Init() {
 | 
			
		||||
	this.Nav("", "", "price")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *PricesAction) RunGet(params struct{}) {
 | 
			
		||||
	// 所有价格项目
 | 
			
		||||
	itemsResp, err := this.RPC().NodePriceItemRPC().FindAllEnabledAndOnNodePriceItems(this.AdminContext(), &pb.FindAllEnabledAndOnNodePriceItemsRequest{Type: regionutils.PriceTypeTraffic})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	itemMaps := []maps.Map{}
 | 
			
		||||
	for _, item := range itemsResp.NodePriceItems {
 | 
			
		||||
 | 
			
		||||
		itemMaps = append(itemMaps, maps.Map{
 | 
			
		||||
			"id":             item.Id,
 | 
			
		||||
			"name":           item.Name,
 | 
			
		||||
			"bitsFromString": this.formatBits(item.BitsFrom),
 | 
			
		||||
			"bitsToString":   this.formatBits(item.BitsTo),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["items"] = itemMaps
 | 
			
		||||
 | 
			
		||||
	// 所有区域
 | 
			
		||||
	regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledNodeRegions(this.AdminContext(), &pb.FindAllEnabledNodeRegionsRequest{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	regionMaps := []maps.Map{}
 | 
			
		||||
	for _, region := range regionsResp.NodeRegions {
 | 
			
		||||
		pricesMap := map[string]float32{}
 | 
			
		||||
		if len(region.PricesJSON) > 0 {
 | 
			
		||||
			err = json.Unmarshal(region.PricesJSON, &pricesMap)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.ErrorPage(err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		regionMaps = append(regionMaps, maps.Map{
 | 
			
		||||
			"id":     region.Id,
 | 
			
		||||
			"isOn":   region.IsOn,
 | 
			
		||||
			"name":   region.Name,
 | 
			
		||||
			"prices": pricesMap,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["regions"] = regionMaps
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *PricesAction) formatBits(bits int64) string {
 | 
			
		||||
	sizeHuman := ""
 | 
			
		||||
	if bits < 1000 {
 | 
			
		||||
		sizeHuman = numberutils.FormatInt64(bits) + "BPS"
 | 
			
		||||
	} else if bits < 1_000_000 {
 | 
			
		||||
		sizeHuman = fmt.Sprintf("%.2fKBPS", float64(bits)/1000)
 | 
			
		||||
	} else if bits < 1_000_000_000 {
 | 
			
		||||
		sizeHuman = fmt.Sprintf("%.2fMBPS", float64(bits)/1000/1000)
 | 
			
		||||
	} else if bits < 1_000_000_000_000 {
 | 
			
		||||
		sizeHuman = fmt.Sprintf("%.2fGBPS", float64(bits)/1000/1000/1000)
 | 
			
		||||
	} 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)
 | 
			
		||||
	}
 | 
			
		||||
	return sizeHuman
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
package regionutils
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	PriceTypeTraffic = "traffic"
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,94 @@
 | 
			
		||||
package regions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/actions"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UpdatePricePopupAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdatePricePopupAction) Init() {
 | 
			
		||||
	this.Nav("", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdatePricePopupAction) RunGet(params struct {
 | 
			
		||||
	RegionId int64
 | 
			
		||||
	ItemId   int64
 | 
			
		||||
}) {
 | 
			
		||||
	// 区域
 | 
			
		||||
	regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	region := regionResp.NodeRegion
 | 
			
		||||
	if region == nil {
 | 
			
		||||
		this.NotFound("nodeRegion", params.RegionId)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["region"] = maps.Map{
 | 
			
		||||
		"id":   region.Id,
 | 
			
		||||
		"isOn": region.IsOn,
 | 
			
		||||
		"name": region.Name,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 当前价格
 | 
			
		||||
	pricesMap := map[string]float32{}
 | 
			
		||||
	if len(region.PricesJSON) > 0 {
 | 
			
		||||
		err = json.Unmarshal(region.PricesJSON, &pricesMap)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["price"] = pricesMap[numberutils.FormatInt64(params.ItemId)]
 | 
			
		||||
 | 
			
		||||
	// 价格项
 | 
			
		||||
	itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	item := itemResp.NodePriceItem
 | 
			
		||||
	if item == nil {
 | 
			
		||||
		this.NotFound("nodePriceItem", params.ItemId)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["item"] = maps.Map{
 | 
			
		||||
		"id":       item.Id,
 | 
			
		||||
		"name":     item.Name,
 | 
			
		||||
		"bitsFrom": item.BitsFrom,
 | 
			
		||||
		"bitsTo":   item.BitsTo,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdatePricePopupAction) RunPost(params struct {
 | 
			
		||||
	RegionId int64
 | 
			
		||||
	ItemId   int64
 | 
			
		||||
	Price    float32
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
}) {
 | 
			
		||||
	defer this.CreateLogInfo("修改区域 %d-价格项 %d 的价格", params.RegionId, params.ItemId)
 | 
			
		||||
 | 
			
		||||
	_, err := this.RPC().NodeRegionRPC().UpdateNodeRegionPrice(this.AdminContext(), &pb.UpdateNodeRegionPriceRequest{
 | 
			
		||||
		NodeRegionId: params.RegionId,
 | 
			
		||||
		NodeItemId:   params.ItemId,
 | 
			
		||||
		Price:        params.Price,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
@@ -11,6 +11,7 @@ import (
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/csrf"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								web/views/@default/clusters/regions/@menu.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/views/@default/clusters/regions/@menu.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
<first-menu>
 | 
			
		||||
    <menu-item href="/clusters/regions" code="region">区域</menu-item>
 | 
			
		||||
    <menu-item href="/clusters/regions/prices" code="price">价格</menu-item>
 | 
			
		||||
    <span class="item">|</span>
 | 
			
		||||
    <menu-item><tip-icon content="可以设置节点所属区域,从而利用区域设置进行不同的价格设定。"></tip-icon></menu-item>
 | 
			
		||||
</first-menu>
 | 
			
		||||
@@ -1,14 +1,13 @@
 | 
			
		||||
{$layout}
 | 
			
		||||
{$template "menu"}
 | 
			
		||||
 | 
			
		||||
<first-menu>
 | 
			
		||||
<second-menu>
 | 
			
		||||
    <menu-item @click.prevent="createRegion()">创建</menu-item>
 | 
			
		||||
    <span class="item">|</span>
 | 
			
		||||
    <menu-item><tip-icon content="可以设置节点所属区域,从而利用区域设置进行不同的价格设定。"></tip-icon></menu-item>
 | 
			
		||||
</first-menu>
 | 
			
		||||
</second-menu>
 | 
			
		||||
 | 
			
		||||
<p class="comment" v-if="regions.length == 0">暂时还没有区域。</p>
 | 
			
		||||
 | 
			
		||||
<table class="ui table selectable" v-if="regions.length > 0" style="width: 35em" id="sortable-table">
 | 
			
		||||
<table class="ui table selectable" v-if="regions.length > 0" id="sortable-table">
 | 
			
		||||
    <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th style="width:3em"></th>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								web/views/@default/clusters/regions/items/createPopup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/views/@default/clusters/regions/items/createPopup.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
{$layout "layout_popup"}
 | 
			
		||||
 | 
			
		||||
<h3>添加价格项</h3>
 | 
			
		||||
 | 
			
		||||
<form class="ui form" 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="name" maxlength="100" ref="focus"/>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>最低流量 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div class="ui input right labeled">
 | 
			
		||||
                    <input type="text" name="bitsFrom" maxlength="16" style="width:10em" v-model="bitsFrom" autocomplete="off"/>
 | 
			
		||||
                    <span class="ui label">MBPS</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <p class="comment">采用1000进制<span v-if="bitsFromMB.length > 0">,相当于{{bitsFromMB}}</span>。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>最高流量 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div class="ui input right labeled">
 | 
			
		||||
                    <input type="text" name="bitsTo" maxlength="16" style="width:10em" v-model="bitsTo" autocomplete="off"/>
 | 
			
		||||
                    <span class="ui label">MBPS</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <p class="comment">采用1000进制<span v-if="bitsToMB.length > 0">,相当于{{bitsToMB}}</span>。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <submit-btn></submit-btn>
 | 
			
		||||
</form>
 | 
			
		||||
							
								
								
									
										42
									
								
								web/views/@default/clusters/regions/items/createPopup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								web/views/@default/clusters/regions/items/createPopup.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.bitsFrom = 0
 | 
			
		||||
	this.bitsFromMB = ""
 | 
			
		||||
 | 
			
		||||
	this.bitsTo = 0
 | 
			
		||||
	this.bitsToMB = ""
 | 
			
		||||
 | 
			
		||||
	this.$delay(function () {
 | 
			
		||||
		let that = this
 | 
			
		||||
		this.$watch("bitsFrom", function (v) {
 | 
			
		||||
			this.bitsFromMB = that.formatBits(v)
 | 
			
		||||
		})
 | 
			
		||||
		this.$watch("bitsTo", function (v) {
 | 
			
		||||
			this.bitsToMB = that.formatBits(v)
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	this.formatBits = function (bits) {
 | 
			
		||||
		bits = parseInt(bits)
 | 
			
		||||
		if (isNaN(bits)) {
 | 
			
		||||
			bits = 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000) {
 | 
			
		||||
			return bits + "MB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000 * 1000) {
 | 
			
		||||
			return (bits / 1000) + "GB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000 * 1000 * 1000) {
 | 
			
		||||
			return (bits / 1000 / 1000) + "TB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000 * 1000 * 1000 * 1000) {
 | 
			
		||||
			return (bits / 1000 / 1000 / 1000) + "PB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										37
									
								
								web/views/@default/clusters/regions/items/updatePopup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web/views/@default/clusters/regions/items/updatePopup.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
{$layout "layout_popup"}
 | 
			
		||||
 | 
			
		||||
<h3>修改价格项</h3>
 | 
			
		||||
 | 
			
		||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
 | 
			
		||||
    <csrf-token></csrf-token>
 | 
			
		||||
    <input type="hidden" name="itemId" :value="item.id"/>
 | 
			
		||||
    <table class="ui table definition selectable">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">名称 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <input type="text" name="name" maxlength="100" ref="focus" v-model="item.name"/>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>最低流量 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div class="ui input right labeled">
 | 
			
		||||
                    <input type="text" name="bitsFrom" maxlength="16" style="width:10em" v-model="bitsFrom" autocomplete="off"/>
 | 
			
		||||
                    <span class="ui label">MBPS</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <p class="comment">采用1000进制<span v-if="bitsFromMB.length > 0">,相当于{{bitsFromMB}}</span>。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>最高流量 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div class="ui input right labeled">
 | 
			
		||||
                    <input type="text" name="bitsTo" maxlength="16" style="width:10em" v-model="bitsTo" autocomplete="off"/>
 | 
			
		||||
                    <span class="ui label">MBPS</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <p class="comment">采用1000进制<span v-if="bitsToMB.length > 0">,相当于{{bitsToMB}}</span>。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <submit-btn></submit-btn>
 | 
			
		||||
</form>
 | 
			
		||||
							
								
								
									
										45
									
								
								web/views/@default/clusters/regions/items/updatePopup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								web/views/@default/clusters/regions/items/updatePopup.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.bitsFrom = this.item.bitsFrom / 1000 / 1000
 | 
			
		||||
	this.bitsFromMB = ""
 | 
			
		||||
 | 
			
		||||
	this.bitsTo = this.item.bitsTo / 1000 / 1000
 | 
			
		||||
	this.bitsToMB = ""
 | 
			
		||||
 | 
			
		||||
	this.$delay(function () {
 | 
			
		||||
		let that = this
 | 
			
		||||
		this.$watch("bitsFrom", function (v) {
 | 
			
		||||
			this.bitsFromMB = that.formatBits(v)
 | 
			
		||||
		})
 | 
			
		||||
		this.$watch("bitsTo", function (v) {
 | 
			
		||||
			this.bitsToMB = that.formatBits(v)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		this.bitsFromMB = that.formatBits(this.bitsFrom)
 | 
			
		||||
		this.bitsToMB = that.formatBits(this.bitsTo)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	this.formatBits = function (bits) {
 | 
			
		||||
		bits = parseInt(bits)
 | 
			
		||||
		if (isNaN(bits)) {
 | 
			
		||||
			bits = 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000) {
 | 
			
		||||
			return bits + "MB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000 * 1000) {
 | 
			
		||||
			return (bits / 1000) + "GB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000 * 1000 * 1000) {
 | 
			
		||||
			return (bits / 1000 / 1000) + "TB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bits < 1000 * 1000 * 1000 * 1000) {
 | 
			
		||||
			return (bits / 1000 / 1000 / 1000) + "PB"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										15
									
								
								web/views/@default/clusters/regions/prices.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								web/views/@default/clusters/regions/prices.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
th span {
 | 
			
		||||
  font-size: 0.9em;
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
  color: grey;
 | 
			
		||||
}
 | 
			
		||||
th a {
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
td a {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
td:hover a {
 | 
			
		||||
  display: inline;
 | 
			
		||||
}
 | 
			
		||||
/*# sourceMappingURL=prices.css.map */
 | 
			
		||||
							
								
								
									
										1
									
								
								web/views/@default/clusters/regions/prices.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/views/@default/clusters/regions/prices.css.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"version":3,"sources":["prices.less"],"names":[],"mappings":"AAAA,EACC;EACC,gBAAA;EACA,mBAAA;EACA,WAAA;;AAJF,EAOC;EACC,mBAAA;;AAIF,EACC;EACC,aAAA;;AAIF,EAAE,MACD;EACC,eAAA","file":"prices.css"}
 | 
			
		||||
							
								
								
									
										31
									
								
								web/views/@default/clusters/regions/prices.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								web/views/@default/clusters/regions/prices.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
{$layout}
 | 
			
		||||
{$template "menu"}
 | 
			
		||||
 | 
			
		||||
<p class="comment" v-if="regions.length == 0">暂时还没有区域。</p>
 | 
			
		||||
 | 
			
		||||
<table class="ui table selectable">
 | 
			
		||||
    <thead>
 | 
			
		||||
    <tr>
 | 
			
		||||
        <th style="width:7em">区域\范围</th>
 | 
			
		||||
        <th v-for="item in items">
 | 
			
		||||
            {{item.name}}
 | 
			
		||||
              <a href="" title="修改" @click.prevent="updateItem(item.id)"><i class="icon pencil small"></i></a>  
 | 
			
		||||
            <a href="" title="删除" @click.prevent="deleteItem(item.id)"><i class="icon remove small"></i></a>
 | 
			
		||||
            <br/>
 | 
			
		||||
            <span>{{item.bitsFromString}}-{{item.bitsToString}}</span>
 | 
			
		||||
        </th>
 | 
			
		||||
        <th class="width10">
 | 
			
		||||
            <a href="" @click.prevent="createItem">[+添加价格项]</a>
 | 
			
		||||
        </th>
 | 
			
		||||
    </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tr v-for="region in regions">
 | 
			
		||||
        <td>{{region.name}}</td>
 | 
			
		||||
        <td v-for="item in items">
 | 
			
		||||
            <span v-if="region.prices[item.id.toString()] != null">¥{{region.prices[item.id.toString()]}}元/GB  </span>
 | 
			
		||||
 | 
			
		||||
            <a href="" title="修改单位价格" @click.prevent="updatePrice(region.id, item.id)">[设置]</a>
 | 
			
		||||
        </td>
 | 
			
		||||
        <td></td>
 | 
			
		||||
    </tr>
 | 
			
		||||
</table>
 | 
			
		||||
							
								
								
									
										42
									
								
								web/views/@default/clusters/regions/prices.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								web/views/@default/clusters/regions/prices.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.createItem = function () {
 | 
			
		||||
		teaweb.popup(Tea.url(".items.createPopup"), {
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				teaweb.success("保存成功", function () {
 | 
			
		||||
					teaweb.reload()
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.updateItem = function (itemId) {
 | 
			
		||||
		teaweb.popup(Tea.url(".items.updatePopup", {itemId: itemId}), {
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				teaweb.success("保存成功", function () {
 | 
			
		||||
					teaweb.reload()
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.deleteItem = function (itemId) {
 | 
			
		||||
		let that = this
 | 
			
		||||
		teaweb.confirm("确定要删除此价格项吗?", function () {
 | 
			
		||||
			that.$post(".items.delete")
 | 
			
		||||
				.params({
 | 
			
		||||
					itemId: itemId
 | 
			
		||||
				})
 | 
			
		||||
				.refresh()
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.updatePrice = function (regionId, itemId) {
 | 
			
		||||
		teaweb.popup(Tea.url(".updatePricePopup", {regionId: regionId, itemId: itemId}), {
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				teaweb.success("保存成功", function () {
 | 
			
		||||
					teaweb.reload()
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										23
									
								
								web/views/@default/clusters/regions/prices.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/views/@default/clusters/regions/prices.less
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
th {
 | 
			
		||||
	span {
 | 
			
		||||
		font-size: 0.9em;
 | 
			
		||||
		font-weight: normal;
 | 
			
		||||
		color: grey;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	a {
 | 
			
		||||
		font-weight: normal;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td {
 | 
			
		||||
	a {
 | 
			
		||||
		display: none;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td:hover {
 | 
			
		||||
	a {
 | 
			
		||||
		display: inline;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								web/views/@default/clusters/regions/updatePricePopup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web/views/@default/clusters/regions/updatePricePopup.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
{$layout "layout_popup"}
 | 
			
		||||
 | 
			
		||||
<h3>修改价格</h3>
 | 
			
		||||
<form class="ui form" method="post" data-tea-action="$" data-tea-success="success">
 | 
			
		||||
    <csrf-token></csrf-token>
 | 
			
		||||
    <input type="hidden" name="regionId" :value="region.id"/>
 | 
			
		||||
    <input type="hidden" name="itemId" :value="item.id"/>
 | 
			
		||||
 | 
			
		||||
    <table class="ui table definition selectable">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">区域</td>
 | 
			
		||||
            <td>{{region.name}}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>价格项</td>
 | 
			
		||||
            <td>{{item.name}}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">单位价格 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div class="ui input right labeled">
 | 
			
		||||
                    <input type="text" name="price" maxlength="10" style="width:6em" ref="focus" v-model="price"/>
 | 
			
		||||
                    <span class="ui label">元/GB</span>
 | 
			
		||||
                </div>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
    <submit-btn></submit-btn>
 | 
			
		||||
</form>
 | 
			
		||||
		Reference in New Issue
	
	Block a user