是否可以组合两个PrintDocuments或... [英] Is it possible to combine two PrintDocuments or...

查看:44
本文介绍了是否可以组合两个PrintDocuments或...的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

将它们添加到一个PrintDocument中:

PrintDocument pd1 = new PrintDocument();

PrintDocument pd2 = new PrintDocument();


PrintDocument pdCombined = new PrintDocument();

pdCombined = pd1 + pd2;

pdCombined.Print();

谢谢,

Trint

add them into one PrintDocument:
PrintDocument pd1 = new PrintDocument();
PrintDocument pd2 = new PrintDocument();

PrintDocument pdCombined = new PrintDocument();
pdCombined = pd1 + pd2;
pdCombined.Print();
Thanks,
Trint

推荐答案

你有什么大问题?你不能像你描述的那样直接这样做,但PrintDocument对象实际上并不包含要打印的

信息。它依赖于你的

应用程序中的一些外部对象来响应其PrintPage事件并将每个页面绘制为需要




其中你的两个PrintDocuments pd1和pd2来自哪里?例如,

如果它们是来自两个文件的两个输入流,那么制作组合流应该是一个简单的问题。从另一个读取,然后从另一个读取
,并将其提供给您的印刷类。


换句话说,我知道如何将两个结合起来在他们被制作成后,将文件合并为一个

PrintDocuments,但您应该能够以前执行此操作,并且只创建一个PrintDocument。当然,所有

取决于实际页面渲染的位置....

那么,你想要做什么,在更高的层次?

What is your larger problem? You can''t do this directly as you
described, but a PrintDocument object doesn''t actually contain the
information to print. It relies on some outside object in your
application responding to its PrintPage events and drawing each page as
needed.

Where are your two PrintDocuments pd1 and pd2 coming from? For example,
if they are two input streams from two files, it should be a simple
matter to make a "combined stream" that reads from one and then from
the other, and feed that to your printing class.

In other words, I know of know way to combine two documents into one
after they''ve been "made into" PrintDocuments, but you should be able
to do it before, and create only one PrintDocument. Of course, that all
depends upon where the actual page rendering is taking place....
So, what are you trying to do, at a higher level?


Bruce,

我正在尝试这样做:

将我的副本值设置为3

开始第一个副本,纸质来源(托盘)为4

,当第一个副本呈现时,设置纸张来源为

第二个副本来来自纸张来源5.

当呈现第二个副本时,将纸张来源设置为来自6并且

就是全部。因此,当打印机实际开始打印时,它已被发送,它会打印三份副本;每张选定纸张的一份副本

托盘(因为我们需要一份白皮书副本,一份黄色纸和一份粉红色副本)。

这是我目前的代码:

class PrintReporttoLaserjet:Form1

{

PrintDocument pd = new PrintDocument();

// PrintReporttoLaserjet NeWrs = new PrintReporttoLaserjet();


// Form1 Form1Form = new Form1();

// Form1Form.ReportingService rs = new ReportingService();

private byte [] [] m_renderedReport;

private Graphics.EnumerateMetafileProc m_delegate = null;

private MemoryStream m_currentPageStream;

private元文件m_metafile = null;

int m_numberOfPages;

private int m_currentPrintingPage;

private int m_lastPrintingPage;


public PrintReporttoLaserjet()

{

//创建代理对象并进行身份验证

//Console.WriteLine("Authenticating to the Web服务...");

rs = new ReportingService();

rs.Credentials = System.Net.CredentialCache.DefaultCredentials;

}


public byte [] [] RenderReport(string reportPath)

{

//渲染的私有变量

string deviceInfo = null;

string format =" IMAGE";

Byte [] firstPage = null;

字符串编码;

string mimeType;

警告[] warnings = null;

ParameterValue [] reportHistoryParameters = null;

string [] streamIDs = null ;

Byte [] [] pages = null;

/// mine

//根据起始页面构建设备信息


deviceInfo =

String.Format(@"< DeviceInfo>" +

"< OutputFormat> {0}< / OutputFormat>" +

"< Toolbar> False< / Toolbar>" +

"< PageHeight> 55.82cm< / PageHeight>" +

"< PageWidth> 40.26cm< / PageWidth>< / DeviceInfo>"," emf");


//准备报告参数。

ParameterValue [] parameters = new ParameterValue [1];

parameters [0] = new ParameterValue();

参数[ 0] .Name =" InvoiceNumber";

parameters [0] .Value = Class1.inVoiceNumber;

//parameters[0].Value =" 5440517" ;

// Exectute报告并获取页数。

尝试

{

//呈现第一个报告的页面并返回的流ID为

//后续页面

// firstPage = rs.Render(

firstPage = rs.Render(

reportPath,

格式,

null,

deviceInfo,

参数,

null,

null,
out encoding,

out mimeType,

out reportHistoryParameters,

out警告,

out streamIDs);


//报告的总页数是1 + streamID

m_numberOfPages = streamIDs.Length + 1;

pages = new Byte [m_numberOfPages] [];


//第一页已经呈现

pages [0] = firstPage;


for(int pageIndex = 1; pageIndex< m_numberOfPages; pageIndex ++)

{

//基于起始页面构建设备信息

deviceInfo =

String.Format( @"< DeviceInfo>< OutputFormat> {0}< / OutputFormat>< Star

tPage> {1}< / StartPage>< / DeviceInfo>"," emf",pageIndex + 1);


页面[pageIndex] = rs.Render(

reportPath,

格式,

null,

deviceInfo,

参数,

null,

null,

out编码,

out mimeType,

out reportHistoryParameters,

out警告,

out streamIDs);

}

}


catch(SoapException ex)

{

//Console.WriteLine(ex.Detail.InnerXml);

使用(StreamWriter sw = new StreamWriter(" TestFile.txt"))

{

sw.Write(ex.Detail.InnerXml);

}


}

终于

{

//Console.WriteLine(& t;页数:{0}",pages.Length);

}


返回页面;

}


public bool PrintReport(string printerName)

{

int PDcounter;


// for(PDcounter = 0; PDcounter< = 2; Class1.counter ++)

// {

this.RenderedReport = this.RenderReport(@" / Report

PackingList1 / Report1");

// D:\Inetpub \ www.root \PackingList \Report Project1 \printtest

//报告/ PackingList1 / Report1

PrintReporttoLaserjet f1 = new PrintReporttoLaserjet();


try

{

//等待报告完全呈现。

if(m_numberOfPages< 1)

返回false;

PrinterSettings printerSettings = new PrinterSettings();

printerSettings .MaximumPage = m_numberOfPages;

printerSettings.MinimumPage = 1;

printerSettings.PrintRange = PrintRange.SomePages;

printerSettings.FromPage = 1;

printerSettings.ToPage = m_numberOfPages;

printerSettings.Copies = 3;

printerSettings.PrinterName = printerName;


pd.DefaultPageSettings.PaperSource =

pd.PrinterS ettings.PaperSources [comboBox1.SelectedIndex = 4];


m_currentPrintingPage = 1;

m_lastPrintingPage = m_numberOfPages;

pd.PrinterSettings = printerSettings;

//打印报告

pd.PrintPage + = new PrintPageEventHandler(this.pd_PrintPage);


try

{

///从托盘2打印

///从托盘3打印

///打印来自托盘4

//根据组合框中的选择设置纸张来源。

// pd.DefaultPageSettings.PaperSource =

// pd.PrinterSettings.PaperSources [comboBox1.SelectedIndex =

Class1.counter];


int pCounter = 0;

// while(pd.Print())

// {

// pCounter ++;

//

// if(printerSettings.Copies == 2)

// {

// pd.DefaultPageSettings.PaperSource =

// pd .PrinterSettings.PaperSources [组合框1.SelectedIndex = 5];

//

//}

// if(printerSettings.Copies == 1)

// {

// pd.DefaultPageSettings.PaperSource =

// pd.PrinterSettings.PaperSources [comboBox1.SelectedIndex = 6];

//

//}

//

// if(pCounter == 6)

//休息;

//

//}

pd.Print();

//Wmis.PStatus();

}

catch(例外ee)

{

MessageBox.Show (ee.Message);

}

终于

{

// PrintDirect.ClosePrinter(lhPrinter);

}


}


catch(例外情况)

{

MessageBox.Show(ex.Message);

}

终于

{

//清理就在这里。

}

// if(PDcounter == 2)

//休息;

//}

返回true;

}


private void pd_PrintPage(object sender,PrintPageEventArgs ev)

{

ev.HasMorePages = false;

if(m_currentPrintingPage< = m_lastPrintingPage&&

MoveToPage(m_currentPrintingPage))

{

//绘制页面

ReportDrawPage(ev.Graphics);

//如果下一页小于或等于最后一页,

//打印另一页。

if(++ m_currentPrintingPage< = m_lastPrintingPage)

ev.HasMorePages = true;

}

}


//绘制当前emf内存流的方法

private void ReportDrawPage(Graphics g)

{

if(null == m_currentPageStream || 0 == m_currentPageStream.Length ||

null == m_metafile)

return;

lock(this)

{

//设置图元文件委托。


int width = m_metafile.Width;

int height = m_metafile.Height ;

m_delegate = new Graphics.EnumerateMetafileProc(MetafileCallback);

//在矩形中绘制


Point [] points = new Point [3];

Point destPoint = new Point(0,0);

Point destPoint1 = new Point(width,0);

Point destPoint2 = new Point(0,height);


points [0] = destPoint;

points [1] = destPoint1;

points [2] = destPoint2;

g.EnumerateMetafile(m_metafile,points,m_delegate);


//g.EnumerateMetafile(m_metafile ,destPoint,m_delegate);

//清理

m_delegate = null;

}

}


private bool MoveToPage(Int32页)

{

//检查以确保电流t页面存在于

//数组列表

if(null == this.RenderedReport [m_currentPrintingPage-1])

返回false;

//设置当前页面流等于渲染页面

m_currentPageStream = new

MemoryStream(this.RenderedReport [m_currentPrintingPage-1]);

//设置它的位置开始。

m_currentPageStream.Position = 0;

//初始化元文件

if(null!= m_metafile)

{

m_metafile.Dispose();

m_metafile = null;

}

//加载此页面的图元文件图像

m_metafile = new图元文件((流)m_currentPageStream);

返回true; < br $>
}


私人布尔元MetafileCallback(

EmfPlusRecordType recordType,

int flags,

int dataSize,
IntPtr数据,

PlayRecordCallback callbackData)

{

byte [] dataArray = null;

//在unma周围跳舞naged code。

if(data!= IntPtr.Zero)

{

//将非托管记录复制到托管字节缓冲区

//可以由PlayRecord使用。

// dataArray =新字节[dataSize];

dataArray =新字节[dataSize];

Marshal.Copy(data,dataArray,0,dataSize);

}

//播放记录。

m_metafile.PlayRecord(recordType,flags,dataSize,dataArray);


返回true;

}


public byte [] [] RenderedReport

{

get

{

return m_renderedReport;

}

套装

{

m_renderedReport = value;

}

}

}


谢谢,

Trint


..Net程序员
tr***********@gmail.com


***通过开发人员指南发送 http:// www.developersdex.com ***

不要只参加USENET ......获得奖励!
Bruce,
I am trying to do this:
Set my copies value to 3
start the first copy with a paper source (tray) as 4
when first copy is rendered, set paper source for
second copy to come from paper source 5.
When second copy is rendered, set paper source to come from 6 and that
is all. So that when printer actually begins the printjob it has been
sent, it prints three copies; one copy from each of the selected paper
trays (since we need a white paper copy, a yellow one and a pink one).
Here is my code so far:
class PrintReporttoLaserjet: Form1
{
PrintDocument pd = new PrintDocument();
// PrintReporttoLaserjet NeWrs = new PrintReporttoLaserjet();

// Form1 Form1Form = new Form1();
// Form1Form.ReportingService rs = new ReportingService();
private byte[][] m_renderedReport;
private Graphics.EnumerateMetafileProc m_delegate = null;
private MemoryStream m_currentPageStream;
private Metafile m_metafile = null;
int m_numberOfPages;
private int m_currentPrintingPage;
private int m_lastPrintingPage;

public PrintReporttoLaserjet()
{
// Create proxy object and authenticate
//Console.WriteLine("Authenticating to the Web service...");
rs = new ReportingService();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
}

public byte[][] RenderReport(string reportPath)
{
// Private variables for rendering
string deviceInfo = null;
string format = "IMAGE";
Byte[] firstPage = null;
string encoding;
string mimeType;
Warning[] warnings = null;
ParameterValue[] reportHistoryParameters = null;
string[] streamIDs = null;
Byte[][] pages = null;
///mine
// Build device info based on the start page

deviceInfo =
String.Format(@"<DeviceInfo>" +
"<OutputFormat>{0}</OutputFormat>" +
"<Toolbar>False</Toolbar>" +
"<PageHeight>55.82cm</PageHeight>" +
"<PageWidth>40.26cm</PageWidth></DeviceInfo>","emf");

// Prepare report parameter.
ParameterValue[] parameters = new ParameterValue[1];
parameters[0] = new ParameterValue();
parameters[0].Name = "InvoiceNumber";
parameters[0].Value = Class1.inVoiceNumber;
//parameters[0].Value = "5440517";
//Exectute the report and get page count.
try
{
// Renders the first page of the report and returns streamIDs for
// subsequent pages
//firstPage = rs.Render(
firstPage = rs.Render(
reportPath,
format,
null,
deviceInfo,
parameters,
null,
null,
out encoding,
out mimeType,
out reportHistoryParameters,
out warnings,
out streamIDs);

// The total number of pages of the report is 1 + the streamIDs
m_numberOfPages = streamIDs.Length + 1;
pages = new Byte[m_numberOfPages][];

// The first page was already rendered
pages[0] = firstPage;

for (int pageIndex = 1; pageIndex < m_numberOfPages; pageIndex++)
{
// Build device info based on start page
deviceInfo =
String.Format(@"<DeviceInfo><OutputFormat>{0}</OutputFormat><Star
tPage>{1}</StartPage></DeviceInfo>","emf", pageIndex+1);

pages[pageIndex] = rs.Render(
reportPath,
format,
null,
deviceInfo,
parameters,
null,
null,
out encoding,
out mimeType,
out reportHistoryParameters,
out warnings,
out streamIDs);
}
}

catch (SoapException ex)
{
//Console.WriteLine(ex.Detail.InnerXml);
using (StreamWriter sw = new StreamWriter("TestFile.txt"))
{
sw.Write(ex.Detail.InnerXml);
}

}
finally
{
//Console.WriteLine("Number of pages: {0}", pages.Length);
}

return pages;
}

public bool PrintReport(string printerName)
{
int PDcounter;

// for (PDcounter = 0; PDcounter <= 2; Class1.counter++)
// {
this.RenderedReport = this.RenderReport(@"/Report
PackingList1/Report1");
//D:\Inetpub\wwwroot\PackingList\Report Project1\printtest
//Report/PackingList1/Report1
PrintReporttoLaserjet f1 = new PrintReporttoLaserjet();

try
{
// Wait for the report to completely render.
if(m_numberOfPages < 1)
return false;
PrinterSettings printerSettings = new PrinterSettings();
printerSettings.MaximumPage = m_numberOfPages;
printerSettings.MinimumPage = 1;
printerSettings.PrintRange = PrintRange.SomePages;
printerSettings.FromPage = 1;
printerSettings.ToPage = m_numberOfPages;
printerSettings.Copies = 3;
printerSettings.PrinterName = printerName;

pd.DefaultPageSettings.PaperSource =
pd.PrinterSettings.PaperSources[comboBox1.SelectedIndex = 4];

m_currentPrintingPage = 1;
m_lastPrintingPage = m_numberOfPages;
pd.PrinterSettings = printerSettings;
// Print report
pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);

try
{
///Print from tray 2
///Print from tray 3
///Print from tray 4
// Set the paper source based upon the selection in the combo box.
// pd.DefaultPageSettings.PaperSource =
// pd.PrinterSettings.PaperSources[comboBox1.SelectedIndex =
Class1.counter];

int pCounter = 0;
// while (pd.Print())
// {
// pCounter++;
//
// if(printerSettings.Copies == 2)
// {
// pd.DefaultPageSettings.PaperSource =
// pd.PrinterSettings.PaperSources[comboBox1.SelectedIndex = 5];
//
// }
// if(printerSettings.Copies == 1)
// {
// pd.DefaultPageSettings.PaperSource =
// pd.PrinterSettings.PaperSources[comboBox1.SelectedIndex = 6];
//
// }
//
// if(pCounter == 6)
// break;
//
// }
pd.Print();
//Wmis.PStatus();
}
catch(Exception ee)
{
MessageBox.Show(ee.Message);
}
finally
{
// PrintDirect.ClosePrinter(lhPrinter);
}

}

catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// Clean up goes here.
}
// if(PDcounter == 2)
// break;
// }
return true;
}

private void pd_PrintPage(object sender, PrintPageEventArgs ev)
{
ev.HasMorePages = false;
if (m_currentPrintingPage <= m_lastPrintingPage &&
MoveToPage(m_currentPrintingPage))
{
// Draw the page
ReportDrawPage(ev.Graphics);
// If the next page is less than or equal to the last page,
// print another page.
if (++m_currentPrintingPage <= m_lastPrintingPage)
ev.HasMorePages = true;
}
}

// Method to draw the current emf memory stream
private void ReportDrawPage(Graphics g)
{
if(null == m_currentPageStream || 0 == m_currentPageStream.Length ||
null ==m_metafile)
return;
lock(this)
{
// Set the metafile delegate.

int width = m_metafile.Width;
int height= m_metafile.Height;
m_delegate = new Graphics.EnumerateMetafileProc(MetafileCallback);
// Draw in the rectangle

Point[] points = new Point[3];
Point destPoint = new Point(0, 0);
Point destPoint1 = new Point(width, 0);
Point destPoint2 = new Point(0, height);

points[0]=destPoint;
points[1]=destPoint1;
points[2]=destPoint2;
g.EnumerateMetafile(m_metafile,points, m_delegate);

//g.EnumerateMetafile(m_metafile,destPoint , m_delegate);
// Clean up
m_delegate = null;
}
}

private bool MoveToPage(Int32 page)
{
// Check to make sure that the current page exists in
// the array list
if(null == this.RenderedReport[m_currentPrintingPage-1])
return false;
// Set current page stream equal to the rendered page
m_currentPageStream = new
MemoryStream(this.RenderedReport[m_currentPrintingPage-1]);
// Set its postion to start.
m_currentPageStream.Position = 0;
// Initialize the metafile
if(null != m_metafile)
{
m_metafile.Dispose();
m_metafile = null;
}
// Load the metafile image for this page
m_metafile = new Metafile((Stream)m_currentPageStream);
return true;
}

private bool MetafileCallback(
EmfPlusRecordType recordType,
int flags,
int dataSize,
IntPtr data,
PlayRecordCallback callbackData)
{
byte[] dataArray = null;
// Dance around unmanaged code.
if (data != IntPtr.Zero)
{
// Copy the unmanaged record to a managed byte buffer
// that can be used by PlayRecord.
// dataArray = new byte[dataSize];
dataArray = new byte[dataSize];
Marshal.Copy(data, dataArray, 0, dataSize);
}
// play the record.
m_metafile.PlayRecord(recordType, flags, dataSize, dataArray);

return true;
}

public byte[][] RenderedReport
{
get
{
return m_renderedReport;
}
set
{
m_renderedReport = value;
}
}
}

Thanks,
Trint

..Net programmer
tr***********@gmail.com

*** Sent via Developersdex http://www.developersdex.com ***
Don''t just participate in USENET...get rewarded for it!


据我所知你不能这样做。


每个打印作业(PrintDocument)都是一个对象,它指定了它的b / b
纸的位置正在被喂食,无论是使用双工,还是那样的东西。

一份工作有一套属性。


当你发送三份副本时打印机,PC没有三次发送工作

,或者监控工作进度。它将作业发送一次

并要求打印机制作三份副本。 (这适用于大多数现代的b $ b /激光打印机。没有复杂软件的旧款打印机可能会以不同的方式工作。不管怎样,驱动程序都是为了你的
$而设计的。 b $ b软件既不知道也不关心如何制作副本。)

如果您的打印机在打印服务器上,则可以打印作业,并且
$ b在您的申请终止之后很久就可以创建$ b副本。


一种方法可以创建三个单独的打印作业:一个

喂养来自纸张来源4,一个来自纸质来源5,另一个来自

纸张来源6.不幸的是,这创造了三个单独的打印作业,

,并不保证按顺序打印,并且不会交错你的

彩色页面,这可能是你想要的。 (你可能想要

第一页白色,黄色,粉红色,然后第二页白色,黄色,

粉红色等等)


为了很好地完成这项工作,你需要做的就是改变pd_PrintPage。


首先,你需要一个pd_PrintPage的计数器,这样才能代替

每次递增m_currentPrintingPage,你将

计数器增加到3,然后递增m_currentPrintingPage。所以,实际上

调用pd_PrintPage如下所示:


pd_PrintPage:m_currentPrintingPage 1,copy 1

pd_PrintPage:m_currentPrintingPage 1 ,复制2

pd_PrintPage:m_currentPrintingPage 1,copy 3

pd_PrintPage:m_currentPrintingPage 2,copy 1

pd_PrintPage:m_currentPrintingPage 2,copy 1

pd_PrintPage:m_currentPrintingPage 2,copy 1




所以,现在你已经为每个每个副本调用了一次pd_PrintPage br />
页面。 (当然,你可以摆脱你的设置

printerSettings.Copies = 3,因为你现在正在制作副本

手动在pd_PrintPage中。)


现在,您会注意到pd_PrintPage收到了PrintPageEventAr * gs

类。这个类的成员之一是PageSettings,在

里面你会找到PaperSource。在pd_PrintPage里面只需这样做:


开关(m_copy)

{

案例1:

ev.PageSettings.PaperSource = ...;

休息;

案例2:

ev.PageSettings.PaperSource = ...;

休息;

案例3:

ev.PageSettings.PaperSource = ...;

break;

默认值:

休息;

}


现在您将纸张来源设置为另一个托盘每个

页面的每个副本。

So far as I know you can''t do it that way.

Each print job (PrintDocument) is an object that specifies where its
paper is being fed from, whether it is using duplex, stuff like that.
One job has one set of attributes.

When you send three copies to a printer, the PC doesn''t send the job
three times, or monitor the progress of the job. It sends the job once
and asks the printer to make three copies. (This applies to most modern
/ laser printers. Older printers without sophisticated software may
work differently. Regardless, the drivers are all designed so that your
software neither knows nor cares how the copies are being made.)

If your printer is on a print server then the job may print, and the
copies may be created, long after your application terminates.

One way to do what you want is to create three separate print jobs: one
feeding from paper source 4, one from paper source 5, and one from
paper source 6. Unfortunately, this creates three separate print jobs,
which aren''t guaranteed to print in sequence, and won''t interleave your
coloured pages, which is probably what you want. (You probably want the
first page white, yellow, pink, then the second page white, yellow,
pink, etc.)

To do the job up nicely, what you need to do is change pd_PrintPage.

First, you need a counter for pd_PrintPage, so that instead of
incrementing m_currentPrintingPage every time, you increment the
counter up to 3, then increment m_currentPrintingPage. So, in effect
calls to pd_PrintPage look like this:

pd_PrintPage: m_currentPrintingPage 1, copy 1
pd_PrintPage: m_currentPrintingPage 1, copy 2
pd_PrintPage: m_currentPrintingPage 1, copy 3
pd_PrintPage: m_currentPrintingPage 2, copy 1
pd_PrintPage: m_currentPrintingPage 2, copy 1
pd_PrintPage: m_currentPrintingPage 2, copy 1
etc.

So, now you have pd_PrintPage being called once for each copy of each
page. (Of course, you can get rid of your setting
printerSettings.Copies = 3, because you''re now making the copies
"manually" inside pd_PrintPage.)

Now, you''ll notice that pd_PrintPage receives a PrintPageEventAr*gs
class. One of the members of this class is PageSettings, and inside
there you''ll find PaperSource. Inside pd_PrintPage just do this:

switch (m_copy)
{
case 1:
ev.PageSettings.PaperSource = ... ;
break;
case 2:
ev.PageSettings.PaperSource = ... ;
break;
case 3:
ev.PageSettings.PaperSource = ... ;
break;
default:
break;
}

Now you set the paper source to a different tray for each copy of each
page.


这篇关于是否可以组合两个PrintDocuments或...的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆