feat: update auto render

This commit is contained in:
2025-08-07 20:03:53 +08:00
parent fd67864247
commit d7e8ca38f8
39 changed files with 5684 additions and 130 deletions

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.TableAlias}} - 数据管理</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-50">
<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-6">{{.TableAlias}}</h1>
{{if gt (len .Data) 0}}
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
{{range .Columns}}
{{if .ShowInList}}
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
{{.Alias}}
</th>
{{end}}
{{end}}
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{{range $row := .Data}}
<tr class="hover:bg-gray-50">
{{range $col := $.Columns}}
{{if $col.ShowInList}}
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{index $row $col.Name}}
</td>
{{end}}
{{end}}
</tr>
{{end}}
</tbody>
</table>
</div>
{{else}}
<div class="text-center py-8">
<p class="text-gray-500">暂无数据</p>
</div>
{{end}}
<div class="mt-4 text-sm text-gray-600">
总计: {{.Total}} 条记录
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,27 @@
{{define "content"}}
<h1>Debug Information</h1>
<pre>Table: {{.Table}}
TableAlias: {{.TableAlias}}
Total: {{.Total}}
Page: {{.Page}}
Pages: {{.Pages}}
Columns: {{json .Columns}}
Data Type: {{printf "%T" .Data}}
Data Length: {{len .Data}}
First Row: {{json (index .Data 0)}}
</pre>
<div>
<h2>Columns</h2>
<ul>
{{range .Columns}}
<li>{{.Name}} ({{.Alias}}) - Show: {{.ShowInList}}</li>
{{end}}
</ul>
</div>
<div>
<h2>Data</h2>
{{range $i, $row := .Data}}
<div>Row {{$i}}: {{json $row}}</div>
{{end}}
</div>
{{end}}

View File

@@ -0,0 +1,3 @@
{{define "field-category"}}
<span class="text-gray-900">{{.Value}}</span>
{{end}}

View File

@@ -0,0 +1,9 @@
{{define "field-markdown"}}
{{if .Value}}
<div class="prose prose-sm max-w-none">
{{.Value}}
</div>
{{else}}
<span class="text-gray-400">-</span>
{{end}}
{{end}}

View File

@@ -0,0 +1,3 @@
{{define "field-raw"}}
<span class="text-gray-900">{{.Value}}</span>
{{end}}

View File

@@ -0,0 +1,14 @@
{{define "field-tag"}}
{{if .Value}}
{{$tag := index .Column.values .Value}}
{{if $tag}}
<span class="px-2 py-1 text-xs font-medium rounded-full" style="background-color: {{$tag.color}}; color: white;">
{{$tag.label}}
</span>
{{else}}
<span class="text-gray-900">{{.Value}}</span>
{{end}}
{{else}}
<span class="text-gray-400">-</span>
{{end}}
{{end}}

View File

@@ -0,0 +1,17 @@
{{define "field-text"}}
<span class="text-gray-900">{{.Value}}</span>
{{end}}
{{define "field"}}
{{if eq .Type "time"}}
{{template "field-time" .}}
{{else if eq .Type "tag"}}
{{template "field-tag" .}}
{{else if eq .Type "markdown"}}
{{template "field-markdown" .}}
{{else if eq .Type "raw"}}
{{template "field-raw" .}}
{{else}}
{{template "field-text" .}}
{{end}}
{{end}}

View File

@@ -0,0 +1,9 @@
{{define "field-time"}}
<span class="text-gray-900">
{{if .Value}}
{{.Value}}
{{else}}
-
{{end}}
</span>
{{end}}

View File

@@ -0,0 +1,162 @@
{{define "layout"}}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}} - 数据管理</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
.tag {
@apply inline-block px-2 py-1 text-xs font-medium rounded-full;
}
.modal {
@apply fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden;
}
.modal-content {
@apply bg-white rounded-lg max-w-4xl w-full mx-4 max-h-[80vh] overflow-y-auto;
}
.loading {
@apply animate-pulse bg-gray-200 rounded;
}
</style>
</head>
<body class="bg-gray-50">
<div class="container mx-auto px-4 py-8">
<!-- 表选择器 -->
<div class="mb-6">
<select id="tableSelector" class="border rounded px-3 py-2" onchange="changeTable(this.value)">
<option value="">选择数据表...</option>
{{range .Tables}}
<option value="{{.Alias}}" {{if eq .Alias $.CurrentTable}}selected{{end}}>
{{.Alias}}
</option>
{{end}}
</select>
</div>
<!-- 主要内容区域 -->
<div id="mainContent">
{{template "content" .}}
</div>
</div>
<!-- 详情弹窗 -->
<div id="detailModal" class="modal">
<div class="modal-content">
<div class="p-6">
<h3 class="text-lg font-medium mb-4">详情信息</h3>
<div id="detailContent"></div>
<div class="mt-4 flex justify-end">
<button onclick="closeModal()" class="px-4 py-2 bg-gray-300 rounded">
关闭
</button>
</div>
</div>
</div>
</div>
<!-- 通用JavaScript -->
<script>
// 全局配置
const CONFIG = {
apiBase: '/api',
templates: {
builtin: '/templates/builtin',
custom: '/templates/custom'
}
};
// 模板系统核心
class TemplateSystem {
static async loadTemplate(path) {
const response = await fetch(path);
return response.text();
}
static async render(data, templateName, tableName) {
// 优先使用自定义模板,回退到默认模板
const customPath = `${CONFIG.templates.custom}/${tableName}/${templateName}.html`;
const defaultPath = `${CONFIG.templates.builtin}/${templateName}.html`;
try {
return await this.loadTemplate(customPath);
} catch {
return await this.loadTemplate(defaultPath);
}
}
static async renderField(value, type, column) {
const fieldMap = {
'raw': v => v,
'time': v => new Date(v).toLocaleString('zh-CN'),
'tag': (v, col) => {
const tag = col.values[v];
return tag ? `<span class="tag" style="background-color: ${tag.color}; color: white;">${tag.label}</span>` : v;
},
'markdown': v => marked.parse(v || ''),
'category': v => v
};
return fieldMap[type]?.(value, column) || value;
}
}
// 数据加载器
class DataLoader {
static async loadTableData(tableAlias, page = 1, perPage = 20, search = '', sort = '', order = 'desc') {
const params = new URLSearchParams({
page,
per_page: perPage,
search,
sort,
order
});
const response = await fetch(`${CONFIG.apiBase}/data/${tableAlias}?${params}`);
return response.json();
}
static async loadTableDetail(tableAlias, id) {
const response = await fetch(`${CONFIG.apiBase}/data/${tableAlias}/detail/${id}`);
return response.json();
}
}
// 通用函数
function changeTable(tableAlias) {
if (!tableAlias) return;
window.location.href = `/?table=${tableAlias}&page=1`;
}
function closeModal() {
document.getElementById('detailModal').classList.add('hidden');
}
function showLoading() {
document.getElementById('mainContent').innerHTML =
'<div class="text-center py-8"><div class="loading h-4 w-32 mx-auto"></div></div>';
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
// 绑定全局事件
window.closeModal = closeModal;
window.changeTable = changeTable;
window.TemplateSystem = TemplateSystem;
window.DataLoader = DataLoader;
});
</script>
<!-- 页面特定内容占位符 -->
{{template "scripts" .}}
<!-- 性能监控JS -->
<script src="/static/js/performance-monitor.js"></script>
<script src="/static/js/api-client.js"></script>
<script src="/static/js/template-engine.js"></script>
<script src="/static/js/ui-controller.js"></script>
</body>
</html>
{{end}}

View File

@@ -0,0 +1,128 @@
{{define "content"}}
<div class="bg-white rounded-lg shadow overflow-hidden">
{{if .Data}}
{{if eq .TemplateType "table"}}
{{template "table-view" .}}
{{else if eq .TemplateType "card"}}
{{template "card-view" .}}
{{else}}
{{template "table-view" .}}
{{end}}
{{else}}
<div class="text-center py-8 text-gray-500">
暂无数据
</div>
{{end}}
</div>
<!-- 分页 -->
<div class="mt-4 flex justify-between items-center">
<div class="text-sm text-gray-700">
共 {{.Total}} 条记录,第 {{.Page}} / {{.Pages}} 页
</div>
<div class="flex space-x-2">
{{if gt .Page 1}}
<a href="?table={{.Table}}&page={{sub .Page 1}}&per_page={{.PerPage}}"
class="px-3 py-1 border rounded text-sm hover:bg-gray-50">
上一页
</a>
{{end}}
{{range until .Pages}}
{{$page := add . 1}}
{{if eq $page $.Page}}
<span class="px-3 py-1 bg-blue-500 text-white rounded text-sm">{{$page}}</span>
{{else}}
<a href="?table={{$.Table}}&page={{$page}}&per_page={{$.PerPage}}"
class="px-3 py-1 border rounded text-sm hover:bg-gray-50">
{{$page}}
</a>
{{end}}
{{end}}
{{if lt .Page .Pages}}
<a href="?table={{$.Table}}&page={{add $.Page 1}}&per_page={{$.PerPage}}"
class="px-3 py-1 border rounded text-sm hover:bg-gray-50">
下一页
</a>
{{end}}
</div>
</div>
{{end}}
{{define "table-view"}}
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
{{range .Columns}}
{{if .ShowInList}}
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{{.Alias}}
{{if .Sortable}}
<button onclick="sortBy('{{.Name}}')" class="ml-1 text-gray-400 hover:text-gray-600">
</button>
{{end}}
</th>
{{end}}
{{end}}
<th class="px-6 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">
{{range $row := .Data}}
<tr class="hover:bg-gray-50">
{{range $col := $.Columns}}
{{if $col.ShowInList}}
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{template "field" dict "Value" (index $row $col.Name) "Type" $col.RenderType "Column" $col}}
</td>
{{end}}
{{end}}
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<button onclick="showDetail('{{index $row "id"}}')"
class="text-blue-600 hover:text-blue-900">
查看详情
</button>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{define "card-view"}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
{{range $row := .Data}}
<div class="bg-white border rounded-lg p-4 hover:shadow-md transition-shadow">
{{range $col := $.Columns}}
{{if $col.ShowInList}}
<div class="mb-2">
<label class="block text-sm font-medium text-gray-700">{{$col.Alias}}</label>
<div class="mt-1 text-sm text-gray-900">
{{template "field" dict "Value" (index $row $col.Name) "Type" $col.RenderType "Column" $col}}
</div>
</div>
{{end}}
{{end}}
<div class="mt-4">
<button onclick="showDetail('{{index $row "id"}}')"
class="text-blue-600 hover:text-blue-900 text-sm">
查看详情
</button>
</div>
</div>
{{end}}
</div>
{{end}}
{{define "scripts"}}
<!-- 列表页面特定脚本 -->
<script>
// 列表页面特定功能可以在这里添加
console.log('List template loaded for:', '{{.Table}}');
</script>
{{end}}

View File

@@ -0,0 +1,33 @@
{{define "content"}}
<div class="p-4">
<h1 class="text-2xl font-bold mb-4">{{.TableAlias}} - 简易视图</h1>
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
{{range .Columns}}
{{if .ShowInList}}
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{{.Alias}}
</th>
{{end}}
{{end}}
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{{range $row := .Data}}
<tr class="hover:bg-gray-50">
{{range $col := $.Columns}}
{{if $col.ShowInList}}
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{index $row $col.Name}}
</td>
{{end}}
{{end}}
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{end}}