首页 > web前端 > js教程 > 如何构建Trello Chrome扩展名 - 导出列表

如何构建Trello Chrome扩展名 - 导出列表

William Shakespeare
发布: 2025-02-20 11:23:13
原创
476 人浏览过

如何构建Trello Chrome扩展名 - 导出列表

在上一部分中,我们构建了扩展的基础知识,通过自定义基础供电的设置屏幕实现身份验证,并使用Trello的JavaScript客户端库来构建了身份验证。在这一部分中,我们将通过添加导出逻辑和UI来完成扩展。

>

钥匙要点

    >使用`chrome.extension.sendMessage`API进行安全令牌传输之间的“设置”页面和Chrome Extension的背景页面之间
  • >在初始安装时或在没有身份验证的情况下访问trello板时自动打开扩展程序的设置页面。
  • >
  • >在Trello的UI中集成自定义菜单选项以启用列表导出,使用jQuery处理动态DOM更改。
  • >通过涉及检查列表中的卡元素的解决方案来检索trello列表ID,这是由于trello ui限制而需要的。
  • >在获取列表ID后,在背景脚本中传递消息以从Trello的API中获取列表卡。>
  • >使用定制的模态弹出窗口以显示导出数据,并规避CSS与Trello的本机样式冲突。
  • >
  • 消息传递
  • >当我们在设置屏幕上使用Trello进行身份验证时,Trello令牌将保存在本地存储中。但是,设置页面是其自己的页面,有效地是其自己的环境 - Ergo,扩展程序的背景页面或扩展程序的内容脚本都无法访问它。这是我们需要使用消息传递的地方。

chrome.extension.sendmessage API用于从背景页面发送消息。在我们的情况下,我们将使用它将令牌从设置页面发送到我们的背景页面。 由于我们的设置冒险就完成后就完成了,因此我们不妨自动关闭选项卡,以提高用户友好。

>

>在设置中更新初始函数的第一部分。

使用此逻辑,我们告诉Trello库在身份验证完成时向扩展发送消息,并且一旦收到返回消息,说明消息已收到(这是功能(数据)部分),我们就会关闭该消息当前选项卡。

>现在让我们处理背景页面。首先,将背景内容的内容更改为:

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

>我们像以前一样加载应用程序密钥,我们将用于逻辑的背景脚本以及Trello客户端。显然,我们也需要jQuery - 这是Trello的依赖。

> 然后,将脚本/background.js更改为:

<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
登录后复制
登录后复制
登录后复制
登录后复制
这是从设置页面接收消息的部分。它从请求中获取令牌,并将其保存到LocalStorage中以供将来使用。我们将对象形成与命令用作主要密钥,因为我们打算稍后将其他命令发送到背景页面。

>自动安装

在Savetkoken命令上方,让我们另一个块:

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
如果我们没有发出特定命令,并且我们的用户尚未使用Trello进行身份验证,请在新选项卡中打开“设置”页面。这确保在扩展程序初始安装后立即打开“设置”页面,一旦在浏览器中访问了trello板。

>

添加菜单选项

Trello的UI对自定义非常非常友好。这些列表在其元素中没有ID,无论是数据属性还是任何形式的链接,而卡片则没有。单击右上角的“列表选项”按钮时产生的上下文菜单每次被称为(过重的杀伤性?),而全部从一个盒子中归类为“ pop-over”,这是如果您几乎单击UI中的所有其他菜单,也可以召唤。更糟糕的是,一旦您调用了列表的弹出菜单,菜单本身就没有列出的列表的标识符,因此您对其上下文一无所知,这使得很难轻松获取列表的ID,以便正确查询trello api围绕它,并获取出口卡。这就是为什么接下来的事情看起来像是很多可怕的黑客,但这是因为,

>要将菜单选项添加到上下文菜单,我们需要编辑main.js内容脚本。将其变成:

>

>从var popover = $(“。pop-over”);,我们设置了一个变量以容纳弹出对象,这样我们就不必继续重新提出它。然后,当单击列表上的菜单按钮(.list-header-menu-icon)时,我们召唤一个间隔,不断注意是否可见弹出声。一旦可见,检查停止和菜单选项就会附加到所有选项的底部,专门设计为其余的,因此适合。最后,单击事件处理程序绑定到此选项,以便我们可以单击选项时调用“导出”。但是..我们怎么知道我们需要出口什么?我们以哪种格式导出?
<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
登录后复制
登录后复制
登录后复制
登录后复制

查找列表ID

就像我之前说过的,Trello的UI众所周知,开发人员不友好。它没有提供带有DOM元素的列表ID,因此找到它们的ID远不及直接。为什么我们需要列表ID?要查询trello api并获取卡片,以便我们可以导出它们 - 我们已经说,由于它在大型板上的不稳定性,我们不会在UI上解析,但会依靠API。

>幸运的是,如果我们在各个列表中检查卡片,我们可以看到实际上确实具有HREF属性,并且它包含卡ID。通过了解卡ID,我们可以查询Trello的信息并获取其父列表的ID。但是..如果弹出菜单未连接到列表中,我们如何查找单击的列表?我们不能只抓住我们遇到的第一张卡片,那太随机了。

>

>当单击菜单按钮时,我们可以使用jQuery发射的事件。这很重要!我们使用原始单击菜单按钮,而不是单击“导出”选项,因为当原始按钮绑定到我们有兴趣导出的列表时,所产生的实际菜单不是,因此,使我们几乎不可能找出我们要处理的列表。代替上述代码中的//导出列表注释,添加以下内容:

>
<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
然后,创建函数:

<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
登录后复制
登录后复制
登录后复制
登录后复制
最后,创建FindfirstCardid函数:

chrome<span>.extension.onMessage.addListener(
</span>    <span>function (request<span>, sender, sendResponse</span>) {
</span>        chrome<span>.pageAction.show(sender.tab.id);
</span>
        <span>// Now we have a token saved locally, as fetched from the settings page after authorization.
</span>        <span>if (request.command == 'saveToken') {
</span>            <span>localStorage.setItem('trello_token', request.token);
</span>            <span>sendResponse();
</span>            <span>return true;
</span>        <span>}
</span>
    <span>});</span>
登录后复制
>我们获取了活动目标的祖父母(列表),并在其中找到第一个卡标题。标题包含以下形状的HREF:

>

如何构建Trello Chrome扩展名 - 导出列表

如果找不到标题,我们会提醒用户列表无法导出。否则,我们提取并返回卡的ID。

>现在,我们的导出列表功能具有卡ID,我们可以使用它来找出列表ID。如果查看API文档,我们可以使用URL卡/{{id}}来获取我们需要的东西。为了最大程度地减少我们要求Trello返回的数据量,我们还可以将查询限制在带有字段参数的iDlist属性中。让我们在背景中添加一个新命令。

我们也需要定义trelloinit函数。我们可以在调用与Trello交互的命令之前每次都可以打电话,因此设置了令牌和密钥,我们100%确定我们的请求已得到认证。
<span>if (!request.command && !localStorage.getItem('trello_token')) {
</span>            chrome<span>.tabs.create({url: chrome.extension.getURL('settings/index.html')});
</span>            <span>sendResponse();
</span>            <span>return true;
</span>        <span>}</span>
登录后复制

我们现在正在成功获取列表ID。

chrome<span>.extension.sendMessage({}, function (response) {
</span>    <span>var readyStateCheckInterval = setInterval(function () {
</span>        <span>if (document.readyState === "complete") {
</span>            <span>clearInterval(readyStateCheckInterval);
</span>
            <span>var popover = $(".pop-over");
</span>            <span>$('.list-header-menu-icon').click(function(event) {
</span>                <span>var popover_summoned_interval = setInterval(function () {
</span>                    <span>if ($(popover).is(':visible')) {
</span>                        <span>clearInterval(popover_summoned_interval);
</span>                        <span>$(".pop-over .content").append('<hr><ul > <li><a  href="#">Export This List</a></li> </ul>');
</span>                        <span>$(".js-export-list").click(function(e){
</span>                            <span>// EXPORT LIST
</span>                        <span>});
</span>                    <span>}
</span>                <span>}, 50);
</span>            <span>});
</span>        <span>}
</span>    <span>}, 10);
</span><span>});</span>
登录后复制
获取列表卡

>带有另外几行代码,返回Main.js,我们现在拥有一个看起来像这样的导出函数:>

在“人类”中,这是:

获取第一张卡的ID
<span>exportList(event);</span>
登录后复制

如果找不到ID,列表显然为空

    如果找到ID,请致电背景页面,并告诉我们通过呼叫Trello API
  • 给我们列表ID
  • 如果列表ID很好,请再次呼叫背景页面获取列表的卡,然后在控制台中输出结果。
  • >回到背景页面,我们现在可以根据API文档构建GetListCards命令。
  • >如果您现在重新加载并测试扩展程序,则不仅可以看到列表中的导出选项出现在您的列表中,而且还可以在单​​击该选项后在控制台中寻找导出的数据。
  • >
>导出格式

>目前,我们将采用一种简化的方法来导出,因为本教程的运行时间要长。我们将为用户提供TXT或JSON的选择,并具有形状和形式,并由我们预定。现在,文本输出将看起来像这样:
<span>function exportList(event) {
</span>
    <span>var first_card_id = findFirstCardId(event);
</span>    <span>if (!first_card_id) {
</span>        <span>alert('No cards found in the list.');
</span>        <span>return false;
</span>    <span>}
</span><span>}</span>
登录后复制
>

> JSON将满足从Trello收到的满足,即:>

如何构建Trello Chrome扩展名 - 导出列表

显然,JSON数据将产生更多的信息,但是编辑也更容易 - 只需将其粘贴到任何IDE或JSON Editor Online或JSON之类的工具中,您就可以使用。 >

要导出,我们需要一个模态窗口来粘贴数据。 这里的一个吸引人的选择是基础框架,因为我们已经在设置页面上使用了它,并且它具有自己的模态弹出组件,但是Foundation的和Trello的CSS均未适当地命名为空位,并且在Trello的CSS中包括Trello的CSS引起冲突。我们也有jQuery预先包含的,但是要再次进行对话框,我们需要包括jQuery UI,甚至还不够 - Chrome Extensions不支持通过相对URL中的CSS加载图像( )语法,这是jQuery UI使用的 - 我们必须重写jQuery UI的CSS来使用本地扩展URL或使用Base64编码图像,都没有吸引力接近。

相反,我们将制作自己的弹出窗口,并使用Trello的一些现有样式,忽略一路上的所有冲突。我将最终代码放在这里,然后进行解释。请创建lib/trellohelper/js/exportpopup.js,并给出以下内容:>

>我选择在主脚本之外具有弹出逻辑,以便以后可以轻松地改进它。我还选择了一种“面向对象的”方法,只是因为我喜欢它。我们通过三种方法定义了一个新的TrelloexportPopup“ class”:INIT,显示和隐藏。内容脚本加载后,初始化将立即调用。这是负责构建弹出窗口,附加正确的事件听众并将整个内容添加到Trello板的HTML的方法。将.button类添加到弹出窗口标题的按钮上,请确保我们获得与当前Trello UI一致的外观。我要在这里寻找的外观是一种“选项卡”接口 - 单击文本,文本导出显示,单击JSON和JSON。

隐藏方法将隐藏弹出窗口,但前提是它以可见的形式存在于页面上的某个地方。 Show方法自动激活第一个(JSON)选项卡视图,并使用所需数据填充导出区域。 JSON区域是一个简单的Stringify转储 - JSON数据以字符串形式的输出,而文本区域目前仅在单独的行上输出该卡的标题和描述,在卡之间有两个空行 - 高度“复制 - 帕斯特友好”。

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
我们现在需要做的就是样式。这是lib/trellohelper/css/exportpopup.css的内容:

>确保弹出窗口是中心的,看起来像本机Trello弹出窗口。它还可以确保将向我们展示导出内容的文本方面填充了弹出窗口的其余空间。现在,让我们将这些文件包含在我们的内容脚本中:

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

>最后,让我们使用新的弹出逻辑来调整main.js。 main.js的最终版本看起来像这样:

<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
登录后复制
登录后复制
登录后复制
登录后复制

>我们首先“实例化”了TrelloexportPopup,因此我们可以在代码中使用其方法。然后,在将点击事件侦听器绑定到菜单之前,我们将使用tep.init()初始化弹出窗口,以便在需要之前就可以在DOM中进行准备。单击导出链接后,我们像以前一样称呼我们的导出函数。

>

>在导出列表函数中,我们首先使用tep.hide()隐藏弹出窗口,以防万一我们在浏览另一个列表的菜单时开放,然后,一旦我们从背景页面上获得卡,我们就会显示带有tep.show的导出弹出窗口(数据)。就是这样!

>现在重新加载扩展名,刷新Trello页面,您应该具有功能功能的导出选项!

>

如何构建Trello Chrome扩展名 - 导出列表

>错误和改进

>我故意留下了一些错误和警告。如果有足够的兴趣,我们将与以后的帖子中的那些人打交道,调整和优化故障安全的扩展。这是一些仍然可以的改进:

>

缓存

>为将来的用途加快速度,我们可以使用LocalStorage记住列表所属的板。请注意,如果您将列表从董事会转移到董事会,请仔细实现此功能 - 请确保添加另一个侦听器以进行列表移动,因此您可以将缓存数据无效!

重复导出选项spawns

>如果您在菜单仍打开时手动单击菜单图标,则将继续在菜单底部添加新的“导出”选项。需要实现故障安全,以检查该选项是否已经存在。

>

初始问题

在拥有数百个董事会和成员的巨大董事会上,Trello的UI变得非常慢。这会导致对文档准备就绪事件的失误,并且在有任何UI元素可以绑定听众之前,我们的脚本的初始部分执行。因此,菜单有时没有导出选项,并且在刷新之前才能获得。

>

更改板

更换板杀死了当前的UI并为新董事会重建。但是,问题在于,与活动听众的元素也被杀死 - 因此我们的菜单不再召唤出口选项。与上面的问题类似,需要在板上触发重新引导才能使一切正常工作。

> unifinite循环

>在菜单点击菜单之后,弹出窗口不会呈现的天文数字很小 - 也许Trello更改了UI中的某些内容,也许他们更改了班级,或者也许他们只是某种UI错误 - 在这种情况下循环检查其可见性将变得无限,占用大量的CPU资源,直到TAB的过程被杀死为止。对此的保障会很好。

>

结论

在这个简短的系列中,我们为Trello构建了一个简单的Chrome扩展名,使我们可以从给定列表中导出作为JSON或TXT列表的卡片。使用此示例在其上构建,并创建自己的Trello扩展名 - 您可以完成的事情仅受您的想象力(以及Trello的API提供的功能)的限制:)。身份验证已经为您解决,并且逻辑模板已经到位 - 开始编码!

我们在本教程系列中编写的代码可在GitHub上找到。您想看到本教程的延续吗?实施了更多功能?让我知道!反馈赞赏!

经常询问有关导出Trello列表的问题(常见问题解答)

>如何将Trello列表导出到Excel?

>将Trello列表导出到Excel是一个简单的过程。首先,您需要安装称为“ Trello的导出”的镀铬扩展名。安装后,您可以导航到Trello板,然后单击扩展图标。您将看到一个选项来导出trello列表。选择Excel格式,您的Trello列表将作为Excel文件下载。该文件将包含您的TRELLO列表的所有详细信息,包括卡名称,描述,标签等。

我可以将Trello列表导出到PDF?

是的,您可以将Trello列表导出到Trello列表中PDF。类似于导出到Excel,您需要安装“ Trello” Chrome扩展名的“导出”。安装后,导航到Trello板,单击扩展图标,然后选择用于导出的PDF格式。您的trello列表将被下载为PDF文件。

>

>是否可以将Trello列表导出到CSV?

>是的,可以将Trello列表导出到CSV格式。该过程类似于导出到Excel或PDF。您需要安装“ trello for Trello” Chrome扩展名,导航到Trello板,单击扩展图标,然后选择用于导出的CSV格式。您的trello列表将被下载为CSV文件。

我可以将Trello列表导出到图像吗?

是的,可以将Trello列表导出为图像。可以使用“ Trello的导出” Chrome Extension完成此操作。安装扩展程序后,导航到Trello板,单击扩展图标,然后选择用于导出的图像格式。您的trello列表将被下载为图像文件。

>我可以用标签导出trello列表吗?

是的,当您使用“ Trello” Chrome Extension的“导出Trello”列表时,所有这些包括标签在内的Trello列表的详细信息已导出。这意味着即使导出了trello列表,您也可以跟踪您的标签。

>

>我可以用卡描述导出trello列表吗?这意味着您即使导出了trello列表,您也可以跟踪卡描述。

我可以以适当的日期导出trello列表? Trello” Chrome Extension,包括到期日期的Trello列表中的所有详细信息均已导出。这意味着您即使导出了trello列表,您也可以跟踪到期日期。

我可以带有附件的trello列表吗?

不幸的是,“ trello” Chromeexension的“导出”不支持出口附件。但是,可以导出包括卡名称,描述,标签和到期日的所有其他详细信息。 Trello” Chrome扩展名不支持导出评论。但是,可以导出trello列表的所有其他详细信息,包括卡名称,描述,标签和到期日期。

>

>我可以一次从多个板上导出trello列表吗?对于Trello”,Chrome扩展程序使您可以一次从一个板上导出Trello列表。如果您想从多个董事会导出列表,则需要导航到每个板并分别导出列表。

以上是如何构建Trello Chrome扩展名 - 导出列表的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板