返回提交历史
Modified
XFENewsApplication.Test/Program.cs
+12
-13
Modified
XFENewsApplication.Test/XFENewsApplication.Test.csproj
+1
-0
Added
XFENewsApplication/Models/ArticlePart.cs
+6
-0
Added
XFENewsApplication/Models/ImageArticlePart.cs
+3
-0
Added
XFENewsApplication/Models/TextArticlePart.cs
+10
-0
Added
XFENewsApplication/Utilities/Helper/ArticleDisplayHelper.cs
+41
-0
Added
XFENewsApplication/Utilities/Selector/ArticleDataTemplateSelector.cs
+19
-0
Modified
XFENewsApplication/ViewModels/NewsViewPageViewModel.cs
+16
-9
Modified
XFENewsApplication/Views/NewsViewPage.xaml
+22
-5
Modified
XFENewsApplication/Views/NewsViewPage.xaml.cs
+4
-3
Modified
XFENewsApplication/XFENewsApplication.csproj
+0
-2
XFEstudio/XFENewsApplication
基本完善MSN新闻架构
5f07b67
代码差异
11 个文件
+134
-32
@@ -1,22 +1,21 @@
1
using System.Text.Json;
2
using XFEExtension.NetCore.XFETransform.JsonConverter;
1
using HtmlAgilityPack;
3
2
4
3
class Program
5
4
{
6
[SMTest]
7
public static async Task ClimbTest()
5
[SMTest("""
6
<p>1、《夏日之恋》</p><p>小说改编,小镇压抑女孩和富家女,拍的又清新又燥热,结尾sadie和河边很厉害,人家只是假日消遣过家家,你却死去活来以为真,古早的艾米莉布朗特。清澈,简洁,真实,晃动的镜头。也许因为是英音—— 中文的字幕错误多的一塌糊涂,全不得要领,一定要下载英文的字幕,不然会直接给电影拉低两颗星。 结尾的反转残酷也现实。</p><div><img data-reference=\"image\" data-document-id=\"cms/api/amp/image/AA1AXNbb\"></div><p>2、《火车上的女孩》</p><p>两位金发的女演员令人脸盲症发作,有点分不清楚,导演故作玄虚的剪辑更让人观影难度增加。结尾草率匆忙。不知道是否忠于原著,应该多点铺垫和人物行为逻辑。三个女演员非常捧。缺点是既忠于原著又不得不修改,因为原著的文学性加分,在电影上反而容易减分,不能直接用文学悬疑手法,电影应该增加悬疑度。线索也有点乱。总之看在主角表演不错。</p><div><img data-reference=\"image\" data-document-id=\"cms/api/amp/image/AA1AXPFO\"></div><p>3、《寂静之地》</p><p>很不错的一部影片,全片没几句台词,在寂静中烘托出了恐怖的氛围,全程紧张氛围满满,在恐怖的氛围下影片塑造的一家人同甘共苦的亲情也是让人感觉温暖,丈夫和妻子带着耳机翩翩起舞,父亲为女儿制作声音放大器耳麦等等,最后为了拯救孩子,父亲打着手语表达爱意都让人泪奔。</p><div><img data-reference=\"image\" data-document-id=\"cms/api/amp/image/AA1AXPFY\"></div><p>4、《明日边缘》</p><p>很有游戏感,循环死亡刷经验,打掉大boss获得自主读档的能力。“人”味的一面是主角的成长和爱情,但说真的,这种题材里的爱情线实在是千篇一律。动作戏好看,虽然是很老套的剧情,但是爆米花你能要求什么,不过汤姆 克鲁斯确实,这种硬汉很帅,结局的温情戏感觉没烘托出来,不过也算是好看的。</p><div><img data-reference=\"image\" data-document-id=\"cms/api/amp/image/AA1AXPG3\"></div><p>5、《边境杀手》</p><p>节奏把控很好,一直让人摸不透又悬着心。镜头很讲究,最后一场“地道战”在美丽的夕阳下,黑色的人影慢慢没入漆黑的大地,很棒!整体给人一种总有你想不到的黑暗隐藏在日常生活的表象下的感觉,就好像片头的尸体都藏在墙里。一定的留白给了人感慨和喘息的空间。</p><div><img data-reference=\"image\" data-document-id=\"cms/api/amp/image/AA1AXS3L\"></div><p>艾米莉·布朗特主演的5部电影,每部都值得细细品味!</p>
7
""")]
8
public static void TestMethod(string html)
8
9
{
9
using var client = new HttpClient();
10
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0");
11
QueryableJsonNode authJsonNode = await client.GetStringAsync("https://www.msn.cn/resolver/api/resolve/v3/config/?expType=AppConfig&expInstance=default&apptype=homePage&v=20250401.183&targetScope={%22audienceMode%22:%22adult%22,%22browser%22:{%22browserType%22:%22edgeChromium%22,%22version%22:%22135%22,%22ismobile%22:%22false%22},%22deviceFormFactor%22:%22desktop%22,%22domain%22:%22www.msn.cn%22,%22locale%22:{%22content%22:{%22language%22:%22zh%22,%22market%22:%22cn%22},%22display%22:{%22language%22:%22zh%22,%22market%22:%22cn%22}},%22os%22:%22windows%22,%22platform%22:%22web%22,%22pageType%22:%22hp%22,%22pageExperiments%22:[]}");
12
var result = await client.GetStringAsync($"https://assets.msn.com/service/news/feed/pages/weblayout?User=&activityId=&adoffsets=c1:-1,c2:-1&apikey={authJsonNode["configs"]["StripeWC/default"]["properties"]["apikey"]}&audienceMode=adult&cm=zh-cn&colstatus=c1:0,c2:0&column=c2&colwidth=300&contentType=article,video,slideshow,webcontent&it=edgeid&l3v=2&layout=c2&memory=8&newsSkip=0&newsTop=48&ocid=hponeservicefeed&pgc=11&private=1&scn=APP_ANON&timeOut=1000&vpSize=721x674&wposchema=byregion");
13
using var document = JsonDocument.Parse(result);
14
int i = 0;
15
foreach (var subNode in document.RootElement.GetProperty("sections").EnumerateArray().ElementAt(0).GetProperty("cards").EnumerateArray())
10
var doc = new HtmlDocument();
11
doc.LoadHtml(html);
12
foreach (var node in doc.DocumentNode.Descendants())
16
13
{
17
if (i++ == 0)
14
if (node.Name == "img")
15
Console.WriteLine($"图片:{node.GetAttributeValue("data-document-id", string.Empty)}");
16
if (node.HasChildNodes)
18
17
continue;
19
Console.WriteLine($"{subNode.GetProperty("title").GetString()}\n{subNode.GetProperty("abstract").GetString()}\n{subNode.GetProperty("url").GetString()}\n");
18
Console.WriteLine(node.InnerText);
20
19
}
21
20
}
22
21
}
@@ -8,6 +8,7 @@
8
8
</PropertyGroup>
9
9
10
10
<ItemGroup>
11
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
11
12
<PackageReference Include="XFEExtension.NetCore" Version="3.3.1" />
12
13
<PackageReference Include="XFEExtension.NetCore.XUnit" Version="2.0.4" />
13
14
</ItemGroup>
@@ -0,0 +1,6 @@
1
namespace XFENewsApplication.Models;
2
3
public abstract class ArticlePart
4
{
5
public string Content { get; set; } = string.Empty;
6
}
@@ -0,0 +1,3 @@
1
namespace XFENewsApplication.Models;
2
3
public class ImageArticlePart : ArticlePart { }
@@ -0,0 +1,10 @@
1
using Microsoft.UI.Text;
2
using Windows.UI.Text;
3
4
namespace XFENewsApplication.Models;
5
6
public class TextArticlePart : ArticlePart
7
{
8
public double FontSize { get; set; } = 14;
9
public FontWeight FontWeight { get; set; } = FontWeights.Normal;
10
}
@@ -0,0 +1,41 @@
1
using HtmlAgilityPack;
2
using Microsoft.UI.Text;
3
using System.Text.Json;
4
using XFENewsApplication.Models;
5
6
namespace XFENewsApplication.Utilities.Helper;
7
8
public static class ArticleDisplayHelper
9
{
10
public static List<ArticlePart> ConvertToArticlePart(string newsDocument)
11
{
12
var articleParts = new List<ArticlePart>();
13
var imageDictionary = new Dictionary<string, string>();
14
var document = JsonDocument.Parse(newsDocument);
15
articleParts.Add(new TextArticlePart
16
{
17
FontSize = 40,
18
FontWeight = FontWeights.Bold,
19
Content = document.RootElement.GetProperty("title").ToString()
20
});
21
foreach (var imageNode in document.RootElement.GetProperty("imageResources").EnumerateArray())
22
imageDictionary.Add(imageNode.GetProperty("cmsId").ToString(), imageNode.GetProperty("url").ToString());
23
var htmlDocument = new HtmlDocument();
24
htmlDocument.LoadHtml(document.RootElement.GetProperty("body").ToString());
25
foreach (var node in htmlDocument.DocumentNode.Descendants())
26
{
27
if (node.Name == "img")
28
articleParts.Add(new ImageArticlePart
29
{
30
Content = imageDictionary[node.GetAttributeValue("data-document-id", string.Empty).Replace("\"", string.Empty)]
31
});
32
if (node.HasChildNodes)
33
continue;
34
articleParts.Add(new TextArticlePart
35
{
36
Content = node.InnerText
37
});
38
}
39
return articleParts;
40
}
41
}
@@ -0,0 +1,19 @@
1
2
using XFENewsApplication.Models;
3
4
namespace XFENewsApplication.Utilities.Selector;
5
6
public partial class ArticleDataTemplateSelector : DataTemplateSelector
7
{
8
public DataTemplate? DefaultDataTemplate { get; set; }
9
public DataTemplate? TextDataTemplate { get; set; }
10
public DataTemplate? ImageDataTemplate { get; set; }
11
protected override DataTemplate? SelectTemplateCore(object item)
12
{
13
if (item is TextArticlePart)
14
return TextDataTemplate;
15
if (item is ImageArticlePart)
16
return ImageDataTemplate;
17
return DefaultDataTemplate;
18
}
19
}
@@ -9,7 +9,9 @@ using XFENewsApplication.Core.Models;
9
9
using XFENewsApplication.Core.Utilities.Helpers;
10
10
using XFENewsApplication.Implements.Services;
11
11
using XFENewsApplication.Interface.Services;
12
using XFENewsApplication.Models;
12
13
using XFENewsApplication.Profiles.CrossVersionProfiles;
14
using XFENewsApplication.Utilities.Helper;
13
15
14
16
namespace XFENewsApplication.ViewModels;
15
17
@@ -22,15 +24,16 @@ public partial class NewsViewPageViewModel : ViewModelBase
22
24
private List<NewsSource> newsList = [];
23
25
public INavigationParameterService<string> NavigationParameterService { get; set; } = ServiceManager.GetService<INavigationParameterService<string>>();
24
26
public INavigationViewService? NavigationViewService { get; set; } = ServiceManager.GetService<INavigationViewService>();
25
public IListViewService ListViewService { get; set; } = ServiceManager.GetService<IListViewService>();
27
public IListViewService NewsListViewService { get; set; } = ServiceManager.GetService<IListViewService>();
28
public IListViewService ContentListViewService { get; set; } = ServiceManager.GetService<IListViewService>();
26
29
public IDialogService DialogService { get; set; } = ServiceManager.GetService<IDialogService>();
27
public IWebViewService WebViewService { get; set; } = new WebViewService();
28
30
public ObservableCollection<NewsSource> NewsList { get; } = [];
31
public ObservableCollection<ArticlePart> ContentList { get; } = [];
29
32
30
33
public NewsViewPageViewModel()
31
34
{
32
35
NavigationParameterService.ParameterChange += NavigationParameterService_ParameterChange;
33
ListViewService.SelectionChanged += ListViewService_SelectionChanged;
36
NewsListViewService.SelectionChanged += ListViewService_SelectionChanged;
34
37
}
35
38
36
39
private async void ListViewService_SelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -39,18 +42,15 @@ public partial class NewsViewPageViewModel : ViewModelBase
39
42
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0");
40
43
try
41
44
{
42
if (ListViewService.ListView.SelectedItem is NewsSource newsSource)
45
if (NewsListViewService.ListView.SelectedItem is NewsSource newsSource)
43
46
{
44
47
var result = await client.GetStringAsync($"https://assets.msn.cn/content/view/v2/Detail/zh-cn/{newsSource.ID}");
45
var document = JsonDocument.Parse(result);
46
await WebViewService.WebView.EnsureCoreWebView2Async();
47
WebViewService.NavigateToString($"<h1>{document.RootElement.GetProperty("title")}</h1>\n{document.RootElement.GetProperty("body")}");
48
LoadContentList(ArticleDisplayHelper.ConvertToArticlePart(result));
48
49
}
49
50
}
50
51
catch (Exception ex)
51
52
{
52
await WebViewService.WebView.EnsureCoreWebView2Async();
53
WebViewService.NavigateToString($"<h1>{ex.Message}</h1>\n<p>{ex}</p>");
53
ServiceManager.GetGlobalService<IMessageService>()?.ShowMessage(ex.Message, "无法获取新闻", InfoBarSeverity.Warning);
54
54
}
55
55
}
56
56
@@ -73,6 +73,13 @@ public partial class NewsViewPageViewModel : ViewModelBase
73
73
NewsList.Add(news);
74
74
}
75
75
76
private void LoadContentList(IEnumerable<ArticlePart> articleParts)
77
{
78
ContentList.Clear();
79
foreach (var article in articleParts)
80
ContentList.Add(article);
81
}
82
76
83
[RelayCommand]
77
84
async Task GetNews()
78
85
{
@@ -5,10 +5,23 @@
5
5
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6
6
xmlns:local="using:XFENewsApplication.Views"
7
7
xmlns:coremodel="using:XFENewsApplication.Core.Models"
8
xmlns:model="using:XFENewsApplication.Models"
9
xmlns:converter="using:XFEExtension.NetCore.WinUIHelper.Utilities.Converter"
10
xmlns:selector="using:XFENewsApplication.Utilities.Selector"
8
11
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
9
12
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
10
13
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
11
14
mc:Ignorable="d">
15
<Page.Resources>
16
<converter:URLImageConverter x:Key="URLImageConverter"/>
17
<DataTemplate x:Key="TextDataTemplate" x:DataType="model:TextArticlePart">
18
<TextBlock Margin="0,10" Text="{x:Bind Content}" FontSize="{x:Bind FontSize}" FontWeight="{x:Bind FontWeight}" TextWrapping="Wrap"/>
19
</DataTemplate>
20
<DataTemplate x:Key="ImageDataTemplate" x:DataType="model:ImageArticlePart">
21
<Image Source="{x:Bind Content, Converter={StaticResource URLImageConverter}}"/>
22
</DataTemplate>
23
<selector:ArticleDataTemplateSelector x:Key="ArticleDataTemplateSelector" DefaultDataTemplate="{StaticResource TextDataTemplate}" TextDataTemplate="{StaticResource TextDataTemplate}" ImageDataTemplate="{StaticResource ImageDataTemplate}"/>
24
</Page.Resources>
12
25
13
26
<Grid>
14
27
<Grid.ColumnDefinitions>
@@ -35,10 +48,14 @@
35
48
</ListView>
36
49
</Border>
37
50
<controls:ContentSizer Grid.Column="1" TargetControl="{x:Bind sideBar}" CornerRadius="4"/>
38
<Grid Grid.Column="2">
39
<ScrollView>
40
<StackPanel x:Name="contentStackPanel" Orientation="Vertical" Spacing="10"/>
41
</ScrollView>
42
</Grid>
51
<Border Grid.Column="2">
52
<ListView x:Name="contentListView" ItemsSource="{x:Bind ViewModel.ContentList}" SelectionMode="None" ItemTemplateSelector="{StaticResource ArticleDataTemplateSelector}">
53
<ListView.ItemContainerStyle>
54
<Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
55
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
56
</Style>
57
</ListView.ItemContainerStyle>
58
</ListView>
59
</Border>
43
60
</Grid>
44
61
</Page>
@@ -1,4 +1,5 @@
1
using Microsoft.UI.Xaml.Navigation;
1
using Microsoft.UI.Text;
2
using Microsoft.UI.Xaml.Navigation;
2
3
3
4
namespace XFENewsApplication.Views;
4
5
@@ -15,8 +16,8 @@ public sealed partial class NewsViewPage : Page
15
16
Current = this;
16
17
this.InitializeComponent();
17
18
ViewModel.NavigationParameterService.Initialize(this);
18
ViewModel.ListViewService.Initialize(newsSourceListView);
19
ViewModel.WebViewService.Initialize(webView);
19
ViewModel.NewsListViewService.Initialize(newsSourceListView);
20
ViewModel.ContentListViewService.Initialize(contentListView);
20
21
}
21
22
22
23
protected override void OnNavigatedTo(NavigationEventArgs e)
@@ -64,9 +64,7 @@
64
64
<Using Include="Microsoft.UI.Xaml.Controls" />
65
65
</ItemGroup>
66
66
<ItemGroup>
67
<Folder Include="Models\" />
68
67
<Folder Include="Profiles\CacheProfiles\" />
69
<Folder Include="Utilities\Helper\" />
70
68
</ItemGroup>
71
69
<ItemGroup>
72
70
<ProjectReference Include="..\XFENewsApplication.Core\XFENewsApplication.Core.csproj" />