testFlow/ui/page/_doc_list.html
Wyle.Gong-巩文昕 7a1aae1e2f ui
2025-04-23 11:21:08 +08:00

755 lines
20 KiB
HTML
Raw Permalink 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>
<head>
<meta charset="utf-8" />
<title>文档管理</title>
<style>
body {
font-family: "Microsoft YaHei", sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
font-size: 12px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
font-size: 15px;
}
.header h1 {
margin: 0;
color: #333;
}
.breadcrumb {
margin-bottom: 20px;
color: #666;
}
.breadcrumb a {
color: #1890ff;
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
.control-panel {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.search-box {
flex-grow: 1;
margin-right: 20px;
}
input[type="text"] {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
button {
padding: 8px 16px;
margin-right: 8px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #40a9ff;
}
button.danger {
background-color: #ff4d4f;
}
button.danger:hover {
background-color: #ff7875;
}
button.secondary {
background-color: #52c41a;
}
button.secondary:hover {
background-color: #73d13d;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th,
td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #fafafa;
font-weight: 600;
}
tr:hover {
background-color: #f5f5f5;
}
.action-buttons {
display: flex;
gap: 8px;
}
.action-buttons button {
padding: 4px 10px;
font-size: 12px;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fff;
margin: 10% auto;
padding: 20px;
border-radius: 8px;
width: 50%;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-header h2 {
margin: 0;
}
.close {
font-size: 24px;
font-weight: bold;
cursor: pointer;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.form-group textarea {
min-height: 100px;
resize: vertical;
}
.modal-footer {
margin-top: 20px;
text-align: right;
}
.empty-state {
text-align: center;
padding: 40px 0;
color: #999;
}
.empty-state p {
margin-bottom: 20px;
}
.file-upload {
margin-top: 10px;
}
.file-upload input[type="file"] {
display: none;
}
.file-upload label {
display: inline-block;
padding: 8px 16px;
background-color: #f0f0f0;
color: #333;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.file-upload label:hover {
background-color: #e0e0e0;
}
.file-name {
margin-left: 10px;
font-size: 14px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="breadcrumb">
<a href="project_list.html">项目列表</a> &gt;
<span id="project-name">文档管理</span>
</div>
<div class="header">
<h1>文档管理</h1>
<div>
<button id="back-btn" style="display: none">返回项目列表</button>
</div>
</div>
<div class="control-panel">
<div class="search-box">
<input type="text" id="search-input" placeholder="搜索文档..." />
</div>
<button id="add-doc-btn">添加文档</button>
<button id="refresh-btn" style="display: none">刷新</button>
</div>
<div id="docs-table-container">
<table id="docs-table">
<thead>
<tr>
<th>文档名称</th>
<th>类型</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="docs-list">
<!-- 文档列表将通过JavaScript动态生成 -->
</tbody>
</table>
<div id="empty-state" class="empty-state" style="display: none">
<p>暂无文档数据</p>
<button id="create-first-doc-btn">创建第一个文档</button>
</div>
</div>
</div>
<!-- 添加/编辑文档的模态框 -->
<div id="doc-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2 id="modal-title">添加文档</h2>
<span class="close">&times;</span>
</div>
<div class="modal-body">
<form id="doc-form">
<input type="hidden" id="doc-id" />
<div class="form-group">
<label for="doc-name">文档名称</label>
<input type="text" id="doc-name" required />
</div>
<div class="form-group">
<label for="doc-type">文档类型</label>
<select id="doc-type">
<option value="requirement">需求文档</option>
<option value="api">API文档</option>
<option value="design">设计文档</option>
<option value="other">其他</option>
</select>
</div>
<div class="form-group">
<label for="doc-description">文档描述</label>
<textarea id="doc-description"></textarea>
</div>
<div class="form-group file-upload">
<label for="doc-file">上传文件</label>
<input type="file" id="doc-file" />
<span class="file-name" id="file-name">未选择文件</span>
</div>
</form>
</div>
<div class="modal-footer">
<button id="cancel-btn">取消</button>
<button id="save-doc-btn">保存</button>
</div>
</div>
</div>
<!-- 确认删除的模态框 -->
<div id="confirm-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>确认删除</h2>
<span class="close">&times;</span>
</div>
<div class="modal-body">
<p>确定要删除这个文档吗?此操作不可撤销。</p>
</div>
<div class="modal-footer">
<button id="cancel-delete-btn">取消</button>
<button id="confirm-delete-btn" class="danger">删除</button>
</div>
</div>
</div>
<script>
// 全局变量
let docs = [];
let currentDocId = null;
let projectId = null;
let projectName = "";
// DOM元素
const docsListEl = document.getElementById("docs-list");
const emptyStateEl = document.getElementById("empty-state");
const searchInputEl = document.getElementById("search-input");
const docModal = document.getElementById("doc-modal");
const confirmModal = document.getElementById("confirm-modal");
const modalTitle = document.getElementById("modal-title");
const docForm = document.getElementById("doc-form");
const docIdInput = document.getElementById("doc-id");
const docNameInput = document.getElementById("doc-name");
const docTypeInput = document.getElementById("doc-type");
const docDescInput = document.getElementById("doc-description");
const docFileInput = document.getElementById("doc-file");
const fileNameEl = document.getElementById("file-name");
const projectNameEl = document.getElementById("project-name");
// 按钮
const backBtn = document.getElementById("back-btn");
const addDocBtn = document.getElementById("add-doc-btn");
const refreshBtn = document.getElementById("refresh-btn");
const createFirstDocBtn = document.getElementById("create-first-doc-btn");
const saveDocBtn = document.getElementById("save-doc-btn");
const cancelBtn = document.getElementById("cancel-btn");
const confirmDeleteBtn = document.getElementById("confirm-delete-btn");
const cancelDeleteBtn = document.getElementById("cancel-delete-btn");
const closeButtons = document.querySelectorAll(".close");
// 初始化
// 初始化
init();
function init() {
const urlParams = new URLSearchParams(window.location.search);
projectId = urlParams.get("project_id");
if (!projectId) {
alert("未指定项目ID将返回项目列表");
window.location.href = "project_list.html";
return;
}
// 获取项目信息
fetchProjectInfo();
// 获取文档列表
fetchDocs();
// 设置事件监听器
setupEventListeners();
}
// 设置事件监听器
function setupEventListeners() {
// 返回按钮
backBtn.addEventListener("click", () => {
window.location.href = "project_list.html";
});
// 添加文档按钮
addDocBtn.addEventListener("click", () => openDocModal());
// 刷新按钮
refreshBtn.addEventListener("click", fetchDocs);
// 创建第一个文档按钮
createFirstDocBtn.addEventListener("click", () => openDocModal());
// 保存文档按钮
saveDocBtn.addEventListener("click", saveDoc);
// 取消按钮
cancelBtn.addEventListener("click", closeDocModal);
// 确认删除按钮
confirmDeleteBtn.addEventListener("click", deleteDoc);
// 取消删除按钮
cancelDeleteBtn.addEventListener("click", closeConfirmModal);
// 关闭按钮
closeButtons.forEach((button) => {
button.addEventListener("click", function () {
docModal.style.display = "none";
confirmModal.style.display = "none";
});
});
// 搜索输入框
searchInputEl.addEventListener("input", filterDocs);
// 文件选择监听
docFileInput.addEventListener("change", function () {
if (this.files.length > 0) {
fileNameEl.textContent = this.files[0].name;
if (docNameInput.value === "") {
console.log("changeName");
docNameInput.value = this.files[0].name;
}
} else {
fileNameEl.textContent = "未选择文件";
}
});
// 点击模态框外部关闭
window.addEventListener("click", function (event) {
if (event.target === docModal) {
closeDocModal();
}
if (event.target === confirmModal) {
closeConfirmModal();
}
});
}
// 获取项目信息
async function fetchProjectInfo() {
try {
const response = await fetch(
`http://127.0.0.1:5002/api/project/${projectId}`
);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const result = await response.json();
if (result.code === 0 && result.data) {
projectName = result.data.name || "未命名项目";
projectNameEl.textContent = projectName;
document.title = `${projectName} - 文档管理`;
} else {
console.error("获取项目信息失败:", result.message || "未知错误");
}
} catch (error) {
console.error("获取项目信息错误:", error);
}
}
// 获取文档列表
async function fetchDocs() {
try {
const response = await fetch(
`http://127.0.0.1:5002/api/doc/${projectId}/`
);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const result = await response.json();
if (result.code === 0 && Array.isArray(result.data)) {
docs = result.data.sort((a, b) => {
return new Date(b.created_at) - new Date(a.created_at);
});
//console.log("docs", docs)
renderDocs(docs);
} else {
console.error("获取文档失败:", result.message || "未知错误");
showEmptyState(true, "加载失败,请重试");
}
} catch (error) {
console.error("获取文档错误:", error);
showEmptyState(true, "加载失败,请重试");
}
}
// 渲染文档列表
function renderDocs(docsToRender) {
if (!docsToRender || docsToRender.length === 0) {
showEmptyState(true);
return;
}
showEmptyState(false);
docsListEl.innerHTML = "";
docsToRender.forEach((doc) => {
const row = document.createElement("tr");
// 格式化日期
const createdAt = new Date(doc.created_at);
const formattedDate = createdAt.toLocaleString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
// 格式化文档类型
let docType = "其他";
switch (doc.type) {
case "api":
docType = "API文档";
break;
case "requirement":
docType = "需求文档";
break;
case "design":
docType = "设计文档";
break;
}
row.innerHTML = `
<td>${doc.name}</td>
<td>${docType}</td>
<td>${formattedDate}</td>
<td class="action-buttons">
<button class="view-demands-btn secondary" data-id="${doc.id}">查看需求</button>
<button class="edit-btn" data-id="${doc.id}">编辑</button>
<button class="delete-btn danger" data-id="${doc.id}">删除</button>
</td>
`;
docsListEl.appendChild(row);
});
// 添加按钮事件
document.querySelectorAll(".view-demands-btn").forEach((btn) => {
btn.addEventListener("click", function () {
const docId = this.getAttribute("data-id");
window.location.href = `demand.html?doc_id=${docId}`;
});
});
document.querySelectorAll(".edit-btn").forEach((btn) => {
btn.addEventListener("click", function () {
const docId = this.getAttribute("data-id");
const doc = docs.find((d) => d.id === docId);
openDocModal(doc);
});
});
document.querySelectorAll(".delete-btn").forEach((btn) => {
btn.addEventListener("click", function () {
const docId = this.getAttribute("data-id");
openConfirmModal(docId);
});
});
}
// 显示/隐藏空状态
function showEmptyState(show, message = "暂无文档数据") {
if (show) {
document.getElementById("docs-table").style.display = "none";
emptyStateEl.style.display = "block";
emptyStateEl.querySelector("p").textContent = message;
} else {
document.getElementById("docs-table").style.display = "table";
emptyStateEl.style.display = "none";
}
}
// 过滤文档
function filterDocs() {
const searchTerm = searchInputEl.value.toLowerCase();
if (!searchTerm) {
renderDocs(docs);
return;
}
const filteredDocs = docs.filter(
(doc) =>
doc.name.toLowerCase().includes(searchTerm) ||
(doc.description &&
doc.description.toLowerCase().includes(searchTerm))
);
renderDocs(filteredDocs);
}
// 打开文档模态框
function openDocModal(doc = null) {
modalTitle.textContent = doc ? "编辑文档" : "添加文档";
if (doc) {
docIdInput.value = doc.id;
docNameInput.value = doc.name || "";
docTypeInput.value = doc.type || "other";
docDescInput.value = doc.description || "";
fileNameEl.textContent = "保持原文件";
} else {
docForm.reset();
docIdInput.value = "";
fileNameEl.textContent = "未选择文件";
}
docModal.style.display = "block";
}
// 关闭文档模态框
function closeDocModal() {
docModal.style.display = "none";
docForm.reset();
fileNameEl.textContent = "未选择文件";
}
// 打开确认删除模态框
function openConfirmModal(docId) {
currentDocId = docId;
confirmModal.style.display = "block";
}
// 关闭确认删除模态框
function closeConfirmModal() {
confirmModal.style.display = "none";
currentDocId = null;
}
// 保存文档
async function saveDoc() {
const docId = docIdInput.value;
const name = docNameInput.value.trim();
const type = docTypeInput.value;
const description = docDescInput.value.trim();
if (!name) {
alert("请输入文档名称");
return;
}
// 创建FormData对象用于文件上传
const formData = new FormData();
formData.append("name", name);
formData.append("type", type);
formData.append("description", description);
formData.append("project_id", projectId);
// 如果有选择文件添加到FormData
if (docFileInput.files.length > 0) {
formData.append("file", docFileInput.files[0]);
}
try {
let url = "http://127.0.0.1:5002/api/doc/";
let method = "POST";
if (docId) {
url += docId;
method = "PATCH";
}
const response = await fetch(url, {
method,
body: formData,
});
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const result = await response.json();
if (result.code === 0) {
alert(docId ? "文档更新成功" : "文档创建成功");
closeDocModal();
fetchDocs();
} else {
alert(`操作失败: ${result.message || "未知错误"}`);
}
} catch (error) {
console.error("保存文档错误:", error);
alert(`操作失败: ${error.message}`);
}
}
// 删除文档
async function deleteDoc() {
if (!currentDocId) return;
try {
const response = await fetch(
`http://127.0.0.1:5002/api/doc/${currentDocId}`,
{
method: "DELETE",
}
);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const result = await response.json();
if (result.code === 0) {
alert("文档删除成功");
closeConfirmModal();
fetchDocs();
} else {
alert(`删除失败: ${result.message || "未知错误"}`);
}
} catch (error) {
console.error("删除文档错误:", error);
alert(`删除失败: ${error.message}`);
}
}
</script>
</body>
</html>