771 lines
22 KiB
Vue
771 lines
22 KiB
Vue
<template>
|
||
<div class="space-y-6">
|
||
<!-- 标题和操作按钮 -->
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
</div>
|
||
<div class="flex gap-2">
|
||
<Button
|
||
@click="refreshRecords"
|
||
:loading="loading"
|
||
variant="outline"
|
||
size="sm"
|
||
>
|
||
<RefreshCwIcon class="h-4 w-4 mr-1" />
|
||
刷新
|
||
</Button>
|
||
<Button
|
||
v-if="selectedRecords.length > 0"
|
||
@click="batchDeleteRecords"
|
||
variant="outline"
|
||
size="sm"
|
||
class="text-red-600 hover:text-red-700"
|
||
>
|
||
<Trash2Icon class="h-4 w-4 mr-1" />
|
||
删除选中 ({{ selectedRecords.length }})
|
||
</Button>
|
||
<Button
|
||
@click="addNewRow"
|
||
variant="solid"
|
||
size="sm"
|
||
>
|
||
<PlusIcon class="h-4 w-4 mr-1" />
|
||
添加记录
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="flex items-center justify-center py-12">
|
||
<div class="flex items-center space-x-2">
|
||
<Loader2Icon class="h-5 w-5 animate-spin text-gray-500" />
|
||
<span class="text-gray-500">正在加载DNS记录...</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 错误状态 -->
|
||
<div v-else-if="error" class="rounded-md border border-red-200 bg-red-50 p-4">
|
||
<div class="flex items-center">
|
||
<AlertCircleIcon class="h-5 w-5 text-red-400 mr-2" />
|
||
<div>
|
||
<h3 class="text-sm font-medium text-red-800">加载失败</h3>
|
||
<p class="text-sm text-red-700 mt-1">{{ error }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DNS记录列表 -->
|
||
<div v-else-if="!loading && !error" class="space-y-4">
|
||
<div class="overflow-hidden rounded-md border border-gray-200">
|
||
<table class="min-w-full divide-y divide-gray-200">
|
||
<thead class="bg-gray-50">
|
||
<tr>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
<input
|
||
type="checkbox"
|
||
class="rounded border-gray-300"
|
||
:checked="isAllSelected"
|
||
:indeterminate="isIndeterminate"
|
||
@change="toggleSelectAll"
|
||
/>
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
编号
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
主机名 *
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
类型 *
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
线路
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
对应值 *
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
TTL
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
优先级
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
状态
|
||
</th>
|
||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
操作
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="bg-white divide-y divide-gray-200">
|
||
<!-- 现有记录 -->
|
||
<tr v-for="(record, index) in dnsRecords" :key="record.id || `temp-${index}`" class="hover:bg-gray-50">
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<input
|
||
type="checkbox"
|
||
class="rounded border-gray-300"
|
||
:checked="selectedRecords.includes(String(record.id))"
|
||
@change="toggleRecordSelection(record.id)"
|
||
:disabled="record.isNew"
|
||
/>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
|
||
{{ record.isNew ? '-' : (dnsRecords.filter(r => !r.isNew).findIndex(r => r.id === record.id) + 1) }}
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<!-- 主机名编辑 -->
|
||
<div v-if="record.editing" class="flex items-center space-x-2">
|
||
<input
|
||
v-model="record.item"
|
||
type="text"
|
||
class="w-24 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
:class="{ 'bg-gray-100 text-gray-500 cursor-not-allowed': !record.isNew }"
|
||
placeholder="主机名"
|
||
:disabled="!record.isNew"
|
||
/>
|
||
</div>
|
||
<div v-else class="text-sm text-gray-900">
|
||
{{ record.item || '-' }}
|
||
</div>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<!-- 类型编辑 -->
|
||
<div v-if="record.editing" class="flex items-center space-x-2">
|
||
<select
|
||
v-model="record.type"
|
||
class="w-20 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
:class="{ 'bg-gray-100 text-gray-500 cursor-not-allowed': !record.isNew }"
|
||
:disabled="!record.isNew"
|
||
>
|
||
<option value="A">A</option>
|
||
<option value="AAAA">AAAA</option>
|
||
<option value="CNAME">CNAME</option>
|
||
<option value="MX">MX</option>
|
||
<option value="NS">NS</option>
|
||
<option value="TXT">TXT</option>
|
||
<option value="SRV">SRV</option>
|
||
</select>
|
||
</div>
|
||
<div v-else>
|
||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
|
||
:class="{
|
||
'bg-green-100 text-green-800': record.type === 'A',
|
||
'bg-blue-100 text-blue-800': record.type === 'AAAA',
|
||
'bg-yellow-100 text-yellow-800': record.type === 'CNAME',
|
||
'bg-purple-100 text-purple-800': record.type === 'MX',
|
||
'bg-indigo-100 text-indigo-800': record.type === 'NS',
|
||
'bg-gray-100 text-gray-800': record.type === 'TXT',
|
||
'bg-orange-100 text-orange-800': record.type === 'SRV',
|
||
'bg-gray-100 text-gray-800': !record.type
|
||
}">
|
||
{{ record.type || '未知' }}
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<!-- 线路编辑 -->
|
||
<div v-if="record.editing" class="flex items-center space-x-2">
|
||
<select
|
||
v-model="record.line"
|
||
class="w-20 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
>
|
||
<option value="">默认</option>
|
||
<option value="LTEL">电信</option>
|
||
<option value="LCNC">联通</option>
|
||
<option value="LMOB">移动</option>
|
||
<option value="LEDU">教育网</option>
|
||
<option value="LSEO">搜索引擎</option>
|
||
</select>
|
||
</div>
|
||
<div v-else class="text-sm text-gray-500">
|
||
{{ getLineDisplayName(record.line) }}
|
||
</div>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<!-- 对应值编辑 -->
|
||
<div v-if="record.editing" class="flex items-center space-x-2">
|
||
<input
|
||
v-model="record.value"
|
||
type="text"
|
||
class="w-32 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="记录值"
|
||
/>
|
||
</div>
|
||
<div v-else class="text-sm text-gray-900 max-w-xs truncate" :title="record.value">
|
||
{{ record.value || '-' }}
|
||
</div>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<!-- TTL编辑 -->
|
||
<div v-if="record.editing" class="flex items-center space-x-2">
|
||
<input
|
||
v-model="record.ttl"
|
||
type="number"
|
||
min="60"
|
||
max="86400"
|
||
class="w-20 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
</div>
|
||
<div v-else class="text-sm text-gray-500">
|
||
{{ record.ttl || '600' }}
|
||
</div>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap">
|
||
<!-- 优先级编辑 -->
|
||
<div v-if="record.editing" class="flex items-center space-x-2">
|
||
<input
|
||
v-model="record.level"
|
||
type="number"
|
||
min="1"
|
||
max="100"
|
||
class="w-16 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
</div>
|
||
<div v-else class="text-sm text-gray-500">
|
||
{{ record.type === 'MX' && record.level ? record.level : '-' }}
|
||
</div>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap text-sm">
|
||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
||
正常
|
||
</span>
|
||
</td>
|
||
<td class="px-4 py-4 whitespace-nowrap text-sm">
|
||
<div class="flex items-center space-x-1">
|
||
<Button
|
||
v-if="!record.editing"
|
||
@click="editRecord(record)"
|
||
variant="ghost"
|
||
size="sm"
|
||
class="text-blue-600 hover:text-blue-700"
|
||
>
|
||
编辑
|
||
</Button>
|
||
<Button
|
||
v-if="record.editing"
|
||
@click="saveRecord(record)"
|
||
variant="ghost"
|
||
size="sm"
|
||
class="text-green-600 hover:text-green-700"
|
||
>
|
||
保存
|
||
</Button>
|
||
<Button
|
||
v-if="record.editing"
|
||
@click="cancelEdit(record)"
|
||
variant="ghost"
|
||
size="sm"
|
||
class="text-gray-600 hover:text-gray-700"
|
||
>
|
||
取消
|
||
</Button>
|
||
<Button
|
||
v-if="!record.editing && !record.isNew"
|
||
@click="deleteRecord(record)"
|
||
variant="ghost"
|
||
size="sm"
|
||
class="text-red-600 hover:text-red-700"
|
||
>
|
||
删除
|
||
</Button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="pagination.total > pagination.limit" class="flex flex-col items-center space-y-4">
|
||
<div class="text-sm text-gray-700">
|
||
显示第 {{ (pagination.pageno - 1) * pagination.limit + 1 }} -
|
||
{{ Math.min(pagination.pageno * pagination.limit, pagination.total) }} 条,
|
||
共 {{ pagination.total }} 条记录
|
||
</div>
|
||
<div class="flex items-center space-x-2">
|
||
<Button
|
||
@click="changePage(pagination.pageno - 1)"
|
||
:disabled="pagination.pageno <= 1"
|
||
variant="outline"
|
||
size="sm"
|
||
class="px-3"
|
||
>
|
||
←
|
||
</Button>
|
||
<div class="flex items-center space-x-1">
|
||
<Button
|
||
v-for="page in getVisiblePages()"
|
||
:key="page"
|
||
@click="changePage(page)"
|
||
:class="[
|
||
'px-3 py-1 text-sm rounded',
|
||
page === pagination.pageno
|
||
? '!bg-[#1fc76f] text-white'
|
||
: 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50'
|
||
]"
|
||
:disabled="page === pagination.pageno"
|
||
>
|
||
{{ page }}
|
||
</Button>
|
||
</div>
|
||
<Button
|
||
@click="changePage(pagination.pageno + 1)"
|
||
:disabled="pagination.pageno >= pagination.pagecount"
|
||
variant="outline"
|
||
size="sm"
|
||
class="px-3"
|
||
>
|
||
→
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 空状态 -->
|
||
<div v-else-if="!loading && !error && dnsRecords.length === 0" class="text-center py-12">
|
||
<GlobeIcon class="mx-auto h-12 w-12 text-gray-400" />
|
||
<h3 class="mt-2 text-sm font-medium text-gray-900">暂无DNS记录</h3>
|
||
<p class="mt-1 text-sm text-gray-500">开始添加您的第一个DNS解析记录</p>
|
||
<div class="mt-6">
|
||
<Button @click="addNewRow" variant="solid">
|
||
<PlusIcon class="h-4 w-4 mr-1" />
|
||
添加记录
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { getCachedDocumentResource, Badge, Button, createResource } from 'jingrow-ui';
|
||
import { h, defineAsyncComponent } from 'vue';
|
||
import { toast } from 'vue-sonner';
|
||
import { getToastErrorMessage } from '../utils/toast';
|
||
import { renderDialog, confirmDialog } from '../utils/components';
|
||
import RefreshCwIcon from '~icons/lucide/refresh-cw';
|
||
import PlusIcon from '~icons/lucide/plus';
|
||
import Loader2Icon from '~icons/lucide/loader-2';
|
||
import AlertCircleIcon from '~icons/lucide/alert-circle';
|
||
import EditIcon from '~icons/lucide/edit';
|
||
import Trash2Icon from '~icons/lucide/trash-2';
|
||
import GlobeIcon from '~icons/lucide/globe';
|
||
import SettingsIcon from '~icons/lucide/settings';
|
||
|
||
export default {
|
||
name: 'JsiteDomainDNSRecords',
|
||
props: ['domain'],
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
error: null,
|
||
dnsRecords: [],
|
||
pagination: {
|
||
pageno: 1,
|
||
limit: 10,
|
||
total: 0,
|
||
pagecount: 0
|
||
},
|
||
selectedRecords: [] // 用于存储选中的记录ID
|
||
};
|
||
},
|
||
methods: {
|
||
// 获取DNS记录
|
||
async loadDNSRecords() {
|
||
if (!this.$domain.pg?.domain) {
|
||
this.error = '域名信息不存在';
|
||
return;
|
||
}
|
||
|
||
this.loading = true;
|
||
this.error = null;
|
||
|
||
try {
|
||
const request = createResource({
|
||
url: 'jcloud.api.domain_west.get_west_domain_dns_records',
|
||
params: {
|
||
domain: this.$domain.pg.domain,
|
||
limit: this.pagination.limit,
|
||
pageno: this.pagination.pageno
|
||
},
|
||
onSuccess: (response) => {
|
||
this.loading = false;
|
||
if (response.status === 'success' && response.data) {
|
||
// 为每个记录添加编辑状态
|
||
this.dnsRecords = (response.data.items || []).map(record => ({
|
||
...record,
|
||
editing: false,
|
||
isNew: false
|
||
}));
|
||
|
||
this.pagination = {
|
||
pageno: response.data.pageno || 1,
|
||
limit: response.data.limit || 20,
|
||
total: response.data.total || 0,
|
||
pagecount: response.data.pagecount || 0
|
||
};
|
||
} else {
|
||
this.error = response.message || '获取DNS记录失败';
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
this.loading = false;
|
||
this.error = getToastErrorMessage(error);
|
||
}
|
||
});
|
||
request.submit();
|
||
} catch (error) {
|
||
this.loading = false;
|
||
this.error = '获取DNS记录时发生错误';
|
||
console.error('加载DNS记录失败:', error);
|
||
}
|
||
},
|
||
|
||
// 刷新记录
|
||
refreshRecords() {
|
||
this.loadDNSRecords();
|
||
},
|
||
|
||
// 切换页面
|
||
changePage(page) {
|
||
if (page >= 1 && page <= this.pagination.pagecount) {
|
||
this.pagination.pageno = page;
|
||
this.loadDNSRecords();
|
||
}
|
||
},
|
||
|
||
// 获取记录类型样式
|
||
getRecordTypeVariant(type) {
|
||
const variantMap = {
|
||
'A': 'success',
|
||
'AAAA': 'info',
|
||
'CNAME': 'warning',
|
||
'MX': 'primary',
|
||
'NS': 'info',
|
||
'TXT': 'secondary',
|
||
'SRV': 'warning'
|
||
};
|
||
return variantMap[type] || 'default';
|
||
},
|
||
|
||
// 获取线路显示名称
|
||
getLineDisplayName(line) {
|
||
const lineMap = {
|
||
'LTEL': '电信',
|
||
'LCNC': '联通',
|
||
'LMOB': '移动',
|
||
'LEDU': '教育网',
|
||
'LSEO': '搜索引擎',
|
||
'': '默认'
|
||
};
|
||
return lineMap[line] || line;
|
||
},
|
||
|
||
// 添加新行
|
||
addNewRow() {
|
||
const newRecord = {
|
||
id: null, // 新增记录没有ID
|
||
item: '',
|
||
type: 'A',
|
||
line: '',
|
||
value: '',
|
||
ttl: 600,
|
||
level: 10,
|
||
editing: true, // 新增记录直接进入编辑模式
|
||
isNew: true
|
||
};
|
||
|
||
// 添加新记录到最前面
|
||
this.dnsRecords.unshift(newRecord);
|
||
},
|
||
|
||
// 编辑记录
|
||
editRecord(record) {
|
||
// 保存原始值用于取消编辑
|
||
record._original = { ...record };
|
||
record.editing = true;
|
||
record.isNew = false; // 确保不是新增记录
|
||
},
|
||
|
||
// 保存记录
|
||
async saveRecord(record) {
|
||
if (!record.item || !record.type || !record.value) {
|
||
toast.error('主机名、类型和记录值不能为空');
|
||
return;
|
||
}
|
||
|
||
// 验证TTL值
|
||
if (record.ttl < 60 || record.ttl > 86400) {
|
||
toast.error('TTL值必须在60~86400秒之间');
|
||
return;
|
||
}
|
||
|
||
// 验证优先级
|
||
if (record.level < 1 || record.level > 100) {
|
||
toast.error('优先级必须在1~100之间');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
let url, params;
|
||
|
||
if (record.isNew) {
|
||
// 新增记录
|
||
url = 'jcloud.api.domain_west.west_domain_add_dns_record';
|
||
params = {
|
||
domain: this.$domain.pg.domain,
|
||
record_type: record.type,
|
||
host: record.item,
|
||
value: record.value,
|
||
ttl: record.ttl,
|
||
level: record.level,
|
||
line: record.line
|
||
};
|
||
} else {
|
||
// 修改记录 - 只传递可修改的字段
|
||
url = 'jcloud.api.domain_west.west_domain_modify_dns_record';
|
||
params = {
|
||
domain: this.$domain.pg.domain,
|
||
record_id: record.id,
|
||
value: record.value,
|
||
ttl: record.ttl,
|
||
level: record.level,
|
||
line: record.line
|
||
};
|
||
}
|
||
|
||
const request = createResource({
|
||
url: url,
|
||
params: params,
|
||
onSuccess: (response) => {
|
||
if (response.status === 'success') {
|
||
toast.success('DNS记录保存成功');
|
||
record.editing = false;
|
||
record.isNew = false;
|
||
// 重新加载记录列表
|
||
this.loadDNSRecords();
|
||
} else {
|
||
toast.error(response.message || '保存DNS记录失败');
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
toast.error(getToastErrorMessage(error));
|
||
}
|
||
});
|
||
request.submit();
|
||
} catch (error) {
|
||
toast.error('保存DNS记录失败');
|
||
console.error('保存DNS记录失败:', error);
|
||
}
|
||
},
|
||
|
||
// 取消编辑
|
||
cancelEdit(record) {
|
||
if (record.isNew) {
|
||
// 如果是新增记录,直接删除
|
||
const index = this.dnsRecords.indexOf(record);
|
||
if (index > -1) {
|
||
this.dnsRecords.splice(index, 1);
|
||
}
|
||
} else {
|
||
// 如果是编辑中的记录,恢复其原始值
|
||
if (record._original) {
|
||
Object.assign(record, record._original);
|
||
delete record._original;
|
||
}
|
||
record.editing = false;
|
||
}
|
||
},
|
||
|
||
// 删除记录
|
||
deleteRecord(record) {
|
||
confirmDialog({
|
||
title: '删除DNS记录',
|
||
message: `确定要删除这条DNS记录吗?\n类型: ${record.type}\n主机记录: ${record.item}\n记录值: ${record.value}`,
|
||
primaryAction: {
|
||
label: '确定',
|
||
variant: 'solid',
|
||
class: 'bg-black text-white hover:bg-gray-800',
|
||
onClick: ({ hide }) => {
|
||
this.performDeleteRecord(record, hide);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 执行删除记录
|
||
async performDeleteRecord(record, hide) {
|
||
try {
|
||
const request = createResource({
|
||
url: 'jcloud.api.domain_west.west_domain_delete_dns_record',
|
||
params: {
|
||
domain: this.$domain.pg.domain,
|
||
record_id: record.id
|
||
},
|
||
onSuccess: (response) => {
|
||
if (response.status === 'success') {
|
||
toast.success('DNS记录删除成功');
|
||
hide();
|
||
this.loadDNSRecords();
|
||
} else {
|
||
toast.error(response.message || '删除DNS记录失败');
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
toast.error(getToastErrorMessage(error));
|
||
}
|
||
});
|
||
request.submit();
|
||
} catch (error) {
|
||
toast.error('删除DNS记录失败');
|
||
console.error('删除DNS记录失败:', error);
|
||
}
|
||
},
|
||
|
||
// 批量删除记录
|
||
async batchDeleteRecords() {
|
||
if (this.selectedRecords.length === 0) {
|
||
toast.info('请选择要删除的记录');
|
||
return;
|
||
}
|
||
|
||
confirmDialog({
|
||
title: '批量删除DNS记录',
|
||
message: `确定要删除选中的 ${this.selectedRecords.length} 条DNS记录吗?`,
|
||
primaryAction: {
|
||
label: '确定',
|
||
variant: 'solid',
|
||
class: 'bg-black text-white hover:bg-gray-800',
|
||
onClick: ({ hide }) => {
|
||
this.performBatchDeleteRecords(this.selectedRecords, hide);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 执行批量删除记录
|
||
async performBatchDeleteRecords(recordIds, hide) {
|
||
// 过滤出有效的记录ID
|
||
const validRecordIds = recordIds
|
||
.filter(id => id)
|
||
.map(id => String(id))
|
||
.filter(id => !id.startsWith('temp-'));
|
||
|
||
if (validRecordIds.length === 0) {
|
||
toast.error('没有有效的记录可以删除');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const request = createResource({
|
||
url: 'jcloud.api.domain_west.west_domain_delete_dns_records',
|
||
params: {
|
||
domain: this.$domain.pg.domain,
|
||
record_ids: validRecordIds
|
||
},
|
||
onSuccess: (response) => {
|
||
if (response.status === 'success') {
|
||
toast.success(response.message || `批量删除 ${validRecordIds.length} 条DNS记录成功`);
|
||
hide();
|
||
this.loadDNSRecords();
|
||
this.selectedRecords = []; // 清空选中
|
||
} else if (response.status === 'partial_success') {
|
||
toast.success(response.message || '批量删除部分成功');
|
||
hide();
|
||
this.loadDNSRecords();
|
||
this.selectedRecords = []; // 清空选中
|
||
} else {
|
||
toast.error(response.message || '批量删除DNS记录失败');
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
toast.error(getToastErrorMessage(error));
|
||
}
|
||
});
|
||
request.submit();
|
||
} catch (error) {
|
||
toast.error('批量删除DNS记录失败');
|
||
console.error('批量删除DNS记录失败:', error);
|
||
}
|
||
},
|
||
|
||
// 切换记录选中状态
|
||
toggleRecordSelection(recordId) {
|
||
// 只允许选择有效的记录ID(排除新增记录和空ID)
|
||
if (!recordId) {
|
||
return;
|
||
}
|
||
|
||
// 确保recordId是字符串类型
|
||
const recordIdStr = String(recordId);
|
||
if (recordIdStr.startsWith('temp-')) {
|
||
return;
|
||
}
|
||
|
||
const index = this.selectedRecords.indexOf(recordIdStr);
|
||
if (index > -1) {
|
||
this.selectedRecords.splice(index, 1);
|
||
} else {
|
||
this.selectedRecords.push(recordIdStr);
|
||
}
|
||
},
|
||
|
||
// 全选/取消全选
|
||
toggleSelectAll(event) {
|
||
const checkbox = event.target;
|
||
if (checkbox.checked) {
|
||
// 只选择有真实ID的记录(排除新增的记录)
|
||
this.selectedRecords = this.dnsRecords
|
||
.filter(record => record.id && !record.isNew)
|
||
.map(record => String(record.id));
|
||
} else {
|
||
this.selectedRecords = [];
|
||
}
|
||
},
|
||
|
||
|
||
|
||
// 获取可见的页码
|
||
getVisiblePages() {
|
||
const current = this.pagination.pageno;
|
||
const total = this.pagination.pagecount;
|
||
const pages = [];
|
||
|
||
// 如果总页数小于等于5,显示所有页码
|
||
if (total <= 5) {
|
||
for (let i = 1; i <= total; i++) {
|
||
pages.push(i);
|
||
}
|
||
} else {
|
||
// 显示当前页附近的页码
|
||
const start = Math.max(1, current - 2);
|
||
const end = Math.min(total, current + 2);
|
||
|
||
for (let i = start; i <= end; i++) {
|
||
pages.push(i);
|
||
}
|
||
}
|
||
|
||
return pages;
|
||
}
|
||
},
|
||
computed: {
|
||
$domain() {
|
||
return getCachedDocumentResource('Jsite Domain', this.domain);
|
||
},
|
||
|
||
// 判断是否全选
|
||
isAllSelected() {
|
||
return this.dnsRecords.length > 0 && this.selectedRecords.length === this.dnsRecords.length;
|
||
},
|
||
|
||
// 判断是否半选
|
||
isIndeterminate() {
|
||
return this.selectedRecords.length > 0 && this.selectedRecords.length < this.dnsRecords.length;
|
||
}
|
||
},
|
||
mounted() {
|
||
this.loadDNSRecords();
|
||
}
|
||
};
|
||
</script> |