1.4.5.2
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
Vue.component("plan-bandwidth-limit-view", {
|
||||
props: ["value"],
|
||||
template: `<div style="font-size: 0.8em; color: grey" v-if="value != null && value.bandwidthLimitPerNode != null && value.bandwidthLimitPerNode.count > 0">
|
||||
带宽限制:<bandwidth-size-capacity-view :v-value="value.bandwidthLimitPerNode"></bandwidth-size-capacity-view>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,226 @@
|
||||
Vue.component("plan-bandwidth-ranges", {
|
||||
props: ["value"],
|
||||
data: function () {
|
||||
let ranges = this.value
|
||||
if (ranges == null) {
|
||||
ranges = []
|
||||
}
|
||||
return {
|
||||
ranges: ranges,
|
||||
isAdding: false,
|
||||
|
||||
minMB: "",
|
||||
minMBUnit: "mb",
|
||||
|
||||
maxMB: "",
|
||||
maxMBUnit: "mb",
|
||||
|
||||
pricePerMB: "",
|
||||
totalPrice: "",
|
||||
addingRange: {
|
||||
minMB: 0,
|
||||
maxMB: 0,
|
||||
pricePerMB: 0,
|
||||
totalPrice: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add: function () {
|
||||
this.isAdding = !this.isAdding
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.minMB.focus()
|
||||
})
|
||||
},
|
||||
cancelAdding: function () {
|
||||
this.isAdding = false
|
||||
},
|
||||
confirm: function () {
|
||||
if (this.addingRange.minMB < 0) {
|
||||
teaweb.warn("带宽下限需要大于0")
|
||||
return
|
||||
}
|
||||
if (this.addingRange.maxMB < 0) {
|
||||
teaweb.warn("带宽上限需要大于0")
|
||||
return
|
||||
}
|
||||
if (this.addingRange.pricePerMB <= 0) {
|
||||
teaweb.warn("请设置单位价格或者总价格")
|
||||
return
|
||||
}
|
||||
|
||||
this.isAdding = false
|
||||
this.minMB = ""
|
||||
this.maxMB = ""
|
||||
this.pricePerMB = ""
|
||||
this.totalPrice = ""
|
||||
this.ranges.push(this.addingRange)
|
||||
this.ranges.$sort(function (v1, v2) {
|
||||
if (v1.minMB < v2.minMB) {
|
||||
return -1
|
||||
}
|
||||
if (v1.minMB == v2.minMB) {
|
||||
if (v2.maxMB == 0 || v1.maxMB < v2.maxMB) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
})
|
||||
this.change()
|
||||
this.addingRange = {
|
||||
minMB: 0,
|
||||
maxMB: 0,
|
||||
pricePerMB: 0,
|
||||
totalPrice: 0
|
||||
}
|
||||
},
|
||||
remove: function (index) {
|
||||
this.ranges.$remove(index)
|
||||
this.change()
|
||||
},
|
||||
change: function () {
|
||||
this.$emit("change", this.ranges)
|
||||
},
|
||||
formatMB: function (mb) {
|
||||
return teaweb.formatBits(mb * 1024 * 1024)
|
||||
},
|
||||
changeMinMB: function (v) {
|
||||
let minMB = parseFloat(v.toString())
|
||||
if (isNaN(minMB) || minMB < 0) {
|
||||
minMB = 0
|
||||
}
|
||||
switch (this.minMBUnit) {
|
||||
case "gb":
|
||||
minMB *= 1024
|
||||
break
|
||||
case "tb":
|
||||
minMB *= 1024 * 1024
|
||||
break
|
||||
}
|
||||
this.addingRange.minMB = minMB
|
||||
},
|
||||
changeMaxMB: function (v) {
|
||||
let maxMB = parseFloat(v.toString())
|
||||
if (isNaN(maxMB) || maxMB < 0) {
|
||||
maxMB = 0
|
||||
}
|
||||
switch (this.maxMBUnit) {
|
||||
case "gb":
|
||||
maxMB *= 1024
|
||||
break
|
||||
case "tb":
|
||||
maxMB *= 1024 * 1024
|
||||
break
|
||||
}
|
||||
this.addingRange.maxMB = maxMB
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
minMB: function (v) {
|
||||
this.changeMinMB(v)
|
||||
},
|
||||
minMBUnit: function () {
|
||||
this.changeMinMB(this.minMB)
|
||||
},
|
||||
maxMB: function (v) {
|
||||
this.changeMaxMB(v)
|
||||
},
|
||||
maxMBUnit: function () {
|
||||
this.changeMaxMB(this.maxMB)
|
||||
},
|
||||
pricePerMB: function (v) {
|
||||
let pricePerMB = parseFloat(v.toString())
|
||||
if (isNaN(pricePerMB) || pricePerMB < 0) {
|
||||
pricePerMB = 0
|
||||
}
|
||||
this.addingRange.pricePerMB = pricePerMB
|
||||
},
|
||||
totalPrice: function (v) {
|
||||
let totalPrice = parseFloat(v.toString())
|
||||
if (isNaN(totalPrice) || totalPrice < 0) {
|
||||
totalPrice = 0
|
||||
}
|
||||
this.addingRange.totalPrice = totalPrice
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<!-- 已有价格 -->
|
||||
<div v-if="ranges.length > 0">
|
||||
<div class="ui label basic small" v-for="(range, index) in ranges" style="margin-bottom: 0.5em">
|
||||
{{formatMB(range.minMB)}} - <span v-if="range.maxMB > 0">{{formatMB(range.maxMB)}}</span><span v-else>∞</span> 价格:<span v-if="range.totalPrice > 0">{{range.totalPrice}}元</span><span v-else="">{{range.pricePerMB}}元/Mbps</span>
|
||||
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
|
||||
<!-- 添加 -->
|
||||
<div v-if="isAdding">
|
||||
<table class="ui table">
|
||||
<tr>
|
||||
<td class="title">带宽下限 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" placeholder="最小带宽" style="width: 7em" maxlength="10" ref="minMB" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="minMB"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" v-model="minMBUnit">
|
||||
<option value="mb">Mbps</option>
|
||||
<option value="gb">Gbps</option>
|
||||
<option value="tb">Tbps</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">带宽上限 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" placeholder="最大带宽" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="maxMB"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" v-model="maxMBUnit">
|
||||
<option value="mb">Mbps</option>
|
||||
<option value="gb">Gbps</option>
|
||||
<option value="tb">Tbps</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment">如果填0,表示上不封顶。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">单位价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" placeholder="单位价格" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="pricePerMB"/>
|
||||
<span class="ui label">元/Mbps</span>
|
||||
</div>
|
||||
<p class="comment">和总价格二选一。如果设置了单位价格,那么"总价格 = 单位价格 x 带宽/Mbps"。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>总价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" placeholder="总价格" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="totalPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">固定的总价格,和单位价格二选一。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button class="ui button small" type="button" @click.prevent="confirm">确定</button>
|
||||
<a href="" title="取消" @click.prevent="cancelAdding"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<div v-if="!isAdding">
|
||||
<button class="ui button small" type="button" @click.prevent="add">+</button>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
// 显示流量限制说明
|
||||
Vue.component("plan-limit-view", {
|
||||
props: ["value", "v-single-mode"],
|
||||
data: function () {
|
||||
let config = this.value
|
||||
|
||||
let hasLimit = false
|
||||
if (!this.vSingleMode) {
|
||||
if (config.trafficLimit != null && config.trafficLimit.isOn && ((config.trafficLimit.dailySize != null && config.trafficLimit.dailySize.count > 0) || (config.trafficLimit.monthlySize != null && config.trafficLimit.monthlySize.count > 0))) {
|
||||
hasLimit = true
|
||||
} else if (config.dailyRequests > 0 || config.monthlyRequests > 0) {
|
||||
hasLimit = true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
config: config,
|
||||
hasLimit: hasLimit
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatNumber: function (n) {
|
||||
return teaweb.formatNumber(n)
|
||||
},
|
||||
composeCapacity: function (capacity) {
|
||||
return teaweb.convertSizeCapacityToString(capacity)
|
||||
}
|
||||
},
|
||||
template: `<div style="font-size: 0.8em; color: grey">
|
||||
<div class="ui divider" v-if="hasLimit"></div>
|
||||
<div v-if="config.trafficLimit != null && config.trafficLimit.isOn">
|
||||
<span v-if="config.trafficLimit.dailySize != null && config.trafficLimit.dailySize.count > 0">日流量限制:{{composeCapacity(config.trafficLimit.dailySize)}}<br/></span>
|
||||
<span v-if="config.trafficLimit.monthlySize != null && config.trafficLimit.monthlySize.count > 0">月流量限制:{{composeCapacity(config.trafficLimit.monthlySize)}}<br/></span>
|
||||
</div>
|
||||
<div v-if="config.dailyRequests > 0">单日请求数限制:{{formatNumber(config.dailyRequests)}}</div>
|
||||
<div v-if="config.monthlyRequests > 0">单月请求数限制:{{formatNumber(config.monthlyRequests)}}</div>
|
||||
<div v-if="config.dailyWebsocketConnections > 0">单日Websocket限制:{{formatNumber(config.dailyWebsocketConnections)}}</div>
|
||||
<div v-if="config.monthlyWebsocketConnections > 0">单月Websocket限制:{{formatNumber(config.monthlyWebsocketConnections)}}</div>
|
||||
<div v-if="config.maxUploadSize != null && config.maxUploadSize.count > 0">文件上传限制:{{composeCapacity(config.maxUploadSize)}}</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,108 @@
|
||||
Vue.component("plan-price-bandwidth-config-box", {
|
||||
props: ["v-plan-price-bandwidth-config"],
|
||||
data: function () {
|
||||
let config = this.vPlanPriceBandwidthConfig
|
||||
if (config == null) {
|
||||
config = {
|
||||
percentile: 95,
|
||||
base: 0,
|
||||
ranges: [],
|
||||
supportRegions: false
|
||||
}
|
||||
}
|
||||
|
||||
if (config.ranges == null) {
|
||||
config.ranges = []
|
||||
}
|
||||
|
||||
return {
|
||||
config: config,
|
||||
bandwidthPercentile: config.percentile,
|
||||
priceBase: config.base,
|
||||
isEditing: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
priceBase: function (v) {
|
||||
let f = parseFloat(v)
|
||||
if (isNaN(f) || f < 0) {
|
||||
this.config.base = 0
|
||||
} else {
|
||||
this.config.base = f
|
||||
}
|
||||
},
|
||||
bandwidthPercentile: function (v) {
|
||||
let i = parseInt(v)
|
||||
if (isNaN(i) || i < 0) {
|
||||
this.config.percentile = 0
|
||||
} else {
|
||||
this.config.percentile = i
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
edit: function () {
|
||||
this.isEditing = !this.isEditing
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="bandwidthPriceJSON" :value="JSON.stringify(config)"/>
|
||||
<div>
|
||||
带宽百分位:<span v-if="config.percentile > 0">{{config.percentile}}th</span><span v-else class="disabled">没有设置</span> |
|
||||
基础带宽价格:<span v-if="config.base > 0">{{config.base}}元/Mbps</span><span v-else class="disabled">没有设置</span> |
|
||||
阶梯价格:<span v-if="config.ranges.length > 0">{{config.ranges.length}}段</span><span v-else class="disabled">没有设置</span> <span v-if="config.supportRegions">| 支持区域带宽计费</span>
|
||||
<span v-if="config.bandwidthAlgo == 'avg'"> | 使用平均带宽算法</span>
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" @click.prevent="edit">修改 <i class="icon angle" :class="{up: isEditing, down: !isEditing}"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isEditing" style="margin-top: 0.5em">
|
||||
<table class="ui table definition selectable" style="margin-top: 0">
|
||||
<tr>
|
||||
<td class="title">带宽百分位 *</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" style="width: 4em" maxlength="3" v-model="bandwidthPercentile"/>
|
||||
<span class="ui label">th</span>
|
||||
</div>
|
||||
<p class="comment">带宽计费位置,在1-100之间。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">基础带宽费用</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" v-model="priceBase" maxlength="10" style="width: 7em"/>
|
||||
<span class="ui label">元/Mbps</span>
|
||||
</div>
|
||||
<p class="comment">没有定义带宽阶梯价格时,使用此价格。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>带宽阶梯价格</td>
|
||||
<td>
|
||||
<plan-bandwidth-ranges v-model="config.ranges"></plan-bandwidth-ranges>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>支持按区域带宽计费</td>
|
||||
<td>
|
||||
<checkbox v-model="config.supportRegions"></checkbox>
|
||||
<p class="comment">选中后,表示可以根据节点所在区域设置不同的带宽价格。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>带宽算法</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="config.bandwidthAlgo">
|
||||
<option value="secondly">峰值带宽</option>
|
||||
<option value="avg">平均带宽</option>
|
||||
</select>
|
||||
<p class="comment" v-if="config.bandwidthAlgo == 'secondly'">按在计时时间段内(5分钟)最高带宽峰值计算,比如5分钟内最高的某个时间点带宽为100Mbps,那么就认为此时间段内的峰值带宽为100Mbps。修改此选项会同时影响到用量统计图表。</p>
|
||||
<p class="comment" v-if="config.bandwidthAlgo == 'avg'">按在计时时间段内(5分钟)平均带宽计算,即此时间段内的总流量除以时间段的秒数,比如5分钟(300秒)内总流量600MiB,那么带宽即为<code-label>600MiB * 8bit/300s = 16Mbps</code-label>;通常平均带宽算法要比峰值带宽要少很多。修改此选项会同时影响到用量统计图表。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,223 @@
|
||||
// 套餐价格配置
|
||||
Vue.component("plan-price-config-box", {
|
||||
props: ["v-price-type", "v-monthly-price", "v-seasonally-price", "v-yearly-price", "v-traffic-price", "v-bandwidth-price", "v-disable-period"],
|
||||
data: function () {
|
||||
let priceType = this.vPriceType
|
||||
if (priceType == null) {
|
||||
priceType = "bandwidth"
|
||||
}
|
||||
|
||||
// 按时间周期计费
|
||||
let monthlyPriceNumber = 0
|
||||
let monthlyPrice = this.vMonthlyPrice
|
||||
if (monthlyPrice == null || monthlyPrice <= 0) {
|
||||
monthlyPrice = ""
|
||||
} else {
|
||||
monthlyPrice = monthlyPrice.toString()
|
||||
monthlyPriceNumber = parseFloat(monthlyPrice)
|
||||
if (isNaN(monthlyPriceNumber)) {
|
||||
monthlyPriceNumber = 0
|
||||
}
|
||||
}
|
||||
|
||||
let seasonallyPriceNumber = 0
|
||||
let seasonallyPrice = this.vSeasonallyPrice
|
||||
if (seasonallyPrice == null || seasonallyPrice <= 0) {
|
||||
seasonallyPrice = ""
|
||||
} else {
|
||||
seasonallyPrice = seasonallyPrice.toString()
|
||||
seasonallyPriceNumber = parseFloat(seasonallyPrice)
|
||||
if (isNaN(seasonallyPriceNumber)) {
|
||||
seasonallyPriceNumber = 0
|
||||
}
|
||||
}
|
||||
|
||||
let yearlyPriceNumber = 0
|
||||
let yearlyPrice = this.vYearlyPrice
|
||||
if (yearlyPrice == null || yearlyPrice <= 0) {
|
||||
yearlyPrice = ""
|
||||
} else {
|
||||
yearlyPrice = yearlyPrice.toString()
|
||||
yearlyPriceNumber = parseFloat(yearlyPrice)
|
||||
if (isNaN(yearlyPriceNumber)) {
|
||||
yearlyPriceNumber = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 按流量计费
|
||||
let trafficPrice = this.vTrafficPrice
|
||||
let trafficPriceBaseNumber = 0
|
||||
if (trafficPrice != null) {
|
||||
trafficPriceBaseNumber = trafficPrice.base
|
||||
} else {
|
||||
trafficPrice = {
|
||||
base: 0
|
||||
}
|
||||
}
|
||||
let trafficPriceBase = ""
|
||||
if (trafficPriceBaseNumber > 0) {
|
||||
trafficPriceBase = trafficPriceBaseNumber.toString()
|
||||
}
|
||||
|
||||
// 按带宽计费
|
||||
let bandwidthPrice = this.vBandwidthPrice
|
||||
if (bandwidthPrice == null) {
|
||||
bandwidthPrice = {
|
||||
percentile: 95,
|
||||
ranges: []
|
||||
}
|
||||
} else if (bandwidthPrice.ranges == null) {
|
||||
bandwidthPrice.ranges = []
|
||||
}
|
||||
|
||||
return {
|
||||
priceType: priceType,
|
||||
monthlyPrice: monthlyPrice,
|
||||
seasonallyPrice: seasonallyPrice,
|
||||
yearlyPrice: yearlyPrice,
|
||||
|
||||
monthlyPriceNumber: monthlyPriceNumber,
|
||||
seasonallyPriceNumber: seasonallyPriceNumber,
|
||||
yearlyPriceNumber: yearlyPriceNumber,
|
||||
|
||||
trafficPriceBase: trafficPriceBase,
|
||||
trafficPrice: trafficPrice,
|
||||
|
||||
bandwidthPrice: bandwidthPrice,
|
||||
bandwidthPercentile: bandwidthPrice.percentile
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeBandwidthPriceRanges: function (ranges) {
|
||||
this.bandwidthPrice.ranges = ranges
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
monthlyPrice: function (v) {
|
||||
let price = parseFloat(v)
|
||||
if (isNaN(price)) {
|
||||
price = 0
|
||||
}
|
||||
this.monthlyPriceNumber = price
|
||||
},
|
||||
seasonallyPrice: function (v) {
|
||||
let price = parseFloat(v)
|
||||
if (isNaN(price)) {
|
||||
price = 0
|
||||
}
|
||||
this.seasonallyPriceNumber = price
|
||||
},
|
||||
yearlyPrice: function (v) {
|
||||
let price = parseFloat(v)
|
||||
if (isNaN(price)) {
|
||||
price = 0
|
||||
}
|
||||
this.yearlyPriceNumber = price
|
||||
},
|
||||
trafficPriceBase: function (v) {
|
||||
let price = parseFloat(v)
|
||||
if (isNaN(price)) {
|
||||
price = 0
|
||||
}
|
||||
this.trafficPrice.base = price
|
||||
},
|
||||
bandwidthPercentile: function (v) {
|
||||
let percentile = parseInt(v)
|
||||
if (isNaN(percentile) || percentile <= 0) {
|
||||
percentile = 95
|
||||
} else if (percentile > 100) {
|
||||
percentile = 100
|
||||
}
|
||||
this.bandwidthPrice.percentile = percentile
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="priceType" :value="priceType"/>
|
||||
<input type="hidden" name="monthlyPrice" :value="monthlyPriceNumber"/>
|
||||
<input type="hidden" name="seasonallyPrice" :value="seasonallyPriceNumber"/>
|
||||
<input type="hidden" name="yearlyPrice" :value="yearlyPriceNumber"/>
|
||||
<input type="hidden" name="trafficPriceJSON" :value="JSON.stringify(trafficPrice)"/>
|
||||
<input type="hidden" name="bandwidthPriceJSON" :value="JSON.stringify(bandwidthPrice)"/>
|
||||
|
||||
<div>
|
||||
<radio :v-value="'bandwidth'" :value="priceType" v-model="priceType"> 按带宽</radio>
|
||||
<radio :v-value="'traffic'" :value="priceType" v-model="priceType"> 按流量</radio>
|
||||
<radio :v-value="'period'" :value="priceType" v-model="priceType" v-show="typeof(vDisablePeriod) != 'boolean' || !vDisablePeriod"> 按时间周期</radio>
|
||||
</div>
|
||||
|
||||
<!-- 按时间周期 -->
|
||||
<div v-show="priceType == 'period'">
|
||||
<div class="ui divider"></div>
|
||||
<table class="ui table">
|
||||
<tr>
|
||||
<td class="title">月度价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" style="width: 7em" maxlength="10" v-model="monthlyPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">如果为0表示免费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">季度价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" style="width: 7em" maxlength="10" v-model="seasonallyPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">如果为0表示免费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">年度价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" style="width: 7em" maxlength="10" v-model="yearlyPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">如果为0表示免费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 按流量 -->
|
||||
<div v-show="priceType =='traffic'">
|
||||
<div class="ui divider"></div>
|
||||
<table class="ui table">
|
||||
<tr>
|
||||
<td class="title">基础流量费用 *</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" v-model="trafficPriceBase" maxlength="10" style="width: 7em"/>
|
||||
<span class="ui label">元/GB</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 按带宽 -->
|
||||
<div v-show="priceType == 'bandwidth'">
|
||||
<div class="ui divider"></div>
|
||||
<table class="ui table">
|
||||
<tr>
|
||||
<td class="title">带宽百分位 *</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" style="width: 4em" maxlength="3" v-model="bandwidthPercentile"/>
|
||||
<span class="ui label">th</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>带宽价格</td>
|
||||
<td>
|
||||
<plan-bandwidth-ranges v-model="bandwidthPrice.ranges" @change="changeBandwidthPriceRanges"></plan-bandwidth-ranges>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,75 @@
|
||||
Vue.component("plan-price-traffic-config-box", {
|
||||
props: ["v-plan-price-traffic-config"],
|
||||
data: function () {
|
||||
let config = this.vPlanPriceTrafficConfig
|
||||
if (config == null) {
|
||||
config = {
|
||||
base: 0,
|
||||
ranges: [],
|
||||
supportRegions: false
|
||||
}
|
||||
}
|
||||
|
||||
if (config.ranges == null) {
|
||||
config.ranges = []
|
||||
}
|
||||
|
||||
return {
|
||||
config: config,
|
||||
priceBase: config.base,
|
||||
isEditing: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
priceBase: function (v) {
|
||||
let f = parseFloat(v)
|
||||
if (isNaN(f) || f < 0) {
|
||||
this.config.base = 0
|
||||
} else {
|
||||
this.config.base = f
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
edit: function () {
|
||||
this.isEditing = !this.isEditing
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="trafficPriceJSON" :value="JSON.stringify(config)"/>
|
||||
<div>
|
||||
基础流量价格:<span v-if="config.base > 0">{{config.base}}元/GB</span><span v-else class="disabled">没有设置</span> |
|
||||
阶梯价格:<span v-if="config.ranges.length > 0">{{config.ranges.length}}段</span><span v-else class="disabled">没有设置</span> <span v-if="config.supportRegions">| 支持区域流量计费</span>
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" @click.prevent="edit">修改 <i class="icon angle" :class="{up: isEditing, down: !isEditing}"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isEditing" style="margin-top: 0.5em">
|
||||
<table class="ui table definition selectable" style="margin-top: 0">
|
||||
<tr>
|
||||
<td class="title">基础流量费用</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" v-model="priceBase" maxlength="10" style="width: 7em"/>
|
||||
<span class="ui label">元/GB</span>
|
||||
</div>
|
||||
<p class="comment">没有定义流量阶梯价格时,使用此价格。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>流量阶梯价格</td>
|
||||
<td>
|
||||
<plan-traffic-ranges v-model="config.ranges"></plan-traffic-ranges>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>支持按区域流量计费</td>
|
||||
<td>
|
||||
<checkbox v-model="config.supportRegions"></checkbox>
|
||||
<p class="comment">选中后,表示可以根据节点所在区域设置不同的流量价格;并且开启此项后才可以使用流量包。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
34
EdgeAdmin/web/public/js/components/plans/plan-price-view.js
Normal file
34
EdgeAdmin/web/public/js/components/plans/plan-price-view.js
Normal file
@@ -0,0 +1,34 @@
|
||||
Vue.component("plan-price-view", {
|
||||
props: ["v-plan"],
|
||||
data: function () {
|
||||
return {
|
||||
plan: this.vPlan
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<span v-if="plan.priceType == 'period'">
|
||||
按时间周期计费
|
||||
<div>
|
||||
<span class="grey small">
|
||||
<span v-if="plan.monthlyPrice > 0">月度:¥{{plan.monthlyPrice}}元<br/></span>
|
||||
<span v-if="plan.seasonallyPrice > 0">季度:¥{{plan.seasonallyPrice}}元<br/></span>
|
||||
<span v-if="plan.yearlyPrice > 0">年度:¥{{plan.yearlyPrice}}元</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span v-if="plan.priceType == 'traffic'">
|
||||
按流量计费
|
||||
<div>
|
||||
<span class="grey small">基础价格:¥{{plan.trafficPrice.base}}元/GiB</span>
|
||||
</div>
|
||||
</span>
|
||||
<div v-if="plan.priceType == 'bandwidth' && plan.bandwidthPrice != null && plan.bandwidthPrice.percentile > 0">
|
||||
按{{plan.bandwidthPrice.percentile}}th带宽计费
|
||||
<div>
|
||||
<div v-for="range in plan.bandwidthPrice.ranges">
|
||||
<span class="small grey">{{range.minMB}} - <span v-if="range.maxMB > 0">{{range.maxMB}}MiB</span><span v-else>∞</span>: <span v-if="range.totalPrice > 0">{{range.totalPrice}}元</span><span v-else="">{{range.pricePerMB}}元/MiB</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,234 @@
|
||||
Vue.component("plan-traffic-ranges", {
|
||||
props: ["value"],
|
||||
data: function () {
|
||||
let ranges = this.value
|
||||
if (ranges == null) {
|
||||
ranges = []
|
||||
}
|
||||
return {
|
||||
ranges: ranges,
|
||||
isAdding: false,
|
||||
|
||||
minGB: "",
|
||||
minGBUnit: "gb",
|
||||
|
||||
maxGB: "",
|
||||
maxGBUnit: "gb",
|
||||
|
||||
pricePerGB: "",
|
||||
totalPrice: "",
|
||||
addingRange: {
|
||||
minGB: 0,
|
||||
maxGB: 0,
|
||||
pricePerGB: 0,
|
||||
totalPrice: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add: function () {
|
||||
this.isAdding = !this.isAdding
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.minGB.focus()
|
||||
})
|
||||
},
|
||||
cancelAdding: function () {
|
||||
this.isAdding = false
|
||||
},
|
||||
confirm: function () {
|
||||
if (this.addingRange.minGB < 0) {
|
||||
teaweb.warn("流量下限需要大于0")
|
||||
return
|
||||
}
|
||||
if (this.addingRange.maxGB < 0) {
|
||||
teaweb.warn("流量上限需要大于0")
|
||||
return
|
||||
}
|
||||
if (this.addingRange.pricePerGB <= 0 && this.addingRange.totalPrice <= 0) {
|
||||
teaweb.warn("请设置单位价格或者总价格")
|
||||
return;
|
||||
}
|
||||
|
||||
this.isAdding = false
|
||||
this.minGB = ""
|
||||
this.maxGB = ""
|
||||
this.pricePerGB = ""
|
||||
this.totalPrice = ""
|
||||
this.ranges.push(this.addingRange)
|
||||
this.ranges.$sort(function (v1, v2) {
|
||||
if (v1.minGB < v2.minGB) {
|
||||
return -1
|
||||
}
|
||||
if (v1.minGB == v2.minGB) {
|
||||
if (v2.maxGB == 0 || v1.maxGB < v2.maxGB) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
})
|
||||
this.change()
|
||||
this.addingRange = {
|
||||
minGB: 0,
|
||||
maxGB: 0,
|
||||
pricePerGB: 0,
|
||||
totalPrice: 0
|
||||
}
|
||||
},
|
||||
remove: function (index) {
|
||||
this.ranges.$remove(index)
|
||||
this.change()
|
||||
},
|
||||
change: function () {
|
||||
this.$emit("change", this.ranges)
|
||||
},
|
||||
formatGB: function (gb) {
|
||||
return teaweb.formatBytes(gb * 1024 * 1024 * 1024)
|
||||
},
|
||||
changeMinGB: function (v) {
|
||||
let minGB = parseFloat(v.toString())
|
||||
if (isNaN(minGB) || minGB < 0) {
|
||||
minGB = 0
|
||||
}
|
||||
switch (this.minGBUnit) {
|
||||
case "tb":
|
||||
minGB *= 1024
|
||||
break
|
||||
case "pb":
|
||||
minGB *= 1024 * 1024
|
||||
break
|
||||
case "eb":
|
||||
minGB *= 1024 * 1024 * 1024
|
||||
break
|
||||
}
|
||||
this.addingRange.minGB = minGB
|
||||
},
|
||||
changeMaxGB: function (v) {
|
||||
let maxGB = parseFloat(v.toString())
|
||||
if (isNaN(maxGB) || maxGB < 0) {
|
||||
maxGB = 0
|
||||
}
|
||||
switch (this.maxGBUnit) {
|
||||
case "tb":
|
||||
maxGB *= 1024
|
||||
break
|
||||
case "pb":
|
||||
maxGB *= 1024 * 1024
|
||||
break
|
||||
case "eb":
|
||||
maxGB *= 1024 * 1024 * 1024
|
||||
break
|
||||
}
|
||||
this.addingRange.maxGB = maxGB
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
minGB: function (v) {
|
||||
this.changeMinGB(v)
|
||||
},
|
||||
minGBUnit: function (v) {
|
||||
this.changeMinGB(this.minGB)
|
||||
},
|
||||
maxGB: function (v) {
|
||||
this.changeMaxGB(v)
|
||||
},
|
||||
maxGBUnit: function (v) {
|
||||
this.changeMaxGB(this.maxGB)
|
||||
},
|
||||
pricePerGB: function (v) {
|
||||
let pricePerGB = parseFloat(v.toString())
|
||||
if (isNaN(pricePerGB) || pricePerGB < 0) {
|
||||
pricePerGB = 0
|
||||
}
|
||||
this.addingRange.pricePerGB = pricePerGB
|
||||
},
|
||||
totalPrice: function (v) {
|
||||
let totalPrice = parseFloat(v.toString())
|
||||
if (isNaN(totalPrice) || totalPrice < 0) {
|
||||
totalPrice = 0
|
||||
}
|
||||
this.addingRange.totalPrice = totalPrice
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<!-- 已有价格 -->
|
||||
<div v-if="ranges.length > 0">
|
||||
<div class="ui label basic small" v-for="(range, index) in ranges" style="margin-bottom: 0.5em">
|
||||
{{formatGB(range.minGB)}} - <span v-if="range.maxGB > 0">{{formatGB(range.maxGB)}}</span><span v-else>∞</span> 价格:<span v-if="range.totalPrice > 0">{{range.totalPrice}}元</span><span v-else="">{{range.pricePerGB}}元/GB</span>
|
||||
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
|
||||
<!-- 添加 -->
|
||||
<div v-if="isAdding">
|
||||
<table class="ui table">
|
||||
<tr>
|
||||
<td class="title">流量下限 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" placeholder="最小流量" style="width: 7em" maxlength="10" ref="minGB" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="minGB"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" v-model="minGBUnit">
|
||||
<option value="gb">GB</option>
|
||||
<option value="tb">TB</option>
|
||||
<option value="pb">PB</option>
|
||||
<option value="eb">EB</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">流量上限 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" placeholder="最大流量" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="maxGB"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" v-model="maxGBUnit">
|
||||
<option value="gb">GB</option>
|
||||
<option value="tb">TB</option>
|
||||
<option value="pb">PB</option>
|
||||
<option value="eb">EB</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment">如果填0,表示上不封顶。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">单位价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" placeholder="单位价格" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="pricePerGB"/>
|
||||
<span class="ui label">元/GB</span>
|
||||
</div>
|
||||
<p class="comment">和总价格二选一。如果设置了单位价格,那么"总价格 = 单位价格 x 流量/GB"。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>总价格</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" placeholder="总价格" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="totalPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">固定的总价格,和单位价格二选一。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button class="ui button small" type="button" @click.prevent="confirm">确定</button>
|
||||
<a href="" title="取消" @click.prevent="cancelAdding"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<div v-if="!isAdding">
|
||||
<button class="ui button small" type="button" @click.prevent="add">+</button>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
Vue.component("plan-user-selector", {
|
||||
props: ["v-user-id"],
|
||||
data: function () {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
change: function (userId) {
|
||||
this.$emit("change", userId)
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<user-selector :v-user-id="vUserId" data-url="/plans/users/options" @change="change"></user-selector>
|
||||
</div>`
|
||||
})
|
||||
Reference in New Issue
Block a user