250 lines
8.8 KiB
Plaintext
250 lines
8.8 KiB
Plaintext
|
|
@page "/applications"
|
||
|
|
@rendermode @(new InteractiveServerRenderMode())
|
||
|
|
@inject Services.ApiClientService ApiClient
|
||
|
|
@inject IJSRuntime JSRuntime
|
||
|
|
|
||
|
|
<PageTitle>内容管理 - DRS9 信息发布系统</PageTitle>
|
||
|
|
|
||
|
|
<h3 class="mb-4">内容管理</h3>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||
|
|
<span><i class="bi bi-grid"></i> 内容列表</span>
|
||
|
|
<button class="btn btn-primary btn-sm" @onclick="ShowAddModal">
|
||
|
|
<i class="bi bi-plus-lg"></i> 添加内容
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="card-body">
|
||
|
|
@if (_applications == null)
|
||
|
|
{
|
||
|
|
<div class="text-center py-4">
|
||
|
|
<div class="spinner-border text-primary" role="status"></div>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
else if (_applications.Count == 0)
|
||
|
|
{
|
||
|
|
<div class="text-center text-muted py-4">
|
||
|
|
<i class="bi bi-inbox fs-1 d-block mb-3"></i>
|
||
|
|
<p>暂无内容,点击上方按钮添加</p>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
<div class="row">
|
||
|
|
@foreach (var app in _applications)
|
||
|
|
{
|
||
|
|
<div class="col-md-4 mb-3">
|
||
|
|
<div class="card h-100">
|
||
|
|
<div class="card-body">
|
||
|
|
<div class="d-flex align-items-start">
|
||
|
|
<div class="content-icon @GetAppTypeClass(app.Type)">
|
||
|
|
<i class="bi @GetAppIcon(app.Type)"></i>
|
||
|
|
</div>
|
||
|
|
<div class="flex-grow-1">
|
||
|
|
<h6 class="mb-1">@app.Name</h6>
|
||
|
|
<p class="text-muted mb-2 small">@app.Type</p>
|
||
|
|
<p class="mb-2 small">@app.Description</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card-footer d-flex justify-content-end gap-2">
|
||
|
|
<button class="btn btn-sm btn-outline-primary" @onclick="@(() => EditApp(app))">
|
||
|
|
<i class="bi bi-pencil"></i> 编辑
|
||
|
|
</button>
|
||
|
|
<button class="btn btn-sm btn-outline-danger" @onclick="@(() => DeleteApp(app))">
|
||
|
|
<i class="bi bi-trash"></i> 删除
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 添加/编辑 Modal -->
|
||
|
|
@if (_showModal)
|
||
|
|
{
|
||
|
|
<div class="modal fade show d-block" style="background-color: rgba(0,0,0,0.5)" tabindex="-1" @onclick="CloseModal">
|
||
|
|
<div class="modal-dialog" @onclick:stopPropagation="true">
|
||
|
|
<div class="modal-content">
|
||
|
|
<div class="modal-header">
|
||
|
|
<h5 class="modal-title">@(_editingApp?.Id == null ? "添加内容" : "编辑内容")</h5>
|
||
|
|
<button type="button" class="btn-close" @onclick="CloseModal"></button>
|
||
|
|
</div>
|
||
|
|
<div class="modal-body">
|
||
|
|
@if (!string.IsNullOrEmpty(_errorMessage))
|
||
|
|
{
|
||
|
|
<div class="alert alert-danger" role="alert">
|
||
|
|
@_errorMessage
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
<div class="mb-3">
|
||
|
|
<label class="form-label">内容名称</label>
|
||
|
|
<input type="text" class="form-control" @bind="_appName" />
|
||
|
|
</div>
|
||
|
|
<div class="mb-3">
|
||
|
|
<label class="form-label">内容类型</label>
|
||
|
|
<select class="form-select" @bind="_appType" disabled="@(_editingApp != null)">
|
||
|
|
<option value="">请选择...</option>
|
||
|
|
<option value="Dashboard">Dashboard</option>
|
||
|
|
<option value="WebRotator">WebRotator</option>
|
||
|
|
<option value="Image">Image</option>
|
||
|
|
<option value="Video">Video</option>
|
||
|
|
</select>
|
||
|
|
@if (_editingApp != null)
|
||
|
|
{
|
||
|
|
<small class="text-muted">内容类型创建后不可修改</small>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
<div class="mb-3">
|
||
|
|
<label class="form-label">内容 URL</label>
|
||
|
|
<input type="url" class="form-control" @bind="_appContentUrl" placeholder="https://..." />
|
||
|
|
</div>
|
||
|
|
<div class="mb-3">
|
||
|
|
<label class="form-label">描述</label>
|
||
|
|
<textarea class="form-control" @bind="_appDescription" rows="3"></textarea>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="modal-footer">
|
||
|
|
<button type="button" class="btn btn-secondary" @onclick="CloseModal">取消</button>
|
||
|
|
<button type="button" class="btn btn-primary" @onclick="SaveApp">保存</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
|
||
|
|
@code {
|
||
|
|
private List<ApplicationDto> _applications = new();
|
||
|
|
private ApplicationDto? _editingApp;
|
||
|
|
private string _appName = "";
|
||
|
|
private string _appType = "";
|
||
|
|
private string _appContentUrl = "";
|
||
|
|
private string _appDescription = "";
|
||
|
|
private bool _showModal = false;
|
||
|
|
private string _errorMessage = "";
|
||
|
|
|
||
|
|
protected override async Task OnInitializedAsync()
|
||
|
|
{
|
||
|
|
_applications = await ApiClient.GetApplicationsAsync();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ShowAddModal()
|
||
|
|
{
|
||
|
|
_editingApp = null;
|
||
|
|
_appName = "";
|
||
|
|
_appType = "";
|
||
|
|
_appContentUrl = "";
|
||
|
|
_appDescription = "";
|
||
|
|
_errorMessage = "";
|
||
|
|
_showModal = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void EditApp(ApplicationDto app)
|
||
|
|
{
|
||
|
|
_editingApp = app;
|
||
|
|
_appName = app.Name;
|
||
|
|
_appType = app.Type;
|
||
|
|
_appContentUrl = app.ContentUrl;
|
||
|
|
_appDescription = app.Description ?? "";
|
||
|
|
_errorMessage = "";
|
||
|
|
_showModal = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void CloseModal()
|
||
|
|
{
|
||
|
|
_showModal = false;
|
||
|
|
_editingApp = null;
|
||
|
|
_errorMessage = "";
|
||
|
|
}
|
||
|
|
|
||
|
|
private async Task SaveApp()
|
||
|
|
{
|
||
|
|
// Validate inputs
|
||
|
|
if (string.IsNullOrWhiteSpace(_appName))
|
||
|
|
{
|
||
|
|
_errorMessage = "请输入内容名称";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (string.IsNullOrWhiteSpace(_appType))
|
||
|
|
{
|
||
|
|
_errorMessage = "请选择内容类型";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (string.IsNullOrWhiteSpace(_appContentUrl))
|
||
|
|
{
|
||
|
|
_errorMessage = "请输入内容 URL";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool success;
|
||
|
|
if (_editingApp == null)
|
||
|
|
{
|
||
|
|
success = await ApiClient.CreateApplicationAsync(new ApplicationCreateRequest
|
||
|
|
{
|
||
|
|
Name = _appName,
|
||
|
|
Type = _appType,
|
||
|
|
ContentUrl = _appContentUrl,
|
||
|
|
Description = _appDescription
|
||
|
|
});
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
success = await ApiClient.UpdateApplicationAsync(_editingApp.Id, new ApplicationUpdateRequest
|
||
|
|
{
|
||
|
|
Name = _appName,
|
||
|
|
Description = _appDescription,
|
||
|
|
ContentUrl = _appContentUrl
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if (success)
|
||
|
|
{
|
||
|
|
CloseModal();
|
||
|
|
_applications = await ApiClient.GetApplicationsAsync();
|
||
|
|
await JSRuntime.InvokeVoidAsync("alert", "保存成功");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_errorMessage = "保存失败,请检查输入数据或稍后重试";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private async Task DeleteApp(ApplicationDto app)
|
||
|
|
{
|
||
|
|
if (await JSRuntime.InvokeAsync<bool>("confirm", $"确定要删除内容 '{app.Name}' 吗?"))
|
||
|
|
{
|
||
|
|
var success = await ApiClient.DeleteApplicationAsync(app.Id);
|
||
|
|
if (success)
|
||
|
|
{
|
||
|
|
_applications = await ApiClient.GetApplicationsAsync();
|
||
|
|
await JSRuntime.InvokeVoidAsync("alert", "删除成功");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
await JSRuntime.InvokeVoidAsync("alert", "删除失败");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private string GetAppTypeClass(string type) => type switch
|
||
|
|
{
|
||
|
|
"Dashboard" => "dashboard",
|
||
|
|
"WebRotator" => "web",
|
||
|
|
"Image" => "image",
|
||
|
|
"Video" => "video",
|
||
|
|
_ => ""
|
||
|
|
};
|
||
|
|
|
||
|
|
private string GetAppIcon(string type) => type switch
|
||
|
|
{
|
||
|
|
"Dashboard" => "bi-bar-chart",
|
||
|
|
"WebRotator" => "bi-arrow-repeat",
|
||
|
|
"Image" => "bi-image",
|
||
|
|
"Video" => "bi-play-circle",
|
||
|
|
_ => "bi-file-earmark"
|
||
|
|
};
|
||
|
|
}
|