Files
YG_FT_Platform/web/pages/model-manage-create.html
WIN-JHFT4D3SIVT\caoxiaozhu 03b6071856 1. 修改了合并模型导出模型的逻辑
2. 修改了一些冗余的bug
3. 页面上表格的调整
2026-01-30 12:47:25 +08:00

703 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>添加模型 / 远光软件微调平台</title>
<script src="../lib/tailwindcss/tailwind.js"></script>
<script>
if (typeof console !== 'undefined' && console.warn) {
const originalWarn = console.warn;
console.warn = function(...args) {
if (args[0] && args[0].includes && args[0].includes('cdn.tailwindcss.com')) {
return;
}
originalWarn.apply(console, args);
};
}
</script>
<link href="../lib/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<style>
.sidebar-section-title {
padding: 0.5rem 1rem;
font-size: 0.75rem;
color: rgba(191, 203, 217, 0.7);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.nav-link:hover {
background-color: rgba(0, 21, 41, 0.2);
}
.form-input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
font-size: 0.875rem;
transition: border-color 0.2s, outline 0.2s;
}
.form-input:focus {
border-color: #1890ff;
outline: none;
}
.form-select {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
font-size: 0.875rem;
transition: border-color 0.2s, outline 0.2s;
appearance: none;
background-color: white;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
}
.form-select:focus {
border-color: #1890ff;
outline: none;
}
.upload-area:hover,
.upload-area.drag-over {
border-color: #1890ff;
background-color: rgba(24, 144, 255, 0.05);
}
.bg-primary { background-color: #1890ff; }
.text-primary { color: #1890ff; }
.border-primary { border-color: #1890ff; }
:root { --primary: #1890ff; --danger: #f5222d; --success: #52c41a; }
/* 侧边栏滑块动画 */
.sidebar-slider {
position: absolute;
width: 4px;
height: 0;
background-color: #1890ff;
border-radius: 0 2px 2px 0;
transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1),
height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
z-index: 10;
}
.nav-item-wrapper {
position: relative;
}
.nav-link {
position: relative;
z-index: 1;
}
</style>
</head>
<body class="antialiased bg-gray-50 flex h-screen overflow-hidden">
<!-- 侧边导航 -->
<aside class="w-64 text-[#bfcbd9] flex-shrink-0 hidden md:block flex flex-col h-full" style="background-color: #001529;">
<!-- 平台LOGO区域 -->
<div class="pt-5 pb-3 border-b border-[#001529]/30 flex items-center justify-center pl-2">
<img src="../assets/logo/logo.png" alt="Logo" class="w-8 h-8 object-contain mr-2">
<span class="text-white font-medium text-base">远光软件微调平台</span>
</div>
<!-- 导航主区域 -->
<nav class="flex-1 overflow-y-auto py-2 relative">
<!-- 滑块指示器 -->
<div class="sidebar-slider" id="sidebar-slider"></div>
<!-- 第一分区:模型服务 -->
<div class="sidebar-section-title">模型服务</div>
<div class="nav-item-wrapper">
<a href="main.html" data-page="fine-tune" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-cogs w-5 text-center"></i>
<span class="ml-2">模型调优</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="main.html?page=my-models" data-page="my-models" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-database w-5 text-center"></i>
<span class="ml-2">我的模型</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="main.html?page=model-eval" data-page="model-eval" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-line-chart w-5 text-center"></i>
<span class="ml-2">模型评测</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="main.html?page=model-compare" data-page="model-compare" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-server w-5 text-center"></i>
<span class="ml-2">模型对比</span>
</a>
</div>
<!-- 第二分区:资源管理 -->
<div class="sidebar-section-title mt-6">资源管理</div>
<div class="nav-item-wrapper">
<a href="main.html?page=model-manage" data-page="model-manage" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-cube w-5 text-center"></i>
<span class="ml-2">模型管理</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="main.html?page=dataset-manage" data-page="dataset-manage" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-file-text w-5 text-center"></i>
<span class="ml-2">数据集管理</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="main.html?page=data-generate" data-page="data-generate" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-database w-5 text-center"></i>
<span class="ml-2">其他工具</span>
</a>
</div>
<!-- 第三分区:系统设置 -->
<div class="sidebar-section-title mt-6">系统设置</div>
<div class="nav-item-wrapper">
<a href="main.html?page=config" data-page="config" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-bar-chart w-5 text-center"></i>
<span class="ml-2">平台性能</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="main.html?page=logs" data-page="logs" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-file-text w-5 text-center"></i>
<span class="ml-2">查看日志</span>
</a>
</div>
</nav>
<!-- 底部信息区域 -->
<div class="p-4 border-t border-[#001529]/30 text-xs mt-auto">
<div class="mb-2 text-[#bfcbd9]/80">默认业务空间</div>
<div class="flex items-center justify-between">
<span class="text-[#bfcbd9]">版本 v1.0.0</span>
<i class="fa fa-question-circle-o text-[#bfcbd9]/70"></i>
</div>
</div>
</aside>
<!-- 主内容区 -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- 顶部导航 -->
<header class="bg-white border-b border-gray-200 shadow-sm">
<div class="flex items-center justify-between px-6 h-14">
<div class="flex items-center space-x-4">
<a href="#" onclick="goBack()" class="text-gray-500 hover:text-gray-700 flex items-center">
<i class="fa fa-arrow-left"></i>
<span class="ml-1">上一步</span>
</a>
</div>
<div class="flex items-center space-x-4">
<div class="relative group">
<img src="https://picsum.photos/id/1005/32/32" class="w-8 h-8 rounded-full cursor-pointer" alt="用户头像">
<div class="absolute right-0 top-full mt-2 bg-white rounded shadow-lg py-1 hidden group-hover:block border border-gray-100 min-w-[140px]">
<a href="login.html" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 whitespace-nowrap">
<i class="fa fa-sign-out mr-1"></i>退出登录
</a>
</div>
</div>
</div>
</div>
</header>
<!-- 内容区域 -->
<main class="flex-1 overflow-y-auto p-6 bg-gray-50">
<!-- 大标题 -->
<div class="bg-white rounded-lg shadow-sm p-4 border-b border-gray-100">
<div class="flex items-center text-sm">
<span id="breadcrumbParent" class="text-primary cursor-pointer hover:underline" onclick="goBack()">模型管理</span>
<span class="mx-2 text-gray-300">/</span>
<span id="pageTitle" class="text-gray-800 font-medium">添加模型</span>
</div>
</div>
<!-- 表单内容 -->
<div class="bg-white rounded-lg shadow-sm">
<div class="p-6">
<form id="modelForm">
<!-- 基本信息 -->
<div class="mb-6">
<h3 class="text-sm font-semibold text-gray-700 mb-4 pb-2 border-b border-gray-100">基本信息</h3>
<div class="max-w-xl">
<div>
<label class="form-label">
<span class="text-red-500 mr-1">*</span>模型名称
</label>
<input type="text" name="name" class="form-input" placeholder="请输入模型名称" maxlength="100">
<p class="text-xs text-gray-400 mt-1">支持中文、英文、数字、下划线最多100个字符</p>
</div>
<div class="mt-4">
<label class="form-label">
<span class="text-red-500 mr-1">*</span>模型类型
</label>
<select name="type" class="form-input">
<option value="LLM" selected>大语言模型 (LLM)</option>
<option value="CV">计算机视觉 (CV)</option>
<option value="NLP">自然语言处理 (NLP)</option>
<option value="Embedding">向量模型 (Embedding)</option>
<option value="Other">其他</option>
</select>
</div>
</div>
<!-- 模型用途 -->
<div class="mt-8 mb-6">
<label class="form-label flex items-center mb-3">
<span class="text-red-500 mr-1">*</span>模型用途
</label>
<div class="flex items-center space-x-6">
<label class="flex items-center cursor-pointer">
<input type="radio" name="purpose" value="training" class="mr-2">
<span class="text-sm">训练基座模型</span>
</label>
<label class="flex items-center cursor-pointer">
<input type="radio" name="purpose" value="inference" class="mr-2" checked>
<span class="text-sm">推理对比模型</span>
</label>
<label class="flex items-center cursor-pointer">
<input type="radio" name="purpose" value="evaluation" class="mr-2">
<span class="text-sm">评测模型</span>
</label>
</div>
</div>
</div>
<div class="mb-6">
<h3 class="text-sm font-semibold text-gray-700 mb-4 pb-2 border-b border-gray-100">模型来源</h3>
<!-- 模型来源选择 -->
<div class="mb-4">
<div class="flex items-center space-x-6">
<label class="flex items-center cursor-pointer">
<input type="radio" name="model_source" value="local" checked class="mr-2" onchange="toggleModelSource('local')">
<span class="text-sm">本地模型</span>
</label>
<label class="flex items-center cursor-pointer">
<input type="radio" name="model_source" value="api" class="mr-2" onchange="toggleModelSource('api')">
<span class="text-sm">在线模型</span>
</label>
</div>
</div>
<!-- 本地模型配置 -->
<div id="localModelPanel" class="max-w-xl">
<label class="form-label">
<span class="text-red-500 mr-1">*</span>模型加载路径
</label>
<select name="local_path" id="localModelSelect" class="form-select">
<option value="">-- 选择本地模型 --</option>
</select>
</div>
<!-- 在线模型配置 -->
<div id="onlineModelPanel" class="hidden max-w-xl">
<div class="mb-4">
<label class="form-label">
<span class="text-red-500 mr-1">*</span>API 地址
</label>
<input type="text" name="api_url" class="form-input" placeholder="如https://api.openai.com/v1">
</div>
<div class="mb-4">
<label class="form-label">
<span class="text-red-500 mr-1">*</span>API Key
</label>
<input type="password" name="api_key" class="form-input" placeholder="请输入API Key">
</div>
<div class="mb-4">
<label class="form-label">
<span class="text-red-500 mr-1">*</span>模型名称
</label>
<input type="text" name="online_model_name" class="form-input" placeholder="如gpt-4、qwen-turbo">
</div>
</div>
</div>
<!-- 描述信息 -->
<div class="mb-6">
<h3 class="text-sm font-semibold text-gray-700 mb-4 pb-2 border-b border-gray-100">描述信息</h3>
<div>
<label class="form-label">模型描述</label>
<textarea name="description" rows="4" class="form-input resize-none" placeholder="请输入模型描述,包含模型特点、适用场景等信息" maxlength="500"></textarea>
<p class="text-xs text-gray-400 mt-1"><span id="descCount">0</span> / 500</p>
</div>
</div>
<!-- 底部按钮 -->
<div class="flex items-center justify-between pt-6 border-t border-gray-100">
<div class="flex items-center space-x-3">
<button type="button" onclick="submitForm()" class="px-4 py-2 bg-primary text-white rounded-lg text-sm hover:bg-primary/90 transition-colors flex items-center">
<i class="fa fa-check mr-2"></i>保存
</button>
<a href="main.html?page=model-manage" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg text-sm hover:bg-gray-300 transition-colors flex items-center">
<i class="fa fa-times mr-2"></i>取消
</a>
</div>
</div>
</form>
</div>
</div>
</main>
</div>
<script>
// 使用 IIFE 避免全局变量污染
(function() {
// API 基础地址 - 优先使用 main.html 中定义的全局变量
const getApiBase = () => {
const protocol = window.location.protocol;
const hostname = window.location.hostname;
return `${protocol}//${hostname}:7861/api`;
};
const API_BASE = typeof window.API_BASE !== 'undefined' ? window.API_BASE : getApiBase();
// 返回页面
let backUrl = 'main.html?page=model-manage';
// 编辑模式
let isEditMode = false;
let editId = null;
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 根据URL参数设置返回页面
const urlParams = new URLSearchParams(window.location.search);
const from = urlParams.get('from');
const id = urlParams.get('id');
const breadcrumbParent = document.getElementById('breadcrumbParent');
if (from === 'fine-tune') {
backUrl = 'fine-tune-create.html';
if (breadcrumbParent) {
breadcrumbParent.textContent = '创建训练任务';
}
}
// 编辑模式
if (id) {
isEditMode = true;
editId = parseInt(id);
// 修改标题
if (breadcrumbParent) {
breadcrumbParent.textContent = '模型管理';
}
// 修改页面标题
const pageTitle = document.getElementById('pageTitle');
if (pageTitle) {
pageTitle.textContent = '编辑模型';
}
document.title = '编辑模型 / 远光软件微调平台';
// 修改按钮文字
const saveBtn = document.querySelector('button[onclick="submitForm()"]');
if (saveBtn) {
saveBtn.innerHTML = '<i class="fa fa-check mr-2"></i>保存修改';
}
// 加载模型数据
loadModelData(editId);
}
// 描述字数统计
const descInput = document.querySelector('textarea[name="description"]');
if (descInput) {
descInput.addEventListener('input', () => {
document.getElementById('descCount').textContent = descInput.value.length;
});
}
// 绑定导航点击事件
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', function(e) {
if (!this.href.includes('model-manage-create')) {
e.preventDefault();
window.location.href = this.href;
}
});
});
// 如果默认选中本地模型,立即加载模型列表
const localRadio = document.querySelector('input[name="model_source"][value="local"]');
if (localRadio && localRadio.checked) {
loadLocalModels();
}
// 设置侧边栏当前页高亮
const currentPage = 'model-manage';
document.querySelectorAll('.nav-link').forEach(link => {
if (link.dataset.page === currentPage) {
link.classList.add('bg-[#1890ff]/10', 'text-[#1890ff]');
link.classList.remove('hover:bg-[#001529]/20', 'transition-colors');
}
});
updateSidebarSlider();
});
// 更新侧边栏滑块位置
function updateSidebarSlider() {
const slider = document.getElementById('sidebar-slider');
if (!slider) return;
const activeLink = document.querySelector('.nav-link.bg-\\[\\#1890ff\\]\\/10');
if (activeLink) {
const wrapper = activeLink.closest('.nav-item-wrapper');
if (wrapper) {
slider.style.top = wrapper.offsetTop + 'px';
slider.style.height = wrapper.offsetHeight + 'px';
}
}
}
// 加载模型数据(编辑模式)
async function loadModelData(id) {
try {
const response = await fetch(`${API_BASE}/model-manage/${id}`);
const result = await response.json();
if (result.code === 0 && result.data) {
const model = result.data;
// 填充表单
document.querySelector('input[name="name"]').value = model.name || '';
document.querySelector('select[name="type"]').value = model.type || 'LLM';
// 设置模型来源(兼容旧的 'online' 值,转换为新的 'api' 值)
let modelSource = model.model_source || 'local';
if (modelSource === 'online') {
modelSource = 'api'; // 兼容旧数据
}
document.querySelectorAll('input[name="model_source"]').forEach(radio => {
radio.checked = radio.value === modelSource;
});
// 根据来源显示对应面板
toggleModelSource(modelSource);
// 填充来源相关字段
if (modelSource === 'local') {
const path = model.path || '';
// 尝试在选择器中找到匹配的模型
loadLocalModels().then(() => {
const select = document.getElementById('localModelSelect');
if (select && path) {
// 尝试精确匹配或部分匹配
for (let i = 0; i < select.options.length; i++) {
if (select.options[i].value === path ||
select.options[i].textContent === path ||
path.includes(select.options[i].textContent)) {
select.selectedIndex = i;
break;
}
}
}
});
} else {
document.querySelector('input[name="api_url"]').value = model.api_url || '';
document.querySelector('input[name="api_key"]').value = model.api_key || '';
document.querySelector('input[name="online_model_name"]').value = model.model_name || '';
}
// 填充描述
const descInput = document.querySelector('textarea[name="description"]');
descInput.value = model.description || '';
document.getElementById('descCount').textContent = descInput.value.length;
// 填充用途(兼容旧数据没有 purpose 字段的情况)
const purpose = model.purpose || 'inference';
document.querySelectorAll('input[name="purpose"]').forEach(radio => {
radio.checked = radio.value === purpose;
});
}
} catch (error) {
console.error('加载模型数据失败:', error);
}
}
// 切换模型来源
function toggleModelSource(source) {
const localPanel = document.getElementById('localModelPanel');
const onlinePanel = document.getElementById('onlineModelPanel');
if (source === 'local') {
localPanel.classList.remove('hidden');
onlinePanel.classList.add('hidden');
// 切换到本地模型时加载模型列表
loadLocalModels();
} else {
localPanel.classList.add('hidden');
onlinePanel.classList.remove('hidden');
}
}
// 为了兼容性,保留 online 作为别名
window.toggleModelSource = toggleModelSource;
// 加载本地模型列表
async function loadLocalModels() {
const select = document.getElementById('localModelSelect');
if (!select) return;
// 检查是否已经加载过
if (select.options.length > 1) return;
try {
const response = await fetch(`${API_BASE}/model-manage/local-models`);
const result = await response.json();
if (result.code === 0 && result.data && result.data.models) {
result.data.models.forEach(model => {
const option = document.createElement('option');
option.value = model.path;
option.textContent = model.name;
select.appendChild(option);
});
}
} catch (error) {
console.warn('加载本地模型列表失败:', error);
}
}
// 模型选择变化
function onModelSelectChange() {
// 保留空函数以兼容可能的其他调用
}
// 提交表单
async function submitForm() {
console.log('submitForm called');
const form = document.getElementById('modelForm');
const formData = new FormData(form);
const modelSource = formData.get('model_source');
const purpose = formData.get('purpose');
console.log('modelSource:', modelSource, 'purpose:', purpose);
const data = {
name: formData.get('name'),
type: formData.get('type'),
model_source: modelSource,
description: formData.get('description'),
purpose: purpose
};
// 根据模型来源设置不同的字段
if (modelSource === 'local') {
data.path = formData.get('local_path');
if (!data.path) {
showMessage('提示', '请输入模型加载路径', 'warning');
return;
}
} else {
data.api_url = formData.get('api_url');
data.api_key = formData.get('api_key');
data.model_name = formData.get('online_model_name');
if (!data.api_url) {
showMessage('提示', '请输入API地址', 'warning');
return;
}
if (!data.api_key) {
showMessage('提示', '请输入API Key', 'warning');
return;
}
if (!data.model_name) {
showMessage('提示', '请输入模型名称', 'warning');
return;
}
}
// 验证
if (!data.name) {
showMessage('提示', '请输入模型名称', 'warning');
return;
}
if (!data.type) {
showMessage('提示', '请选择模型类型', 'warning');
return;
}
try {
let url = `${API_BASE}/model-manage`;
let method = 'POST';
let successMsg = '模型添加成功!';
if (isEditMode) {
url = `${API_BASE}/model-manage/${editId}`;
method = 'PUT';
successMsg = '模型修改成功!';
}
console.log('Sending request to:', url);
console.log('Request data:', data);
const response = await fetch(url, {
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
console.log('Response status:', response.status);
const result = await response.json();
console.log('Response data:', result);
if (result.code === 0) {
showMessage('成功', successMsg, 'success', () => {
window.location.href = 'main.html?page=model-manage';
});
} else {
showMessage('错误', result.message || '操作失败', 'error');
}
} catch (error) {
showMessage('错误', '操作失败: ' + error.message, 'error');
}
}
// ============ 自定义消息弹窗 ============
function showMessage(title, message, type = 'info', onConfirm) {
const modal = document.getElementById('customModal');
const modalTitle = document.getElementById('modalTitle');
const modalMessage = document.getElementById('modalMessage');
const modalIcon = document.getElementById('modalIcon');
const modalConfirmBtn = document.getElementById('modalConfirmBtn2');
const modalSingleBtnGroup = document.getElementById('modalSingleBtnGroup');
modalTitle.textContent = title;
modalMessage.innerHTML = message;
if (type === 'success') {
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-green-100 flex items-center justify-center"><i class="fa fa-check text-xl text-green-600"></i></div>';
} else if (type === 'error') {
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-red-100 flex items-center justify-center"><i class="fa fa-times text-xl text-red-600"></i></div>';
} else if (type === 'warning') {
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-yellow-100 flex items-center justify-center"><i class="fa fa-exclamation text-xl text-yellow-600"></i></div>';
} else {
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-blue-100 flex items-center justify-center"><i class="fa fa-info text-xl text-blue-600"></i></div>';
}
modalSingleBtnGroup.classList.remove('hidden');
modalConfirmBtn.className = type === 'error' ? 'px-6 py-2 bg-danger text-white rounded-lg hover:bg-danger/90 transition-colors' : 'px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors';
modal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
modalConfirmBtn.onclick = () => {
modal.classList.add('hidden');
document.body.style.overflow = '';
if (onConfirm) onConfirm();
};
}
// 返回上一页
function goBack() {
window.location.href = backUrl;
}
window.goBack = goBack;
})();
</script>
<!-- 自定义消息弹窗 -->
<div id="customModal" class="hidden fixed inset-0 bg-black/50 z-50 flex items-center justify-center">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full mx-4 overflow-hidden">
<div class="p-6 text-center">
<div id="modalIcon"></div>
<h3 id="modalTitle" class="text-lg font-medium text-gray-800 mb-2"></h3>
<p id="modalMessage" class="text-gray-600 text-sm"></p>
</div>
<div id="modalSingleBtnGroup" class="px-6 pb-6 flex justify-center">
<button id="modalConfirmBtn2" class="px-6 py-2 text-white rounded-lg transition-colors">确定</button>
</div>
</div>
</div>
</body>
</html>