547 lines
16 KiB
Vue
547 lines
16 KiB
Vue
<template>
|
||
<div class="space-y-4">
|
||
<!-- 页面头部 -->
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<h2 class="text-lg font-medium text-gray-900">域名所有者模板</h2>
|
||
<p class="text-sm text-gray-600">管理域名注册使用的所有者信息模板</p>
|
||
</div>
|
||
<Button
|
||
@click="createDomainOwner"
|
||
variant="solid"
|
||
>
|
||
新建模板
|
||
</Button>
|
||
</div>
|
||
|
||
<!-- 搜索和筛选 -->
|
||
<div class="flex flex-col sm:flex-row gap-4">
|
||
<div class="flex-1">
|
||
<TextInput
|
||
v-model="searchQuery"
|
||
placeholder="搜索所有者姓名、单位名称..."
|
||
class="w-full"
|
||
>
|
||
<template #prefix>
|
||
<SearchIcon class="h-4 w-4 text-gray-400" />
|
||
</template>
|
||
</TextInput>
|
||
</div>
|
||
<div class="flex gap-2">
|
||
<select
|
||
v-model="selectedType"
|
||
class="rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||
>
|
||
<option value="">全部类型</option>
|
||
<option value="I">个人</option>
|
||
<option value="E">企业/组织</option>
|
||
</select>
|
||
<select
|
||
v-model="selectedStatus"
|
||
class="rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||
>
|
||
<option value="">全部状态</option>
|
||
<option value="1">已实名认证</option>
|
||
<option value="0">未实名认证</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 列表内容 -->
|
||
<div class="rounded-lg border border-gray-200 bg-white">
|
||
<!-- 表头 -->
|
||
<div class="grid grid-cols-12 gap-4 border-b border-gray-200 bg-gray-50 px-6 py-3 text-sm font-medium text-gray-700">
|
||
<div class="col-span-3">名称</div>
|
||
<div class="col-span-2">类型</div>
|
||
<div class="col-span-2">实名状态</div>
|
||
<div class="col-span-3">联系信息</div>
|
||
<div class="col-span-2">操作</div>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="isLoading" class="flex items-center justify-center py-12">
|
||
<Loader2Icon class="h-6 w-6 animate-spin text-gray-400" />
|
||
<span class="ml-2 text-gray-600">加载中...</span>
|
||
</div>
|
||
|
||
<!-- 错误状态 -->
|
||
<div v-else-if="error" class="flex items-center justify-center py-12">
|
||
<AlertCircleIcon class="h-6 w-6 text-red-500" />
|
||
<span class="ml-2 text-red-600">{{ error }}</span>
|
||
</div>
|
||
|
||
<!-- 空状态 -->
|
||
<div v-else-if="filteredOwners.length === 0" class="flex flex-col items-center justify-center py-12 text-gray-500">
|
||
<UsersIcon class="mb-4 h-12 w-12 text-gray-300" />
|
||
<p class="text-lg font-medium">暂无域名所有者模板</p>
|
||
<p class="text-sm">点击上方按钮创建第一个模板</p>
|
||
</div>
|
||
|
||
<!-- 数据列表 -->
|
||
<div v-else class="divide-y divide-gray-200">
|
||
<div
|
||
v-for="owner in filteredOwners"
|
||
:key="owner.name"
|
||
class="grid grid-cols-12 gap-4 px-6 py-4 hover:bg-gray-50 transition-colors cursor-pointer"
|
||
@click="viewOwner(owner)"
|
||
>
|
||
<!-- 名称列 -->
|
||
<div class="col-span-3">
|
||
<div class="font-medium text-gray-900">{{ getDisplayName(owner) }}</div>
|
||
<div class="text-sm text-gray-500">{{ owner.title || owner.name }}</div>
|
||
</div>
|
||
|
||
<!-- 类型列 -->
|
||
<div class="col-span-2 flex items-center">
|
||
<Badge :variant="owner.c_regtype === 'I' ? 'blue' : 'purple'">
|
||
{{ owner.c_regtype === 'I' ? '个人' : '企业/组织' }}
|
||
</Badge>
|
||
</div>
|
||
|
||
<!-- 实名状态列 -->
|
||
<div class="col-span-2 flex items-center">
|
||
<Badge :variant="getRealNameStatusVariant(owner.r_status)">
|
||
{{ getRealNameStatusText(owner.r_status) }}
|
||
</Badge>
|
||
</div>
|
||
|
||
<!-- 联系信息列 -->
|
||
<div class="col-span-3">
|
||
<div class="text-sm text-gray-900">{{ owner.c_em || '-' }}</div>
|
||
<div class="text-sm text-gray-500">{{ owner.c_ph || '-' }}</div>
|
||
</div>
|
||
|
||
<!-- 操作列 -->
|
||
<div class="col-span-2 flex items-center gap-2" @click.stop>
|
||
<Button
|
||
@click="viewOwner(owner)"
|
||
variant="ghost"
|
||
size="sm"
|
||
class="text-blue-600 hover:text-blue-700"
|
||
title="查看详情"
|
||
>
|
||
<EyeIcon class="h-4 w-4" />
|
||
</Button>
|
||
<Button
|
||
@click="editOwner(owner)"
|
||
variant="ghost"
|
||
size="sm"
|
||
class="text-gray-600 hover:text-gray-700"
|
||
title="编辑"
|
||
>
|
||
<EditIcon class="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div v-if="pagination.total > pagination.limit" class="flex items-center justify-between">
|
||
<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 gap-2">
|
||
<Button
|
||
@click="previousPage"
|
||
:disabled="pagination.pageno <= 1"
|
||
variant="outline"
|
||
size="sm"
|
||
>
|
||
上一页
|
||
</Button>
|
||
<Button
|
||
@click="nextPage"
|
||
:disabled="pagination.pageno >= pagination.pagecount"
|
||
variant="outline"
|
||
size="sm"
|
||
>
|
||
下一页
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 创建域名所有者弹窗 -->
|
||
<DomainOwnerDialog
|
||
:visible="showCreateDialog"
|
||
:isLoading="createLoading"
|
||
@close="handleCloseDialog"
|
||
@submit="handleCreateSubmit"
|
||
/>
|
||
|
||
<!-- 详情查看弹窗 -->
|
||
<div v-if="showDetailDialog" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg shadow-2xl max-w-2xl w-full mx-4 max-h-[90vh] flex flex-col overflow-hidden">
|
||
<!-- 标题栏 -->
|
||
<div class="p-4 border-b border-gray-200 bg-white rounded-t-lg flex-shrink-0">
|
||
<div class="flex items-center justify-between">
|
||
<h3 class="text-lg font-medium text-gray-900">域名所有者详情</h3>
|
||
<button
|
||
@click="closeDetailDialog"
|
||
class="text-gray-400 hover:text-gray-600 transition-colors duration-200"
|
||
>
|
||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内容区域 -->
|
||
<div class="p-6 overflow-y-auto flex-1" v-if="selectedOwner">
|
||
<div class="space-y-6">
|
||
<!-- 基本信息 -->
|
||
<div>
|
||
<h4 class="text-base font-medium text-gray-900 mb-4">基本信息</h4>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">所有者类型</label>
|
||
<p class="mt-1 text-sm text-gray-900">
|
||
<Badge :variant="selectedOwner.c_regtype === 'I' ? 'blue' : 'purple'">
|
||
{{ selectedOwner.c_regtype === 'I' ? '个人' : '企业/组织' }}
|
||
</Badge>
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">实名认证状态</label>
|
||
<p class="mt-1 text-sm text-gray-900">
|
||
<Badge :variant="getRealNameStatusVariant(selectedOwner.r_status)">
|
||
{{ getRealNameStatusText(selectedOwner.r_status) }}
|
||
</Badge>
|
||
</p>
|
||
</div>
|
||
<div v-if="selectedOwner.c_regtype === 'E'">
|
||
<label class="block text-sm font-medium text-gray-700">单位名称</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_org_m || '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">姓名</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ getDisplayName(selectedOwner) }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 联系信息 -->
|
||
<div>
|
||
<h4 class="text-base font-medium text-gray-900 mb-4">联系信息</h4>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">电子邮箱</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_em || '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">手机号码</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_ph || '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">省份</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_st_m || '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">城市</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_ct_m || '-' }}</p>
|
||
</div>
|
||
<div class="col-span-2">
|
||
<label class="block text-sm font-medium text-gray-700">通讯地址</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_adr_m || '-' }}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">邮编</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_pc || '-' }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 证件信息 -->
|
||
<div>
|
||
<h4 class="text-base font-medium text-gray-900 mb-4">证件信息</h4>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">证件类型</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ getCertificateTypeName(selectedOwner.c_idtype_gswl) }}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700">证件号码</label>
|
||
<p class="mt-1 text-sm text-gray-900">{{ selectedOwner.c_idnum_gswl || '-' }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部按钮 -->
|
||
<div class="p-4 border-t border-gray-200 bg-white rounded-b-lg flex-shrink-0">
|
||
<div class="flex justify-end gap-3">
|
||
<Button
|
||
@click="closeDetailDialog"
|
||
variant="outline"
|
||
>
|
||
关闭
|
||
</Button>
|
||
<Button
|
||
@click="editOwner(selectedOwner)"
|
||
variant="solid"
|
||
>
|
||
编辑
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { Badge, Button, TextInput, createResource } from 'jingrow-ui';
|
||
import { toast } from 'vue-sonner';
|
||
import { getToastErrorMessage } from '../utils/toast';
|
||
import { confirmDialog } from '../utils/components';
|
||
import DomainOwnerDialog from './DomainOwnerDialog.vue';
|
||
import SearchIcon from '~icons/lucide/search';
|
||
import EyeIcon from '~icons/lucide/eye';
|
||
import EditIcon from '~icons/lucide/edit';
|
||
import Loader2Icon from '~icons/lucide/loader-2';
|
||
import AlertCircleIcon from '~icons/lucide/alert-circle';
|
||
import UsersIcon from '~icons/lucide/users';
|
||
|
||
export default {
|
||
name: 'DomainOwner',
|
||
components: {
|
||
Badge,
|
||
Button,
|
||
TextInput,
|
||
SearchIcon,
|
||
EyeIcon,
|
||
EditIcon,
|
||
Loader2Icon,
|
||
AlertCircleIcon,
|
||
UsersIcon,
|
||
DomainOwnerDialog,
|
||
},
|
||
data() {
|
||
return {
|
||
owners: [],
|
||
isLoading: false,
|
||
error: null,
|
||
searchQuery: '',
|
||
selectedType: '',
|
||
selectedStatus: '',
|
||
pagination: {
|
||
pageno: 1,
|
||
limit: 20,
|
||
total: 0,
|
||
pagecount: 0
|
||
},
|
||
showCreateDialog: false,
|
||
createLoading: false,
|
||
showDetailDialog: false,
|
||
selectedOwner: null
|
||
};
|
||
},
|
||
computed: {
|
||
filteredOwners() {
|
||
let filtered = [...this.owners];
|
||
|
||
// 搜索过滤
|
||
if (this.searchQuery) {
|
||
const query = this.searchQuery.toLowerCase();
|
||
filtered = filtered.filter(owner => {
|
||
const displayName = this.getDisplayName(owner).toLowerCase();
|
||
const title = (owner.title || '').toLowerCase();
|
||
const email = (owner.c_em || '').toLowerCase();
|
||
return displayName.includes(query) || title.includes(query) || email.includes(query);
|
||
});
|
||
}
|
||
|
||
// 类型过滤
|
||
if (this.selectedType) {
|
||
filtered = filtered.filter(owner => owner.c_regtype === this.selectedType);
|
||
}
|
||
|
||
// 状态过滤
|
||
if (this.selectedStatus) {
|
||
filtered = filtered.filter(owner => String(owner.r_status) === this.selectedStatus);
|
||
}
|
||
|
||
return filtered;
|
||
}
|
||
},
|
||
methods: {
|
||
// 获取显示名称
|
||
getDisplayName(owner) {
|
||
if (owner.c_regtype === 'I') {
|
||
// 个人:优先显示 fullname,没有的话显示 姓+名
|
||
if (owner.fullname) {
|
||
return owner.fullname;
|
||
}
|
||
const lastName = owner.c_ln_m || '';
|
||
const firstName = owner.c_fn_m || '';
|
||
return `${lastName}${firstName}`.trim() || owner.title || owner.name;
|
||
} else if (owner.c_regtype === 'E') {
|
||
// 企业:优先显示 c_org_m,没有的话显示 title
|
||
return owner.c_org_m || owner.title || owner.name;
|
||
}
|
||
return owner.title || owner.name;
|
||
},
|
||
|
||
// 获取实名认证状态文本
|
||
getRealNameStatusText(status) {
|
||
const statusMap = {
|
||
'1': '已认证',
|
||
'0': '未认证',
|
||
'2': '认证中',
|
||
'3': '认证失败',
|
||
'4': '认证过期',
|
||
'5': '待审核'
|
||
};
|
||
return statusMap[String(status)] || '未知';
|
||
},
|
||
|
||
// 获取实名认证状态样式
|
||
getRealNameStatusVariant(status) {
|
||
const variantMap = {
|
||
'1': 'success', // 已认证 - 绿色
|
||
'0': 'warning', // 未认证 - 橙色
|
||
'2': 'blue', // 认证中 - 蓝色
|
||
'3': 'danger', // 认证失败 - 红色
|
||
'4': 'danger', // 认证过期 - 红色
|
||
'5': 'blue' // 待审核 - 蓝色
|
||
};
|
||
return variantMap[String(status)] || 'default';
|
||
},
|
||
|
||
// 获取域名所有者列表
|
||
async fetchOwners() {
|
||
this.isLoading = true;
|
||
this.error = null;
|
||
|
||
try {
|
||
const ownersRequest = createResource({
|
||
url: 'jcloud.api.domain_west.get_domain_owners',
|
||
onSuccess: (response) => {
|
||
if (response.status === 'Success') {
|
||
this.owners = response.data || [];
|
||
this.pagination.total = this.owners.length;
|
||
this.pagination.pagecount = Math.ceil(this.pagination.total / this.pagination.limit);
|
||
} else {
|
||
this.error = response.message || '获取域名所有者列表失败';
|
||
}
|
||
this.isLoading = false;
|
||
},
|
||
onError: (error) => {
|
||
this.error = getToastErrorMessage(error);
|
||
this.isLoading = false;
|
||
}
|
||
});
|
||
|
||
ownersRequest.submit();
|
||
} catch (error) {
|
||
this.error = '获取域名所有者列表失败';
|
||
this.isLoading = false;
|
||
}
|
||
},
|
||
|
||
// 创建新的域名所有者
|
||
createDomainOwner() {
|
||
this.showCreateDialog = true;
|
||
},
|
||
|
||
// 处理弹窗关闭
|
||
handleCloseDialog() {
|
||
this.showCreateDialog = false;
|
||
this.createLoading = false;
|
||
},
|
||
|
||
// 处理创建提交
|
||
async handleCreateSubmit(data) {
|
||
this.createLoading = true;
|
||
|
||
try {
|
||
const createRequest = createResource({
|
||
url: 'jcloud.api.domain_west.create_domain_owner',
|
||
params: data,
|
||
onSuccess: (response) => {
|
||
if (response.status === 'Success') {
|
||
toast.success('域名所有者模板创建成功!');
|
||
this.fetchOwners(); // 刷新列表
|
||
this.handleCloseDialog(); // 关闭弹窗
|
||
} else {
|
||
toast.error(response.message || '创建失败');
|
||
this.createLoading = false;
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
toast.error(getToastErrorMessage(error));
|
||
this.createLoading = false;
|
||
}
|
||
});
|
||
|
||
createRequest.submit();
|
||
} catch (error) {
|
||
toast.error('创建失败,请稍后重试');
|
||
this.createLoading = false;
|
||
}
|
||
},
|
||
|
||
// 查看域名所有者详情
|
||
viewOwner(owner) {
|
||
this.selectedOwner = owner;
|
||
this.showDetailDialog = true;
|
||
},
|
||
|
||
// 关闭详情弹窗
|
||
closeDetailDialog() {
|
||
this.showDetailDialog = false;
|
||
this.selectedOwner = null;
|
||
},
|
||
|
||
// 编辑域名所有者
|
||
editOwner(owner) {
|
||
// TODO: 实现编辑功能
|
||
console.log('编辑所有者:', owner);
|
||
// 关闭详情弹窗(如果是从详情弹窗点击编辑)
|
||
if (this.showDetailDialog) {
|
||
this.closeDetailDialog();
|
||
}
|
||
},
|
||
|
||
// 获取证件类型名称
|
||
getCertificateTypeName(type) {
|
||
const typeMap = {
|
||
'SFZ': '身份证',
|
||
'HZ': '护照',
|
||
'YYZZ': '营业执照',
|
||
'ORG': '组织机构代码证'
|
||
};
|
||
return typeMap[type] || type || '-';
|
||
},
|
||
|
||
|
||
|
||
// 上一页
|
||
previousPage() {
|
||
if (this.pagination.pageno > 1) {
|
||
this.pagination.pageno--;
|
||
}
|
||
},
|
||
|
||
// 下一页
|
||
nextPage() {
|
||
if (this.pagination.pageno < this.pagination.pagecount) {
|
||
this.pagination.pageno++;
|
||
}
|
||
},
|
||
|
||
// 刷新列表
|
||
refresh() {
|
||
this.fetchOwners();
|
||
}
|
||
},
|
||
mounted() {
|
||
// 组件挂载时获取数据
|
||
this.fetchOwners();
|
||
}
|
||
};
|
||
</script> |