【问题标题】:Looping through Outlook emails to update Excel spreadsheet by finding string in email body通过在电子邮件正文中查找字符串来循环浏览 Outlook 电子邮件以更新 Excel 电子表格
【发布时间】:2021-01-15 03:25:36
【问题描述】:

我正在尝试遍历 Outlook 中的“已发送”文件夹,并使用电子邮件的“接收时间”更新我的电子表格。

我的电子表格有一列包含记录号。每封电子邮件都包含一个或多个记录号。

如果电子邮件正文有匹配的记录,那么我想提取收到的日期并将其放在一个列中。

我认为问题出在我的 If 语句上。

Option Explicit
 
Private Sub CommandButton1_Click()

    On Error GoTo ErrHandler

    ' Set Outlook application object.
    Dim objOutlook As Object

    Set objOutlook = CreateObject("Outlook.Application")
   
    Dim objNSpace As Object     ' Create and Set a NameSpace OBJECT.
    ' The GetNameSpace() method will represent a specified Namespace.
    Set objNSpace = objOutlook.GetNamespace("MAPI")

    Dim myFolder As Object  ' Create a folder object.
    Set myFolder = objNSpace.GetDefaultFolder(olFolderSentMail)

    Dim objItem As Object
    Dim iRows, iCols As Integer
    Dim sFilter As String

    iRows = 2

    Dim MyRange As Range
    Dim cell As Range
    Dim Wb As Workbook
    Dim FiltRange As Range
   
    Workbooks("RIRQ and RRTNs with LOB Sept 28 2020").Activate

    'Set MyRange = Workbooks("RIRQ and RRTNs with LOB Sept 28 2020").Worksheets("Data").Range(Cells(1, 1).Offset(1, 0), Range("A1").End(xlDown))
    
    ' select the records in column A
    Set MyRange = Workbooks("RIRQ and RRTNs with LOB Sept 28 2020").Worksheets("Data").Range(Cells(2, 1), Range("A1").End(xlDown))

    'Debug.Print MyRange.Address
    'only select the filtered records
    Set FiltRange = MyRange.SpecialCells(xlCellTypeVisible)

    'Debug.Print FiltRange.Address

    'create a filter for emails marked as not completed
    sFilter = "[Categories] = 'Not Completed'"

    'Debug.Print sFilter

    ThisWorkbook.Sheets("Sent_Email").Activate

    ' Loop through each item in the folder.

    'Debug.Print myFolder.Items.Restrict(sFilter).Count
    
    'loop through the emails in the sent folder restricted to specific category
    For Each objItem In myFolder.Items.Restrict(sFilter)

        If objItem.Class = olMail Then

            Dim objMail As Outlook.MailItem
            Set objMail = objItem

            'extract data from email
            Cells(iRows, 1) = objMail.Recipients(1)
            Cells(iRows, 2) = objMail.To
            Cells(iRows, 3) = objMail.Subject
            Cells(iRows, 4) = objMail.ReceivedTime
            Cells(iRows, 5) = objMail.Body

            'If MyRange <> "" Then
                
                'loop throug the records on the spreadsheet to find matches
                For Each cell In FiltRange

                    'Debug.Print MyRange.Find(cell.Value)
                    'Debug.Print cell.Value
                    'Debug.Print Cells(iRows, 5)

                    'if the email body contain the matching record or specific string then copy the received time to the row for the matching record
                    If InStr(LCase(Cells(iRows, 5)), cell.Value > 0) And InStr(LCase(Cells(iRows, 5)), LCase("GTPRM")) > 0 Then
    enter code here
                        Debug.Print cell.Value
                        cell(, 35).Value = Cells(iRows, 4).Value

                    End If

                Next cell

            'End If

        End If

        iRows = iRows + 1

    Next

    Set objMail = Nothing

    ' Release.
    Set objOutlook = Nothing
    Set objNSpace = Nothing
    Set myFolder = Nothing

ErrHandler:
    Debug.Print Err.Description

End Sub

【问题讨论】:

  • 你有什么问题,你的问题是什么?
  • 删除On Error GoTo ErrHandler。该声明说:不要告诉我哪个声明导致了问题;只是放弃宏。在开发过程中,您希望在问题陈述上停止执行。这使得尽可能容易地发现原因。也许你忘了初始化一个对象。如果可能,请确保执行不会失败,并在出现宏无法解决的问题时显示用户友好的消息。您也不希望在操作宏中使用此语句,因为它对用户非常不友好。
  • 有时您无法解决问题。例如,如果您要打开一个文件,您可以检查它是否存在,但不能检查您是否拥有必要的访问权限。在这种情况下,您使用:On Error or Resume NextProblem statementOn Error GoTo 0Check Err.Number。这不是一个完整的解释,但我需要发布一个答案才能给出完整的解释。如果您要求,我会发布答案。
  • 嗨托尼,谢谢你的回复,我的问题是我的 if 语句: If InStr(LCase(Cells(iRows, 5)), cell.Value > 0) And InStr(LCase(Cells (iRows, 5)), LCase("GTPRM")) > 0 Then Debug.Print cell.Value cell(, 35).Value = Cells(iRows, 4).Value End If .. 它没有赋值单元格(iRows,4).value 到单元格(,35)
  • 如果无法访问您的数据,就不可能诊断出为什么某个特定语句无法给出您所寻求的结果。但是,您的代码难以诊断的原因有很多。我将开始研究您的代码并创建一个解释如何改进它的答案。我将分阶段发布答案,以便您思考我在写什么。

标签: excel vba loops if-statement outlook


【解决方案1】:

正如我在评论中所说,我无法解释为什么您的问题陈述会在没有看到您的数据的情况下失败。相反,我会找出使您的代码更有可能失败和更难诊断的问题。

On Error GoTo ErrHandler

我看到应该更了解的作者使用了这种说法。我无法想象这种说法在开发过程中会有所帮助的情况。如果有问题,我希望在导致问题的语句上停止执行。我不想从错误描述中猜测是哪个语句导致了问题。使用操作宏更难确定最佳方法。

有两种类型的运行时错误:可以避免的错误和无法避免的错误。您可以避免的错误示例包括数组绑定错误和被零除。您将尝试在测试期间消除此类错误,但始终存在您不允许的罕见错误的风险。您的选择是让执行在给出错误的语句处停止或跳转到错误处理程序。两者都不是一个有吸引力的提议。代码将停止在几乎没有优雅失败的机会的中间。我的用户一直是这样的,我可以留下关于记录什么以及在执行停止时调用谁的详细说明,因此我不必考虑错误处理程序。

您无法避免的错误示例是尝试打开您没有必要访问权限的文件。为了处理这种情况,我推荐这个代码:

Dim ErrDesc As String
Dim ErrNum As Long
  :
Err.Clear
On Error Result Next
Statement that might fail
ErrNum = Err.Number
ErrDesc = Err.Description
On Error GoTo 0
If ErrNum <> 0 Then
  Code to handle failure
End If  

在这个例程中使用了这种技术的一种变体:

Public Function WshtExists(ByRef Wbk As Workbook, ByVal WshtName As String) As Boolean

  ' Returns True if Worksheet WshtName exists within
  '  * if Wbk Is Nothing the workbook containing the macros
  '  * else workbook Wbk

  Dim WbkLocal As Workbook
  Dim Wsht As Worksheet
  
  If Wbk Is Nothing Then
    Set WbkLocal = ThisWorkbook
  Else
    Set WbkLocal = Wbk
  End If
  
  Err.Clear
  On Error Resume Next
  Set Wsht = WbkLocal.Worksheets(WshtName)
  On Error GoTo 0
  If Wsht Is Nothing Then
    WshtExists = False
  Else
    WshtExists = True
  End If

End Function

这是我用来检查工作簿中是否存在工作表的例程。我本可以使用 For-Loop 循环浏览工作簿的工作表,这样我就可以避免使用 On Error Resume Next。但是,For-Loop 方法要慢得多。我不会经常检查工作表是否存在以节省时间,但我更喜欢这段代码。您可能更喜欢 For-Loop 方法。

变量声明的位置

如果您在子例程或函数之外编写Public A As Long ,则变量A 可由工作簿内任何模块内的任何子例程或函数访问。

如果您在子例程或函数之外编写Dim A As Long ,则变量A 可以被同一模块内的任何子例程或函数访问。

如果您在子例程或函数中编写Dim A As Long ,则变量A 可以被同一子例程或函数中的任何语句访问。

放置声明的位置决定了变量的范围。这些是您使用 VBA 的三种选择:公共、本地到模块和本地到例程。我听说很难找到变量范围的定义。有些语言具有额外的作用域,但不适用于 VBA。

您已在例程中首次使用之前声明了您的变量。我已经阅读了关于这是最好的方法的论点。我更喜欢将我所有的声明按字母顺序放在我的例程顶部。当我在写完 6 或 12 个月后回到常规时,我可以在顶部找到我所有的声明和解释。我觉得这很有帮助,但如果是你的选择。

将变量声明为对象

将变量声明为对象称为后期绑定。编译器不知道将在变量中放置什么类型的对象。解释器在运行时设置变量时发现类型。

另一种方法是引用 Outlook 库。在 VBA 编辑器中,单击 [Tools],然后单击 [References...]。这将显示数百个库的列表,这些库定义了您可以在工作簿中使用的变量类型和例程。一个这样的库是“Microsoft Outlook nn.0 对象库”,其中 nn 取决于您使用的 Office 版本。勾选此库右侧的框。由于您在代码中使用了Outlook.MailItem,因此您可能已经记下了它。

如果我引用 Outlook 对象库,我可以这样写:

Dim objOutlook As Outlook.Application
Dim objNSpace As Namespace
Dim myFolder As Outlook.Folder

我发现声明我将放置在变量中的对象类型很有帮助。这称为早期绑定。您可以在网络上找到有关早期绑定和后期绑定优点的讨论。

你写Dim objItem As Object是正确的。已发送文件夹不太可能包含 MailItems 以外的任何内容,但这是可能的。像您一样测试 objItem 的类是谨慎的。

将 iRows 变暗,iCols 为整数

对于大多数语言,Integer 和 Long 类型的变量的大小取决于目标机器的字长。对于 VBA,Integer 是 16 位,Long 是 32 位。我读到整数变量比 32 位和 64 位 PC 上的长变量慢,因为它们需要特殊处理。我对时间的尝试没有发现任何差异。 Integer 中可以保存的最大值为 65,535。工作表可以有 1,048,576 行,因此保存行号的变量应该是 Long。我将我所有的整数变量都声明为 Long,因为没有明显的缺点,并且它减少了溢出的机会。

设置 objOutlook = CreateObject("Outlook.Application")

我使用以下代码从 Excel 中打开和关闭 Outlook:

Dim AppOut As Outlook.Application
Dim Created As Boolean
Dim OutNs As Outlook.Namespace

Set AppOut = OutAppGetCreate(Created)
Set OutNs = AppOut.Session

Code accessing Outlook

Call OutAppClose(AppOut, Created) 

OutAppGetCreate() 内的 cmets 解释原因:

Public Sub OutAppClose(ByRef OutApp As Outlook.Application, ByVal Created As Boolean)

  ' If Created is True, quit the current instance of Outlook.
  
  If Created Then
    OutApp.Quit
  End If
  
  Set OutApp = Nothing

End Sub
Public Function OutAppGetCreate(ByRef Created As Boolean) As Outlook.Application

  ' Return a reference to the Outlook Application.
  ' Set Created to True if the reference is to a new application and to
  ' False if the reference is to an existing application.
  
  ' If Nothing is returned, the routine has been unable to get or create a reference.
  
  ' Only one instance of Outlook can be running.  CreateObject("Outlook.Application")
  ' will return a reference to the existing instance if one is already running or
  ' will start a new instance if one is not running.  The disadvantage of using
  ' CreateObject, is the caller does not know if Outlook was running so does not know
  ' whether or not to quit Outlook when it has finished using Outlook.  By setting
  ' Created, this routine allows the caller to only quit if this is appropriate.

  Set OutAppGetCreate = Nothing
  On Error Resume Next
  Set OutAppGetCreate = GetObject(, "Outlook.Application")
  On Error GoTo 0
  If OutAppGetCreate Is Nothing Then
    On Error Resume Next
    Set OutAppGetCreate = CreateObject("Outlook.Application")
    On Error GoTo 0
    If OutAppGetCreate Is Nothing Then
      Call MsgBox("I am unable to access Outlook", vbOKOnly)
      Exit Function
    End If
    Created = True
  Else
    Created = False
  End If

End Function

设置 myFolder = objNSpace.GetDefaultFolder(olFolderSentMail)

我无法在笔记本电脑上使用GetDefaultFolder。我有多个电子邮件地址,每个地址都有一个存储,外加默认存储“Outlook 数据”。这是向导创建的默认 Outlook 安装。这些商店中的每一个都有自己的收件箱和已发送文件夹。 “Outlook Data”中的空白是GetDefaultFolder返回的默认值。

如果您使用的是工作安装,您可能只有一个商店,并且默认发送的文件夹是您期望的那个。如果您担心收到错误的发送文件夹,请尝试Debug.Print myFolder.Parent.Name

工作簿(“2020 年 9 月 28 日具有 LOB 的 RIRQ 和 RRTN”)。激活 ThisWorkbook.Sheets("Sent_Email").Activate

标准建议是避免使用Activate。给出的主要原因是它是一种缓慢的方法。当然,如果您使用Activate 在工作簿或工作表之间切换,您可以显着增加宏的持续时间。如果您省略Application.ScreenUpdating = False,则尤其如此,因为每次更新当前工作表或切换工作簿或工作表时都会重新绘制屏幕。我避免使用Activate,因为它很难识别当前的工作簿和工作表。

如果你写:

With Workbooks("RIRQ and RRTNs with LOB Sept 28 2020").Activate
  Set MyRange =.Worksheets("Data") .Range(Cells(2, 1), Range("A1").End(xlDown))
End With
With ThisWorkbook
  With .Sheets("Sent_Email")
      :
    .Cells(iRows, 1) = objMail.Recipients(1)
      :
  End With
End With

您查看前面的With 语句以确定正在访问哪个工作表或工作簿。使用您的方法,您必须检查将当前工作簿或工作表更改为副作用的语句。您还必须担心子程序会更改当前工作簿或工作表。

.Range("A1").End(xlDown)

这相当于将光标定位到单元格 A1,然后单击 Ctrl+。如果A列数据中间有一个空白单元格,它将停在第一个空白单元格上方的单元格处。

End 的推荐用法是.Cells(Rows.Count,1).End(xlUp)。这将在最后一个非空白单元格处停止。

'Debug.Print MyRange.Address
'Debug.Print FiltRange.Address

我在开发过程中大量使用了 Debug.Print 和 Debug.Assert`,所以我同意了。但是,在我让代码工作之前,我不会将这些语句注释掉。

设置 FiltRange = MyRange.SpecialCells(xlCellTypeVisible)

这句话想要达到什么目的?您真的在 A 列中有隐藏单元格吗?你想丢失空白单元格吗?空白单元格可见。如果这是您想要的,可以消除空白单元格。

如果 MyRange 中的每个非空白单元格都是一个值,请使用:

Set FiltRange = MyRange.SpecialCells(xlCellTypeConstants)

如果 MyRange 中的每个非空白单元格都是公式,请使用:

Set FiltRange = MyRange.SpecialCells(xlCellTypeFormulas)

如果 MyRange 是值、公式和空格的混合体,请使用:

Union(MyRange.SpecialCells(xlCellTypeConstants), _
      MyRange.SpecialCells(xlCellTypeFormulas))

如果 InStr(LCase(Cells(iRows, 5)), cell.Value > 0) 和 _
InStr(LCase(Cells(iRows, 5)), LCase("GTPRM")) > 0 那么

我认为这会更清楚:

If InStr(LCase(objMail.Body), cell.Value > 0) And _
   InStr(LCase(objMail.Body), ("gtprm")) > 0 Then

或者更好:

LcBody = LCase(objMail.Body)       'Outside loop
   :
If InStr(LcBody, cell.Value > 0) And _
   InStr(LcBody, ("gtprm")) > 0 Then

只是在这种简化之后,我才注意到语句应该是:

If InStr(LcBody, cell.Value) > 0 And _
   InStr(LcBody, ("gtprm")) > 0 Then

If InStr(LCase(Cells(iRows, 5)), cell.Value) > 0 And 
   InStr(LCase(Cells(iRows, 5)), LCase("GTPRM")) > 0 Then 

【讨论】:

  • 首先,非常感谢您花时间处理我的请求并提供非常有用的提示,我使用以下语句 Set FiltRange = MyRange.SpecialCells(xlCellTypeVisible) 仅选择可见行(已过滤) ,在使用该语句之前,循环会遍历每一行
  • @Madjry SpecialCells(xlCellTypeVisible) 排除已隐藏的单元格。如果这是您希望的,它不会排除空的单元格。你在隐藏细胞吗? Debug.Print FiltRange.Address 是否显示了您期望的地址?我在 A1:A13 范围内的一些单元格中放置了值。 Debug.Print Range("A1:A13").Address 输出 $A$1:$A$13。然后我隐藏了第 10 行。Debug.Print Range("A1:A13").SpecialCells(xlCellTypeVisible).Address 输出 $A$1:$A$9,$A$11:$A$13 这是你得到的那种输出吗?
  • 正确,我只想遍历可见单元格而不是隐藏单元格。所以是的,我得到了类似的输出 $a$1:$A$9,$A$11,....跨度>
  • 我再次检查,我不明白为什么我的陈述永远不会正确: If InStr(LCase(Cells(iRows, 5)), cell.Value) > 0 And InStr(LCase(Cells( iRows, 5)), LCase("GTPRM")) > 0 然后.我要查找的字符串肯定在电子邮件正文中..我想现在我需要隔离一封我知道包含字符串和步骤的电子邮件再次通过我的代码
  • @Madjry 我不清楚 For Each cell In FiltRange 返回的 cell 的值,您认为应该排除这些值。您能否详细介绍一个无法按预期工作的特定示例?列出 A 列中的值和隐藏的行,并报告循环中使用了哪些值。
【解决方案2】:

我改进的代码正在做我需要的事情。

从我发送的电子邮件文件夹中提取数据并写入电子表格需要一些时间。

Private Sub Follow_Up_Update()

    'On Error GoTo ErrHandler

    ' Set Outlook application object.
    Dim objOutlook As Object
    Set objOutlook = CreateObject("Outlook.Application")

    Dim objNSpace As Object     ' Create and Set a NameSpace OBJECT.

    ' The GetNameSpace() method will represent a specified Namespace.
    Set objNSpace = objOutlook.GetNamespace("MAPI")

    Dim myFolder As Object  ' Create a folder object.

    Set myFolder = objNSpace.GetDefaultFolder(olFolderSentMail)

    Dim RangeObject As Range
    Dim objItem As Object
    Dim iRows, iCols As Integer
    Dim sFilter As String

    iRows = 2

    Dim MyRange As Range
    Dim cell As Range
    Dim WB As Workbook
    Dim FiltRange As Range
    Dim Lcbody As String
    Dim RIRQWorkbbok As Workbook
    Dim Sup_Number As Range
    Dim objMail As Outlook.MailItem
    Dim Sent_Email As Workbook
    Dim Data As Range
    Dim StringData As String
    Dim CellContent As Range
    Dim Range_SentEmail As Range
    Dim answer As Integer
    Dim sFilter_porfolio As String
    Dim answer_RiRQ As String

    'filter out data
    'Tracker_Filter

    Dim Range_RIRQ As Range

    'Set Sent_Email = ThisWorkbook

    Debug.Print ThisWorkbook.Name

    With ThisWorkbook.Worksheets("Sent_Email")
        .Activate
        Set Range_SentEmail = .Range("E2", Range("E2").End(xlDown))
    End With

    'Debug.Print Sent_Email.Address

    Application.ScreenUpdating = False

    'prompt user to select a file
    answer = MsgBox("Do you want to Open RIRQ/RRTN tracker?", vbYesNoCancel, "Select file")

    If answer = vbYes Then
        FileOpenDialogBox
    Else
        MsgBox "you haven't selected a file !"
        Exit Sub
    End If

    'Debug.Print ActiveWorkbook.Name

    'Tracker_Filter
    'activate the worksheet "Data" on the newly opened workbook

    With ActiveWorkbook.Worksheets("Data")
        .Activate
        'add a header to the column that will contain extracted data from the sent emails folder
        .Cells(1, 36).Value = "Reach outs Date"

        'filter only the RRTN and RIRQs programs
        Tracker_Filter

        'set a reference to the cells on the active sheet
        Set MyRange = .Range(Cells(2, 1), Range("A1").End(xlDown))
    End With

    'set a reference to the visible cells on filtered activesheet
    Set FiltRange = MyRange.SpecialCells(xlCellTypeVisible)

    'Debug.Print FiltRange.Address

    'create a filter based on folder categorie "not Completed" and subject line "Action required: Portfolio Reassessment"

    sFilter = "[Categories] = 'Not Completed' And [Subject] = 'Action required: Portfolio Reassessment'"

    sFilter_porfolio = "[Categories] = 'Not Completed' And [Subject] = 'Urgent: SEMS Portfolio Reassessment: Phase 1'"

    'Debug.Print sFilter

    'loop through the sent emails folder and write the data to the sheet called "Sent_Email"
    If ThisWorkbook.Sheets("Sent_Email").Cells(2, 1) = "" Then

        For Each objItem In myFolder.Items.Restrict(sFilter)

            If objItem.Class = olMail Then

                Set objMail = objItem
                With ThisWorkbook.Sheets("Sent_Email")
                    .Cells(iRows, 1) = objMail.Recipients(1)
                    '.Cells(iRows, 2) = objMail.To
                    .Cells(iRows, 3) = objMail.Subject
                    .Cells(iRows, 4) = objMail.ReceivedTime
                    .Cells(iRows, 5) = objMail.Body
                    .Cells(iRows, 6) = ResolveDisplayNameToSMTP(objMail.Recipients(1))

                    'End With
                End With
            End If

            iRows = iRows + 1

        Next objItem

    End If

    'ask user if they want to extract dates when emails were seent
    answer_RiRQ = MsgBox("Do you want to update RRTN/RRIQs reach outs dates?", vbYesNoCancel, "RRTN/RIRQ")

    If ThisWorkbook.Sheets("RIRQ-RRTN_emails").Cells(2, 1) = "" Then

        If answer = vbYes Then

            iRows = 2

            For Each objItem In myFolder.Items.Restrict(sFilter_porfolio)

                If objItem.Class = olMail Then

                    Set objMail = objItem

                    With ThisWorkbook.Sheets("RIRQ-RRTN_emails")
                        .Cells(iRows, 1) = objMail.Recipients(1)
                        '.Cells(iRows, 2) = objMail.To
                        .Cells(iRows, 3) = objMail.Subject
                        .Cells(iRows, 4) = objMail.ReceivedTime
                        .Cells(iRows, 5) = objMail.Body
                        .Cells(iRows, 6) = ResolveDisplayNameToSMTP(objMail.Recipients(1))

                        'End With
                     End With
                 End If

                 iRows = iRows + 1

            Next objItem

        Else

             MsgBox "script will continue to run!"

        End If

    End If

    'activate the worksheet where the data was written based on the filter : sFilter_porfolio

    With ThisWorkbook.Worksheets("RIRQ-RRTN_emails")
        .Activate

        'set a reference to the cells that contain data extracted based on filter called sFilter_porfolio

        Set Range_RIRQ = .Range("E2", Range("E2").End(xlDown))

    End With    

    For Each cell In FiltRange

        cell.Offset(, 12).Value = Trim(cell.Offset(, 12).Value)

        'write to the main sheet based on the condition
        For Each Data In Range_RIRQ
            If InStr(Data.Value, cell.Value) > 0 Then
                cell.Offset(, 36).Value = "Esccalation note sent on " & Data.Offset(, -1).Value
                'Debug.Print cell.Offset(, 35).Value
                'Debug.Print cell.Offset(, 35)
            End If
        Next Data

        'Next cell

        Application.ActiveSheet.Columns("AK:AK").AutoFit

        'End If

        For Each Data In Range_SentEmail
            'write to the main sheet based on the condition
            If InStr(Data.Value, cell.Value) > 0 Then
                cell.Offset(, 35).Value = "first communication sent on " & Data.Offset(, -1).Value          
            End If
        Next Data

    Next cell

    Application.ActiveSheet.Columns("AJ:AJ").AutoFit

    Application.ScreenUpdating = True

    Set objMail = Nothing

    ' Release.
    Set objOutlook = Nothing
    Set objNSpace = Nothing
    Set myFolder = Nothing

'ErrHandler:

    'Debug.Print Err.Description

End Sub

【讨论】:

  • @tony,我从上面改进的代码正在做我需要的事情。因此需要一些时间(运行时间)从我发送的电子邮件文件夹中提取数据并写入电子表格
猜你喜欢
  • 1970-01-01
  • 2018-03-11
  • 2010-11-20
  • 1970-01-01
  • 2017-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多