feat(app_detail_window): 新增应用详情窗口独立组件
refactor(leonapp_gui): 重构应用详情展示逻辑,直接创建窗口实例 fix(version_control.php): 修复文件路径处理和删除逻辑问题 feat(upload_app.php): 添加Markdown预览功能 style(dashboard.php): 移除冗余的padding样式 chore: 更新favicon和清理pyc缓存文件
This commit is contained in:
BIN
E093F984D9FDB988C9F20A2EE554657D.png
Normal file
BIN
E093F984D9FDB988C9F20A2EE554657D.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 557 KiB |
@@ -63,7 +63,8 @@ if (!($conn instanceof mysqli)) {
|
|||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
background-color: #f4f4f4;
|
background-color: #f4f4f4;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px;
|
/* 操了原来问题出现在这里 */
|
||||||
|
/* padding: 20px; */
|
||||||
}
|
}
|
||||||
.page-transition {
|
.page-transition {
|
||||||
animation: fadeIn 0.5s ease-in-out;
|
animation: fadeIn 0.5s ease-in-out;
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ if (!($conn instanceof mysqli)) {
|
|||||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>保存更改</button>
|
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>保存更改</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="../js/bootstrap.bundle.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.classList.add('page-transition');
|
document.body.classList.add('page-transition');
|
||||||
|
|||||||
@@ -186,7 +186,6 @@ if (!($conn instanceof mysqli)) {
|
|||||||
// 最终修正:10个参数对应10个类型字符
|
// 最终修正:10个参数对应10个类型字符
|
||||||
// 根据参数实际类型修正类型字符串(整数用i,字符串用s)
|
// 根据参数实际类型修正类型字符串(整数用i,字符串用s)
|
||||||
// 移除多余的$status参数,匹配SQL中9个占位符
|
// 移除多余的$status参数,匹配SQL中9个占位符
|
||||||
// 修正age_rating_description类型为字符串,并确保9个参数与占位符匹配
|
|
||||||
// 修复变量名错误:使用已验证的$appFilePath替换未定义的$file_path
|
// 修复变量名错误:使用已验证的$appFilePath替换未定义的$file_path
|
||||||
$stmt->bind_param('ssssssssis', $appName, $appDescription, $platforms_json, $ageRating, $ageRatingDescription, $version, $changelog, $appRelativePath, $developerId, $developerEmail);
|
$stmt->bind_param('ssssssssis', $appName, $appDescription, $platforms_json, $ageRating, $ageRatingDescription, $version, $changelog, $appRelativePath, $developerId, $developerEmail);
|
||||||
if (!$stmt->execute()) {
|
if (!$stmt->execute()) {
|
||||||
@@ -292,7 +291,9 @@ if (!($conn instanceof mysqli)) {
|
|||||||
<!-- 自定义CSS -->
|
<!-- 自定义CSS -->
|
||||||
<link rel="stylesheet" href="../styles.css">
|
<link rel="stylesheet" href="../styles.css">
|
||||||
<script src="/js/sweetalert.js"></script>
|
<script src="/js/sweetalert.js"></script>
|
||||||
<!-- Fluent Design 模糊效果 -->
|
<!-- Markdown解析库 -->
|
||||||
|
<script src="/js/marked.js"></script>
|
||||||
|
<!-- Fluent Design 模糊效果和Markdown预览样式 -->
|
||||||
<style>
|
<style>
|
||||||
.blur-bg {
|
.blur-bg {
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
@@ -316,9 +317,61 @@ if (!($conn instanceof mysqli)) {
|
|||||||
#ageRatingDescriptionGroup {
|
#ageRatingDescriptionGroup {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
/* Markdown预览样式 */
|
||||||
|
.markdown-content {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.markdown-content h1,
|
||||||
|
.markdown-content h2,
|
||||||
|
.markdown-content h3,
|
||||||
|
.markdown-content h4,
|
||||||
|
.markdown-content h5,
|
||||||
|
.markdown-content h6 {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.markdown-content p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.markdown-content ul,
|
||||||
|
.markdown-content ol {
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.markdown-content pre {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.markdown-content code {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
.markdown-content blockquote {
|
||||||
|
border-left: 4px solid #007BFF;
|
||||||
|
padding-left: 1rem;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
.markdown-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.markdown-content a {
|
||||||
|
color: #007BFF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.markdown-content a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Bootstrap JS with Popper -->
|
|
||||||
<script src="/js/bootstrap.bundle.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<!-- 导航栏 -->
|
<!-- 导航栏 -->
|
||||||
@@ -346,26 +399,6 @@ if (!($conn instanceof mysqli)) {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<style>
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
.btn-primary {
|
|
||||||
background-color: #007BFF;
|
|
||||||
border-color: #007BFF;
|
|
||||||
}
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
border-color: #0056b3;
|
|
||||||
}
|
|
||||||
.back-link {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
#ageRatingDescriptionGroup {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
<script>
|
||||||
// 年龄分级说明显示控制
|
// 年龄分级说明显示控制
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
@@ -442,6 +475,43 @@ if (!($conn instanceof mysqli)) {
|
|||||||
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
|
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Markdown预览功能
|
||||||
|
const descriptionTextarea = document.getElementById('description');
|
||||||
|
const markdownPreview = document.getElementById('markdown-preview');
|
||||||
|
const previewContent = document.getElementById('preview-content');
|
||||||
|
const togglePreviewBtn = document.getElementById('toggle-preview');
|
||||||
|
const closePreviewBtn = document.getElementById('close-preview');
|
||||||
|
|
||||||
|
function updatePreview() {
|
||||||
|
const markdownText = descriptionTextarea.value;
|
||||||
|
if (markdownText.trim() === '') {
|
||||||
|
previewContent.innerHTML = '<div class="text-muted">请输入Markdown内容以查看预览</div>';
|
||||||
|
} else {
|
||||||
|
// 使用marked.js解析Markdown
|
||||||
|
try {
|
||||||
|
const html = marked.parse(markdownText);
|
||||||
|
previewContent.innerHTML = html;
|
||||||
|
} catch (error) {
|
||||||
|
previewContent.innerHTML = '<div class="text-danger">Markdown解析错误: ' + error.message + '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePreviewBtn.addEventListener('click', function() {
|
||||||
|
markdownPreview.style.display = 'block';
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
closePreviewBtn.addEventListener('click', function() {
|
||||||
|
markdownPreview.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
descriptionTextarea.addEventListener('input', function() {
|
||||||
|
if (markdownPreview.style.display === 'block') {
|
||||||
|
updatePreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
@@ -486,7 +556,19 @@ if (!($conn instanceof mysqli)) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label for="description" class="form-label">应用描述(支持Markdown)</label>
|
<label for="description" class="form-label">应用描述(支持Markdown)</label>
|
||||||
|
<div class="mb-2">
|
||||||
|
<button type="button" id="toggle-preview" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<i class="fas fa-eye"></i> 预览
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<textarea id="description" name="description" rows="5" class="form-control" required></textarea>
|
<textarea id="description" name="description" rows="5" class="form-control" required></textarea>
|
||||||
|
<div id="markdown-preview" class="mt-3 p-3 border rounded bg-light" style="display: none;">
|
||||||
|
<div class="preview-header d-flex justify-content-between items-center mb-2">
|
||||||
|
<h5 class="mb-0">Markdown预览</h5>
|
||||||
|
<button type="button" id="close-preview" class="btn-close"></button>
|
||||||
|
</div>
|
||||||
|
<div id="preview-content" class="markdown-content"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label for="age_rating" class="form-label">年龄分级</label>
|
<label for="age_rating" class="form-label">年龄分级</label>
|
||||||
@@ -585,5 +667,8 @@ if (!($conn instanceof mysqli)) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS with Popper -->
|
||||||
|
<script src="/js/bootstrap.bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -38,6 +38,7 @@ $platforms = json_decode($app['platforms'], true);
|
|||||||
|
|
||||||
$success = '';
|
$success = '';
|
||||||
$error = '';
|
$error = '';
|
||||||
|
|
||||||
// 处理版本上传请求
|
// 处理版本上传请求
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_version'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_version'])) {
|
||||||
// 验证版本信息
|
// 验证版本信息
|
||||||
@@ -45,7 +46,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_version'])) {
|
|||||||
$error = '版本号和安装包不能为空';
|
$error = '版本号和安装包不能为空';
|
||||||
} else {
|
} else {
|
||||||
// 处理App文件上传
|
// 处理App文件上传
|
||||||
$uploadDir = '../files/';
|
$uploadDir = __DIR__ . '/../files/';
|
||||||
if (!is_dir($uploadDir)) {
|
if (!is_dir($uploadDir)) {
|
||||||
mkdir($uploadDir, 0755, true);
|
mkdir($uploadDir, 0755, true);
|
||||||
}
|
}
|
||||||
@@ -64,10 +65,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_version'])) {
|
|||||||
$error = '版本保存失败,请稍后再试';
|
$error = '版本保存失败,请稍后再试';
|
||||||
unlink($targetPath); // 清理已上传文件
|
unlink($targetPath); // 清理已上传文件
|
||||||
} else {
|
} else {
|
||||||
$verStmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
|
$verStmt->bind_param("isss", $appId, $version, $changelog, $fileName);
|
||||||
|
|
||||||
if ($verStmt->execute()) {
|
if ($verStmt->execute()) {
|
||||||
// 更新应用表中的最新版本
|
|
||||||
// 更新应用表中的最新版本
|
// 更新应用表中的最新版本
|
||||||
$updateAppSql = "UPDATE apps SET version = ? WHERE id = ?";
|
$updateAppSql = "UPDATE apps SET version = ? WHERE id = ?";
|
||||||
$updStmt = $conn->prepare($updateAppSql);
|
$updStmt = $conn->prepare($updateAppSql);
|
||||||
@@ -99,7 +99,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['version_id'])) {
|
|||||||
|
|
||||||
// 检查是否有新文件上传
|
// 检查是否有新文件上传
|
||||||
if (!empty($_FILES['new_app_file']['name'])) {
|
if (!empty($_FILES['new_app_file']['name'])) {
|
||||||
$uploadDir = '../files/';
|
$uploadDir = __DIR__ . '/../files/';
|
||||||
if (!is_dir($uploadDir)) {
|
if (!is_dir($uploadDir)) {
|
||||||
mkdir($uploadDir, 0755, true);
|
mkdir($uploadDir, 0755, true);
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['version_id'])) {
|
|||||||
$oldPathResult = $getOldPathStmt->get_result();
|
$oldPathResult = $getOldPathStmt->get_result();
|
||||||
if ($oldPathResult->num_rows > 0) {
|
if ($oldPathResult->num_rows > 0) {
|
||||||
$oldPathRow = $oldPathResult->fetch_assoc();
|
$oldPathRow = $oldPathResult->fetch_assoc();
|
||||||
$oldFilePath = $oldPathRow['file_path'];
|
$oldFileName = $oldPathRow['file_path'];
|
||||||
|
$oldFilePath = $uploadDir . $oldFileName;
|
||||||
if (file_exists($oldFilePath)) {
|
if (file_exists($oldFilePath)) {
|
||||||
unlink($oldFilePath);
|
unlink($oldFilePath);
|
||||||
}
|
}
|
||||||
@@ -134,7 +135,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['version_id'])) {
|
|||||||
$error = '版本修改失败,请稍后再试';
|
$error = '版本修改失败,请稍后再试';
|
||||||
unlink($newFilePath);
|
unlink($newFilePath);
|
||||||
} else {
|
} else {
|
||||||
$updateVersionStmt->bind_param("sssi", $version, $changelog, $newFilePath, $versionId);
|
$updateVersionStmt->bind_param("sssi", $version, $changelog, $fileName, $versionId);
|
||||||
if ($updateVersionStmt->execute()) {
|
if ($updateVersionStmt->execute()) {
|
||||||
$success = '版本修改成功';
|
$success = '版本修改成功';
|
||||||
} else {
|
} else {
|
||||||
@@ -167,13 +168,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['version_id'])) {
|
|||||||
// 处理版本删除请求
|
// 处理版本删除请求
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_version'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_version'])) {
|
||||||
$versionId = $_POST['version_id'];
|
$versionId = $_POST['version_id'];
|
||||||
$filePath = $_POST['file_path'];
|
$uploadDir = __DIR__ . '/../files/';
|
||||||
|
|
||||||
// 删除文件
|
// 获取文件路径
|
||||||
if (file_exists($filePath)) {
|
$getFilePathSql = "SELECT file_path FROM app_versions WHERE id = ?";
|
||||||
if (!unlink($filePath)) {
|
$getFileStmt = $conn->prepare($getFilePathSql);
|
||||||
log_error("文件删除失败: " . $filePath, __FILE__, __LINE__);
|
if (!$getFileStmt) {
|
||||||
$error = '版本删除失败,请稍后再试';
|
log_error("获取文件路径查询准备失败: " . $conn->error, __FILE__, __LINE__);
|
||||||
|
$error = '版本删除失败,请稍后再试';
|
||||||
|
} else {
|
||||||
|
$getFileStmt->bind_param("i", $versionId);
|
||||||
|
$getFileStmt->execute();
|
||||||
|
$fileResult = $getFileStmt->get_result();
|
||||||
|
if ($fileResult->num_rows > 0) {
|
||||||
|
$fileRow = $fileResult->fetch_assoc();
|
||||||
|
$fileName = $fileRow['file_path'];
|
||||||
|
$filePath = $uploadDir . $fileName;
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
if (file_exists($filePath)) {
|
||||||
|
if (!unlink($filePath)) {
|
||||||
|
log_error("文件删除失败: " . $filePath, __FILE__, __LINE__);
|
||||||
|
$error = '版本删除失败,请稍后再试';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +344,7 @@ if (!$verStmt) {
|
|||||||
<td>
|
<td>
|
||||||
<a href="../download.php?id=<?php echo $ver['id']; ?>&type=version" class="btn btn-sm btn-outline-primary"><i class="fas fa-download me-1"></i>下载</a>
|
<a href="../download.php?id=<?php echo $ver['id']; ?>&type=version" class="btn btn-sm btn-outline-primary"><i class="fas fa-download me-1"></i>下载</a>
|
||||||
<a href="#" class="btn btn-sm btn-outline-warning ms-2" onclick="openEditModal(<?php echo $ver['id']; ?>, '<?php echo htmlspecialchars($ver['version']); ?>', '<?php echo htmlspecialchars($ver['changelog']); ?>')"><i class="fas fa-edit me-1"></i>修改</a>
|
<a href="#" class="btn btn-sm btn-outline-warning ms-2" onclick="openEditModal(<?php echo $ver['id']; ?>, '<?php echo htmlspecialchars($ver['version']); ?>', '<?php echo htmlspecialchars($ver['changelog']); ?>')"><i class="fas fa-edit me-1"></i>修改</a>
|
||||||
<a href="#" class="btn btn-sm btn-outline-danger ms-2" onclick="confirmDelete(<?php echo $ver['id']; ?>, '<?php echo htmlspecialchars($ver['file_path']); ?>')"><i class="fas fa-trash-alt me-1"></i>删除</a>
|
<a href="#" class="btn btn-sm btn-outline-danger ms-2" onclick="confirmDelete(<?php echo $ver['id']; ?>)"><i class="fas fa-trash-alt me-1"></i>删除</a>
|
||||||
<?php if ($ver['is_current'] == 1): ?>
|
<?php if ($ver['is_current'] == 1): ?>
|
||||||
<span class="badge bg-success">当前版本</span>
|
<span class="badge bg-success">当前版本</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -401,11 +419,10 @@ if (!$verStmt) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete(versionId, filePath) {
|
function confirmDelete(versionId) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('delete_version', 'true');
|
formData.append('delete_version', 'true');
|
||||||
formData.append('version_id', versionId);
|
formData.append('version_id', versionId);
|
||||||
formData.append('file_path', filePath);
|
|
||||||
|
|
||||||
fetch('version_control.php', { method: 'POST', body: formData })
|
fetch('version_control.php', { method: 'POST', body: formData })
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
|
|||||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
favicon.jpeg
BIN
favicon.jpeg
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 124 KiB |
2524
favicon.svg
2524
favicon.svg
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 313 KiB |
BIN
pyqt5fluentdesign/__pycache__/app_detail_window.cpython-312.pyc
Normal file
BIN
pyqt5fluentdesign/__pycache__/app_detail_window.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
142
pyqt5fluentdesign/app_detail_window.py
Normal file
142
pyqt5fluentdesign/app_detail_window.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||||
|
QPushButton, QTextEdit, QGroupBox, QFormLayout, QScrollArea)
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from qfluentwidgets import (InfoBar, InfoBarPosition, TitleLabel, SubtitleLabel,
|
||||||
|
PrimaryPushButton, PushButton)
|
||||||
|
import json
|
||||||
|
|
||||||
|
class AppDetailWindow(QMainWindow):
|
||||||
|
def __init__(self, api_client, app_id, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.api_client = api_client
|
||||||
|
self.app_id = app_id
|
||||||
|
self.parent_window = parent
|
||||||
|
|
||||||
|
# 设置窗口属性
|
||||||
|
self.setWindowTitle("应用详情")
|
||||||
|
self.resize(800, 600)
|
||||||
|
|
||||||
|
# 创建中心部件
|
||||||
|
self.central_widget = QWidget()
|
||||||
|
self.setCentralWidget(self.central_widget)
|
||||||
|
|
||||||
|
# 创建主布局
|
||||||
|
self.main_layout = QVBoxLayout(self.central_widget)
|
||||||
|
|
||||||
|
# 创建标题区域
|
||||||
|
self.title_layout = QHBoxLayout()
|
||||||
|
self.title_label = TitleLabel("应用详情")
|
||||||
|
self.close_button = PushButton("关闭")
|
||||||
|
self.close_button.clicked.connect(self.close)
|
||||||
|
|
||||||
|
self.title_layout.addWidget(self.title_label)
|
||||||
|
self.title_layout.addStretch()
|
||||||
|
self.title_layout.addWidget(self.close_button)
|
||||||
|
|
||||||
|
# 创建滚动区域
|
||||||
|
self.scroll_area = QScrollArea()
|
||||||
|
self.scroll_area.setWidgetResizable(True)
|
||||||
|
self.scroll_content = QWidget()
|
||||||
|
self.scroll_layout = QVBoxLayout(self.scroll_content)
|
||||||
|
|
||||||
|
# 添加滚动区域到主布局
|
||||||
|
self.scroll_area.setWidget(self.scroll_content)
|
||||||
|
|
||||||
|
# 将标题区域和滚动区域添加到主布局
|
||||||
|
self.main_layout.addLayout(self.title_layout)
|
||||||
|
self.main_layout.addWidget(self.scroll_area)
|
||||||
|
|
||||||
|
# 加载应用详情
|
||||||
|
self.load_app_detail()
|
||||||
|
|
||||||
|
def load_app_detail(self):
|
||||||
|
"""加载应用详情"""
|
||||||
|
try:
|
||||||
|
# 这里应该调用API获取应用详情
|
||||||
|
# 暂时使用模拟数据
|
||||||
|
app_data = {
|
||||||
|
"id": self.app_id,
|
||||||
|
"name": "示例应用",
|
||||||
|
"description": "这是一个示例应用,用于展示应用详情页面",
|
||||||
|
"developer_id": "1",
|
||||||
|
"developer_name": "示例开发者",
|
||||||
|
"status": "approved",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"created_at": "2023-01-01 10:00:00"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 清空滚动区域
|
||||||
|
for i in reversed(range(self.scroll_layout.count())):
|
||||||
|
widget = self.scroll_layout.itemAt(i).widget()
|
||||||
|
if widget is not None:
|
||||||
|
widget.setParent(None)
|
||||||
|
widget.deleteLater()
|
||||||
|
|
||||||
|
# 显示应用基本信息
|
||||||
|
self.display_app_info(app_data)
|
||||||
|
|
||||||
|
# 显示应用描述
|
||||||
|
self.display_app_description(app_data)
|
||||||
|
|
||||||
|
# 显示操作按钮
|
||||||
|
self.display_action_buttons()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
InfoBar.error(
|
||||||
|
title="错误",
|
||||||
|
content=f"加载应用详情失败: {str(e)}",
|
||||||
|
orient=Qt.Horizontal,
|
||||||
|
isClosable=True,
|
||||||
|
position=InfoBarPosition.TOP,
|
||||||
|
duration=3000,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
|
||||||
|
def display_app_info(self, app_data):
|
||||||
|
"""显示应用基本信息"""
|
||||||
|
info_group = QGroupBox("基本信息")
|
||||||
|
info_layout = QFormLayout()
|
||||||
|
|
||||||
|
# 设置表单布局标签对齐
|
||||||
|
info_layout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
|
|
||||||
|
# 添加基本信息字段
|
||||||
|
info_layout.addRow("应用ID:", QLabel(app_data.get("id", "--")))
|
||||||
|
info_layout.addRow("应用名称:", QLabel(app_data.get("name", "--")))
|
||||||
|
info_layout.addRow("开发者ID:", QLabel(app_data.get("developer_id", "--")))
|
||||||
|
info_layout.addRow("开发者名称:", QLabel(app_data.get("developer_name", "--")))
|
||||||
|
info_layout.addRow("应用状态:", QLabel(app_data.get("status", "--")))
|
||||||
|
info_layout.addRow("当前版本:", QLabel(app_data.get("version", "--")))
|
||||||
|
info_layout.addRow("创建时间:", QLabel(app_data.get("created_at", "--")))
|
||||||
|
|
||||||
|
info_group.setLayout(info_layout)
|
||||||
|
self.scroll_layout.addWidget(info_group)
|
||||||
|
|
||||||
|
def display_app_description(self, app_data):
|
||||||
|
"""显示应用描述"""
|
||||||
|
description_group = QGroupBox("应用描述")
|
||||||
|
description_layout = QVBoxLayout()
|
||||||
|
|
||||||
|
description_text = QTextEdit()
|
||||||
|
description_text.setReadOnly(True)
|
||||||
|
description_text.setPlainText(app_data.get("description", "无描述信息"))
|
||||||
|
|
||||||
|
description_layout.addWidget(description_text)
|
||||||
|
description_group.setLayout(description_layout)
|
||||||
|
self.scroll_layout.addWidget(description_group)
|
||||||
|
|
||||||
|
def display_action_buttons(self):
|
||||||
|
"""显示操作按钮"""
|
||||||
|
buttons_layout = QHBoxLayout()
|
||||||
|
buttons_layout.addStretch()
|
||||||
|
|
||||||
|
refresh_button = PushButton("刷新")
|
||||||
|
refresh_button.clicked.connect(self.load_app_detail)
|
||||||
|
|
||||||
|
buttons_layout.addWidget(refresh_button)
|
||||||
|
self.scroll_layout.addLayout(buttons_layout)
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""关闭窗口事件"""
|
||||||
|
# 如果需要在关闭时执行清理操作,可以在这里添加
|
||||||
|
event.accept()
|
||||||
@@ -240,7 +240,10 @@ class AppTab(QWidget):
|
|||||||
def show_app_detail(self, row, column):
|
def show_app_detail(self, row, column):
|
||||||
"""显示应用详情"""
|
"""显示应用详情"""
|
||||||
app_id = self.table.item(row, 0).text()
|
app_id = self.table.item(row, 0).text()
|
||||||
self.parent().show_app_detail(app_id)
|
# 直接创建应用详情窗口,而不是通过父对象调用方法
|
||||||
|
from app_detail_window import AppDetailWindow
|
||||||
|
detail_window = AppDetailWindow(self.api_client, app_id, self)
|
||||||
|
detail_window.show()
|
||||||
|
|
||||||
def show_progress(self):
|
def show_progress(self):
|
||||||
"""显示进度条"""
|
"""显示进度条"""
|
||||||
|
|||||||
Reference in New Issue
Block a user