using JiDangJia.Core;
using JiDangJia.Contracts;
using JiDangJia.Services;
using System.Text.Json;
namespace JiDangJia.Views;
public sealed class ContentAdminPage : ContentPage
{
private readonly VerticalStackLayout list=Ui.Stack();
private readonly Entry community=new(){Placeholder="官方社群 / 服务页面 HTTPS 地址",MaxLength=2048};
private AppContent? content;
private static readonly JsonSerializerOptions json=new(JsonSerializerDefaults.Web);
public ContentAdminPage()
{
if(!AuthClient.Current.IsAdmin)throw new InvalidOperationException("需要管理员权限");Title="首页广告管理";
var body=Ui.Stack(Ui.Card(Ui.Stack(Ui.Section("全局内容"),Ui.Text("此处发布的首页广告会同步到记账端门店。普通账户只能查看。",13,Ui.Muted))),Ui.Button("+ 新增滚动广告",()=>Edit(null)),list,Ui.Button("刷新后台内容",Load,false));
body.Padding=18;Content=new ScrollView{Content=body};
}
protected override async void OnAppearing(){base.OnAppearing();try{await Load();}catch(Exception ex){await DisplayAlertAsync("无法加载",ex.Message,"知道了");}}
private async Task Load()
{
using var result=await AuthClient.Current.AdminRequest("/content");content=result.RootElement.Deserialize<AppContent>(json)??throw new InvalidOperationException("后台内容数据无效");Render();
}
private async Task Save(List<Advertisement> ads,string url)
{
if(content is null)throw new InvalidOperationException("请先刷新后台内容");
using var result=await AuthClient.Current.AdminRequest("/content",new UpdateContentRequest(content.Revision,ads,url));
content=result.RootElement.Deserialize<AppContent>(json)??throw new InvalidOperationException("后台内容数据无效");ContentClient.Remember(content);Render();
}
private void Render()
{
if(content is null)return;list.Clear();community.Text=content.CommunityUrl;
list.Add(Ui.Text($"{content.Ads.Count} 个广告位 · 更新 {DateTime.Parse(content.UpdatedAt).ToLocalTime():MM-dd HH:mm}",12,Ui.Muted));
foreach(var ad in content.Ads)
{
var item=Ui.Stack(Ui.Spread(Ui.Text(ad.Title,17,bold:true),Ui.Pill(ad.Enabled?"已启用":"已停用")),Ui.Text(ad.Subtitle,12,Ui.Muted));
if(ad.Image.Length>0)item.Add(new Image{Source=MediaFiles.Source(ad.Image),HeightRequest=120,Aspect=Aspect.AspectFill});
item.Add(Ui.Spread(Ui.Button("编辑广告",()=>Edit(ad),false),Ui.Button("删除",async()=>{if(await DisplayAlertAsync("删除全局广告","所有门店同步后将不再显示此广告。","删除","取消"))await Save(content.Ads.Where(a=>a.Id!=ad.Id).ToList(),content.CommunityUrl);},false)));list.Add(Ui.Card(item));
}
if(content.Ads.Count==0)list.Add(Ui.Card(Ui.Empty("暂无广告","新增并发布后,所有门店首页会显示广告")));
}
private Task Edit(Advertisement? ad)
{
if(content is null)throw new InvalidOperationException("请先刷新后台内容");
if(ad is null && content.Ads.Count>=20)throw new ArgumentException("最多 20 个广告位");
return Navigation.PushAsync(new AdvertisementEditor(ad,async updated=>
{
var ads=content.Ads.Where(a=>a.Id!=updated.Id).Append(updated).ToList();await Save(ads,content.CommunityUrl);
}));
}
}
public sealed class AdvertisementEditor : ContentPage
{
public AdvertisementEditor(Advertisement? ad,Func<Advertisement,Task> publish)
{
if(!AuthClient.Current.IsAdmin)throw new InvalidOperationException("需要管理员权限");Title=ad is null?"新增全局广告":"编辑全局广告";
var row=new DbRow{["title"]=ad?.Title??"",["subtitle"]=ad?.Subtitle??"",["adPhotos"]=JsonSerializer.Serialize(ad is {Image.Length:>0}?new[]{ad.Image}:Array.Empty<string>()),["link"]=ad?.Link??"",["enabled"]=ad is null || ad.Enabled?1:0,["sortOrder"]=ad?.SortOrder??0};
var fields=new FieldSet(row,[new("title","广告标题",Required:true),new("subtitle","副标题"),new("adPhotos","广告图片(JPEG / PNG,最多 1 MB)",FieldKind.Photos),new("link","点击跳转 HTTPS 链接"),new("sortOrder","排序",FieldKind.Number,true),new("enabled","启用广告",FieldKind.Toggle)]);
var id=ad?.Id??Guid.NewGuid().ToString("N");var body=Ui.Stack(Ui.Card(fields.View),Ui.Button("发布到所有门店",async()=>
{
var value=fields.Read();if(value.Money("sortOrder")%1!=0 || value.Money("sortOrder") is <0 or >9999)throw new ArgumentException("排序请填写 0 至 9999 的整数");
var updated=new Advertisement(id,value.Text("title"),value.Text("subtitle"),MediaFiles.Parse(value.Text("adPhotos")).FirstOrDefault()??"",value.Text("link"),value.Int("enabled")==1,value.Int("sortOrder"));
await publish(updated);await DisplayAlertAsync("已发布","各门店联网后会自动同步。","完成");await Navigation.PopAsync();
}));body.Padding=18;Content=new ScrollView{Content=body};
}
}
using JiDangJia.Core;
using JiDangJia.Contracts;
using JiDangJia.Services;
using System.Text.Json;
namespace JiDangJia.Views;
public sealed class ContentAdminPage : ContentPage
{
private readonly VerticalStackLayout list=Ui.Stack();
private readonly Entry community=new(){Placeholder="官方社群 / 服务页面 HTTPS 地址",MaxLength=2048};
private AppContent? content;
private static readonly JsonSerializerOptions json=new(JsonSerializerDefaults.Web);
public ContentAdminPage()
{
if(!AuthClient.Current.IsAdmin)throw new InvalidOperationException("需要管理员权限");Title="首页广告管理";
var body=Ui.Stack(Ui.Card(Ui.Stack(Ui.Section("全局内容"),Ui.Text("此处发布的首页广告会同步到记账端门店。普通账户只能查看。",13,Ui.Muted))),Ui.Button("+ 新增滚动广告",()=>Edit(null)),list,Ui.Button("刷新后台内容",Load,false));
body.Padding=18;Content=new ScrollView{Content=body};
}
protected override async void OnAppearing(){base.OnAppearing();try{await Load();}catch(Exception ex){await DisplayAlertAsync("无法加载",ex.Message,"知道了");}}
private async Task Load()
{
using var result=await AuthClient.Current.AdminRequest("/content");content=result.RootElement.Deserialize<AppContent>(json)??throw new InvalidOperationException("后台内容数据无效");Render();
}
private async Task Save(List<Advertisement> ads,string url)
{
if(content is null)throw new InvalidOperationException("请先刷新后台内容");
using var result=await AuthClient.Current.AdminRequest("/content",new UpdateContentRequest(content.Revision,ads,url));
content=result.RootElement.Deserialize<AppContent>(json)??throw new InvalidOperationException("后台内容数据无效");ContentClient.Remember(content);Render();
}
private void Render()
{
if(content is null)return;list.Clear();community.Text=content.CommunityUrl;
list.Add(Ui.Text($"{content.Ads.Count} 个广告位 · 更新 {DateTime.Parse(content.UpdatedAt).ToLocalTime():MM-dd HH:mm}",12,Ui.Muted));
foreach(var ad in content.Ads)
{
var item=Ui.Stack(Ui.Spread(Ui.Text(ad.Title,17,bold:true),Ui.Pill(ad.Enabled?"已启用":"已停用")),Ui.Text(ad.Subtitle,12,Ui.Muted));
if(ad.Image.Length>0)item.Add(new Image{Source=MediaFiles.Source(ad.Image),HeightRequest=120,Aspect=Aspect.AspectFill});
item.Add(Ui.Spread(Ui.Button("编辑广告",()=>Edit(ad),false),Ui.Button("删除",async()=>{if(await DisplayAlertAsync("删除全局广告","所有门店同步后将不再显示此广告。","删除","取消"))await Save(content.Ads.Where(a=>a.Id!=ad.Id).ToList(),content.CommunityUrl);},false)));list.Add(Ui.Card(item));
}
if(content.Ads.Count==0)list.Add(Ui.Card(Ui.Empty("暂无广告","新增并发布后,所有门店首页会显示广告")));
}
private Task Edit(Advertisement? ad)
{
if(content is null)throw new InvalidOperationException("请先刷新后台内容");
if(ad is null && content.Ads.Count>=20)throw new ArgumentException("最多 20 个广告位");
return Navigation.PushAsync(new AdvertisementEditor(ad,async updated=>
{
var ads=content.Ads.Where(a=>a.Id!=updated.Id).Append(updated).ToList();await Save(ads,content.CommunityUrl);
}));
}
}
public sealed class AdvertisementEditor : ContentPage
{
public AdvertisementEditor(Advertisement? ad,Func<Advertisement,Task> publish)
{
if(!AuthClient.Current.IsAdmin)throw new InvalidOperationException("需要管理员权限");Title=ad is null?"新增全局广告":"编辑全局广告";
var row=new DbRow{["title"]=ad?.Title??"",["subtitle"]=ad?.Subtitle??"",["adPhotos"]=JsonSerializer.Serialize(ad is {Image.Length:>0}?new[]{ad.Image}:Array.Empty<string>()),["link"]=ad?.Link??"",["enabled"]=ad is null || ad.Enabled?1:0,["sortOrder"]=ad?.SortOrder??0};
var fields=new FieldSet(row,[new("title","广告标题",Required:true),new("subtitle","副标题"),new("adPhotos","广告图片(JPEG / PNG,最多 1 MB)",FieldKind.Photos),new("link","点击跳转 HTTPS 链接"),new("sortOrder","排序",FieldKind.Number,true),new("enabled","启用广告",FieldKind.Toggle)]);
var id=ad?.Id??Guid.NewGuid().ToString("N");var body=Ui.Stack(Ui.Card(fields.View),Ui.Button("发布到所有门店",async()=>
{
var value=fields.Read();if(value.Money("sortOrder")%1!=0 || value.Money("sortOrder") is <0 or >9999)throw new ArgumentException("排序请填写 0 至 9999 的整数");
var updated=new Advertisement(id,value.Text("title"),value.Text("subtitle"),MediaFiles.Parse(value.Text("adPhotos")).FirstOrDefault()??"",value.Text("link"),value.Int("enabled")==1,value.Int("sortOrder"));
await publish(updated);await DisplayAlertAsync("已发布","各门店联网后会自动同步。","完成");await Navigation.PopAsync();
}));body.Padding=18;Content=new ScrollView{Content=body};
}
}