zoukankan      html  css  js  c++  java
  • SQLite for xamarin

    原文地址:http://www.codeproject.com/Articles/1097179/SQLite-with-Xamarin-Forms-Step-by-Step-guide

    SQLite是移动常见关系型数据库,轻便好用。下面我们就一步步实现xamarin对于sqlite的操作。

    创建项目添加NuGet引用

    我们采用第三方由oysteinkrog创建的"SQLite-Net-PCL"引用。

    由于数据库文件的位置在不同平台存在差异,并且SQLite-Net-PCL不能自动初始化数据库连接对象,所以需要采用历来服务来加载。

    Forms interface create

    创建Forms的接口文件如下:

    using SQLite.Net;
    

     

    namespace SQLiteEx
    

    {
    

    
    					public
    							interface ISQLite
    

        {
    

            SQLiteConnection GetConnection();
    

        }
    

    }
    

     

    SQLiteService 

    创建一个名为'class文件SQLiteService '实施ISQLite "接口和写本"实施getConnection像下面给出的代码每个特定平台的代码"对于新数据库:当我们创建应用程序本身的一个数据库那么第一次下面的代码将被使用。 

    Android:

    using System;
    

    using Xamarin.Forms;
    

    using SQLiteEx.Droid;
    

    using System.IO;
    

     

    [assembly: Dependency(typeof(SqliteService))]
    

    namespace SQLiteEx.Droid
    

    {
    

    
    					public
    							class SqliteService : ISQLite
    

        {
    

    
    					public SqliteService() { }
    

     

    
    					#region ISQLite implementation
    

    
    					public SQLite.Net.SQLiteConnection GetConnection()
    

            {
    

    
    					var sqliteFilename = "SQLiteEx.db3";
    

    
    					string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
    

    
    					var path = Path.Combine(documentsPath, sqliteFilename);
    

                Console.WriteLine(path);
    

    
    					if (!File.Exists(path)) File.Create(path);
    

    
    					var plat = new SQLite.Net.Platform.XamarinAndroid.SQLitePlatformAndroid();
    

    
    					var conn = new SQLite.Net.SQLiteConnection(plat, path);
    

    
    					// Return the database connection 
    

    
    					return conn;
    

            }
    

     

    
    					#endregion
    

     

     

        }
    

    }
    

     

    IOS

    using SQLiteEx.iOS;
    

    using System;
    

    using System.IO;
    

    using Xamarin.Forms;
    

     

    [assembly: Dependency(typeof(SqliteService))]
    

    namespace SQLiteEx.iOS
    

    {
    

    
    					public
    							class SqliteService : ISQLite
    

        {
    

    
    					public SqliteService()
    

            {
    

            }
    

    
    					#region ISQLite implementation
    

    
    					public SQLite.Net.SQLiteConnection GetConnection()
    

            {
    

    
    					var sqliteFilename = "SQLiteEx.db3";
    

    
    					string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
    

    
    					string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder
    

    
    					var path = Path.Combine(libraryPath, sqliteFilename);
    

     

    
    					// This is where we copy in the prepopulated database
    

                Console.WriteLine(path);
    

    
    					if (!File.Exists(path))
    

                {
    

                    File.Create(path);
    

                }
    

     

    
    					var plat = new SQLite.Net.Platform.XamarinIOS.SQLitePlatformIOS();
    

    
    					var conn = new SQLite.Net.SQLiteConnection(plat, path);
    

     

    
    					// Return the database connection 
    

    
    					return conn;
    

            }
    

    
    					#endregion
    

        }
    

    }
    

     

    对于预创建数据库:在一些应用场合可能有该数据库是一个要求预先填充了一些数据,我们将使用相同的应用程序。在这种情况下首先在遵循特定平台的项目中提到的地点将数据库文件复制,然后使用按照"SQLiteService'类代码Android:复制数据库文件中的"资源原始" Android平台的项目文件夹中

    修改代码如下:

    using System;
    

    using Xamarin.Forms;
    

    using SQLiteEx.Droid;
    

    using System.IO;
    

     

    [assembly: Dependency(typeof(SqliteService))]
    

    namespace SQLiteEx.Droid
    

    {
    

        
    

        public
    							class SqliteService : ISQLite {
    

     

            public SqliteService () {}
    

     

            #region ISQLite implementation    
    

            public SQLite.Net.SQLiteConnection GetConnection ()
    

            {
    

                var sqliteFilename = "SQLiteEx.db3";
    

                string documentsPath = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal); // Documents folder
    

                var path = Path.Combine(documentsPath, sqliteFilename);
    

     

                // This is where we copy in the prepopulated database
    

                Console.WriteLine (path);
    

                if (!File.Exists(path))
    

                {
    

                    var s = Forms.Context.Resources.OpenRawResource(Resource.Raw.APGameDb);  // RESOURCE NAME ###
    

     

                    // create a write stream
    

                    FileStream writeStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
    

                    // write to the stream
    

                    ReadWriteStream(s, writeStream);
    

                }
    

     

                var plat = new SQLite.Net.Platform.XamarinAndroid.SQLitePlatformAndroid();
    

                var conn = new SQLite.Net.SQLiteConnection(plat, path);
    

     

                // Return the database connection 
    

                return conn;
    

            }
    

            #endregion
    

     

     

            void ReadWriteStream(Stream readStream, Stream writeStream)
    

            {
    

                int Length = 256;
    

                Byte[] buffer = new
    									Byte[Length];
    

                int bytesRead = readStream.Read(buffer, 0, Length);
    

                // write the required bytes
    

                while (bytesRead > 0)
    

                {
    

                    writeStream.Write(buffer, 0, bytesRead);
    

                    bytesRead = readStream.Read(buffer, 0, Length);
    

                }
    

                readStream.Close();
    

                writeStream.Close();
    

            }
    

     

     

        }
    

    }
    

     

    var s = Forms.Context.Resources.OpenRawResource(Resource.Raw.APGameDb); // RESOURCE NAME ###

    如果这句话存在疑问,证明没有默认数据库文件

    我创建了一个文件名sxb.db3 与上文的var sqliteFilename = "SQLiteEx.db3"; 文件名不同 大家自行处理即可,

    修改生成操作作为androidResource 既可以在代码中使用了。

    创建命名为"PCL项目类DataAccess"。这个类将包含所有应用程序的数据访问代码

    using SQLite.Net;
    

    using Xamarin.Forms;
    

     

    namespace SQLiteEx
    

    {
    

    
    					public
    							class DataAccess
    

        {
    

            SQLiteConnection dbConn;
    

    
    					public DataAccess()
    

            {
    

                dbConn = DependencyService.Get<ISQLite>().GetConnection();
    

    
    					// create the table(s)
    

                dbConn.CreateTable<Employee>();
    

            }
    

    
    					public List<Employee> GetAllEmployees()
    

            {
    

    
    					return dbConn.Query<Employee>("Select * From [Employee]");
    

            }
    

    
    					public
    							int SaveEmployee(Employee aEmployee)
    

            {
    

    
    					return dbConn.Insert(aEmployee);            
    

            }
    

    
    					public
    							int DeleteEmployee(Employee aEmployee)
    

            {
    

    
    					return dbConn.Delete(aEmployee);
    

            }
    

    
    					public
    							int EditEmployee(Employee aEmployee)
    

            {
    

    
    					return dbConn.Update(aEmployee);
    

            }
    

        }
    

    }
    

     

    通过DataAccess针对员工表进行操作。其次,代码创建的表没有构造函数,为此,我们将有即使我们使用的是预创建数据库的方法,在数据库中的表会自动检查给,如果不存在,创建它。

    创建普通老式CLR对象(POCO)。

    例如,员工 ''上面给出的表类数据访问 "类。

    员工:

    using SQLite.Net.Attributes;
    

    using System;
    

     

    namespace SQLiteEx
    

    {
    

    
    					public
    							class Employee
    

        {
    

            [PrimaryKey, AutoIncrement]
    

    
    					public
    							long EmpId
    

            { get; set; }
    

            [NotNull]
    

    
    					public
    							string EmpName
    

            { get; set; }
    

    
    					public
    							string Designation
    

            { get; set; }
    

    
    					public
    							string Department
    

            { get; set; }
    

    
    					public
    							string Qualification
    

            { get; set; }
    

        }
    

    }
    

    创建"的静态对象属性数据访问应用程序像下面的代码'类。这将使我们能够使用来自应用程序的任何屏幕上操作数据。

    using Xamarin.Forms;
    

    using Xamarin.Forms.Xaml;
    

     

    [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
    

    namespace SQLiteEx
    

    {
    

    
    					public
    							class App : Application
    

        {
    

    
    					static DataAccess dbUtils;
    

    
    					public App()
    

            {
    

    
    					// The root page of your application
    

                MainPage = new NavigationPage(new ManageEmployee());
    

            }
    

    
    					public
    							static DataAccess DAUtil
    

            {
    

    
    					get
    						

                {
    

    
    					if (dbUtils == null)
    

                    {
    

                        dbUtils = new DataAccess();
    

                    }
    

    
    					return dbUtils;
    

                }
    

            }
    

    
    					protected
    							override
    									void OnStart()
    

            {
    

    
    					// Handle when your app starts
    

            }
    

     

    
    					protected
    							override
    									void OnSleep()
    

            {
    

    
    					// Handle when your app sleeps
    

            }
    

     

    
    					protected
    							override
    									void OnResume()
    

            {
    

    
    					// Handle when your app resumes
    

            }
    

        }
    

    }
    

     

    Ios版本

    AppDelegate 中增加DataAccess 属性同上

    这样就完成了使用SQLite数据库坚持应用程序的数据应用的基本结构
    现在,让我们创建示例应用程序看到CRUD操作code.The应用将包含以下屏幕的屏幕:

    1. 管理员工
    2. 添加员工
    3. 显示员工详细信息
    4. 编辑员工

      Xaml

       

      管理员工:此屏幕是应用程序的主屏幕上,它会显示目前员工名单,并给予一个选项,以添加新的员工,查看员工的详细信息和管理部门。此页面的XAML代码将是这样的:

      <?xml
      								version="1.0"
      											encoding="utf-8"
      														?>
      															

      <ContentPage
      									xmlns="http://xamarin.com/schemas/2014/forms"
      											

      
      							xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      									

      
      							x:Class="SQLiteEx.ManageEmployee"
      										Title="Manage Employees"
      													>
      														

      
      							<ContentPage.Padding>
      										

      
      							<OnPlatform
      										x:TypeArguments="Thickness"
      													iOS="0, 20, 0, 0"
      																/>
      																	

      
      							</ContentPage.Padding>
      										

      
      							<ContentPage.Content>
      										

      
      							<ListView
      										x:Name="lstData"
      													HasUnevenRows="false"
      																Header="Header Value"
      																			Footer="Footer"
      																						ItemSelected="OnSelection"
      																									>
      																										

      
      							<ListView.HeaderTemplate>
      										

      
      							<DataTemplate>
      										

      
      							<StackLayout
      										Orientation="Horizontal"
      													BackgroundColor="Blue"
      																Padding="5,5,5,5">
      																		

      
      							<Label
      										Text="Name"
      													FontSize="Medium"
      																FontAttributes="Bold"
      																			TextColor="White"/>
      																					

      
      							<Label
      										Text="Designation"
      													FontSize="Medium"
      																FontAttributes="Bold"
      																			TextColor="White"/>
      																					

      
      							<Label
      										Text="Department"
      													FontSize="Medium"
      																FontAttributes="Bold"
      																			TextColor="White"/>
      																					

      
      							</StackLayout>
      										

      
      							</DataTemplate>
      										

      
      							</ListView.HeaderTemplate>
      										

      
      							<ListView.ItemTemplate>
      										

      
      							<DataTemplate>
      										

      
      							<ViewCell>
      										

      
      							<StackLayout
      										Orientation="Horizontal"
      													Padding="5,5,5,5">
      															

      
      							<Label
      										Text="{Binding EmpName}"
      													FontSize="Medium"
      																/>
      																	

      
      							<Label
      										Text="{Binding Designation}"
      													FontSize="Medium"
      																/>
      																	

      
      							<Label
      										Text="{Binding Department}"
      													FontSize="Medium"
      																/>
      																	

      
      							</StackLayout>
      										

      
      							</ViewCell>
      										

      
      							</DataTemplate>
      										

      
      							</ListView.ItemTemplate>
      										

      
      							<ListView.FooterTemplate>
      										

      
      							<DataTemplate>
      										

      
      							<StackLayout
      										Orientation="Horizontal"
      													Padding="5,5,5,5">
      															

      
      							<Button
      										Text="Add New Employee"
      													Clicked="OnNewClicked"
      																/>
      																	

      
      							</StackLayout>
      										

      
      							</DataTemplate>
      										

      
      							</ListView.FooterTemplate>
      										

      
      							</ListView>
      										

      
      							</ContentPage.Content>
      										

      </ContentPage>
      									

       

      按照上面的XAML,在应用程序执行OnSelection''的方法ItemSelected名单"事件,以显示详细的员工信息,"OnNewClicked ''的方法点击了 ''事件添加新员工 "按钮。本页面背后的代码将包含以下代码。

      using System;
      

      using Xamarin.Forms;
      

       

      namespace SQLiteEx
      

      {
      

      
      							public
      									partial
      											class ManageEmployee : ContentPage
      

          {
      

      
      							public ManageEmployee()
      

              {
      

                  InitializeComponent();
      

      
      							var vList = App.DAUtil.GetAllEmployees();
      

                  lstData.ItemsSource = vList;
      

              }
      

       

      
      							void OnSelection(object sender, SelectedItemChangedEventArgs e)
      

              {
      

      
      							if (e.SelectedItem == null)
      

                  {
      

      
      							return; 
      

      
      							//ItemSelected is called on deselection, 
      

      
      							//which results in SelectedItem being set to null
      

                  }
      

      
      							var vSelUser = (Employee)e.SelectedItem;
      

                  Navigation.PushAsync(new ShowEmplyee(vSelUser));
      

              }
      

      
      							public
      									void OnNewClicked(object sender, EventArgs args)
      

              {
      

                  Navigation.PushAsync(new AddEmployee());
      

              }
      

          }
      

      }
      

       

      添加员工:顾名思义这个页面将用于增加新的员工,将根据点击的被称为"添加新员工"员工管理"页上的按钮。此页面的XAML代码将是这样的:

      隐藏   复制代码

      <?xml version="1.0" encoding="utf-8" ?>

      <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SQLiteEx.AddEmployee" Title="Add New Employee">

      <ContentPage.Content>

                   <TableView Intent="Settings" BackgroundColor="White">

                       <TableRoot>

                           <TableSection Title="Add New Employee">

                               <EntryCell x:Name="txtEmpName" Label="Employee Name" Keyboard="Text" />

                               <EntryCell x:Name="txtDesign" Label="Designation" Keyboard="Text" />

                               <EntryCell x:Name="txtDepartment" Label="Department" Keyboard="Text" />

                               <EntryCell x:Name="txtQualification" Label="Qualification" Keyboard="Text" />

                               <ViewCell>

                                   <ContentPage Padding="0,0">

      <ContentPage.Padding>

      <OnPlatform x:TypeArguments="Thickness" iOS="10,0" WinPhone="0,15,0,0" />

      </ContentPage.Padding>

      <ContentPage.Content>

      <Button BackgroundColor="#fd6d6d" Text="Save" TextColor="White" Clicked="OnSaveClicked" />

      </ContentPage.Content>

      </ContentPage>

                               </ViewCell>

                           </TableSection>

                       </TableRoot>

                   </TableView>

      </ContentPage.Content>

      </ContentPage>

      本页面背后的代码将包含以下代码:

      隐藏   复制代码

      using System;

      using Xamarin.Forms;

       

      namespace SQLiteEx

      {

      public partial class AddEmployee : ContentPage

      {

      public AddEmployee()

      {

      InitializeComponent();

      }

       

      public void OnSaveClicked(object sender, EventArgs args)

      {

      var vEmployee = new Employee()

      {

      EmpName = txtEmpName.Text,

      Department = txtDepartment.Text,

      Designation = txtDesign.Text,

      Qualification = txtQualification.Text

      };

      App.DAUtil.SaveEmployee(vEmployee);

      Navigation.PushAsync(new ManageEmployee());

      }

      }

      }

       

      显示员工详细信息:顾名思义,这个页面将用于显示雇员的完整细节,也将必须调用"编辑员工页面的选项,"删除员工"从数据库中删除员工的详细信息。此页面的XAML代码是这样的:

      <?xml
      								version="1.0"
      											encoding="utf-8"
      														?>
      															

      <ContentPage
      									xmlns="http://xamarin.com/schemas/2014/forms"
      												xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      															x:Class="SQLiteEx.ShowEmplyee"
      																		Title="View Employee">
      																				

      
      							<ContentPage.Content>
      										

      
      							<Grid>
      										

      
      							<Grid.RowDefinitions>
      										

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							<RowDefinition
      										Height="*"/>
      												

      
      							</Grid.RowDefinitions>
      										

       

      
      							<Grid.ColumnDefinitions>
      										

      
      							<ColumnDefinition
      										Width="10"/>
      												

      
      							<ColumnDefinition
      										Width="*"/>
      												

      
      							<ColumnDefinition
      										Width="*"/>
      												

      
      							<ColumnDefinition
      										Width="10"/>
      												

      
      							</Grid.ColumnDefinitions>
      										

       

      
      							<Label
      										Grid.Row
      												="0"
      														Grid.Column="0"
      																	Grid.ColumnSpan="2"
      																				Text="Employee Details"
      																							/>
      																								

      
      							<Label
      										Grid.Row
      												="1"
      														Grid.Column="1"
      																	Text="Name"
      																				/>
      																					

      
      							<Label
      										Grid.Row="1"
      													Grid.Column="2"
      																Text
      																		="{Binding EmpName}"
      																				/>
      																					

      
      							<Label
      										Grid.Row
      												="2"
      														Grid.Column="1"
      																	Text="Designation"
      																				/>
      																					

      
      							<Label
      										Grid.Row="2"
      													Grid.Column="2"
      																Text
      																		="{Binding Designation}"/>
      																			

      
      							<Label
      										Grid.Row
      												="3"
      														Grid.Column="1"
      																	Text="Department"
      																				/>
      																					

      
      							<Label
      										Grid.Row="3"
      													Grid.Column="2"
      																Text
      																		="{Binding Department}"/>
      																			

      
      							<Label
      										Grid.Row
      												="4"
      														Grid.Column="1"
      																	Text="Qualification"
      																				/>
      																					

      
      							<Label
      										Grid.Row="4"
      													Grid.Column="2"
      																Text
      																		="{Binding Qualification}"
      																				/>
      																					

      
      							<Button
      										Grid.Row
      												="5"
      														Grid.Column="1"
      																	Text="Edit Details"
      																				Clicked="OnEditClicked"
      																							/>
      																								

      
      							<Button
      										Grid.Row="5"
      													Grid.Column="2"
      																Text="Delete"
      																			Clicked="OnDeleteClicked"
      																						/>
      																							

      
      							</Grid>
      										

      
      							</ContentPage.Content>
      										

      </ContentPage>
      									

      而这个页面背后的代码将包含以下代码:

      using System;
      

      using Xamarin.Forms;
      

       

      namespace SQLiteEx
      

      {
      

      
      							public
      									partial
      											class ShowEmplyee : ContentPage
      

          {
      

              Employee mSelEmployee;
      

      
      							public ShowEmplyee(Employee aSelectedEmp)
      

              {
      

                  InitializeComponent();
      

                  mSelEmployee = aSelectedEmp;
      

                  BindingContext = mSelEmployee;
      

              }
      

       

      
      							public
      									void OnEditClicked(object sender, EventArgs args)
      

              {
      

                  Navigation.PushAsync(new EditEmployee(mSelEmployee));
      

              }
      

      
      							public
      									async
      											void  OnDeleteClicked(object sender, EventArgs args)
      

              {
      

      
      							bool accepted = await DisplayAlert("Confirm", "Are you Sure ?", "Yes", "No");
      

      
      							if (accepted)
      

                  {
      

                      App.DAUtil.DeleteEmployee(mSelEmployee);
      

                  }
      

      
      							await Navigation.PushAsync(new ManageEmployee());
      

              }
      

          }
      

      }
      

       

      编辑员工:顾名思义,这个页面将用于编辑员工的详细信息。此页面的XAML代码是这样的:

      <?xml
      								version="1.0"
      											encoding="utf-8"
      														?>
      															

      <ContentPage
      									xmlns="http://xamarin.com/schemas/2014/forms"
      												xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      															x:Class="SQLiteEx.EditEmployee"
      																		Title="Edit Employee">
      																				

      
      							<ContentPage.Content>
      										

      
      							<TableView
      										Intent="Settings"
      													BackgroundColor="White">
      															

      
      							<TableRoot>
      										

      
      							<TableSection
      										Title="Edit Employee">
      												

      
      							<EntryCell
      										x:Name="txtEmpName"
      													Label="Employee Name"
      																Text
      																		="{Binding EmpName}"
      																				Keyboard="Text"
      																							/>
      																								

      
      							<EntryCell
      										x:Name="txtDesign"
      													Label="Designation"
      																Text
      																		="{Binding Designation}"
      																				Keyboard="Text"
      																							/>
      																								

      
      							<EntryCell
      										x:Name="txtDepartment"
      													Label="Department"
      																Text
      																		="{Binding Department}"
      																				Keyboard="Text"
      																							/>
      																								

      
      							<EntryCell
      										x:Name="txtQualification"
      													Label="Qualification"
      																Text
      																		="{Binding Qualification}"
      																				Keyboard="Text"
      																							/>
      																								

      
      							<ViewCell>
      										

      
      							<ContentPage
      										Padding="0,0">
      												

      
      							<ContentPage.Padding>
      										

      
      							<OnPlatform
      										x:TypeArguments="Thickness"
      													iOS="10,0"
      																WinPhone="0,15,0,0"
      																			/>
      																				

      
      							</ContentPage.Padding>
      										

      
      							<ContentPage.Content>
      										

      
      							<Button
      										BackgroundColor="#fd6d6d"
      													Text="Save"
      																TextColor="White"
      																			Clicked="OnSaveClicked"
      																						/>
      																							

      
      							</ContentPage.Content>
      										

      
      							</ContentPage>
      										

      
      							</ViewCell>
      										

      
      							</TableSection>
      										

      
      							</TableRoot>
      										

      
      							</TableView>
      										

      
      							</ContentPage.Content>
      										

      </ContentPage>
      									

      而这个页面背后的代码将包含以下代码:

      而这个页面背后的代码将包含以下代码:

      隐藏   复制代码

      using System;

      using Xamarin.Forms;

       

      namespace SQLiteEx

      {

      public partial class EditEmployee : ContentPage

      {

      Employee mSelEmployee;

      public EditEmployee(Employee aSelectedEmp)

      {

      InitializeComponent();

      mSelEmployee = aSelectedEmp;

      BindingContext = mSelEmployee;

      }

       

      public void OnSaveClicked(object sender, EventArgs args)

      {

      mSelEmployee.EmpName = txtEmpName.Text;

      mSelEmployee.Department = txtDepartment.Text;

      mSelEmployee.Designation = txtDesign.Text;

      mSelEmployee.Qualification = txtQualification.Text;

      App.DAUtil.EditEmployee(mSelEmployee);

      Navigation.PushAsync(new ManageEmployee());

      }

      }

  • 相关阅读:
    Codeforce 270A
    Codeforce 513A
    Codeforce 834A
    51Nod 1433 0和5
    51Nod 1005 大数加法
    51Nod 1136 欧拉函数
    51Nod 1449 砝码称重 (二进制思想)
    Codeforce 459A
    Codeforce 515A
    Codeforce 474A
  • 原文地址:https://www.cnblogs.com/sephiroth-wzc/p/5526700.html
Copyright © 2011-2022 走看看