mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-06-23 00:13:58 +08:00
826 lines
24 KiB
HTML
826 lines
24 KiB
HTML
{extend name="common/layout" /}
|
||
{block name="title"}智能批量添加{/block}
|
||
{block name="main"}
|
||
<style>
|
||
.modal-body .form-group {
|
||
margin-bottom: 15px;
|
||
}
|
||
.batch-input-area {
|
||
min-height: 200px;
|
||
resize: vertical;
|
||
}
|
||
.batch-preview {
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
margin-top: 20px;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 4px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||
}
|
||
.batch-preview table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: #fff;
|
||
margin-bottom: 0;
|
||
}
|
||
.batch-preview th,
|
||
.batch-preview td {
|
||
padding: 8px 10px;
|
||
text-align: left;
|
||
border: 0.5px solid #f0f0f0;
|
||
vertical-align: middle;
|
||
font-size: 13px;
|
||
white-space: nowrap;
|
||
}
|
||
.batch-preview th {
|
||
background-color: #f9f9f9;
|
||
font-weight: 600;
|
||
color: #333;
|
||
border-bottom: 1px solid #e0e0e0;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
}
|
||
.batch-preview tr:hover {
|
||
background-color: #fafafa;
|
||
}
|
||
.batch-preview .label {
|
||
display: inline-block;
|
||
padding: 2px 8px;
|
||
border-radius: 3px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
.batch-preview .label-primary {
|
||
background-color: #337ab7;
|
||
color: #fff;
|
||
}
|
||
.batch-preview .status-success {
|
||
color: #52c41a;
|
||
font-weight: 600;
|
||
font-size: 12px;
|
||
}
|
||
.domain-select-modal {
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
.domain-item {
|
||
cursor: pointer;
|
||
padding: 8px 12px;
|
||
margin-bottom: 4px;
|
||
border: 2px solid #ddd;
|
||
border-radius: 3px;
|
||
transition: all 0.2s;
|
||
}
|
||
.domain-item:hover {
|
||
border-color: #337ab7;
|
||
background-color: #f5f9fc;
|
||
}
|
||
.domain-item.selected {
|
||
border-color: #337ab7;
|
||
background-color: #e7f3ff;
|
||
}
|
||
.domain-item.selected::after {
|
||
content: '✓';
|
||
float: right;
|
||
color: #337ab7;
|
||
font-weight: bold;
|
||
}
|
||
</style>
|
||
<div class="row">
|
||
<div class="col-xs-12 center-block" style="float: none;">
|
||
<div class="panel panel-default">
|
||
<div class="panel-heading">
|
||
<h3 class="panel-title"><a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>智能批量添加解析</h3>
|
||
</div>
|
||
<div class="panel-body">
|
||
<form class="form-horizontal" id="batchForm">
|
||
<div class="form-group">
|
||
<label class="col-sm-3 control-label">批量数据 <span class="text-danger">*</span></label>
|
||
<div class="col-sm-6">
|
||
<textarea class="form-control batch-input-area" id="batchInput" rows="10"
|
||
placeholder="请按以下格式输入(每行一条记录): 格式1:主机记录 记录值 格式2:主机记录 记录值 域名 格式3:记录值 主机记录.域名 格式4:主机记录.域名(使用下方记录值) 示例: www 1.2.3.4 example.com api app.example.com example.com 1.1.1.1 www.example.com example.com 说明: - 如果使用格式4,将使用下方的记录值 - 如果不指定域名,将使用下方选择的默认域名 - 如果检测到多个不同域名,会提示您选择对应的DNS配置"></textarea>
|
||
<p class="help-block">每行一条记录,支持混合输入多个域名的记录</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="col-sm-3 control-label">记录值</label>
|
||
<div class="col-sm-6">
|
||
<input type="text" class="form-control" id="batchValueInput" placeholder="当使用格式4时,将使用此记录值">
|
||
<p class="help-block">留空则不使用格式4</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="col-sm-3 control-label">默认域名</label>
|
||
<div class="col-sm-6">
|
||
<select name="defaultDomain" id="defaultDomainSelect" class="form-control select2">
|
||
<option value="">不使用默认域名(必须每行都指定域名)</option>
|
||
{foreach $domainList as $domain}
|
||
<option value="{$domain.id}">{$domain.name} [{$domain.dnsType}]</option>
|
||
{/foreach}
|
||
</select>
|
||
<p class="help-block">当某行没有指定域名时,使用此默认域名</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="col-sm-3 control-label">记录类型</label>
|
||
<div class="col-sm-6">
|
||
<select name="defaultType" id="defaultTypeSelect" class="form-control">
|
||
<option value="">自动检测</option>
|
||
<option value="A">A</option>
|
||
<option value="CNAME">CNAME</option>
|
||
<option value="AAAA">AAAA</option>
|
||
<option value="NS">NS</option>
|
||
<option value="MX">MX</option>
|
||
<option value="SRV">SRV</option>
|
||
<option value="TXT">TXT</option>
|
||
<option value="CAA">CAA</option>
|
||
</select>
|
||
<p class="help-block">留空则根据记录值自动判断类型</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="col-sm-3 control-label">线路</label>
|
||
<div class="col-sm-6" id="batch_line_list">
|
||
<select name="defaultLine" id="defaultLineSelect" class="form-control" onchange="changeBatchLine(this)">
|
||
<option value="">自动选择</option>
|
||
</select>
|
||
<p class="help-block">留空则使用默认线路</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="col-sm-3 control-label">TTL</label>
|
||
<div class="col-sm-6">
|
||
<input type="number" class="form-control" name="defaultTtl" id="defaultTtlInput" value="600" min="1">
|
||
<p class="help-block">默认TTL时间(秒)</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<div class="col-sm-offset-3 col-sm-6">
|
||
<button type="button" class="btn btn-info" onclick="previewBatchData()"><i class="fa fa-eye"></i> 预览解析结果</button>
|
||
<button type="button" class="btn btn-primary" id="btnBatchAdd" onclick="submitBatchData()"><i class="fa fa-plus-circle"></i> 批量添加解析</button>
|
||
<button type="button" class="btn btn-default" onclick="resetBatchForm()"><i class="fa fa-refresh"></i> 重置</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
<div class="form-group col-sm-12" id="previewSection" style="display:none;margin-top:20px;">
|
||
<label>解析预览</label>
|
||
<div class="table-responsive batch-preview">
|
||
<table style="min-width: 800px;">
|
||
<thead>
|
||
<tr>
|
||
<th style="width:5%">序号</th>
|
||
<th style="width:15%">主机记录</th>
|
||
<th style="width:10%">类型</th>
|
||
<th style="width:25%">记录值</th>
|
||
<th style="width:18%">DNS域名</th>
|
||
<th style="width:12%">线路</th>
|
||
<th style="width:8%">TTL</th>
|
||
<th style="width:7%">状态</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="previewBody">
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="alert alert-info" id="previewSummary" style="margin-top:10px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal" id="modal-domain-select" role="dialog" aria-hidden="true" data-backdrop="static">
|
||
<div class="modal-dialog modal-md">
|
||
<div class="modal-content animated flipInX">
|
||
<div class="modal-header">
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
|
||
<h4 class="modal-title">选择DNS配置</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="alert alert-warning">
|
||
<i class="fa fa-exclamation-triangle"></i> 检测到多个不同的域名,请为每个域名选择对应的DNS配置:
|
||
</div>
|
||
<div class="domain-select-modal" id="domainSelectModal">
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-white" data-dismiss="modal">取消</button>
|
||
<button type="button" class="btn btn-primary" onclick="confirmDomainSelection()">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/block}
|
||
{block name="script"}
|
||
<script src="/static/js/layer/layer.js"></script>
|
||
<script src="/static/js/select2-4.0.13.min.js"></script>
|
||
<script>
|
||
var domainList = [];
|
||
{foreach $domainList as $domain}
|
||
domainList.push({
|
||
id: '{$domain.id}',
|
||
name: '{$domain.name}',
|
||
dnsType: '{$domain.dnsType}'
|
||
});
|
||
{/foreach}
|
||
|
||
var parsedBatchData = [];
|
||
var domainMapping = {};
|
||
|
||
$(document).ready(function(){
|
||
$('#defaultDomainSelect').select2({
|
||
placeholder: '选择默认域名',
|
||
allowClear: true,
|
||
width: '100%',
|
||
language: {
|
||
noResults: function(){ return '未找到匹配的域名'; },
|
||
searching: function(){ return '搜索中...'; }
|
||
}
|
||
});
|
||
|
||
$('#defaultDomainSelect').on('change', function(){
|
||
var domainId = $(this).val();
|
||
if(domainId){
|
||
var ii = layer.load(2);
|
||
$.ajax({
|
||
type : 'POST',
|
||
url : '/record/quickinfo/' + domainId,
|
||
dataType : 'json',
|
||
success : function(data) {
|
||
layer.close(ii);
|
||
if(data.code == 0){
|
||
var lineOptions = '<option value="">自动选择</option>';
|
||
var firstOption = null;
|
||
$.each(data.data.recordLine, function(index, item){
|
||
if(item.parent == null){
|
||
if(!firstOption) firstOption = item.id;
|
||
lineOptions += '<option value="'+item.id+'">'+item.name+'</option>';
|
||
}
|
||
});
|
||
$('#batch_line_list').html('<select name="defaultLine" id="defaultLineSelect" class="form-control" onchange="changeBatchLine(this)">'+lineOptions+'</select>');
|
||
window.currentRecordLine = data.data.recordLine;
|
||
if(firstOption){
|
||
$('#defaultLineSelect').val(firstOption).trigger('change');
|
||
}
|
||
}else{
|
||
layer.alert(data.msg, {icon: 2});
|
||
}
|
||
},
|
||
error : function() {
|
||
layer.close(ii);
|
||
layer.alert('获取域名信息失败', {icon: 2});
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
function changeBatchLine(obj){
|
||
var line = $(obj).val();
|
||
var flag = false;
|
||
$("#batch_line_list").children().each(function(index, elem){
|
||
if(flag) $(elem).remove()
|
||
if(obj == elem){ flag = true; }
|
||
})
|
||
if($(obj).find("option:selected").text() == '子集线路(非必填)') return;
|
||
if(window.currentRecordLine){
|
||
var tempLine = window.currentRecordLine.filter((x) => x.parent == line)
|
||
if(tempLine.length > 0){
|
||
var option = line.substr(0,2) == 'N.' ? '' : '<option value="'+line+'">子集线路(非必填)</option>';
|
||
$.each(tempLine, function(index, item){
|
||
option += '<option value="'+item.id+'">'+item.name+'</option>';
|
||
})
|
||
$("#batch_line_list").append('<select name="defaultLine" class="form-control" onchange="changeBatchLine(this)">'+option+'</select>');
|
||
}
|
||
}
|
||
}
|
||
|
||
function resetBatchForm(){
|
||
$('#batchInput').val('');
|
||
$('#batchValueInput').val('');
|
||
$('#defaultDomainSelect').val(null).trigger('change');
|
||
$('#defaultTypeSelect').val('');
|
||
$('#defaultTtlInput').val(600);
|
||
$('#defaultLineSelect').val('').trigger('change');
|
||
|
||
$('#batch_line_list').empty();
|
||
$('#batch_line_list').append('<select name="defaultLine" id="defaultLineSelect" class="form-control" onchange="changeBatchLine(this)"><option value="">自动选择</option></select>');
|
||
|
||
$('#previewSection').hide();
|
||
parsedBatchData = [];
|
||
domainMapping = {};
|
||
}
|
||
|
||
function previewBatchData(){
|
||
var inputText = $('#batchInput').val().trim();
|
||
|
||
if(!inputText){
|
||
layer.alert('请输入批量解析数据', {icon: 2});
|
||
return;
|
||
}
|
||
|
||
var lines = inputText.split('\n');
|
||
var defaultDomainId = $('#defaultDomainSelect').val();
|
||
var defaultType = $('#defaultTypeSelect').val();
|
||
var defaultLine = $('#batch_line_list select[name=defaultLine]').last().val() || $('#defaultLineSelect').val();
|
||
var defaultTtl = $('#defaultTtlInput').val();
|
||
var batchValue = $('#batchValueInput').val();
|
||
|
||
parsedBatchData = [];
|
||
domainMapping = {};
|
||
var uniqueDomains = new Set();
|
||
var errors = [];
|
||
|
||
$.each(lines, function(index, line){
|
||
line = $.trim(line);
|
||
if(!line) return;
|
||
|
||
var parts = line.split(/\s+/);
|
||
|
||
if(parts.length == 1 && batchValue){
|
||
var domainPart = parts[0];
|
||
var found = false;
|
||
var host = '@';
|
||
var domainName = domainPart;
|
||
|
||
var sortedDomains = domainList.slice().sort(function(a, b){
|
||
return b.name.length - a.name.length;
|
||
});
|
||
|
||
$.each(sortedDomains, function(i, domain){
|
||
var dnsDomainName = domain.name;
|
||
|
||
if(domainPart === dnsDomainName){
|
||
host = '@';
|
||
domainName = domain.name;
|
||
found = true;
|
||
return false;
|
||
}
|
||
else if(domainPart.endsWith('.' + dnsDomainName)){
|
||
host = domainPart.substring(0, domainPart.length - (dnsDomainName.length + 1));
|
||
domainName = domain.name;
|
||
found = true;
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if(!found){
|
||
errors.push('第' + (index + 1) + '行:域名 "' + domainPart + '" 不在你的域名列表中');
|
||
return;
|
||
}
|
||
|
||
value = batchValue;
|
||
} else if(parts.length < 2){
|
||
errors.push('第' + (index + 1) + '行格式错误:至少需要主机记录和记录值');
|
||
return;
|
||
} else if(parts.length == 2){
|
||
var hostDomainPart = parts[1];
|
||
var found = false;
|
||
|
||
var sortedDomains = domainList.slice().sort(function(a, b){
|
||
return b.name.length - a.name.length;
|
||
});
|
||
|
||
$.each(sortedDomains, function(i, domain){
|
||
var dnsDomainName = domain.name;
|
||
|
||
if(hostDomainPart.endsWith('.' + dnsDomainName)){
|
||
host = hostDomainPart.substring(0, hostDomainPart.length - (dnsDomainName.length + 1));
|
||
value = parts[0];
|
||
domainName = domain.name;
|
||
found = true;
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if(!found){
|
||
$.each(domainList, function(i, domain){
|
||
if(hostDomainPart === domain.name){
|
||
host = '@';
|
||
value = parts[0];
|
||
domainName = domain.name;
|
||
found = true;
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if(!found){
|
||
host = parts[0];
|
||
value = parts[1];
|
||
domainName = parts[2] || null;
|
||
}
|
||
}
|
||
} else if(parts.length >= 2){
|
||
host = parts[0];
|
||
value = parts[1];
|
||
domainName = parts[2] || null;
|
||
}
|
||
|
||
var finalDomainId;
|
||
var finalDomainName;
|
||
|
||
if(domainName){
|
||
finalDomainName = domainName;
|
||
|
||
var foundDomain = null;
|
||
$.each(domainList, function(i, d){
|
||
if(d.name.toLowerCase() === domainName.toLowerCase()){
|
||
foundDomain = d;
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if(foundDomain){
|
||
finalDomainId = foundDomain.id;
|
||
domainMapping[domainName] = foundDomain.id;
|
||
}else{
|
||
errors.push('第' + (index + 1) + '行:域名 "' + domainName + '" 不在你的域名列表中');
|
||
return;
|
||
}
|
||
}else if(defaultDomainId){
|
||
finalDomainId = defaultDomainId;
|
||
var defaultDomainObj = null;
|
||
$.each(domainList, function(i, d){
|
||
if(d.id === defaultDomainId){
|
||
defaultDomainObj = d;
|
||
return false;
|
||
}
|
||
});
|
||
finalDomainName = defaultDomainObj ? defaultDomainObj.name : '';
|
||
}else{
|
||
errors.push('第' + (index + 1) + '行:未指定域名且没有设置默认域名');
|
||
return;
|
||
}
|
||
|
||
uniqueDomains.add(finalDomainName);
|
||
|
||
var type = defaultType;
|
||
if(!type){
|
||
type = getDnsType(value);
|
||
}
|
||
|
||
parsedBatchData.push({
|
||
host: host,
|
||
value: value,
|
||
type: type,
|
||
domainId: finalDomainId,
|
||
domainName: finalDomainName,
|
||
line: defaultLine,
|
||
ttl: defaultTtl,
|
||
lineNumber: index + 1,
|
||
status: 'pending'
|
||
});
|
||
});
|
||
|
||
if(errors.length > 0){
|
||
layer.alert('发现以下错误:\n\n' + errors.join('\n'), {icon: 2});
|
||
return;
|
||
}
|
||
|
||
if(parsedBatchData.length === 0){
|
||
layer.alert('没有有效的解析记录', {icon: 2});
|
||
return;
|
||
}
|
||
|
||
var uniqueDnsTypes = new Set();
|
||
var domainDnsMap = {};
|
||
|
||
$.each(parsedBatchData, function(index, row){
|
||
var domainInfo = null;
|
||
$.each(domainList, function(i, d){
|
||
if(d.id === row.domainId){
|
||
domainInfo = d;
|
||
return false;
|
||
}
|
||
});
|
||
if(domainInfo){
|
||
uniqueDnsTypes.add(domainInfo.dnsType);
|
||
domainDnsMap[row.domainId] = domainInfo.dnsType;
|
||
}
|
||
});
|
||
|
||
var uniqueDomainIds = new Set(parsedBatchData.map(r => r.domainId));
|
||
|
||
if(uniqueDomainIds.size > 1){
|
||
showDomainSelectionModal(Array.from(uniqueDomainIds));
|
||
return;
|
||
}
|
||
|
||
renderPreview();
|
||
}
|
||
|
||
function getDnsType(value){
|
||
value = value.toLowerCase();
|
||
if(/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(value)){
|
||
return 'A';
|
||
}else if(/^([a-z0-9]([a-z0-9\-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i.test(value)){
|
||
return 'CNAME';
|
||
}else if(/^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/.test(value)){
|
||
return 'AAAA';
|
||
}else if(/^\d+$/.test(value) && parseInt(value) <= 65535){
|
||
return 'MX';
|
||
}else{
|
||
return 'A';
|
||
}
|
||
}
|
||
|
||
function renderPreview(){
|
||
var html = '';
|
||
var validCount = 0;
|
||
|
||
$.each(parsedBatchData, function(index, row){
|
||
var statusHtml = '<span class="status-success"><i class="fa fa-check"></i> 待添加</span>';
|
||
validCount++;
|
||
|
||
html += '<tr>';
|
||
html += '<td style="text-align:center;">' + row.lineNumber + '</td>';
|
||
html += '<td>' + (row.host == '@' ? '@ (主域名)' : row.host) + '</td>';
|
||
html += '<td style="text-align:center;"><span class="label label-primary">' + row.type + '</span></td>';
|
||
html += '<td title="' + htmlEscape(row.value) + '">' + row.value + '</td>';
|
||
html += '<td><strong>' + row.domainName + '</strong></td>';
|
||
html += '<td style="text-align:center;">' + (row.line ? row.line : '默认') + '</td>';
|
||
html += '<td style="text-align:center;">' + row.ttl + '</td>';
|
||
html += '<td style="text-align:center;">' + statusHtml + '</td>';
|
||
html += '</tr>';
|
||
});
|
||
|
||
$('#previewBody').html(html);
|
||
$('#previewSummary').html('<strong>共 ' + validCount + ' 条记录待添加</strong>');
|
||
$('#previewSection').show();
|
||
}
|
||
|
||
function showDomainSelectionModal(domains){
|
||
var html = '';
|
||
|
||
$.each(domains, function(index, domainIdentifier){
|
||
var domainName = '';
|
||
var domainId = '';
|
||
|
||
if(!isNaN(domainIdentifier)){
|
||
var domainInfo = null;
|
||
$.each(domainList, function(i, d){
|
||
if(d.id === domainIdentifier){
|
||
domainInfo = d;
|
||
return false;
|
||
}
|
||
});
|
||
if(domainInfo){
|
||
domainName = domainInfo.name;
|
||
domainId = domainInfo.id;
|
||
}
|
||
}else{
|
||
domainName = domainIdentifier;
|
||
var domainInfo = null;
|
||
$.each(domainList, function(i, d){
|
||
if(d.name === domainIdentifier){
|
||
domainInfo = d;
|
||
return false;
|
||
}
|
||
});
|
||
if(domainInfo){
|
||
domainId = domainInfo.id;
|
||
}
|
||
}
|
||
|
||
if(!domainName) return;
|
||
|
||
var matches = [];
|
||
$.each(domainList, function(i, d){
|
||
if(d.name === domainName){
|
||
matches.push(d);
|
||
}
|
||
});
|
||
|
||
if(matches.length === 0){
|
||
matches = domainList;
|
||
}
|
||
|
||
html += '<div style="margin-bottom:20px;">';
|
||
html += '<h5><strong>' + domainName + '</strong></h5>';
|
||
html += '<div class="row">';
|
||
$.each(matches, function(j, match){
|
||
var isSelected = j === 0;
|
||
html += '<div class="col-md-6">';
|
||
html += '<div class="domain-item' + (isSelected ? ' selected' : '') + '" ';
|
||
html += 'data-domain="' + domainName + '" data-id="' + match.id + '" ';
|
||
html += 'onclick="selectDomainItem(this)">';
|
||
html += '<strong>' + match.name + '</strong> [' + match.dnsType + ']';
|
||
html += '</div>';
|
||
html += '</div>';
|
||
|
||
if(isSelected){
|
||
domainMapping[domainName] = match.id;
|
||
}
|
||
});
|
||
html += '</div></div>';
|
||
});
|
||
|
||
$('#domainSelectModal').html(html);
|
||
$('#modal-domain-select').modal('show');
|
||
}
|
||
|
||
function selectDomainItem(element){
|
||
var $element = $(element);
|
||
var domainName = $element.data('domain');
|
||
var domainId = $element.data('id');
|
||
|
||
var $row = $element.closest('.row');
|
||
$row.find('.domain-item').removeClass('selected');
|
||
$element.addClass('selected');
|
||
|
||
domainMapping[domainName] = domainId;
|
||
}
|
||
|
||
function confirmDomainSelection(){
|
||
$('#modal-domain-select').modal('hide');
|
||
|
||
$.each(parsedBatchData, function(index, row){
|
||
if(domainMapping[row.domainName]){
|
||
row.domainId = domainMapping[row.domainName];
|
||
}
|
||
});
|
||
|
||
renderPreview();
|
||
|
||
layer.msg('域名配置已更新', {icon: 1, time: 1500});
|
||
}
|
||
|
||
function submitBatchData(){
|
||
if(parsedBatchData.length === 0){
|
||
layer.alert('请先预览解析结果', {icon: 2});
|
||
return;
|
||
}
|
||
|
||
layer.confirm('确定要批量添加这 <strong>' + parsedBatchData.length + '</strong> 条解析记录吗?', {
|
||
title: '确认批量添加',
|
||
icon: 0,
|
||
btn: ['确定添加', '取消']
|
||
}, function(){
|
||
executeBatchAdd();
|
||
});
|
||
}
|
||
|
||
function executeBatchAdd(){
|
||
var groupedByDomain = {};
|
||
$.each(parsedBatchData, function(index, row){
|
||
if(!groupedByDomain[row.domainId]){
|
||
groupedByDomain[row.domainId] = [];
|
||
}
|
||
groupedByDomain[row.domainId].push(row);
|
||
});
|
||
|
||
var totalSuccess = 0;
|
||
var totalFail = 0;
|
||
var completedCount = 0;
|
||
var totalCount = Object.keys(groupedByDomain).length;
|
||
var failReasons = [];
|
||
|
||
var $btn = $('#btnBatchAdd');
|
||
var btnOrigHtml = $btn.html();
|
||
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 正在添加...');
|
||
var ii = layer.load(2);
|
||
|
||
$.each(groupedByDomain, function(domainId, records){
|
||
var recordLines = [];
|
||
$.each(records, function(i, r){
|
||
recordLines.push(r.host + ' ' + r.value);
|
||
});
|
||
var recordStr = recordLines.join('\n');
|
||
|
||
$.ajax({
|
||
type : 'POST',
|
||
url : '/record/batchadd/' + domainId,
|
||
data : function(){
|
||
var data = {
|
||
record: recordStr,
|
||
type: records[0].type,
|
||
ttl: records[0].ttl
|
||
};
|
||
if(records[0].line){
|
||
data.line = records[0].line;
|
||
}
|
||
return data;
|
||
}(),
|
||
dataType : 'json',
|
||
async: false,
|
||
success : function(data) {
|
||
completedCount++;
|
||
if(data.code == 0){
|
||
var match = data.msg.match(/成功(\d+)条/);
|
||
if(match){
|
||
totalSuccess += parseInt(match[1]);
|
||
}
|
||
var failMatch = data.msg.match(/失败(\d+)条/);
|
||
if(failMatch){
|
||
var failCount = parseInt(failMatch[1]);
|
||
totalFail += failCount;
|
||
|
||
if(failCount > 0){
|
||
var startIndex = records.length - failCount;
|
||
for(var i = startIndex; i < records.length; i++){
|
||
var record = records[i];
|
||
failReasons.push('记录 ' + record.host + ' [域名: ' + record.domainName + ']:' + data.msg);
|
||
}
|
||
}
|
||
} else if(data.msg.indexOf('失败') !== -1){
|
||
failReasons.push('域名 ' + records[0].domainName + ':' + data.msg);
|
||
}
|
||
}else{
|
||
totalFail += records.length;
|
||
$.each(records, function(i, record){
|
||
failReasons.push('记录 ' + record.host + ' [域名: ' + record.domainName + ']:' + data.msg);
|
||
});
|
||
}
|
||
|
||
if(completedCount >= totalCount){
|
||
layer.close(ii);
|
||
$btn.prop('disabled', false).html(btnOrigHtml);
|
||
|
||
var msg = '批量添加完成!';
|
||
if(totalSuccess > 0){
|
||
msg += '\n成功:' + totalSuccess + ' 条';
|
||
}
|
||
if(totalFail > 0){
|
||
msg += '\n失败:' + totalFail + ' 条';
|
||
if(failReasons.length > 0){
|
||
msg += '\n\n失败原因:';
|
||
$.each(failReasons, function(i, reason){
|
||
msg += '\n' + (i + 1) + '. ' + reason;
|
||
});
|
||
}
|
||
}
|
||
|
||
layer.alert(msg, {
|
||
icon: totalFail > 0 ? 2 : 1,
|
||
btn: ['确定'],
|
||
yes: function(index){
|
||
layer.close(index);
|
||
try {
|
||
resetBatchForm();
|
||
} catch(e) {
|
||
console.error('Error in callback:', e);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
},
|
||
error : function() {
|
||
completedCount++;
|
||
totalFail += records.length;
|
||
$.each(records, function(i, record){
|
||
failReasons.push('记录 ' + record.host + ' [域名: ' + record.domainName + ']:网络错误,无法连接服务器');
|
||
});
|
||
|
||
if(completedCount >= totalCount){
|
||
layer.close(ii);
|
||
$btn.prop('disabled', false).html(btnOrigHtml);
|
||
var msg = '批量添加完成!';
|
||
if(totalSuccess > 0){
|
||
msg += '\n成功:' + totalSuccess + ' 条';
|
||
}
|
||
if(totalFail > 0){
|
||
msg += '\n失败:' + totalFail + ' 条';
|
||
if(failReasons.length > 0){
|
||
msg += '\n\n失败原因:';
|
||
$.each(failReasons, function(i, reason){
|
||
msg += '\n' + (i + 1) + '. ' + reason;
|
||
});
|
||
}
|
||
}
|
||
|
||
layer.alert(msg, {
|
||
icon: totalFail > 0 ? 2 : 1,
|
||
btn: ['确定'],
|
||
yes: function(index){
|
||
layer.close(index);
|
||
try {
|
||
resetBatchForm();
|
||
} catch(e) {
|
||
console.error('Error in callback:', e);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function htmlEscape(str) {
|
||
return String(str)
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''');
|
||
}
|
||
</script>
|
||
{/block}
|