Spaces:
Runtime error
Runtime error
fdwei4 commited on
Commit ·
8dc6765
1
Parent(s): b9a530a
feat: 首页改为可交互的 Demo CRUD 表格
Browse files
src/main/java/com/example/hfspace/controller/HelloController.java
CHANGED
|
@@ -7,67 +7,148 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|
| 7 |
import org.springframework.web.bind.annotation.RestController;
|
| 8 |
|
| 9 |
/**
|
| 10 |
-
* 演示接口:一个首页 HTML 和一个 JSON 接口
|
| 11 |
*/
|
| 12 |
@RestController
|
| 13 |
public class HelloController {
|
| 14 |
|
| 15 |
-
/** 首页:返回一段 HTML。 */
|
| 16 |
@GetMapping(value = "/", produces = "text/html;charset=UTF-8")
|
| 17 |
public String home() {
|
| 18 |
return """
|
| 19 |
-
<html>
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
-
|
| 26 |
-
<
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
</
|
| 32 |
-
<
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
</
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
<li><a href="/api/cache/health">/api/cache/health</a> - Redis 健康检查</li>
|
| 44 |
-
<li><code>POST /api/cache</code> - 存缓存(Body: <code>{"key":"foo","value":"bar","ttl":60}</code>)</li>
|
| 45 |
-
<li><code>GET /api/cache/{key}</code> - 读缓存</li>
|
| 46 |
-
<li><code>DELETE /api/cache/{key}</code> - 删缓存</li>
|
| 47 |
-
</ul>
|
| 48 |
-
</li>
|
| 49 |
-
<li><strong>Druid 监控</strong>
|
| 50 |
-
<ul>
|
| 51 |
-
<li><a href="/druid/">/druid/</a> - 数据库连接池监控(用户名: admin / 密码: admin123)</li>
|
| 52 |
-
</ul>
|
| 53 |
-
</li>
|
| 54 |
-
</ul>
|
| 55 |
|
| 56 |
-
|
|
|
|
| 57 |
<ul>
|
| 58 |
-
<li><a href="
|
| 59 |
-
<li><a href="https://github.com/yourusername/yourrepo/blob/main/ARCHITECTURE.md">ARCHITECTURE.md</a> - 架构详解</li>
|
| 60 |
-
<li><a href="https://github.com/yourusername/yourrepo/blob/main/DEPLOY.md">DEPLOY.md</a> - 部署清单</li>
|
| 61 |
</ul>
|
|
|
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
</html>
|
| 72 |
""";
|
| 73 |
}
|
|
|
|
| 7 |
import org.springframework.web.bind.annotation.RestController;
|
| 8 |
|
| 9 |
/**
|
| 10 |
+
* 演示接口:一个首页 HTML(含 Demo 数据 CRUD 表格)和一个 JSON 接口。
|
| 11 |
*/
|
| 12 |
@RestController
|
| 13 |
public class HelloController {
|
| 14 |
|
| 15 |
+
/** 首页:返回一段带 CRUD 表格的 HTML,直接对接 /api/demo 接口。 */
|
| 16 |
@GetMapping(value = "/", produces = "text/html;charset=UTF-8")
|
| 17 |
public String home() {
|
| 18 |
return """
|
| 19 |
+
<!DOCTYPE html>
|
| 20 |
+
<html lang="zh">
|
| 21 |
+
<head>
|
| 22 |
+
<meta charset="utf-8">
|
| 23 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 24 |
+
<title>HF Space Demo</title>
|
| 25 |
+
<style>
|
| 26 |
+
body { font-family: -apple-system, sans-serif; padding: 24px; max-width: 960px; margin: 0 auto; color: #1f2329; }
|
| 27 |
+
h1 { margin-bottom: 4px; }
|
| 28 |
+
.sub { color: #646a73; margin-top: 0; }
|
| 29 |
+
.card { border: 1px solid #e5e6eb; border-radius: 8px; padding: 16px; margin: 16px 0; }
|
| 30 |
+
.form-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
| 31 |
+
input { padding: 8px 10px; border: 1px solid #c9cdd4; border-radius: 6px; font-size: 14px; }
|
| 32 |
+
input#title { width: 180px; }
|
| 33 |
+
input#content { flex: 1; min-width: 200px; }
|
| 34 |
+
button { padding: 8px 14px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; }
|
| 35 |
+
.btn-primary { background: #3370ff; color: #fff; }
|
| 36 |
+
.btn-ghost { background: #f2f3f5; color: #1f2329; }
|
| 37 |
+
.btn-danger { background: #f53f3f; color: #fff; }
|
| 38 |
+
.btn-sm { padding: 4px 10px; font-size: 13px; }
|
| 39 |
+
table { width: 100%; border-collapse: collapse; margin-top: 8px; }
|
| 40 |
+
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #f0f0f0; font-size: 14px; vertical-align: top; }
|
| 41 |
+
th { color: #646a73; font-weight: 500; background: #fafafa; }
|
| 42 |
+
td.actions { white-space: nowrap; }
|
| 43 |
+
.muted { color: #8a8f99; font-size: 12px; }
|
| 44 |
+
#msg { margin-left: 8px; font-size: 13px; }
|
| 45 |
+
.ok { color: #00b42a; }
|
| 46 |
+
.err { color: #f53f3f; }
|
| 47 |
+
a { color: #3370ff; }
|
| 48 |
+
</style>
|
| 49 |
+
</head>
|
| 50 |
+
<body>
|
| 51 |
+
<h1>🚀 HF Space Spring Boot Demo</h1>
|
| 52 |
+
<p class="sub">基于 wfly-spring 配置(SQLPub MySQL + Upstash Redis)。下表直接读写真实数据库 <code>demo_items</code> 表。</p>
|
| 53 |
|
| 54 |
+
<div class="card">
|
| 55 |
+
<h3 style="margin-top:0;">Demo 数据管理</h3>
|
| 56 |
+
<div class="form-row">
|
| 57 |
+
<input type="hidden" id="editId">
|
| 58 |
+
<input id="title" placeholder="标题 title">
|
| 59 |
+
<input id="content" placeholder="内容 content">
|
| 60 |
+
<button class="btn-primary" onclick="save()">保存</button>
|
| 61 |
+
<button class="btn-ghost" onclick="resetForm()">清空</button>
|
| 62 |
+
<button class="btn-ghost" onclick="load()">刷新</button>
|
| 63 |
+
<span id="msg"></span>
|
| 64 |
+
</div>
|
| 65 |
+
<table>
|
| 66 |
+
<thead>
|
| 67 |
+
<tr><th>ID</th><th>标题</th><th>内容</th><th>更新时间</th><th>操作</th></tr>
|
| 68 |
+
</thead>
|
| 69 |
+
<tbody id="rows"><tr><td colspan="5" class="muted">加载中…</td></tr></tbody>
|
| 70 |
+
</table>
|
| 71 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
+
<div class="card">
|
| 74 |
+
<h3 style="margin-top:0;">📡 其他接口</h3>
|
| 75 |
<ul>
|
| 76 |
+
<li><a href="/api/hello">/api/hello</a> · <a href="/api/cache/health">/api/cache/health</a>(Redis) · <a href="/druid/">/druid/</a>(监控 admin/admin123)</li>
|
|
|
|
|
|
|
| 77 |
</ul>
|
| 78 |
+
</div>
|
| 79 |
|
| 80 |
+
<script>
|
| 81 |
+
const API = '/api/demo';
|
| 82 |
+
const $ = id => document.getElementById(id);
|
| 83 |
+
function msg(text, ok) {
|
| 84 |
+
const m = $('msg');
|
| 85 |
+
m.textContent = text;
|
| 86 |
+
m.className = ok ? 'ok' : 'err';
|
| 87 |
+
setTimeout(() => { m.textContent = ''; }, 2500);
|
| 88 |
+
}
|
| 89 |
+
function esc(s) {
|
| 90 |
+
return String(s == null ? '' : s).replace(/[&<>"]/g, c => ({'&':'&','<':'<','>':'>','"':'"'}[c]));
|
| 91 |
+
}
|
| 92 |
+
async function load() {
|
| 93 |
+
const tbody = $('rows');
|
| 94 |
+
try {
|
| 95 |
+
const res = await fetch(API);
|
| 96 |
+
const json = await res.json();
|
| 97 |
+
const list = json.data || [];
|
| 98 |
+
if (!list.length) { tbody.innerHTML = '<tr><td colspan="5" class="muted">暂无数据,添加一条试试</td></tr>'; return; }
|
| 99 |
+
tbody.innerHTML = list.map(it => `
|
| 100 |
+
<tr>
|
| 101 |
+
<td>${it.id}</td>
|
| 102 |
+
<td>${esc(it.title)}</td>
|
| 103 |
+
<td>${esc(it.content)}</td>
|
| 104 |
+
<td class="muted">${esc(it.updateTime || it.createTime || '')}</td>
|
| 105 |
+
<td class="actions">
|
| 106 |
+
<button class="btn-ghost btn-sm" onclick="edit(${it.id}, '${esc(it.title).replace(/'/g, "'")}', '${esc(it.content).replace(/'/g, "'")}')">编辑</button>
|
| 107 |
+
<button class="btn-danger btn-sm" onclick="del(${it.id})">删除</button>
|
| 108 |
+
</td>
|
| 109 |
+
</tr>`).join('');
|
| 110 |
+
} catch (e) {
|
| 111 |
+
tbody.innerHTML = '<tr><td colspan="5" class="err">加载失败:' + esc(e.message) + '</td></tr>';
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
async function save() {
|
| 115 |
+
const id = $('editId').value;
|
| 116 |
+
const title = $('title').value.trim();
|
| 117 |
+
const content = $('content').value.trim();
|
| 118 |
+
if (!title) { msg('标题不能为空', false); return; }
|
| 119 |
+
const body = JSON.stringify({ title, content });
|
| 120 |
+
const opts = { method: id ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body };
|
| 121 |
+
const url = id ? (API + '/' + id) : API;
|
| 122 |
+
try {
|
| 123 |
+
const res = await fetch(url, opts);
|
| 124 |
+
const json = await res.json();
|
| 125 |
+
if (json.code === 0) { msg(id ? '已更新' : '已创建', true); resetForm(); load(); }
|
| 126 |
+
else { msg(json.message || '操作失败', false); }
|
| 127 |
+
} catch (e) { msg('请求失败:' + e.message, false); }
|
| 128 |
+
}
|
| 129 |
+
function edit(id, title, content) {
|
| 130 |
+
$('editId').value = id;
|
| 131 |
+
$('title').value = title;
|
| 132 |
+
$('content').value = content;
|
| 133 |
+
$('title').focus();
|
| 134 |
+
}
|
| 135 |
+
function resetForm() {
|
| 136 |
+
$('editId').value = '';
|
| 137 |
+
$('title').value = '';
|
| 138 |
+
$('content').value = '';
|
| 139 |
+
}
|
| 140 |
+
async function del(id) {
|
| 141 |
+
if (!confirm('确定删除 #' + id + ' ?')) return;
|
| 142 |
+
try {
|
| 143 |
+
const res = await fetch(API + '/' + id, { method: 'DELETE' });
|
| 144 |
+
const json = await res.json();
|
| 145 |
+
if (json.code === 0) { msg('已删除', true); load(); }
|
| 146 |
+
else { msg(json.message || '删除失败', false); }
|
| 147 |
+
} catch (e) { msg('请求失败:' + e.message, false); }
|
| 148 |
+
}
|
| 149 |
+
load();
|
| 150 |
+
</script>
|
| 151 |
+
</body>
|
| 152 |
</html>
|
| 153 |
""";
|
| 154 |
}
|