This commit is contained in:
parent
f3d8e73ab4
commit
bd203141d8
@ -1,6 +1,6 @@
|
|||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
host: "localhost"
|
host: "0.0.0.0"
|
||||||
env: "development"
|
env: "development"
|
||||||
|
|
||||||
# 开发环境使用SQLite
|
# 开发环境使用SQLite
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@ -119,6 +120,13 @@ func ShowRobot(c *fiber.Ctx) error {
|
|||||||
func DeleteRobot(c *fiber.Ctx) error {
|
func DeleteRobot(c *fiber.Ctx) error {
|
||||||
id, err := strconv.Atoi(c.Params("id"))
|
id, err := strconv.Atoi(c.Params("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 针对API请求返回JSON
|
||||||
|
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"success": false,
|
||||||
|
"message": "无效的ID",
|
||||||
|
})
|
||||||
|
}
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "无效的ID")
|
return fiber.NewError(fiber.StatusBadRequest, "无效的ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +134,13 @@ func DeleteRobot(c *fiber.Ctx) error {
|
|||||||
db := model.GetDB()
|
db := model.GetDB()
|
||||||
|
|
||||||
if err := db.First(&robot, id).Error; err != nil {
|
if err := db.First(&robot, id).Error; err != nil {
|
||||||
|
// 针对API请求返回JSON
|
||||||
|
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||||
|
"success": false,
|
||||||
|
"message": "机器人不存在",
|
||||||
|
})
|
||||||
|
}
|
||||||
if err == gorm.ErrRecordNotFound {
|
if err == gorm.ErrRecordNotFound {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
||||||
}
|
}
|
||||||
@ -155,9 +170,25 @@ func DeleteRobot(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// 删除数据库记录
|
// 删除数据库记录
|
||||||
if err := db.Delete(&robot).Error; err != nil {
|
if err := db.Delete(&robot).Error; err != nil {
|
||||||
|
// 针对API请求返回JSON
|
||||||
|
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"success": false,
|
||||||
|
"message": "删除机器人失败: " + err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "删除机器人失败: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "删除机器人失败: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 针对API请求返回JSON
|
||||||
|
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"success": true,
|
||||||
|
"message": "机器人已成功删除",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通请求重定向
|
||||||
return c.Redirect("/admin/robots")
|
return c.Redirect("/admin/robots")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +122,10 @@
|
|||||||
{{embed}}
|
{{embed}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!-- Alpine.js -->
|
<!-- 先加载组件库 -->
|
||||||
|
<script src="/public/js/components.js"></script>
|
||||||
|
|
||||||
|
<!-- 然后加载Alpine.js -->
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||||
|
|
||||||
{{if .CustomJS}}
|
{{if .CustomJS}}
|
||||||
|
@ -200,6 +200,77 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 删除机器人确认
|
||||||
|
document.querySelectorAll('.delete-robot').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const robotId = this.dataset.id;
|
||||||
|
const robotName = this.dataset.robotName || '此机器人';
|
||||||
|
|
||||||
|
// 使用美观的确认对话框代替原生的window.confirm
|
||||||
|
if (typeof confirmDialog === 'function') {
|
||||||
|
// 使用confirmDialog函数
|
||||||
|
const confirmed = await confirmDialog(
|
||||||
|
`确定要删除 ${robotName} 吗?此操作不可恢复!`,
|
||||||
|
{ type: 'danger', title: '确认删除' }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
// 显示加载状态
|
||||||
|
this.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
this.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用fetch API发送DELETE请求
|
||||||
|
const response = await fetch(`/admin/robots/${robotId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
// 删除成功,刷新页面
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
// 读取错误信息
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(errorData.message || '删除失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('删除失败: ' + error.message);
|
||||||
|
|
||||||
|
// 恢复按钮状态
|
||||||
|
this.innerHTML = '<i class="fas fa-trash"></i>';
|
||||||
|
this.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 降级方案:使用原生confirm
|
||||||
|
if (window.confirm(`确定要删除 ${robotName} 吗?此操作不可恢复!`)) {
|
||||||
|
// 使用fetch API发送DELETE请求
|
||||||
|
fetch(`/admin/robots/${robotId}`, { method: 'DELETE' })
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
throw new Error('删除失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('删除失败: ' + error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.robot-card {
|
.robot-card {
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.08); /* 保留基础阴影 */
|
box-shadow: 0 2px 10px rgba(0,0,0,0.08); /* 保留基础阴影 */
|
||||||
|
@ -87,33 +87,41 @@ function confirmDialog(message, options = {}) {
|
|||||||
|
|
||||||
// 创建遮罩
|
// 创建遮罩
|
||||||
const overlay = document.createElement('div');
|
const overlay = document.createElement('div');
|
||||||
overlay.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
|
overlay.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 transition-opacity duration-300 opacity-0';
|
||||||
|
overlay.style.backdropFilter = 'blur(2px)';
|
||||||
|
|
||||||
|
// 选择颜色和图标
|
||||||
|
let iconClass = 'text-indigo-500';
|
||||||
|
let buttonClass = 'bg-indigo-600 hover:bg-indigo-700';
|
||||||
|
let icon = '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path></svg>';
|
||||||
|
|
||||||
// 选择颜色
|
|
||||||
let colorClass = 'text-blue-600';
|
|
||||||
let buttonClass = 'bg-blue-600 hover:bg-blue-700';
|
|
||||||
if (settings.type === 'warning') {
|
if (settings.type === 'warning') {
|
||||||
colorClass = 'text-yellow-600';
|
iconClass = 'text-amber-500';
|
||||||
buttonClass = 'bg-yellow-600 hover:bg-yellow-700';
|
buttonClass = 'bg-amber-600 hover:bg-amber-700';
|
||||||
|
icon = '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v4a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>';
|
||||||
} else if (settings.type === 'danger') {
|
} else if (settings.type === 'danger') {
|
||||||
colorClass = 'text-red-600';
|
iconClass = 'text-red-500';
|
||||||
buttonClass = 'bg-red-600 hover:bg-red-700';
|
buttonClass = 'bg-red-600 hover:bg-red-700';
|
||||||
|
icon = '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建对话框
|
// 创建对话框
|
||||||
overlay.innerHTML = `
|
overlay.innerHTML = `
|
||||||
<div class="bg-white rounded-lg shadow-xl overflow-hidden max-w-md w-full mx-4 transform transition-all scale-95 opacity-0">
|
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl overflow-hidden max-w-md w-full mx-4 transform transition-all duration-300 scale-95 opacity-0">
|
||||||
<div class="px-6 py-4 border-b border-gray-200">
|
<div class="p-5 flex items-start space-x-4">
|
||||||
<h3 class="text-lg font-medium ${colorClass}">${settings.title}</h3>
|
<div class="flex-shrink-0 ${iconClass} p-1">
|
||||||
|
${icon}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow">
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-1">${settings.title}</h3>
|
||||||
|
<p class="text-gray-500 dark:text-gray-300">${message}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-6">
|
<div class="bg-gray-50 dark:bg-gray-700/50 px-5 py-4 flex justify-end space-x-3">
|
||||||
<p class="text-gray-700">${message}</p>
|
<button id="cancel-btn" class="px-4 py-2 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200 rounded-lg border border-gray-300 dark:border-gray-500 text-sm font-medium shadow-sm transition-all">
|
||||||
</div>
|
|
||||||
<div class="px-6 py-3 bg-gray-50 flex justify-end space-x-2">
|
|
||||||
<button id="cancel-btn" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-800 rounded-md">
|
|
||||||
${settings.cancelText}
|
${settings.cancelText}
|
||||||
</button>
|
</button>
|
||||||
<button id="confirm-btn" class="px-4 py-2 ${buttonClass} text-white rounded-md">
|
<button id="confirm-btn" class="${buttonClass} text-white px-4 py-2 rounded-lg shadow-sm text-sm font-medium transition-all">
|
||||||
${settings.confirmText}
|
${settings.confirmText}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -124,6 +132,8 @@ function confirmDialog(message, options = {}) {
|
|||||||
|
|
||||||
// 显示动画
|
// 显示动画
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
overlay.classList.remove('opacity-0');
|
||||||
|
overlay.classList.add('opacity-100');
|
||||||
const dialog = overlay.querySelector('div.bg-white');
|
const dialog = overlay.querySelector('div.bg-white');
|
||||||
dialog.classList.remove('scale-95', 'opacity-0');
|
dialog.classList.remove('scale-95', 'opacity-0');
|
||||||
dialog.classList.add('scale-100', 'opacity-100');
|
dialog.classList.add('scale-100', 'opacity-100');
|
||||||
@ -131,18 +141,51 @@ function confirmDialog(message, options = {}) {
|
|||||||
|
|
||||||
// 事件处理
|
// 事件处理
|
||||||
const closeDialog = (result) => {
|
const closeDialog = (result) => {
|
||||||
|
// 关闭动画
|
||||||
|
overlay.classList.remove('opacity-100');
|
||||||
|
overlay.classList.add('opacity-0');
|
||||||
const dialog = overlay.querySelector('div.bg-white');
|
const dialog = overlay.querySelector('div.bg-white');
|
||||||
dialog.classList.remove('scale-100', 'opacity-100');
|
dialog.classList.remove('scale-100', 'opacity-100');
|
||||||
dialog.classList.add('scale-95', 'opacity-0');
|
dialog.classList.add('scale-95', 'opacity-0');
|
||||||
|
|
||||||
|
// 等待动画完成后移除
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
overlay.remove();
|
overlay.remove();
|
||||||
resolve(result);
|
resolve(result);
|
||||||
}, 200);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
overlay.querySelector('#confirm-btn').addEventListener('click', () => closeDialog(true));
|
// 处理键盘事件
|
||||||
overlay.querySelector('#cancel-btn').addEventListener('click', () => closeDialog(false));
|
const handleKeyDown = (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closeDialog(false);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
} else if (e.key === 'Enter') {
|
||||||
|
closeDialog(true);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
|
// 绑定按钮事件
|
||||||
|
overlay.querySelector('#confirm-btn').addEventListener('click', () => {
|
||||||
|
closeDialog(true);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
overlay.querySelector('#cancel-btn').addEventListener('click', () => {
|
||||||
|
closeDialog(false);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击外部区域关闭
|
||||||
|
overlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === overlay) {
|
||||||
|
closeDialog(false);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user