1.4.5.2
This commit is contained in:
27
EdgeAdmin/web/views/@default/settings/ip-library/@menu.html
Normal file
27
EdgeAdmin/web/views/@default/settings/ip-library/@menu.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<first-menu v-if="canAccess">
|
||||
<menu-item href="/settings/ip-library" code="index">IP库</menu-item>
|
||||
<menu-item href="/settings/ip-library/libraries/create" code="create">新制作</menu-item>
|
||||
<menu-item href="/settings/ip-library/upload" code="upload">上传</menu-item>
|
||||
<menu-item href="/settings/ip-library/library/test" code="test">测试</menu-item>
|
||||
<span class="item disabled">|</span>
|
||||
<span class="item"> <tip-icon content="如果没有上传IP库或者没有设置IP库为使用状态,那么将使用内置的IP库。"></tip-icon></span>
|
||||
<span class="item disabled">|</span>
|
||||
<menu-item href="/settings/ip-library/libraries" code="library">IP库源文件</menu-item>
|
||||
<!--<menu-item href="/settings/ip-library/unfinished" code="unfinished">未完成</menu-item>-->
|
||||
<span class="item disabled">|</span>
|
||||
<menu-item href="/settings/ip-library/countries" code="country">国家/地区</menu-item>
|
||||
<menu-item href="/settings/ip-library/provinces" code="province">省份/州</menu-item>
|
||||
<menu-item href="/settings/ip-library/cities" code="city">城市/市</menu-item>
|
||||
<menu-item href="/settings/ip-library/towns" code="town">区/县</menu-item>
|
||||
<menu-item href="/settings/ip-library/providers" code="provider">ISP运营商</menu-item>
|
||||
<span class="item disabled">|</span>
|
||||
<span class="item"><tip-icon content="为什么要修改国家、地区、省份、ISP运营商等别名?<br/>因为每个IP库提供商提供的IP库中的地区、ISP运营商名称都不一致,无法直接对应到数据库中的地区信息,所以需要一一人工校对后才能导入。"></tip-icon></span>
|
||||
</first-menu>
|
||||
|
||||
<first-menu v-if="!canAccess">
|
||||
<menu-item href="/settings/ip-library" code="index">IP库</menu-item>
|
||||
<menu-item href="/settings/ip-library/upload" code="upload">上传</menu-item>
|
||||
<menu-item href="/settings/ip-library/library/test" code="test">测试</menu-item>
|
||||
<span class="item disabled">|</span>
|
||||
<span class="item"><tip-icon content="如果没有上传IP库或者没有设置IP库为使用状态,那么将使用内置的IP库;目前只允许上传MaxMind官方提供的IP库文件。"></tip-icon></span>
|
||||
</first-menu>
|
||||
@@ -0,0 +1,60 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<div class="margin"></div>
|
||||
|
||||
<form class="ui form" method="get" action="/settings/ip-library/cities">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<combo-box name="countryId" :v-items="countries" :v-value="countryId" @change="changeCountry" placeholder="国家/地区"></combo-box>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<combo-box name="provinceId" :data-url="'/settings/ip-library/cities/provinceOptions?countryId=' + countryId" data-key="provinces" :v-value="provinceId" ref="provinceOptionsRef" placeholder="省/州"></combo-box>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<not-found-box v-if="cities.length == 0">暂时还没有城市/市。</not-found-box>
|
||||
|
||||
|
||||
<p class="ui basic message" v-if="cities.length > 0">共 {{cities.length}} 个城市/市。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="cities.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 3em">ID</th>
|
||||
<th style="width: 12em">城市/市名称</th>
|
||||
<th style="width: 12em">内置别名</th>
|
||||
<th style="width: 12em">自定义名称 <tip-icon content="修改在界面上显示的城市/市名称"></tip-icon></th>
|
||||
<th style="width: 12em">自定义别名 <tip-icon content="可以在IP库中通过别名找到当前城市/市,比如通过”北京市“、”北京“都能找到北京"></tip-icon></th>
|
||||
<th class="one op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="city in cities">
|
||||
<td>{{city.id}}</td>
|
||||
<td>{{city.name}}</td>
|
||||
<td>
|
||||
<div v-if="city.codes.length > 0">
|
||||
<span v-for="code in city.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="city.customName.length > 0">{{city.customName}}</span>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="city.customCodes.length > 0">
|
||||
<span v-for="code in city.customCodes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateCity(city.id)">修改</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,20 @@
|
||||
Tea.context(function () {
|
||||
this.changeCountry = function (item) {
|
||||
let provinceOptionsBox = this.$refs.provinceOptionsRef
|
||||
|
||||
if (item != null) {
|
||||
provinceOptionsBox.setDataURL("/settings/ip-library/cities/provinceOptions?countryId=" + item.value)
|
||||
provinceOptionsBox.reloadData()
|
||||
}
|
||||
|
||||
provinceOptionsBox.clear()
|
||||
}
|
||||
|
||||
this.updateCity = function (cityId) {
|
||||
teaweb.popup("/settings/ip-library/cities/updatePopup?cityId=" + cityId, {
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>定制城市/市信息</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="cityId" :value="city.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">省份/州名称</td>
|
||||
<td>{{city.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内置别名</td>
|
||||
<td>
|
||||
<div v-if="city.codes.length > 0">
|
||||
<span v-for="code in city.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义名称</td>
|
||||
<td>
|
||||
<input type="text" name="customName" maxlength="100" v-model="city.customName"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义别名</td>
|
||||
<td>
|
||||
<values-box name="customCodes" :v-values="city.customCodes"></values-box>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,38 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 3em">ID</th>
|
||||
<th style="width: 12em">国家/地区名称</th>
|
||||
<th style="width: 12em">内置别名</th>
|
||||
<th style="width: 12em">自定义名称 <tip-icon content="修改在界面上显示的国家/地区名称"></tip-icon></th>
|
||||
<th style="width: 12em">自定义别名 <tip-icon content="可以在IP库中通过别名找到当前国家/地区,比如通过美国、美利坚合众国都可以找到美国这个国家"></tip-icon></th>
|
||||
<th class="one op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="country in countries">
|
||||
<td>{{country.id}}</td>
|
||||
<td>{{country.name}}</td>
|
||||
<td>
|
||||
<div v-if="country.codes.length > 0">
|
||||
<span v-for="code in country.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="country.customName.length > 0">{{country.customName}}</span>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="country.customCodes.length > 0">
|
||||
<span v-for="code in country.customCodes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateCountry(country.id)">修改</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,9 @@
|
||||
Tea.context(function () {
|
||||
this.updateCountry = function (countryId) {
|
||||
teaweb.popup("/settings/ip-library/countries/updatePopup?countryId=" + countryId, {
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>定制国家/地区信息</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="countryId" :value="country.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">国家/地区名称</td>
|
||||
<td>{{country.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内置别名</td>
|
||||
<td>
|
||||
<div v-if="country.codes.length > 0">
|
||||
<span v-for="code in country.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义名称</td>
|
||||
<td>
|
||||
<input type="text" name="customName" maxlength="100" v-model="country.customName"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义别名</td>
|
||||
<td>
|
||||
<values-box name="customCodes" :v-values="country.customCodes"></values-box>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,4 @@
|
||||
th.number-col {
|
||||
width: 7em;
|
||||
}
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,EAAE;EACD,UAAA","file":"index.css"}
|
||||
39
EdgeAdmin/web/views/@default/settings/ip-library/index.html
Normal file
39
EdgeAdmin/web/views/@default/settings/ip-library/index.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<not-found-box v-if="artifacts.length == 0">暂时还没有IP库。</not-found-box>
|
||||
|
||||
<table class="ui table selectable celled" v-if="artifacts.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP库名称</th>
|
||||
<th class="two wide">IP库类型</th>
|
||||
<th class="two wide">尺寸</th>
|
||||
<th class="one wide center">下载</th>
|
||||
<th class="two wide">是否正在使用</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="artifact in artifacts" :class="{positive: artifact.isPublic}">
|
||||
<td>{{artifact.name}}
|
||||
<div>
|
||||
<span class="small grey">{{artifact.createdTime}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{artifact.libraryType}}</td>
|
||||
<td>
|
||||
<span v-if="artifact.file != null && artifact.file.size > 0">{{teaweb.formatBytes(artifact.file.size)}}</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
<a :href="'/settings/ip-library/download?artifactId=' + artifact.id" title="下载"><i class="icon download small"></i></a>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="artifact.isPublic" class="green">Y</span>
|
||||
<span v-else class="disabled">N</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="deleteArtifact(artifact.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
16
EdgeAdmin/web/views/@default/settings/ip-library/index.js
Normal file
16
EdgeAdmin/web/views/@default/settings/ip-library/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
Tea.context(function () {
|
||||
this.teaweb = teaweb
|
||||
|
||||
this.deleteArtifact = function (artifactId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除此IP库吗?", function () {
|
||||
that.$post("/settings/ip-library/delete")
|
||||
.params({
|
||||
artifactId: artifactId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.successRefresh("删除成功")
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
th.number-col {
|
||||
width: 7em;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.steps .step.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
/*# sourceMappingURL=create.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["create.less"],"names":[],"mappings":"AAAA,MACC,MAAK;EACJ,iBAAA","file":"create.css"}
|
||||
@@ -0,0 +1,300 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<div class="ui steps small fluid">
|
||||
<div class="ui step" :class="{active: step == STEP_TEMPLATE}">
|
||||
<div class="content">设置数据格式</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_UPLOAD}">
|
||||
<div class="content">上传文件</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_COUNTRY}">
|
||||
<div class="content">处理国家/地区</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_PROVINCE}">
|
||||
<div class="content">处理省份/州</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_CITY}">
|
||||
<div class="content">处理城市/市</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_TOWN}">
|
||||
<div class="content">处理县/区</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_PROVIDER}">
|
||||
<div class="content">处理ISP运营商</div>
|
||||
</div>
|
||||
<div class="ui step" :class="{active: step == STEP_FINISH}">
|
||||
<div class="content">完成</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设置件格式 -->
|
||||
<div v-show="step == STEP_TEMPLATE">
|
||||
<form class="ui form">
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td>IP库名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="" v-model="libraryName" ref="libraryName" maxlength="50" v-show="libraryFileId == 0"/>
|
||||
<span v-if="libraryFileId > 0">{{libraryName}}</span>
|
||||
<p class="comment">给当前IP库起一个容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">数据格式模板 *</td>
|
||||
<td>
|
||||
<input type="text" name="" v-model="rowTemplate" ref="rowTemplate" v-show="libraryFileId == 0"/>
|
||||
<span v-if="libraryFileId > 0">{{rowTemplate}}</span>
|
||||
<p class="comment">只支持纯文本(比如.txt)的数据内容,每行数据的格式;其中<code-label>${ipFrom}</code-label>表示开始IP,IPv4和IPv6均支持,<code-label>${ipTo}</code-label>表示结束IP,<code-label>${country}</code-label>表示国家/地区,<code-label>${province}</code-label>表示省份/州,<code-label>${city}</code-label>表示城市/市级单位,<code-label>${town}</code-label>表示区县(暂时不处理),<code-label>${provider}</code-label>表示ISP运营商,<code-label>${any}</code-label>表示不需要识别的内容。<a href="" @click.prevent="formatIP2Region()">[IP2Region示例]</a> <a href="" @click.prevent="formatIP138()">[IP138示例]</a> </p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="rowTemplate.length > 0 && libraryFileId == 0">
|
||||
<td>数据格式分析测试</td>
|
||||
<td>
|
||||
<input type="text" name="" v-model="formatTestText" placeholder="单行测试数据" @input="changeFormatTestText" @keyup.enter="testFormat()" @keypress.enter.prevent="1"/>
|
||||
<p class="comment"><a href="" @click.prevent="testFormat">[执行测试]</a>
|
||||
<span v-if="formatTestResult.length > 0" class="green"> 测试成功:{{formatTestResult}}</span>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>空值列表</td>
|
||||
<td>
|
||||
<values-box ref="emptyValues" :v-values="emptyValues" v-show="libraryFileId == 0"></values-box>
|
||||
<span v-for="emptyValue in emptyValues" class="ui label basic small" v-show="libraryFileId > 0">{{emptyValue}}</span>
|
||||
<p class="comment">内容中如果有这些值,表示没有填写;比如如果空值是<code-label>0</code-label>,那么<code-label>字段1|0|字段2|0</code-label>中只有<code-label>字段1</code-label>、<code-label>字段2</code-label>两个信息是有效的。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>密码</td>
|
||||
<td>
|
||||
<input type="password" maxlength="32" name="password" v-model="password"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button class="ui button primary" type="button" @click.prevent="templateGoNext">下一步</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 上传文件 -->
|
||||
<div v-show="step == STEP_UPLOAD">
|
||||
<form class="ui form">
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">选择数据文件 *</td>
|
||||
<td>
|
||||
<input type="file" accept="text/plain, .txt" ref="dataFile"/>
|
||||
<p class="comment">只支持纯文本(比如.txt)的数据内容,每行数据的格式需要和上一步设置的数据格式模板一致。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button class="ui button primary" type="button" @click.prevent="upload" v-if="!isUploading">开始分析</button>
|
||||
<button class="ui button disabled" type="button" v-if="isUploading">正在上传并分析中...</button>
|
||||
|
||||
<a href="" @click.prevent="goStep(STEP_TEMPLATE)" v-if="!isUploading">上一步</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 处理国家/地区 -->
|
||||
<div v-show="step == STEP_COUNTRY">
|
||||
<div v-if="missingCountries.length == 0 && missingCountriesLoaded">
|
||||
<div class="margin"></div>
|
||||
暂时没有需要处理的国家/地区。
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="missingCountries.length > 0 && missingCountriesLoaded">
|
||||
<p class="comment">以下如无任何可以进行的操作,请直接进入下一步。</p>
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">国家/地区</th>
|
||||
<th>问题</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="missingCountry in missingCountries">
|
||||
<td>{{missingCountry.countryName}}</td>
|
||||
<td><strong>"{{missingCountry.countryName}}"</strong>没有录入数据库
|
||||
<div v-if="missingCountry.similarCountries.length > 0">
|
||||
<div class="ui divider"></div>
|
||||
我们发现了以下类似国家/地区,如果有相同的国家/地区,请选择:
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" v-for="country in missingCountry.similarCountries" class="ui label basic small" @click.prevent="addCountryCustomCode(country, missingCountry.countryName)">{{country.displayName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<button class="ui button primary" type="button" @click.prevent="goStep(STEP_PROVINCE)">下一步</button>
|
||||
<a href="" @click.prevent="reloadCountries">刷新</a>
|
||||
</div>
|
||||
|
||||
<!-- 处理省份/州 -->
|
||||
<div v-show="step == STEP_PROVINCE">
|
||||
<div v-if="missingProvinces.length == 0 && missingProvincesLoaded">
|
||||
<div class="margin"></div>
|
||||
暂时没有需要处理的省份/州。
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="missingProvinces.length > 0 && missingProvincesLoaded">
|
||||
<p class="comment">以下如无任何可以进行的操作,请直接进入下一步。</p>
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">国家/地区</th>
|
||||
<th class="three wide">省份/州</th>
|
||||
<th>问题</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="missingProvince in missingProvinces">
|
||||
<td>{{missingProvince.countryName}}</td>
|
||||
<td>{{missingProvince.provinceName}}</td>
|
||||
<td><strong>"{{missingProvince.countryName}} - {{missingProvince.provinceName}}"</strong>没有录入数据库
|
||||
<div v-if="missingProvince.similarProvinces.length > 0">
|
||||
<div class="ui divider"></div>
|
||||
我们发现了以下类似省份/州,如果有相同的省份/州,请选择:
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" v-for="province in missingProvince.similarProvinces" class="ui label basic small" @click.prevent="addProvinceCustomCode(province, missingProvince.provinceName)">{{province.displayName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<button class="ui button primary" type="button" @click.prevent="goStep(STEP_CITY)">下一步</button>
|
||||
<a href="" @click.prevent="reloadProvinces">刷新</a> <span class="disabled">|</span> <a href="" @click.prevent="goStep(STEP_COUNTRY)">上一步</a>
|
||||
</div>
|
||||
|
||||
<!-- 处理城市/市 -->
|
||||
<div v-show="step == STEP_CITY">
|
||||
<div v-if="missingCities.length == 0 && missingCitiesLoaded">
|
||||
<div class="margin"></div>
|
||||
暂时没有需要处理的城市/市。
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="missingCities.length > 0 && missingCitiesLoaded">
|
||||
<p class="comment">以下如无任何可以进行的操作,请直接进入下一步。每次最多显示{{sizePerPage}}条。</p>
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">国家/地区</th>
|
||||
<th class="three wide">省份/州</th>
|
||||
<th class="three wide">城市/市</th>
|
||||
<th>问题</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="missingCity in missingCities">
|
||||
<td>{{missingCity.countryName}}</td>
|
||||
<td>{{missingCity.provinceName}}</td>
|
||||
<td>{{missingCity.cityName}}</td>
|
||||
<td><strong>"{{missingCity.countryName}} - {{missingCity.provinceName}} - {{missingCity.cityName}}"</strong>没有录入数据库
|
||||
<div v-if="missingCity.similarCities.length > 0">
|
||||
<div class="ui divider"></div>
|
||||
我们发现了以下类似城市/市,如果有相同的城市/市,请选择:
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" v-for="city in missingCity.similarCities" class="ui label basic small" @click.prevent="addCityCustomCode(city, missingCity.cityName)">{{city.displayName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<button class="ui button primary" type="button" @click.prevent="goStep(STEP_TOWN)">下一步</button>
|
||||
<a href="" @click.prevent="reloadCities">刷新</a> <span class="disabled">|</span> <a href="" @click.prevent="goStep(STEP_PROVINCE)">上一步</a>
|
||||
</div>
|
||||
|
||||
<!-- 处理城市/市 -->
|
||||
<div v-show="step == STEP_TOWN">
|
||||
<div v-if="missingTowns.length == 0 && missingTownsLoaded">
|
||||
<div class="margin"></div>
|
||||
暂时没有需要处理的区/县。
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="missingTowns.length > 0 && missingTownsLoaded">
|
||||
<p class="comment">以下如无任何可以进行的操作,请直接进入下一步。每次最多显示{{sizePerPage}}条。</p>
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">国家/地区</th>
|
||||
<th class="three wide">省份/州</th>
|
||||
<th class="three wide">城市/市</th>
|
||||
<th class="three wide">区/县</th>
|
||||
<th>问题</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="missingTown in missingTowns">
|
||||
<td>{{missingTown.countryName}}</td>
|
||||
<td>{{missingTown.provinceName}}</td>
|
||||
<td>{{missingTown.cityName}}</td>
|
||||
<td>{{missingTown.townName}}</td>
|
||||
<td><strong>"{{missingTown.countryName}} - {{missingTown.provinceName}} - {{missingTown.cityName}} - {{missingTown.townName}}"</strong>没有录入数据库
|
||||
<div v-if="missingTown.similarTowns.length > 0">
|
||||
<div class="ui divider"></div>
|
||||
我们发现了以下类似区/县,如果有相同的区/县,请选择:
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" v-for="town in missingTown.similarTowns" class="ui label basic small" @click.prevent="addTownCustomCode(town, missingTown.townName)">{{town.displayName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<button class="ui button primary" type="button" @click.prevent="goStep(STEP_PROVIDER)">下一步</button>
|
||||
<a href="" @click.prevent="reloadTowns">刷新</a> <span class="disabled">|</span> <a href="" @click.prevent="goStep(STEP_CITY)">上一步</a>
|
||||
</div>
|
||||
|
||||
<!-- 处理ISP运营商 -->
|
||||
<div v-show="step == STEP_PROVIDER">
|
||||
<div v-if="missingProviders.length == 0 && missingProvidersLoaded">
|
||||
<div class="margin"></div>
|
||||
暂时没有需要处理的ISP运营商。
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="missingProviders.length > 0 && missingProvidersLoaded">
|
||||
<p class="comment">以下如无任何可以进行的操作,请直接进入下一步。</p>
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">ISP运营商</th>
|
||||
<th>问题</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="missingProvider in missingProviders">
|
||||
<td>{{missingProvider.providerName}}</td>
|
||||
<td><strong>"{{missingProvider.providerName}}"</strong>没有录入数据库
|
||||
<div v-if="missingProvider.similarProviders.length > 0">
|
||||
<div class="ui divider"></div>
|
||||
我们发现了以下类似ISP运营商,如果有相同的ISP运营商,请选择:
|
||||
<div style="margin-top: 0.5em">
|
||||
<a href="" v-for="provider in missingProvider.similarProviders" class="ui label basic small" @click.prevent="addProviderCustomCode(provider, missingProvider.providerName)">{{provider.displayName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<button class="ui button primary" type="button" @click.prevent="goStep(STEP_FINISH)">下一步</button>
|
||||
<a href="" @click.prevent="reloadProviders">刷新</a> <span class="disabled">|</span> <a href="" @click.prevent="goStep(STEP_TOWN)">上一步</a>
|
||||
</div>
|
||||
|
||||
<!-- 完成 -->
|
||||
<div v-show="step == STEP_FINISH">
|
||||
<p>现在可以确认完成当前IP库的上传了,后期仍然可以修改国家/地区、省份/州、城市/市、区/县、ISP运营商等信息。</p>
|
||||
<button class="ui button primary" type="button" @click.prevent="finish" v-if="!isFinishing">确认完成</button>
|
||||
<button class="ui button disabled" type="button" v-if="isFinishing">正在完成最后操作...</button>
|
||||
</div>
|
||||
@@ -0,0 +1,386 @@
|
||||
Tea.context(function () {
|
||||
this.STEP_TEMPLATE = "template"
|
||||
this.STEP_UPLOAD = "upload"
|
||||
this.STEP_COUNTRY = "country"
|
||||
this.STEP_PROVINCE = "province"
|
||||
this.STEP_CITY = "city"
|
||||
this.STEP_TOWN = "town"
|
||||
this.STEP_PROVIDER = "provider"
|
||||
this.STEP_FINISH = "finish"
|
||||
|
||||
this.step = this.STEP_TEMPLATE
|
||||
|
||||
this.goStep = function (step) {
|
||||
this.step = step
|
||||
|
||||
switch (step) {
|
||||
case this.STEP_UPLOAD:
|
||||
if (this.libraryFileId > 0) {
|
||||
this.goStep(this.STEP_COUNTRY)
|
||||
}
|
||||
break
|
||||
case this.STEP_COUNTRY:
|
||||
this.reloadCountries()
|
||||
break
|
||||
case this.STEP_PROVINCE:
|
||||
this.reloadProvinces()
|
||||
break
|
||||
case this.STEP_CITY:
|
||||
this.reloadCities()
|
||||
break
|
||||
case this.STEP_TOWN:
|
||||
this.reloadTowns()
|
||||
break
|
||||
case this.STEP_PROVIDER:
|
||||
this.reloadProviders()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.$delay(function () {
|
||||
switch (this.step) {
|
||||
case this.STEP_TEMPLATE:
|
||||
this.$refs.libraryName.focus()
|
||||
break
|
||||
case this.STEP_COUNTRY:
|
||||
this.reloadCountries()
|
||||
break
|
||||
case this.STEP_PROVINCE:
|
||||
this.reloadProvinces()
|
||||
break
|
||||
case this.STEP_CITY:
|
||||
this.reloadCities()
|
||||
break
|
||||
case this.STEP_TOWN:
|
||||
this.reloadTowns()
|
||||
break
|
||||
case this.STEP_PROVIDER:
|
||||
this.reloadProviders()
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 数据格式
|
||||
*/
|
||||
this.rowTemplate = ""
|
||||
this.formatTestText = ""
|
||||
this.formatTestResult = ""
|
||||
this.password = ""
|
||||
|
||||
this.libraryName = ""
|
||||
this.emptyValues = []
|
||||
|
||||
this.libraryFileId = 0
|
||||
|
||||
if (this.updatingLibraryFile != null) {
|
||||
this.libraryFileId = this.updatingLibraryFile.id
|
||||
this.rowTemplate = this.updatingLibraryFile.template
|
||||
this.libraryName = this.updatingLibraryFile.name
|
||||
if (this.updatingLibraryFile.emptyValues != null) {
|
||||
this.emptyValues = this.updatingLibraryFile.emptyValues
|
||||
}
|
||||
}
|
||||
|
||||
this.formatIP2Region = function () {
|
||||
this.rowTemplate = "${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}"
|
||||
}
|
||||
|
||||
this.formatIP138 = function () {
|
||||
this.rowTemplate = "${any},${any},${ipFrom},${ipTo},${country},${province},${city},${town},${provider},${any},${any},${any}"
|
||||
}
|
||||
|
||||
this.testFormat = function () {
|
||||
this.$post("/settings/ip-library/creating/testFormat")
|
||||
.params({
|
||||
template: this.rowTemplate,
|
||||
text: this.formatTestText
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let pieces = []
|
||||
if (values["country"] != null && values["country"].length > 0) {
|
||||
pieces.push("国家/地区:" + values["country"])
|
||||
}
|
||||
if (values["province"] != null && values["province"].length > 0) {
|
||||
pieces.push("省份/州:" + values["province"])
|
||||
}
|
||||
if (values["city"] != null && values["city"].length > 0) {
|
||||
pieces.push("城市/市:" + values["city"])
|
||||
}
|
||||
if (values["town"] != null && values["town"].length > 0) {
|
||||
pieces.push("区县:" + values["town"])
|
||||
}
|
||||
if (values["provider"] != null && values["provider"].length > 0) {
|
||||
pieces.push("ISP运营商:" + values["provider"])
|
||||
}
|
||||
|
||||
this.formatTestResult = pieces.join(";")
|
||||
})
|
||||
}
|
||||
|
||||
this.changeFormatTestText = function () {
|
||||
this.formatTestResult = ""
|
||||
}
|
||||
|
||||
this.templateGoNext = function () {
|
||||
if (this.libraryName.length == 0) {
|
||||
let that = this
|
||||
teaweb.warn("请输入IP库名字", function () {
|
||||
that.$refs.libraryName.focus()
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.rowTemplate.length == 0) {
|
||||
let that = this
|
||||
teaweb.warn("请先输入数据格式模板", function () {
|
||||
that.$refs.rowTemplate.focus()
|
||||
})
|
||||
return
|
||||
}
|
||||
this.goStep(this.STEP_UPLOAD)
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*/
|
||||
this.isUploading = false
|
||||
|
||||
this.upload = function () {
|
||||
let dataFile = this.$refs.dataFile
|
||||
if (dataFile.files.length == 0) {
|
||||
teaweb.warn("请先上传文件")
|
||||
return
|
||||
}
|
||||
|
||||
this.isUploading = true
|
||||
|
||||
let emptyValues = this.$refs.emptyValues.allValues()
|
||||
|
||||
this.$post("/settings/ip-library/creating/upload")
|
||||
.timeout(300)
|
||||
.params({
|
||||
name: this.libraryName,
|
||||
template: this.rowTemplate,
|
||||
file: dataFile.files[0],
|
||||
emptyValues: emptyValues,
|
||||
password: this.password
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.libraryFileId = resp.data.libraryFileId
|
||||
let that = this
|
||||
teaweb.success("上传成功", function () {
|
||||
that.step = that.STEP_COUNTRY
|
||||
that.reloadCountries()
|
||||
})
|
||||
})
|
||||
.error(function () {
|
||||
teaweb.warn("操作超时,可能是网络太慢")
|
||||
})
|
||||
.done(function () {
|
||||
this.isUploading = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 国家
|
||||
*/
|
||||
this.missingCountries = []
|
||||
this.missingCountriesLoaded = false
|
||||
this.reloadCountries = function () {
|
||||
this.missingCountriesLoaded = false
|
||||
this.$post("/settings/ip-library/creating/countries")
|
||||
.params({
|
||||
"libraryFileId": this.libraryFileId
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.missingCountries = resp.data.missingCountries
|
||||
})
|
||||
.done(function () {
|
||||
this.missingCountriesLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
this.addCountryCustomCode = function (country, code) {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定要将 \"<strong>" + teaweb.encodeHTML(code) + "</strong>\" 加入到 \"<strong>" + teaweb.encodeHTML(country.displayName) + "</strong>\" 别名中吗?<br/>请再三确认无误后,才进行确定操作!", function () {
|
||||
that.$post("/settings/ip-library/creating/addCountryCustomCode")
|
||||
.params({
|
||||
countryId: country.id,
|
||||
code: code
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("操作成功", function () {
|
||||
that.reloadCountries()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 省
|
||||
*/
|
||||
this.missingProvinces = []
|
||||
this.missingProvincesLoaded = false
|
||||
this.reloadProvinces = function () {
|
||||
this.missingProvincesLoaded = false
|
||||
this.$post("/settings/ip-library/creating/provinces")
|
||||
.params({
|
||||
"libraryFileId": this.libraryFileId
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.missingProvinces = resp.data.missingProvinces
|
||||
})
|
||||
.done(function () {
|
||||
this.missingProvincesLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
this.addProvinceCustomCode = function (province, code) {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定要将 \"<strong>" + teaweb.encodeHTML(code) + "</strong>\" 加入到 \"<strong>" + teaweb.encodeHTML(province.displayName) + "</strong>\" 别名中吗?<br/>请再三确认无误后,才进行确定操作!", function () {
|
||||
that.$post("/settings/ip-library/creating/addProvinceCustomCode")
|
||||
.params({
|
||||
provinceId: province.id,
|
||||
code: code
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("操作成功", function () {
|
||||
that.reloadProvinces()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 市
|
||||
*/
|
||||
this.missingCities = []
|
||||
this.missingCitiesLoaded = false
|
||||
this.sizePerPage = 100
|
||||
this.reloadCities = function () {
|
||||
this.missingCitiesLoaded = false
|
||||
this.$post("/settings/ip-library/creating/cities")
|
||||
.params({
|
||||
libraryFileId: this.libraryFileId,
|
||||
size: this.sizePerPage
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.missingCities = resp.data.missingCities
|
||||
})
|
||||
.done(function () {
|
||||
this.missingCitiesLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
this.addCityCustomCode = function (city, code) {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定要将 \"<strong>" + teaweb.encodeHTML(code) + "</strong>\" 加入到 \"<strong>" + teaweb.encodeHTML(city.displayName) + "</strong>\" 别名中吗?<br/>请再三确认无误后,才进行确定操作!", function () {
|
||||
that.$post("/settings/ip-library/creating/addCityCustomCode")
|
||||
.params({
|
||||
cityId: city.id,
|
||||
code: code
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("操作成功", function () {
|
||||
that.reloadCities()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 县
|
||||
*/
|
||||
this.missingTowns = []
|
||||
this.missingTownsLoaded = false
|
||||
this.reloadTowns = function () {
|
||||
this.missingTownsLoaded = false
|
||||
this.$post("/settings/ip-library/creating/towns")
|
||||
.params({
|
||||
"libraryFileId": this.libraryFileId
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.missingTowns = resp.data.missingTowns
|
||||
})
|
||||
.done(function () {
|
||||
this.missingTownsLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
this.addTownCustomCode = function (town, code) {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定要将 \"<strong>" + teaweb.encodeHTML(code) + "</strong>\" 加入到 \"<strong>" + teaweb.encodeHTML(town.displayName) + "</strong>\" 别名中吗?<br/>请再三确认无误后,才进行确定操作!", function () {
|
||||
that.$post("/settings/ip-library/creating/addTownCustomCode")
|
||||
.params({
|
||||
townId: town.id,
|
||||
code: code
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("操作成功", function () {
|
||||
that.reloadTowns()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* ISP
|
||||
*/
|
||||
this.missingProviders = []
|
||||
this.missingProvidersLoaded = false
|
||||
this.reloadProviders = function () {
|
||||
this.missingProvidersLoaded = false
|
||||
this.$post("/settings/ip-library/creating/providers")
|
||||
.params({
|
||||
"libraryFileId": this.libraryFileId
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.missingProviders = resp.data.missingProviders
|
||||
})
|
||||
.done(function () {
|
||||
this.missingProvidersLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
this.addProviderCustomCode = function (provider, code) {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定要将 \"<strong>" + teaweb.encodeHTML(code) + "</strong>\" 加入到 \"<strong>" + teaweb.encodeHTML(provider.displayName) + "</strong>\" 别名中吗?<br/>请再三确认无误后,才进行确定操作!", function () {
|
||||
that.$post("/settings/ip-library/creating/addProviderCustomCode")
|
||||
.params({
|
||||
providerId: provider.id,
|
||||
code: code
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("操作成功", function () {
|
||||
that.reloadProviders()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成
|
||||
*/
|
||||
this.isFinishing = false
|
||||
|
||||
this.finish = function () {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定标记当前IP库已完成?<br/>后期仍然可以修改国家/地区、省份/州、城市/市、区/县、ISP运营商等信息。", function () {
|
||||
that.isFinishing = true
|
||||
that.$post("/settings/ip-library/creating/finish")
|
||||
.params({
|
||||
libraryFileId: this.libraryFileId
|
||||
})
|
||||
.timeout(300)
|
||||
.success(function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
window.location = "/settings/ip-library"
|
||||
})
|
||||
})
|
||||
.done(function () {
|
||||
that.isFinishing = false
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
.steps {
|
||||
.step.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<not-found-box v-if="libraries.length == 0">暂时还没有已完成的IP库。</not-found-box>
|
||||
|
||||
<p class="ui message blue" v-if="isGenerating">正在生成IP库文件,请耐心等待...</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="libraries.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="three wide">IP库名称</th>
|
||||
<th>库文件生成时间</th>
|
||||
<th style="width: 13em">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="library in libraries">
|
||||
<td>{{library.name}}</td>
|
||||
<td>
|
||||
<span v-if="library.generatedFileId > 0">{{library.generatedTime}}
|
||||
<a :href="'/settings/ip-library/library/download?libraryFileId=' + library.id" title="下载"><i class="icon download small"></i></a>
|
||||
</span>
|
||||
<span v-else class="disabled">尚未生成</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="generateLibrary(library.id)" :class="{disabled: isGenerating}">重新生成</a>
|
||||
<span class="disabled">|</span>
|
||||
<a :href="'/settings/ip-library/libraries/create?libraryFileId=' + library.id" :class="{disabled: isGenerating}">修改</a>
|
||||
<span class="disabled">|</span>
|
||||
<a href="" @click.prevent="deleteLibrary(library.id)" :class="{disabled: isGenerating}">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,34 @@
|
||||
Tea.context(function () {
|
||||
this.isGenerating = false
|
||||
|
||||
this.generateLibrary = function (libraryFileId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要重新生成库文件吗?", function () {
|
||||
that.isGenerating = true
|
||||
that.$post("/settings/ip-library/creating/generate")
|
||||
.params({
|
||||
libraryFileId: libraryFileId
|
||||
})
|
||||
.timeout(300)
|
||||
.success(function () {
|
||||
teaweb.successRefresh("生成成功")
|
||||
})
|
||||
.done(function () {
|
||||
that.isGenerating = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteLibrary = function (libraryFileId) {
|
||||
let that = this
|
||||
teaweb.confirm("html:确定要删除此库文件吗?", function () {
|
||||
that.$post("/settings/ip-library/delete")
|
||||
.params({
|
||||
libraryFileId: libraryFileId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.successRefresh("删除成功")
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,113 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<div class="ui segment">
|
||||
<h3>IP库测试</h3>
|
||||
<p class="comment">输入IP地址测试当前使用的IP库是否能正确查询地理位置信息</p>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition">
|
||||
<tr>
|
||||
<td class="title">MaxMind文件状态</td>
|
||||
<td>
|
||||
<div v-if="usingMaxMind">
|
||||
<span v-if="maxMindCityExists" class="green">✓ City数据库已上传</span>
|
||||
<span v-else-if="usingEmbeddedMaxMind" class="green">✓ City数据库(使用嵌入的默认库)</span>
|
||||
<span v-else class="red">✗ City数据库未上传</span>
|
||||
<br/>
|
||||
<span v-if="maxMindASNExists" class="green">✓ ASN数据库已上传</span>
|
||||
<span v-else class="grey">- ASN数据库未上传(可选)</span>
|
||||
<p class="comment green">✓ 当前正在使用MaxMind GeoIP2数据库</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-if="maxMindCityExists" class="green">✓ City数据库已上传</span>
|
||||
<span v-else class="red">✗ City数据库未上传</span>
|
||||
<br/>
|
||||
<span v-if="maxMindASNExists" class="green">✓ ASN数据库已上传</span>
|
||||
<span v-else class="grey">- ASN数据库未上传(可选)</span>
|
||||
<p class="comment">如果已上传MaxMind文件,系统会优先使用MaxMind库。如果没有上传,系统会使用嵌入的默认MaxMind库。</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">IP地址 *</td>
|
||||
<td>
|
||||
<input type="text" name="ip" class="text" maxlength="100" ref="focus" placeholder="例如:8.8.8.8" v-model="ip"/>
|
||||
<p class="comment">输入要测试的IP地址,例如:8.8.8.8(Google DNS,美国)、114.114.114.114(中国DNS)、202.96.0.20(中国北京联通)</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<button type="submit" class="ui button primary">测试查询</button>
|
||||
</form>
|
||||
|
||||
<!-- 查询结果显示区域 -->
|
||||
<div class="ui segment" v-if="result.isDone" ref="resultBox" style="margin-top: 2em;">
|
||||
<h4>查询结果</h4>
|
||||
<div v-if="!result.isOk" class="ui message error">
|
||||
<strong>查询失败:</strong>{{result.error}}
|
||||
</div>
|
||||
<div v-if="result.isOk">
|
||||
<div class="ui message success">
|
||||
<strong>IP地址:</strong>{{result.ip}}<br/>
|
||||
<strong>IP库类型:</strong>{{result.libraryType}}
|
||||
<span v-if="result.libraryVersion"> (版本 {{result.libraryVersion}})</span>
|
||||
</div>
|
||||
<table class="ui table definition">
|
||||
<tr>
|
||||
<td class="title" style="width: 150px;">国家</td>
|
||||
<td>
|
||||
<strong>{{result.country || '-'}}</strong>
|
||||
<span v-if="result.countryId > 0" class="grey"> (ID: {{result.countryId}})</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">省份</td>
|
||||
<td>
|
||||
<strong>{{result.province || '-'}}</strong>
|
||||
<span v-if="result.provinceId > 0" class="grey"> (ID: {{result.provinceId}})</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">城市</td>
|
||||
<td>
|
||||
<strong>{{result.city || '-'}}</strong>
|
||||
<span v-if="result.cityId > 0" class="grey"> (ID: {{result.cityId}})</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">区县</td>
|
||||
<td>
|
||||
<strong>{{result.town || '-'}}</strong>
|
||||
<span v-if="result.townId > 0" class="grey"> (ID: {{result.townId}})</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">ISP/运营商</td>
|
||||
<td>
|
||||
<strong>{{result.provider || '-'}}</strong>
|
||||
<span v-if="result.providerId > 0" class="grey"> (ID: {{result.providerId}})</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="result.regionSummary">
|
||||
<td class="title">区域摘要</td>
|
||||
<td>{{result.regionSummary}}</td>
|
||||
</tr>
|
||||
<tr v-if="result.summary">
|
||||
<td class="title">完整摘要</td>
|
||||
<td>{{result.summary}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="ui message" v-if="result.libraryType == 'MaxMind GeoIP2'" style="margin-top: 1em;">
|
||||
<i class="icon check circle green"></i> 当前使用的是MaxMind GeoIP2数据库,IP库已成功替换!
|
||||
</div>
|
||||
<div class="ui message warning" v-else style="margin-top: 1em;">
|
||||
<i class="icon warning circle"></i> 当前使用的是默认IP库。如果已上传MaxMind文件,请重启服务后再次测试。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
Tea.context(function () {
|
||||
this.ip = ""
|
||||
this.result = {
|
||||
isDone: false
|
||||
}
|
||||
// 如果数据还没有从后端传入(window.TEA.ACTION.data),则设置默认值
|
||||
if (typeof this.maxMindCityExists === "undefined") {
|
||||
this.maxMindCityExists = false
|
||||
}
|
||||
if (typeof this.maxMindASNExists === "undefined") {
|
||||
this.maxMindASNExists = false
|
||||
}
|
||||
if (typeof this.usingMaxMind === "undefined") {
|
||||
this.usingMaxMind = false
|
||||
}
|
||||
if (typeof this.usingEmbeddedMaxMind === "undefined") {
|
||||
this.usingEmbeddedMaxMind = false
|
||||
}
|
||||
|
||||
this.$delay(function () {
|
||||
if (this.$refs.focus) {
|
||||
this.$refs.focus.focus()
|
||||
}
|
||||
|
||||
// 确保表单使用AJAX提交
|
||||
var form = this.$el.querySelector('form')
|
||||
if (form) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
// 使用TeaGo的AJAX提交
|
||||
Tea.runActionOn(form)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 如果数据还没有从后端传入,则通过 AJAX 获取(作为后备方案)
|
||||
if (this.maxMindCityExists === false && this.maxMindASNExists === false && this.usingMaxMind === false) {
|
||||
this.$get("/settings/ip-library/library/test")
|
||||
.success(function (resp) {
|
||||
this.maxMindCityExists = resp.data.maxMindCityExists || false
|
||||
this.maxMindASNExists = resp.data.maxMindASNExists || false
|
||||
this.usingMaxMind = resp.data.usingMaxMind || false
|
||||
this.usingEmbeddedMaxMind = resp.data.usingEmbeddedMaxMind || false
|
||||
})
|
||||
}
|
||||
|
||||
this.success = function (resp) {
|
||||
if (!resp || !resp.data) {
|
||||
return
|
||||
}
|
||||
|
||||
this.result = resp.data.result || {
|
||||
isDone: true,
|
||||
isOk: false
|
||||
}
|
||||
this.maxMindCityExists = resp.data.maxMindCityExists || false
|
||||
this.maxMindASNExists = resp.data.maxMindASNExists || false
|
||||
this.usingMaxMind = resp.data.usingMaxMind || false
|
||||
this.usingEmbeddedMaxMind = resp.data.usingEmbeddedMaxMind || false
|
||||
|
||||
// 滚动到结果区域
|
||||
this.$nextTick(function () {
|
||||
if (this.$refs.resultBox) {
|
||||
this.$refs.resultBox.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<not-found-box v-if="providers.length == 0">暂时还没有ISP运营商。</not-found-box>
|
||||
|
||||
<table class="ui table selectable celled" v-if="providers.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 3em">ID</th>
|
||||
<th style="width: 12em">ISP运营商名称</th>
|
||||
<th style="width: 12em">内置别名</th>
|
||||
<th style="width: 12em">自定义名称 <tip-icon content="修改在界面上显示的ISP运营商名称"></tip-icon></th>
|
||||
<th style="width: 12em">自定义别名 <tip-icon content="可以在IP库中通过别名找到当前ISP运营商,比如通过联通、中国联通都可以找到联通这个ISP服务商"></tip-icon></th>
|
||||
<th class="one op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="provider in providers">
|
||||
<td>{{provider.id}}</td>
|
||||
<td>{{provider.name}}</td>
|
||||
<td>
|
||||
<div v-if="provider.codes.length > 0">
|
||||
<span v-for="code in provider.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="provider.customName.length > 0">{{provider.customName}}</span>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="provider.customCodes.length > 0">
|
||||
<span v-for="code in provider.customCodes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateProvider(provider.id)">修改</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,9 @@
|
||||
Tea.context(function () {
|
||||
this.updateProvider = function (providerId) {
|
||||
teaweb.popup("/settings/ip-library/providers/updatePopup?providerId=" + providerId, {
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>定制ISP运营商信息</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="providerId" :value="provider.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">ISP供应商名称</td>
|
||||
<td>{{provider.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内置别名</td>
|
||||
<td>
|
||||
<div v-if="provider.codes.length > 0">
|
||||
<span v-for="code in provider.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义名称</td>
|
||||
<td>
|
||||
<input type="text" name="customName" maxlength="100" v-model="provider.customName"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义别名</td>
|
||||
<td>
|
||||
<values-box name="customCodes" :v-values="provider.customCodes"></values-box>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,55 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<div class="margin"></div>
|
||||
|
||||
<form class="ui form" action="/settings/ip-library/provinces">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<combo-box :v-items="countries" name="countryId" :v-value="countryId" placeholder="国家/地区"></combo-box>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<not-found-box v-if="provinces.length == 0">暂时还没有省份/州。</not-found-box>
|
||||
|
||||
<p class="ui basic message" v-if="provinces.length > 0">共 {{provinces.length}} 个省份/州。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="provinces.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 3em">ID</th>
|
||||
<th style="width: 12em">省份/州名称</th>
|
||||
<th style="width: 12em">内置别名</th>
|
||||
<th style="width: 12em">自定义名称 <tip-icon content="修改在界面上显示的省份/州名称"></tip-icon></th>
|
||||
<th style="width: 12em">自定义别名 <tip-icon content="可以在IP库中通过别名找到当前省份/州,比如”广西“、”广西壮族自治区“"></tip-icon></th>
|
||||
<th class="one op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="province in provinces">
|
||||
<td>{{province.id}}</td>
|
||||
<td>{{province.name}}</td>
|
||||
<td>
|
||||
<div v-if="province.codes.length > 0">
|
||||
<span v-for="code in province.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="province.customName.length > 0">{{province.customName}}</span>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="province.customCodes.length > 0">
|
||||
<span v-for="code in province.customCodes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateProvince(province.id)">修改</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,9 @@
|
||||
Tea.context(function () {
|
||||
this.updateProvince = function (provinceId) {
|
||||
teaweb.popup("/settings/ip-library/provinces/updatePopup?provinceId=" + provinceId, {
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>定制省份/州信息</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="provinceId" :value="province.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">省份/州名称</td>
|
||||
<td>{{province.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内置别名</td>
|
||||
<td>
|
||||
<div v-if="province.codes.length > 0">
|
||||
<span v-for="code in province.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义名称</td>
|
||||
<td>
|
||||
<input type="text" name="customName" maxlength="100" v-model="province.customName"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义别名</td>
|
||||
<td>
|
||||
<values-box name="customCodes" :v-values="province.customCodes"></values-box>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,62 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<div class="margin"></div>
|
||||
|
||||
<form class="ui form" method="get" action="/settings/ip-library/towns">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<combo-box name="countryId" :v-items="countries" :v-value="countryId" @change="changeCountry" placeholder="国家/地区"></combo-box>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<combo-box name="provinceId" :data-url="'/settings/ip-library/towns/provinceOptions?countryId=' + countryId" data-key="provinces" :v-value="provinceId" ref="provinceOptionsRef" @change="changeProvince" placeholder="省份/州"></combo-box>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<combo-box name="cityId" :data-url="'/settings/ip-library/towns/cityOptions?provinceId=' + provinceId" data-key="cities" :v-value="cityId" ref="cityOptionsRef" placeholder="城市/市"></combo-box>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<not-found-box v-if="towns.length == 0">暂时还没有区/县。</not-found-box>
|
||||
|
||||
<p class="ui basic message" v-if="towns.length > 0">共 {{towns.length}} 个区/县。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="towns.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 3em">ID</th>
|
||||
<th style="width: 12em">区/县名称</th>
|
||||
<th style="width: 12em">内置别名</th>
|
||||
<th style="width: 12em">自定义名称 <tip-icon content="修改在界面上显示的区/县名称"></tip-icon></th>
|
||||
<th style="width: 12em">自定义别名 <tip-icon content="可以在IP库中通过别名找到当前区/县,比如通过”朝阳区“、”朝阳“都能找到朝阳区"></tip-icon></th>
|
||||
<th class="one op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="town in towns">
|
||||
<td>{{town.id}}</td>
|
||||
<td>{{town.name}}</td>
|
||||
<td>
|
||||
<div v-if="town.codes.length > 0">
|
||||
<span v-for="code in town.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="town.customName.length > 0">{{town.customName}}</span>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="town.customCodes.length > 0">
|
||||
<span v-for="code in town.customCodes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">暂无</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateTown(town.id)">修改</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,33 @@
|
||||
Tea.context(function () {
|
||||
this.changeCountry = function (item) {
|
||||
let provinceOptionsBox = this.$refs.provinceOptionsRef
|
||||
|
||||
if (item != null) {
|
||||
provinceOptionsBox.setDataURL("/settings/ip-library/towns/provinceOptions?countryId=" + item.value)
|
||||
provinceOptionsBox.reloadData()
|
||||
}
|
||||
|
||||
provinceOptionsBox.clear()
|
||||
|
||||
this.changeProvince(null)
|
||||
}
|
||||
|
||||
this.changeProvince = function (item) {
|
||||
let cityOptionsBox = this.$refs.cityOptionsRef
|
||||
|
||||
if (item != null) {
|
||||
cityOptionsBox.setDataURL("/settings/ip-library/towns/cityOptions?provinceId=" + item.value)
|
||||
cityOptionsBox.reloadData()
|
||||
}
|
||||
|
||||
cityOptionsBox.clear()
|
||||
}
|
||||
|
||||
this.updateTown = function (townId) {
|
||||
teaweb.popup("/settings/ip-library/towns/updatePopup?townId=" + townId, {
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>定制区/县信息</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="townId" :value="town.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">省份/州名称</td>
|
||||
<td>{{town.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内置别名</td>
|
||||
<td>
|
||||
<div v-if="town.codes.length > 0">
|
||||
<span v-for="code in town.codes" class="ui label basic">{{code}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">无</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义名称</td>
|
||||
<td>
|
||||
<input type="text" name="customName" maxlength="100" v-model="town.customName"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自定义别名</td>
|
||||
<td>
|
||||
<values-box name="customCodes" :v-values="town.customCodes"></values-box>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
25
EdgeAdmin/web/views/@default/settings/ip-library/upload.html
Normal file
25
EdgeAdmin/web/views/@default/settings/ip-library/upload.html
Normal file
@@ -0,0 +1,25 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success" data-tea-timeout="120" data-tea-before="before" data-tea-done="done">
|
||||
<csrf-token></csrf-token>
|
||||
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td>IP库名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">选择IP库文件 *</td>
|
||||
<td>
|
||||
<input type="file" name="file" accept=".mmdb"/>
|
||||
<p class="comment">只允许上传MaxMind专有格式的数据库文件。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn v-show="!isUploading">开始上传</submit-btn>
|
||||
<button class="ui button disabled" type="button" v-if="isUploading">IP库上传中...</button>
|
||||
</form>
|
||||
13
EdgeAdmin/web/views/@default/settings/ip-library/upload.js
Normal file
13
EdgeAdmin/web/views/@default/settings/ip-library/upload.js
Normal file
@@ -0,0 +1,13 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifySuccess("上传成功", "/settings/ip-library")
|
||||
|
||||
this.isUploading = false
|
||||
|
||||
this.before = function () {
|
||||
this.isUploading = true
|
||||
}
|
||||
|
||||
this.done = function () {
|
||||
this.isUploading = false
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user