@@ -709,7 +709,7 @@ Vue.component("traffic-map-box",{props:["v-stats","v-is-attack"],mounted:functio
<div v-if="!isAdding">
<button class="ui button small" type="button" @click.prevent="add">+</button>
</div>
</div> ` } ) , 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 e = this . vPriceType , t = ( null == e && ( e = "bandwidth" ) , 0 ) , i = this . vMonthlyPrice , s = ( null == i || i <= 0 ? i = "" : ( i = i . toString ( ) , t = parseFloat ( i ) , isNaN ( t ) && ( t = 0 ) ) , 0 ) , n = this . vSeasonallyPrice , o = ( null == n || n <= 0 ? n = "" : ( n = n . toString ( ) , s = parseFloat ( n ) , isNaN ( s ) && ( s = 0 ) ) , 0 ) , a = this . vYearlyPrice , l = ( null == a || a <= 0 ? a = "" : ( a = a . toString ( ) , o = parseFloat ( a ) , isNaN ( o ) && ( o = 0 ) ) , this . vTrafficPrice ) , c = 0 , r = ( null != l ? c = l . base : l = { base : 0 } , "" ) , d = ( 0 < c && ( r = c . toString ( ) ) , this . vBandwidthPrice ) ; return null == d ? d = { percentile : 95 , ranges : [ ] } : null == d . ranges && ( d . ranges = [ ] ) , { priceType : e , monthlyPrice : i , seasonallyPrice : n , yearlyPrice : a , monthlyPriceNumber : t , seasonallyPriceNumber : s , yearlyPriceNumber : o , trafficPriceBase : r , trafficPrice : l , bandwidthPrice : d , bandwidthPercentile : d . percentile } } , methods : { changeBandwidthPriceRanges : function ( e ) { this . bandwidthPrice . ranges = e } } , watch : { monthlyPrice : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . monthlyPriceNumber = t } , seasonallyPrice : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . seasonallyPriceNumber = t } , yearlyPrice : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . yearlyPriceNumber = t } , trafficPriceBase : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . trafficPrice . base = t } , bandwidthPercentile : function ( e ) { let t = parseInt ( e ) ; isNaN ( t ) || t <= 0 ? t = 95 : 100 < t && ( t = 100 ) , this . bandwidthPrice . percentile = t } } , template : ` <div>
</div> ` } ) , 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 e = this . vPriceType , t = ( null == e && ( e = "bandwidth" ) , 0 ) , i = this . vMonthlyPrice , s = ( null == i || i <= 0 ? i = "" : ( i = i . toString ( ) , t = parseFloat ( i ) , isNaN ( t ) && ( t = 0 ) ) , 0 ) , n = this . vSeasonallyPrice , o = ( null == n || n <= 0 ? n = "" : ( n = n . toString ( ) , s = parseFloat ( n ) , isNaN ( s ) && ( s = 0 ) ) , 0 ) , a = this . vYearlyPrice , l = ( null == a || a <= 0 ? a = "" : ( a = a . toString ( ) , o = parseFloat ( a ) , isNaN ( o ) && ( o = 0 ) ) , this . vTrafficPrice ) , r = 0 , c = ( null != l ? r = l . base : l = { base : 0 } , "" ) , d = ( 0 < r && ( c = r . toString ( ) ) , this . vBandwidthPrice ) ; return null == d ? d = { percentile : 95 , ranges : [ ] } : null == d . ranges && ( d . ranges = [ ] ) , { priceType : e , monthlyPrice : i , seasonallyPrice : n , yearlyPrice : a , monthlyPriceNumber : t , seasonallyPriceNumber : s , yearlyPriceNumber : o , trafficPriceBase : c , trafficPrice : l , bandwidthPrice : d , bandwidthPercentile : d . percentile } } , methods : { changeBandwidthPriceRanges : function ( e ) { this . bandwidthPrice . ranges = e } } , watch : { monthlyPrice : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . monthlyPriceNumber = t } , seasonallyPrice : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . seasonallyPriceNumber = t } , yearlyPrice : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . yearlyPriceNumber = t } , trafficPriceBase : function ( e ) { let t = parseFloat ( e ) ; isNaN ( t ) && ( t = 0 ) , this . trafficPrice . base = t } , bandwidthPercentile : function ( e ) { let t = parseInt ( e ) ; isNaN ( t ) || t <= 0 ? t = 95 : 100 < t && ( t = 100 ) , this . bandwidthPrice . percentile = t } } , template : ` <div>
<input type="hidden" name="priceType" :value="priceType"/>
<input type="hidden" name="monthlyPrice" :value="monthlyPriceNumber"/>
<input type="hidden" name="seasonallyPrice" :value="seasonallyPriceNumber"/>
@@ -1679,10 +1679,11 @@ Vue.component("traffic-map-box",{props:["v-stats","v-is-attack"],mounted:functio
</thead>
<tr v-for="origin in vOrigins">
<td :class="{disabled:!origin.isOn}"><a href="" @click.prevent="updateOrigin(origin.id)">{{origin.addr}} <i class="icon expand small"></i></a>
<div style="margin-top: 0.3em" v-if="origin.name.length > 0 || origin.hasCert || (origin.host != null && origin.host.length > 0) || (origin.domains != null && origin.domains.length > 0)">
<div style="margin-top: 0.3em" v-if="origin.name.length > 0 || origin.hasCert || (origin.host != null && origin.host.length > 0) || origin.followPort || (origin.domains != null && origin.domains.length > 0)">
<tiny-basic-label v-if="origin.name.length > 0">{{origin.name}}</tiny-basic-label>
<tiny-basic-label v-if="origin.hasCert">证书</tiny-basic-label>
<tiny-basic-label v-if="origin.host != null && origin.host.length > 0">主机名: {{origin.host}}</tiny-basic-label>
<tiny-basic-label v-if="origin.followPort">端口跟随</tiny-basic-label>
<span v-if="origin.domains != null && origin.domains.length > 0"><tiny-basic-label v-for="domain in origin.domains">匹配: {{domain}}</tiny-basic-label></span>
</div>
</td>
@@ -3027,7 +3028,7 @@ Vue.component("traffic-map-box",{props:["v-stats","v-is-attack"],mounted:functio
</div>
</div>
</div> ` } ) , Vue . component ( "http-firewall-captcha-options-viewer" , { props : [ "v-captcha-options" ] , mounted : function ( ) { this . updateSummary ( ) } , data : function ( ) { let e = this . vCaptchaOptions ; return { options : e = null == e ? { life : 0 , maxFails : 0 , failBlockTimeout : 0 , failBlockScopeAll : ! 1 , uiIsOn : ! 1 , uiTitle : "" , uiPrompt : "" , uiButtonTitle : "" , uiShowRequestId : ! 1 , uiCss : "" , uiFooter : "" , uiBody : "" , cookieId : "" , lang : "" } : e , summary : "" } } , methods : { updateSummary : function ( ) { let e = [ ] ; 0 < this . options . life && e . push ( "有效时间" + this . options . life + "秒" ) , 0 < this . options . maxFails && e . push ( "最多失败" + this . options . maxFails + "次" ) , 0 < this . options . failBlockTimeout && e . push ( "失败拦截" + this . options . failBlockTimeout + "秒" ) , this . options . failBlockScopeAll && e . push ( "全局封禁" ) , this . options . uiIsOn && e . push ( "定制UI" ) , 0 == e . length ? this . summary = "默认配置" : this . summary = e . join ( " / " ) } } , template : ` <div>{{summary}}</div>
` } ) , Vue . component ( "reverse-proxy-box" , { props : [ "v-reverse-proxy-ref" , "v-reverse-proxy-config" , "v-is-location" , "v-is-group" , "v-family" ] , data : function ( ) { let e = this . vReverseProxyRef , t = ( null == e && ( e = { isPrior : ! 1 , isOn : ! 1 , reverseProxyId : 0 } ) , this . vReverseProxyConfig ) , i = ( null == ( t = null == t ? { requestPath : "" , stripPrefix : "" , requestURI : "" , requestHost : "" , requestHostType : 0 , addHeaders : [ ] , connTimeout : { count : 0 , unit : "second" } , readTimeout : { count : 0 , unit : "second" } , idleTimeout : { count : 0 , unit : "second" } , maxConns : 0 , maxIdleConns : 0 , followRedirects : ! 1 } : t ) . addHeaders && ( t . addHeaders = [ ] ) , null == t . connTimeout && ( t . connTimeout = { count : 0 , unit : "second" } ) , null == t . readTimeout && ( t . readTimeout = { count : 0 , unit : "second" } ) , null == t . idleTimeout && ( t . idleTimeout = { count : 0 , unit : "second" } ) , null == t . proxyProtocol && Vue . set ( t , "proxyProtocol" , { isOn : ! 1 , version : 1 } ) , [ { name : "X-Real-IP" , isChecked : ! 1 } , { name : "X-Forwarded-For" , isChecked : ! 1 } , { name : "X-Forwarded-By" , isChecked : ! 1 } , { name : "X-Forwarded-Host" , isChecked : ! 1 } , { name : "X-Forwarded-Proto" , isChecked : ! 1 } ] ) ; return i . forEach ( function ( e ) { e . isChecked = t . addHeaders . $contains ( e . name ) } ) , { reverseProxyRef : e , reverseProxyConfig : t , advancedVisible : ! 1 , family : this . vFamily , forwardHeaders : i } } , watch : { "reverseProxyConfig.requestHostType" : function ( e ) { let t = parseInt ( e ) ; isNaN ( t ) && ( t = 0 ) , this . reverseProxyConfig . requestHostType = t } , "reverseProxyConfig.connTimeout.count" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . connTimeout . count = t } , "reverseProxyConfig.readTimeout.count" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . readTimeout . count = t } , "reverseProxyConfig.idleTimeout.count" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . idleTimeout . count = t } , "reverseProxyConfig.maxConns" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . maxConns = t } , "reverseProxyConfig.maxIdleConns" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . maxIdleConns = t } , "reverseProxyConfig.proxyProtocol.version" : function ( e ) { let t = parseInt ( e ) ; isNaN ( t ) && ( t = 1 ) , this . reverseProxyConfig . proxyProtocol . version = t } } , methods : { isOn : function ( ) { return ( ! this . vIsLocation && ! this . vIsGroup || this . reverseProxyRef . isPrior ) && this . reverseProxyRef . isOn } , changeAdvancedVisible : function ( e ) { this . advancedVisible = e } , changeAddHeader : function ( ) { this . reverseProxyConfig . addHeaders = this . forwardHeaders . filter ( function ( e ) { return e . isChecked } ) . map ( function ( e ) { return e . name } ) } } , template : ` <div>
` } ) , Vue . component ( "reverse-proxy-box" , { props : [ "v-reverse-proxy-ref" , "v-reverse-proxy-config" , "v-is-location" , "v-is-group" , "v-family" ] , data : function ( ) { let e = this . vReverseProxyRef , t = ( null == e && ( e = { isPrior : ! 1 , isOn : ! 1 , reverseProxyId : 0 } ) , this . vReverseProxyConfig ) , i = ( null == ( t = null == t ? { requestPath : "" , stripPrefix : "" , requestURI : "" , requestHost : "" , requestHostType : 0 , requestHostExcludingPort : ! 1 , addHeaders: [ ] , connTimeout : { count : 0 , unit : "second" } , readTimeout : { count : 0 , unit : "second" } , idleTimeout : { count : 0 , unit : "second" } , maxConns : 0 , maxIdleConns : 0 , followRedirects : ! 1 } : t ) . addHeaders && ( t . addHeaders = [ ] ) , null == t . connTimeout && ( t . connTimeout = { count : 0 , unit : "second" } ) , null == t . readTimeout && ( t . readTimeout = { count : 0 , unit : "second" } ) , null == t . idleTimeout && ( t . idleTimeout = { count : 0 , unit : "second" } ) , null == t . proxyProtocol && Vue . set ( t , "proxyProtocol" , { isOn : ! 1 , version : 1 } ) , [ { name : "X-Real-IP" , isChecked : ! 1 } , { name : "X-Forwarded-For" , isChecked : ! 1 } , { name : "X-Forwarded-By" , isChecked : ! 1 } , { name : "X-Forwarded-Host" , isChecked : ! 1 } , { name : "X-Forwarded-Proto" , isChecked : ! 1 } ] ) ; return i . forEach ( function ( e ) { e . isChecked = t . addHeaders . $contains ( e . name ) } ) , { reverseProxyRef : e , reverseProxyConfig : t , advancedVisible : ! 1 , family : this . vFamily , forwardHeaders : i } } , watch : { "reverseProxyConfig.requestHostType" : function ( e ) { let t = parseInt ( e ) ; isNaN ( t ) && ( t = 0 ) , this . reverseProxyConfig . requestHostType = t } , "reverseProxyConfig.connTimeout.count" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . connTimeout . count = t } , "reverseProxyConfig.readTimeout.count" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . readTimeout . count = t } , "reverseProxyConfig.idleTimeout.count" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . idleTimeout . count = t } , "reverseProxyConfig.maxConns" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . maxConns = t } , "reverseProxyConfig.maxIdleConns" : function ( e ) { let t = parseInt ( e ) ; ( isNaN ( t ) || t < 0 ) && ( t = 0 ) , this . reverseProxyConfig . maxIdleConns = t } , "reverseProxyConfig.proxyProtocol.version" : function ( e ) { let t = parseInt ( e ) ; isNaN ( t ) && ( t = 1 ) , this . reverseProxyConfig . proxyProtocol . version = t } } , methods : { isOn : function ( ) { return ( ! this . vIsLocation && ! this . vIsGroup || this . reverseProxyRef . isPrior ) && this . reverseProxyRef . isOn } , changeAdvancedVisible : function ( e ) { this . advancedVisible = e } , changeAddHeader : function ( ) { this . reverseProxyConfig . addHeaders = this . forwardHeaders . filter ( function ( e ) { return e . isChecked } ) . map ( function ( e ) { return e . name } ) } } , template : ` <div>
<input type="hidden" name="reverseProxyRefJSON" :value="JSON.stringify(reverseProxyRef)"/>
<input type="hidden" name="reverseProxyJSON" :value="JSON.stringify(reverseProxyConfig)"/>
<table class="ui table selectable definition">
@@ -3045,18 +3046,24 @@ Vue.component("traffic-map-box",{props:["v-stats","v-is-attack"],mounted:functio
<tr v-show="family == null || family == 'http'">
<td>回源主机名<em>( Host) </em></td>
<td>
<radio :v-value="0" v-model="reverseProxyConfig.requestHostType">跟随代理服务 </radio>
<radio :v-value="0" v-model="reverseProxyConfig.requestHostType">跟随CDN域名 </radio>
<radio :v-value="1" v-model="reverseProxyConfig.requestHostType">跟随源站</radio>
<radio :v-value="2" v-model="reverseProxyConfig.requestHostType">自定义</radio>
<div v-show="reverseProxyConfig.requestHostType == 2" style="margin-top: 0.8em">
<input type="text" placeholder="比如example.com" v-model="reverseProxyConfig.requestHost"/>
</div>
<p class="comment">请求源站时的Host, 用于修改源站接收到的域名
<span v-if="reverseProxyConfig.requestHostType == 0">, "跟随代理服务 "是指源站接收到的域名和当前代理服务 保持一致</span>
<span v-if="reverseProxyConfig.requestHostType == 0">, "跟随CDN域名 "是指源站接收到的域名和当前CDN访问域名 保持一致</span>
<span v-if="reverseProxyConfig.requestHostType == 1">, "跟随源站"是指源站接收到的域名仍然是填写的源站地址中的信息,不随代理服务域名改变而改变</span>
<span v-if="reverseProxyConfig.requestHostType == 2">, 自定义Host内容中支持请求变量</span>。</p>
</td>
</tr>
<tr v-show="family == null || family == 'http'">
<td>回源主机名不包含端口</td>
<td><checkbox v-model="reverseProxyConfig.requestHostExcludingPort"></checkbox>
<p class="comment">选中后表示移除回源主机名中的端口部分。</p>
</td>
</tr>
</tbody>
<more-options-tbody @change="changeAdvancedVisible" v-if="isOn()"></more-options-tbody>
<tbody v-show="isOn() && advancedVisible">
@@ -4425,7 +4432,7 @@ 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-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">
</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 ) } ) } } ) ; var e = this . $refs . searchBox ; null != e && ( null != ( e = e . offsetWidth ) && 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': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>