WAF规则中国家/地区、省份、城市、ISP增加候选项检索和选择

This commit is contained in:
刘祥超
2022-06-14 17:38:50 +08:00
parent c82fa4709d
commit 275280d24e
18 changed files with 467 additions and 43 deletions

View File

@@ -352,6 +352,10 @@ func (this *RPCClient) RegionProvinceRPC() pb.RegionProvinceServiceClient {
return pb.NewRegionProvinceServiceClient(this.pickConn())
}
func (this *RPCClient) RegionCityRPC() pb.RegionCityServiceClient {
return pb.NewRegionCityServiceClient(this.pickConn())
}
func (this *RPCClient) RegionProviderRPC() pb.RegionProviderServiceClient {
return pb.NewRegionProviderServiceClient(this.pickConn())
}

View File

@@ -0,0 +1,44 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type CityOptionsAction struct {
actionutils.ParentAction
}
func (this *CityOptionsAction) RunPost(params struct{}) {
citiesResp, err := this.RPC().RegionCityRPC().FindAllEnabledRegionCities(this.AdminContext(), &pb.FindAllEnabledRegionCitiesRequest{
IncludeRegionProvince: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var cityMaps = []maps.Map{}
for _, city := range citiesResp.RegionCities {
if city.Codes == nil {
city.Codes = []string{}
}
var fullname = city.Name
if city.RegionProvince != nil && len(city.RegionProvince.Name) > 0 && city.RegionProvince.Name != city.Name {
fullname = city.RegionProvince.Name + " " + fullname
}
cityMaps = append(cityMaps, maps.Map{
"id": city.Id,
"name": city.Name,
"fullname": fullname,
"codes": city.Codes,
})
}
this.Data["cities"] = cityMaps
this.Success()
}

View File

@@ -0,0 +1,43 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"strings"
)
type CountryOptionsAction struct {
actionutils.ParentAction
}
func (this *CountryOptionsAction) RunPost(params struct{}) {
countriesResp, err := this.RPC().RegionCountryRPC().FindAllEnabledRegionCountries(this.AdminContext(), &pb.FindAllEnabledRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
if country.Codes == nil {
country.Codes = []string{}
}
var letter = ""
if len(country.Pinyin) > 0 && len(country.Pinyin) > 0 && len(country.Pinyin[0]) > 0 {
letter = strings.ToUpper(country.Pinyin[0][:1])
}
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.Name,
"fullname": letter + " " + country.Name,
"codes": country.Codes,
})
}
this.Data["countries"] = countryMaps
this.Success()
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
)
// 下载指定的文本内容
// DownloadAction 下载指定的文本内容
type DownloadAction struct {
actionutils.ParentAction
}

View File

@@ -25,6 +25,10 @@ func init() {
Post("/hideTip", new(HideTipAction)).
Post("/theme", new(ThemeAction)).
Post("/validateIPs", new(ValidateIPsAction)).
Post("/providerOptions", new(ProviderOptionsAction)).
Post("/countryOptions", new(CountryOptionsAction)).
Post("/provinceOptions", new(ProvinceOptionsAction)).
Post("/cityOptions", new(CityOptionsAction)).
EndAll()
// 开发环境下总是动态加载,以便于调试

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type ProviderOptionsAction struct {
actionutils.ParentAction
}
func (this *ProviderOptionsAction) RunPost(params struct{}) {
providersResp, err := this.RPC().RegionProviderRPC().FindAllEnabledRegionProviders(this.AdminContext(), &pb.FindAllEnabledRegionProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.RegionProviders {
if provider.Codes == nil {
provider.Codes = []string{}
}
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,
"name": provider.Name,
"codes": provider.Codes,
})
}
this.Data["providers"] = providerMaps
this.Success()
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type ProvinceOptionsAction struct {
actionutils.ParentAction
}
func (this *ProvinceOptionsAction) RunPost(params struct{}) {
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllEnabledRegionProvincesWithCountryId(this.AdminContext(), &pb.FindAllEnabledRegionProvincesWithCountryIdRequest{RegionCountryId: ChinaCountryId})
if err != nil {
this.ErrorPage(err)
return
}
var provinceMaps = []maps.Map{}
for _, province := range provincesResp.RegionProvinces {
if province.Codes == nil {
province.Codes = []string{}
}
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
"name": province.Name,
"codes": province.Codes,
})
}
this.Data["provinces"] = provinceMaps
this.Success()
}

View File

@@ -4258,10 +4258,10 @@ Vue.component("traffic-map-box",{props:["v-stats","v-is-attack"],mounted:functio
<div class="margin"></div>
</div>`}),Vue.component("request-variables-describer",{data:function(){return{vars:[]}},methods:{update:function(e){this.vars=[];let i=this;e.replace(/\${.+?}/g,function(e){var t=i.findVar(e);if(null==t)return e;i.vars.push(t)})},findVar:function(t){let i=null;return window.REQUEST_VARIABLES.forEach(function(e){e.code==t&&(i=e)}),i}},template:`<span>
<span v-for="(v, index) in vars"><code-label :title="v.description">{{v.code}}</code-label> - {{v.name}}<span v-if="index < vars.length-1"></span></span>
</span>`}),Vue.component("combo-box",{props:["name","title","placeholder","size","v-items","v-value"],data:function(){let e=this.vItems,i=((e=null!=e&&e instanceof Array?e:[]).forEach(function(e){null==e.value&&(e.value=e.id)}),null);if(null!=this.vValue){let t=this;e.forEach(function(e){e.value==t.vValue&&(i=e)})}return{allItems:e,items:e.$copy(),selectedItem:i,keyword:"",visible:!1,hideTimer:null,hoverIndex:0}},methods:{reset:function(){this.selectedItem=null,this.change(),this.hoverIndex=0;let e=this;setTimeout(function(){e.$refs.searchBox&&e.$refs.searchBox.focus()})},changeKeyword:function(){this.hoverIndex=0;let t=this.keyword;0==t.length?this.items=this.allItems.$copy():this.items=this.allItems.$copy().filter(function(e){return teaweb.match(e.name,t)})},selectItem:function(e){this.selectedItem=e,this.change(),this.hoverIndex=0,this.keyword="",this.changeKeyword()},confirm:function(){this.items.length>this.hoverIndex&&this.selectItem(this.items[this.hoverIndex])},show:function(){this.visible=!0},hide:function(){let e=this;this.hideTimer=setTimeout(function(){e.visible=!1},500)},downItem:function(){this.hoverIndex++,this.hoverIndex>this.items.length-1&&(this.hoverIndex=0),this.focusItem()},upItem:function(){this.hoverIndex--,this.hoverIndex<0&&(this.hoverIndex=0),this.focusItem()},focusItem:function(){if(this.hoverIndex<this.items.length){this.$refs.itemRef[this.hoverIndex].focus();let e=this;setTimeout(function(){e.$refs.searchBox.focus(),null!=e.hideTimer&&(clearTimeout(e.hideTimer),e.hideTimer=null)})}},change:function(){this.$emit("change",this.selectedItem);let e=this;setTimeout(function(){null!=e.$refs.selectedLabel&&e.$refs.selectedLabel.focus()})},submitForm:function(e){if("A"==e.target.tagName){let e=this.$refs.selectedLabel.parentNode;for(;;){if(null==(e=e.parentNode)||"BODY"==e.tagName)return;if("FORM"==e.tagName){e.submit();break}}}}},template:`<div style="display: inline; z-index: 10; background: white">
</span>`}),Vue.component("combo-box",{props:["name","title","placeholder","size","v-items","v-value","data-url","data-key","width"],mounted:function(){var e=this.dataUrl;let i=this.dataKey,s=this;null!=e&&0<e.length&&null!=i&&Tea.action(e).post().success(function(t){if(null!=t.data&&"object"==typeof t.data[i]){let e=s.formatItems(t.data[i]);s.allItems=e,s.items=e.$copy(),null!=s.vValue&&e.forEach(function(e){e.value==s.vValue&&(s.selectedItem=e)})}});e=this.$refs.searchBox.offsetWidth;null!=e&&0<e?this.$refs.menu.style.width=e+"px":0<this.styleWidth.length&&(this.$refs.menu.style.width=this.styleWidth)},data:function(){let e=this.vItems,i=(null!=e&&e instanceof Array||(e=[]),e=this.formatItems(e),null);if(null!=this.vValue){let t=this;e.forEach(function(e){e.value==t.vValue&&(i=e)})}let t=this.width;return null==t||0==t.length?t="11em":/\d+$/.test(t)&&(t+="em"),{allItems:e,items:e.$copy(),selectedItem:i,keyword:"",visible:!1,hideTimer:null,hoverIndex:0,styleWidth:t}},methods:{formatItems:function(e){return e.forEach(function(e){null==e.value&&(e.value=e.id)}),e},reset:function(){this.selectedItem=null,this.change(),this.hoverIndex=0;let e=this;setTimeout(function(){e.$refs.searchBox&&e.$refs.searchBox.focus()})},clear:function(){this.selectedItem=null,this.change(),this.hoverIndex=0},changeKeyword:function(){this.hoverIndex=0;let t=this.keyword;0==t.length?this.items=this.allItems.$copy():this.items=this.allItems.$copy().filter(function(e){return!!(null!=e.fullname&&0<e.fullname.length&&teaweb.match(e.fullname,t))||teaweb.match(e.name,t)})},selectItem:function(e){this.selectedItem=e,this.change(),this.hoverIndex=0,this.keyword="",this.changeKeyword()},confirm:function(){this.items.length>this.hoverIndex&&this.selectItem(this.items[this.hoverIndex])},show:function(){this.visible=!0},hide:function(){let e=this;this.hideTimer=setTimeout(function(){e.visible=!1},500)},downItem:function(){this.hoverIndex++,this.hoverIndex>this.items.length-1&&(this.hoverIndex=0),this.focusItem()},upItem:function(){this.hoverIndex--,this.hoverIndex<0&&(this.hoverIndex=0),this.focusItem()},focusItem:function(){if(this.hoverIndex<this.items.length){this.$refs.itemRef[this.hoverIndex].focus();let e=this;setTimeout(function(){e.$refs.searchBox.focus(),null!=e.hideTimer&&(clearTimeout(e.hideTimer),e.hideTimer=null)})}},change:function(){this.$emit("change",this.selectedItem);let e=this;setTimeout(function(){null!=e.$refs.selectedLabel&&e.$refs.selectedLabel.focus()})},submitForm:function(e){if("A"==e.target.tagName){let e=this.$refs.selectedLabel.parentNode;for(;;){if(null==(e=e.parentNode)||"BODY"==e.tagName)return;if("FORM"==e.tagName){e.submit();break}}}}},template:`<div style="display: inline; z-index: 10; background: white" class="combo-box">
<!-- 搜索框 -->
<div v-if="selectedItem == null">
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" style="width: 11em" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
</div>
<!-- 当前选中 -->
@@ -4273,9 +4273,12 @@ Vue.component("traffic-map-box",{props:["v-stats","v-is-attack"],mounted:functio
</div>
<!-- 菜单 -->
<div v-if="selectedItem == null && items.length > 0 && visible">
<div class="ui menu vertical small narrow-scrollbar" style="width: 11em; max-height: 17em; overflow-y: auto; position: absolute; border: rgba(129, 177, 210, 0.81) 1px solid; border-top: 0; z-index: 100">
<a href="" v-for="(item, index) in items" ref="itemRef" class="item" :class="{active: index == hoverIndex, blue: index == hoverIndex}" @click.prevent="selectItem(item)" style="line-height: 1.4">{{item.name}}</a>
<div v-show="selectedItem == null && items.length > 0 && visible">
<div class="ui menu vertical small narrow-scrollbar" ref="menu">
<a href="" v-for="(item, index) in items" ref="itemRef" class="item" :class="{active: index == hoverIndex, blue: index == hoverIndex}" @click.prevent="selectItem(item)" style="line-height: 1.4">
<span v-if="item.fullname != null && item.fullname.length > 0">{{item.fullname}}</span>
<span v-else>{{item.name}}</span>
</a>
</div>
</div>
</div>`}),Vue.component("time-duration-box",{props:["v-name","v-value","v-count","v-unit"],mounted:function(){this.change()},data:function(){let e=this.vValue;return"number"!=typeof(e=null==e?{count:this.vCount,unit:this.vUnit}:e).count&&(e.count=-1),{duration:e,countString:0<=e.count?e.count.toString():""}},watch:{countString:function(e){var e=e.trim();0==e.length?this.duration.count=-1:(e=parseInt(e),isNaN(e)||(this.duration.count=e),this.change())}},methods:{change:function(){this.$emit("change",this.duration)}},template:`<div class="ui fields inline" style="padding-bottom: 0; margin-bottom: 0">

View File

@@ -12802,19 +12802,49 @@ Vue.component("request-variables-describer", {
Vue.component("combo-box", {
props: ["name", "title", "placeholder", "size", "v-items", "v-value"],
// data-url 和 data-key 成对出现
props: ["name", "title", "placeholder", "size", "v-items", "v-value", "data-url", "data-key", "width"],
mounted: function () {
// 从URL中获取选项数据
let dataUrl = this.dataUrl
let dataKey = this.dataKey
let that = this
if (dataUrl != null && dataUrl.length > 0 && dataKey != null) {
Tea.action(dataUrl)
.post()
.success(function (resp) {
if (resp.data != null) {
if (typeof (resp.data[dataKey]) == "object") {
let items = that.formatItems(resp.data[dataKey])
that.allItems = items
that.items = items.$copy()
if (that.vValue != null) {
items.forEach(function (v) {
if (v.value == that.vValue) {
that.selectedItem = v
}
})
}
}
}
})
}
// 设定菜单宽度
let inputWidth = this.$refs.searchBox.offsetWidth
if (inputWidth != null && inputWidth > 0) {
this.$refs.menu.style.width = inputWidth + "px"
} else if (this.styleWidth.length > 0) {
this.$refs.menu.style.width = this.styleWidth
}
},
data: function () {
let items = this.vItems
if (items == null || !(items instanceof Array)) {
items = []
}
// 自动使用ID作为值
items.forEach(function (v) {
if (v.value == null) {
v.value = v.id
}
})
items = this.formatItems(items)
// 当前选中项
let selectedItem = null
@@ -12827,17 +12857,35 @@ Vue.component("combo-box", {
})
}
let width = this.width
if (width == null || width.length == 0) {
width = "11em"
} else {
if (/\d+$/.test(width)) {
width += "em"
}
}
return {
allItems: items,
items: items.$copy(),
selectedItem: selectedItem,
allItems: items, // 原始的所有的items
items: items.$copy(), // 候选的items
selectedItem: selectedItem, // 选中的item
keyword: "",
visible: false,
hideTimer: null,
hoverIndex: 0
hoverIndex: 0,
styleWidth: width
}
},
methods: {
formatItems: function (items) {
items.forEach(function (v) {
if (v.value == null) {
v.value = v.id
}
})
return items
},
reset: function () {
this.selectedItem = null
this.change()
@@ -12850,6 +12898,11 @@ Vue.component("combo-box", {
}
})
},
clear: function () {
this.selectedItem = null
this.change()
this.hoverIndex = 0
},
changeKeyword: function () {
this.hoverIndex = 0
let keyword = this.keyword
@@ -12858,6 +12911,9 @@ Vue.component("combo-box", {
return
}
this.items = this.allItems.$copy().filter(function (v) {
if (v.fullname != null && v.fullname.length > 0 && teaweb.match(v.fullname, keyword)) {
return true
}
return teaweb.match(v.name, keyword)
})
},
@@ -12938,10 +12994,10 @@ Vue.component("combo-box", {
}
}
},
template: `<div style="display: inline; z-index: 10; background: white">
template: `<div style="display: inline; z-index: 10; background: white" class="combo-box">
<!-- 搜索框 -->
<div v-if="selectedItem == null">
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" style="width: 11em" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
</div>
<!-- 当前选中 -->
@@ -12953,9 +13009,12 @@ Vue.component("combo-box", {
</div>
<!-- 菜单 -->
<div v-if="selectedItem == null && items.length > 0 && visible">
<div class="ui menu vertical small narrow-scrollbar" style="width: 11em; max-height: 17em; overflow-y: auto; position: absolute; border: rgba(129, 177, 210, 0.81) 1px solid; border-top: 0; z-index: 100">
<a href="" v-for="(item, index) in items" ref="itemRef" class="item" :class="{active: index == hoverIndex, blue: index == hoverIndex}" @click.prevent="selectItem(item)" style="line-height: 1.4">{{item.name}}</a>
<div v-show="selectedItem == null && items.length > 0 && visible">
<div class="ui menu vertical small narrow-scrollbar" ref="menu">
<a href="" v-for="(item, index) in items" ref="itemRef" class="item" :class="{active: index == hoverIndex, blue: index == hoverIndex}" @click.prevent="selectItem(item)" style="line-height: 1.4">
<span v-if="item.fullname != null && item.fullname.length > 0">{{item.fullname}}</span>
<span v-else>{{item.name}}</span>
</a>
</div>
</div>
</div>`

View File

@@ -1,17 +1,47 @@
Vue.component("combo-box", {
props: ["name", "title", "placeholder", "size", "v-items", "v-value"],
// data-url 和 data-key 成对出现
props: ["name", "title", "placeholder", "size", "v-items", "v-value", "data-url", "data-key", "width"],
mounted: function () {
// 从URL中获取选项数据
let dataUrl = this.dataUrl
let dataKey = this.dataKey
let that = this
if (dataUrl != null && dataUrl.length > 0 && dataKey != null) {
Tea.action(dataUrl)
.post()
.success(function (resp) {
if (resp.data != null) {
if (typeof (resp.data[dataKey]) == "object") {
let items = that.formatItems(resp.data[dataKey])
that.allItems = items
that.items = items.$copy()
if (that.vValue != null) {
items.forEach(function (v) {
if (v.value == that.vValue) {
that.selectedItem = v
}
})
}
}
}
})
}
// 设定菜单宽度
let inputWidth = this.$refs.searchBox.offsetWidth
if (inputWidth != null && inputWidth > 0) {
this.$refs.menu.style.width = inputWidth + "px"
} else if (this.styleWidth.length > 0) {
this.$refs.menu.style.width = this.styleWidth
}
},
data: function () {
let items = this.vItems
if (items == null || !(items instanceof Array)) {
items = []
}
// 自动使用ID作为值
items.forEach(function (v) {
if (v.value == null) {
v.value = v.id
}
})
items = this.formatItems(items)
// 当前选中项
let selectedItem = null
@@ -24,17 +54,35 @@ Vue.component("combo-box", {
})
}
let width = this.width
if (width == null || width.length == 0) {
width = "11em"
} else {
if (/\d+$/.test(width)) {
width += "em"
}
}
return {
allItems: items,
items: items.$copy(),
selectedItem: selectedItem,
allItems: items, // 原始的所有的items
items: items.$copy(), // 候选的items
selectedItem: selectedItem, // 选中的item
keyword: "",
visible: false,
hideTimer: null,
hoverIndex: 0
hoverIndex: 0,
styleWidth: width
}
},
methods: {
formatItems: function (items) {
items.forEach(function (v) {
if (v.value == null) {
v.value = v.id
}
})
return items
},
reset: function () {
this.selectedItem = null
this.change()
@@ -47,6 +95,11 @@ Vue.component("combo-box", {
}
})
},
clear: function () {
this.selectedItem = null
this.change()
this.hoverIndex = 0
},
changeKeyword: function () {
this.hoverIndex = 0
let keyword = this.keyword
@@ -55,6 +108,9 @@ Vue.component("combo-box", {
return
}
this.items = this.allItems.$copy().filter(function (v) {
if (v.fullname != null && v.fullname.length > 0 && teaweb.match(v.fullname, keyword)) {
return true
}
return teaweb.match(v.name, keyword)
})
},
@@ -135,10 +191,10 @@ Vue.component("combo-box", {
}
}
},
template: `<div style="display: inline; z-index: 10; background: white">
template: `<div style="display: inline; z-index: 10; background: white" class="combo-box">
<!-- 搜索框 -->
<div v-if="selectedItem == null">
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" style="width: 11em" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
</div>
<!-- 当前选中 -->
@@ -150,9 +206,12 @@ Vue.component("combo-box", {
</div>
<!-- 菜单 -->
<div v-if="selectedItem == null && items.length > 0 && visible">
<div class="ui menu vertical small narrow-scrollbar" style="width: 11em; max-height: 17em; overflow-y: auto; position: absolute; border: rgba(129, 177, 210, 0.81) 1px solid; border-top: 0; z-index: 100">
<a href="" v-for="(item, index) in items" ref="itemRef" class="item" :class="{active: index == hoverIndex, blue: index == hoverIndex}" @click.prevent="selectItem(item)" style="line-height: 1.4">{{item.name}}</a>
<div v-show="selectedItem == null && items.length > 0 && visible">
<div class="ui menu vertical small narrow-scrollbar" ref="menu">
<a href="" v-for="(item, index) in items" ref="itemRef" class="item" :class="{active: index == hoverIndex, blue: index == hoverIndex}" @click.prevent="selectItem(item)" style="line-height: 1.4">
<span v-if="item.fullname != null && item.fullname.length > 0">{{item.fullname}}</span>
<span v-else>{{item.name}}</span>
</a>
</div>
</div>
</div>`

View File

@@ -824,4 +824,15 @@ textarea.wide-code {
font-family: Menlo, Monaco, "Courier New", monospace !important;
line-height: 1.6 !important;
}
.combo-box .menu {
max-height: 17em;
overflow-y: auto;
position: absolute;
border: rgba(129, 177, 210, 0.81) 1px solid;
border-top: 0;
z-index: 100;
}
.combo-box .menu::-webkit-scrollbar {
width: 4px;
}
/*# sourceMappingURL=@layout.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -876,3 +876,17 @@ textarea.wide-code {
font-family: Menlo, Monaco, "Courier New", monospace !important;
line-height: 1.6 !important;
}
// combo-box
.combo-box .menu {
max-height: 17em;
overflow-y: auto;
position: absolute;
border: rgba(129, 177, 210, 0.81) 1px solid;
border-top: 0;
z-index: 100
}
.combo-box .menu::-webkit-scrollbar {
width: 4px;
}

View File

@@ -88,7 +88,7 @@ p.margin {
}
/** 扩展UI **/
.field.text {
padding: .5em;
padding: 0.5em;
}
/** 右侧主操作区 **/
.main {
@@ -296,4 +296,15 @@ textarea.wide-code {
font-family: Menlo, Monaco, "Courier New", monospace !important;
line-height: 1.6 !important;
}
.combo-box .menu {
max-height: 17em;
overflow-y: auto;
position: absolute;
border: rgba(129, 177, 210, 0.81) 1px solid;
border-top: 0;
z-index: 100;
}
.combo-box .menu::-webkit-scrollbar {
width: 4px;
}
/*# sourceMappingURL=@layout_popup.css.map */

View File

@@ -1 +1 @@
{"version":3,"sources":["@layout_popup.less"],"names":[],"mappings":"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","file":"@layout_popup.css"}
{"version":3,"sources":["@layout_popup.less"],"names":[],"mappings":"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","file":"@layout_popup.css"}

View File

@@ -360,3 +360,17 @@ textarea.wide-code {
font-family: Menlo, Monaco, "Courier New", monospace !important;
line-height: 1.6 !important;
}
// combo-box
.combo-box .menu {
max-height: 17em;
overflow-y: auto;
position: absolute;
border: rgba(129, 177, 210, 0.81) 1px solid;
border-top: 0;
z-index: 100
}
.combo-box .menu::-webkit-scrollbar {
width: 4px;
}

View File

@@ -107,6 +107,29 @@
<!-- 其余数据 -->
<textarea rows="3" maxlength="4096" name="value" v-model="rule.value" @input="changeRuleValue" v-else></textarea>
<!-- 特殊规则 -->
<div style="margin-top: 1em">
<!-- geo country name -->
<div v-if="checkpoint != null && checkpoint.prefix == 'geoCountryName'" >
<combo-box title="已添加" width="14em" data-url="/ui/countryOptions" data-key="countries" placeholder="点这里选择国家/地区" @change="selectGeoCountryName" ref="countryComboBox" key="combo-box-country"></combo-box>
</div>
<!-- geo province name -->
<div v-if="checkpoint != null && checkpoint.prefix == 'geoProvinceName'" >
<combo-box title="已添加" data-url="/ui/provinceOptions" data-key="provinces" placeholder="点这里选择省份名称" @change="selectGeoProvinceName" ref="provinceComboBox" key="combo-box-province"></combo-box>
</div>
<!-- geo city name -->
<div v-if="checkpoint != null && checkpoint.prefix == 'geoCityName'" >
<combo-box title="已添加" data-url="/ui/cityOptions" data-key="cities" placeholder="点这里选择城市名称" @change="selectGeoCityName" ref="cityComboBox" key="combo-box-city"></combo-box>
</div>
<!-- ISP Name -->
<div v-if="checkpoint != null && checkpoint.prefix == 'ispName'" >
<combo-box title="已添加" data-url="/ui/providerOptions" data-key="providers" placeholder="点这里选择ISP名称" @change="selectISPName" ref="ispComboBox" key="combo-box-isp"></combo-box>
</div>
</div>
</td>
</tr>
<tr v-if="rule.operator == 'match' || rule.operator == 'not match'">

View File

@@ -121,4 +121,68 @@ Tea.context(function () {
this.regexpTestResult = resp.data.result
})
}
// isp
this.selectISPName = function (isp) {
if (isp == null) {
return
}
let ispName = isp.name
this.$refs.ispComboBox.clear()
if (this.rule.value.length == 0) {
this.rule.value = ispName
} else {
this.rule.value += "|" + ispName
}
}
// country
this.selectGeoCountryName = function (country) {
if (country == null) {
return
}
let countryName = country.name
this.$refs.countryComboBox.clear()
if (this.rule.value.length == 0) {
this.rule.value = countryName
} else {
this.rule.value += "|" + countryName
}
}
// province
this.selectGeoProvinceName = function (province) {
if (province == null) {
return
}
let provinceName = province.name
this.$refs.provinceComboBox.clear()
if (this.rule.value.length == 0) {
this.rule.value = provinceName
} else {
this.rule.value += "|" + provinceName
}
}
// city
this.selectGeoCityName = function (city) {
if (city == null) {
return
}
let cityName = city.name
this.$refs.cityComboBox.clear()
if (this.rule.value.length == 0) {
this.rule.value = cityName
} else {
this.rule.value += "|" + cityName
}
}
})