vue+exceljs前端下载、导出xlsx文件

首先安装插件

npm install exceljs file-saver

第一种 简单导出

在这里插入图片描述

//页面引入
import ExcelJS from 'exceljs'; 
import {saveAs} from 'file-saver';
export default {
	methods: {
/** 导出操作 */
			async handleExportFun() {
				let that = this
				// 获取当前年月日 用户下载xlsx的文件名称设置
				const now = new Date();
				const year = now.getFullYear();
				const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1,并确保两位数
				const day = String(now.getDate()).padStart(2, '0'); // 确保两位数
				const hours = String(now.getHours()).padStart(2, '0'); // 确保两位数
				const minutes = String(now.getMinutes()).padStart(2, '0'); // 确保两位数
				var currentTime = `${year}${month}${day}${hours}${minutes}`;
				//创建
				const workbook = new ExcelJS.Workbook();
				const worksheet = workbook.addWorksheet('Sheet1');
				// 假设 exportData 的第一行是列头
				const headers = this.exportData[0]; // 确保这里是获取的第一行数据作为列头
				// 填充表头数据  因为我的数据是不固定的二维数组
				worksheet.columns = headers.map((headerText, index) => ({
					header: headerText,
					key: index.toString(),
					width: 22,
				}));
				// 设置表头行高
				const headerRow = worksheet.getRow(1);
				headerRow.height = 35; // 设置行高为35(可以根据需要调整)
				//申明样式
				let style = {
					alignment: {
						vertical: "middle",
						horizontal: "center",
						wrapText: true,
					},
					fill: {
						type: "pattern",
						pattern: "solid",
						fgColor: {
							argb: "EEEEEE"
						},
					},
					border: {
						top: {
							style: "thin",
							color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色
						}, // 设置上边框样式为细线
						bottom: {
							style: "thin",
							color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色
						}, // 设置下边框样式为细线
						left: {
							style: "thin",color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色
						}, // 设置左边框样式为细线
						right: {
							style: "thin",color: { rgb: 'E3F2D9' } // 设置左边框样式和颜色
						}, // 设置右边框样式为细线
					},
					 font: {
					    bold: true, // 设置字体加粗
					    name: 'Arial', // 设置字体名称(可选)
					    size: 12 // 设置字体大小(可选)
					  }
				};
			
				// 填充工作表的其他数据,并设置行高
				for (let rowIndex = 1; rowIndex < this.exportData.length; rowIndex++) {
					const row = this.exportData[rowIndex];
					const excelRow = worksheet.getRow(rowIndex + 1);
					excelRow.height = 30; // 设置其他行的行高为25(可以根据需要调整)
					row.forEach((cellValue, cellIndex) => {
						const cell = excelRow.getCell(cellIndex + 1);
						cell.value = cellValue;
					});
				}
				//给每行设置样式
				worksheet.eachRow((row, rowNumber) => {
					if (rowNumber > 0) {
						row.height = 35;
						row.alignment = style.alignment;
							// 你还可以根据单元格的内容或列号执行不同的操作
						row.eachCell((cell, colNumber) => {
							if (rowNumber === 1&&colNumber<=that.exportData[0].length) {
								//对第一列的单元格执行特殊操作
								cell.font=style.font
								cell.fill=style.fill
								}
							cell.border = style.border;
						});
					}
				});
				try {
					const buffer = await workbook.xlsx.writeBuffer();
					const blob = new Blob([buffer], {
						type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
					});
					saveAs(blob, currentTime + '_设备数据.xlsx'); // 导出的文件名可以根据需要动态设置
				} catch (err) {
					console.error('导出Excel时出错:', err);
				}
			}
		}
	}

第二种复杂的表单导出

如图
在这里插入图片描述数据格式

this.summaryStatistics={"type": null,"xuhao": null,"user": "汇总",
		"timeJson": [{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2023年\"},{\"bxPrice\":129584000.80,\"fyPrice\":129592366.80,\"fyzb\":738.96,\"htPrice\":17537026.00,\"swPrice\":8366.00,\"time\":\"2024年\"}],
		"htXj": 17537026,"htZb": null,"fyXj": 129592366.8,"fyZbXj": 738.96
	}

 this.times = this.summaryStatistics.timeJson
 this.userStatistics = [
		{
			"type": null,"xuhao": 1,"user": "admin",
			"timeJson": "[{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2023年\"},{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":274000.00,\"swPrice\":0.00,\"time\":\"2024年\"}]","htXj": 274000,"htZb": 1.56,"fyXj": 0,"fyZbXj": 0
		},
		{
			"type": null,"xuhao": 2,"user": "肖永梅",
			"timeJson": "[{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2023年\"},{\"bxPrice\":0.00,\"fyPrice\":0.00,\"fyzb\":0.00,\"htPrice\":0.00,\"swPrice\":0.00,\"time\":\"2024年\"}]",
			"htXj": 0,"htZb": 0,"fyXj": 0,"fyZbXj": 0
		}
	]
       //将数据添在第一行
        this.userStatistics.unshift(this.summaryStatistics);
        this.userStatistics.forEach((item) => {
          if (item.timeJson != null) {
            JSON.parse(item.timeJson).forEach((child, index) => {
              item[`htPrice${index}`] = child.htPrice;
              item[`fyPrice${index}`] = child.fyPrice;
              item[`fyzb${index}`] = child.fyzb;
            });
          }
        });

数据准备好后就是导出方法了(handleExport)

/** 导出按钮操作 */
    handleExport() {
      const loading = this.$loading({
        lock: true,
        text: "导出excel中...(若长时间无响应请刷新页面)",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.7)",
      });
      const Exceljs = require("exceljs"); // 引入exceljs
      const workbook = new Exceljs.Workbook(); // 创建工作簿
      const workSheet = workbook.addWorksheet("sheet1"); // 创建工作表(sheet1)
      workSheet.getRow(1).height = 60;
      workSheet.getRow(2).height = 36;
      workSheet.getRow(3).height = 28;
      workSheet.getRow(4).height = 28;

      let style = {
        alignment: {
          vertical: "middle",
          horizontal: "center",
          wrapText: true,
        },
        fill: {
          type: "pattern",
          pattern: "solid",
          fgColor: { argb: "E3F2D9" },
        },
        border: {
          top: { style: "thin" }, // 设置上边框样式为细线
          bottom: { style: "thin" }, // 设置下边框样式为细线
          left: { style: "thin" }, // 设置左边框样式为细线
          right: { style: "thin" }, // 设置右边框样式为细线
        },
      };

      // 获取动态年份条数
      let years = this.times.length;

      workSheet.mergeCells(`A1:${this.numberToMatchLetter(3 * years + 6)}1`); //将A1到xx的单元格合并
      const cell_A1 = workSheet.getCell("A1"); // 获取A1单元格
      cell_A1.value = "碧朗科技销售员销售额及费用统计表";
      cell_A1.font = { size: 18, bold: true, name: "宋体" }; // 设置字体大小为16,加粗,仿宋
      cell_A1.alignment = style.alignment;

      workSheet.mergeCells(
        `${this.numberToMatchLetter(3 * years + 7)}1:${this.numberToMatchLetter(
          3 * years + 10
        )}1`
      ); //将xx1到xx1的单元格合并
      const cell_P1 = workSheet.getCell(
        `${this.numberToMatchLetter(3 * years + 7)}1`
      ); // 获取xx1单元格
      cell_P1.value = "注:第4行的年份根据筛选条件动态变化";
      cell_P1.font = {
        size: 11,
        name: "Times New Roman",
        color: { argb: "FF0000" },
      };
      cell_P1.alignment = style.alignment;

      workSheet.mergeCells("A2:E2"); //将A2到E2的单元格合并
      const cell_A2 = workSheet.getCell("A2"); // 获取A2单元格
      cell_A2.value = `年份:${
        this.queryParams.startYear ? this.queryParams.startYear : ""
      }-${this.queryParams.endYear ? this.queryParams.endYear : ""} 月份:${
        this.queryParams.startMonth ? this.queryParams.startMonth : ""
      }-${this.queryParams.endMonth ? this.queryParams.endMonth : ""}`;
      cell_A2.font = { size: 8, bold: true, name: "宋体" };
      cell_A2.alignment = {
        vertical: "middle",
        wrapText: true,
      };

      workSheet.mergeCells("A3:A4"); //将A3到A4的单元格合并
      const cell_A3 = workSheet.getCell("A3"); // 获取A3单元格
      cell_A3.value = "序号";
      cell_A3.style = style;
      cell_A3.font = { size: 11, bold: true, name: "宋体" };
      // 遍历合并A3到A4的单元格的区域,为其余部分设置边框
      for (let row = 3; row <= 4; row++) {
        for (let col = 1; col <= 1; col++) {
          const cell = workSheet.getCell(row, col);
          cell.border = cell_A3.border;
        }
      }

      workSheet.mergeCells("B3:B4"); //将B3到B4的单元格合并
      const cell_B3 = workSheet.getCell("B3"); // 获取B3单元格
      cell_B3.value = "销售员";
      cell_B3.style = style;
      cell_B3.font = { size: 11, bold: true, name: "宋体" };
      // 遍历合并B3到B4的单元格的区域,为其余部分设置边框
      for (let row = 3; row <= 4; row++) {
        for (let col = 2; col <= 2; col++) {
          const cell = workSheet.getCell(row, col);
          cell.border = cell_B3.border;
        }
      }

      workSheet.mergeCells(`C3:${this.numberToMatchLetter(years + 4)}3`);
      const cell_diy1 = workSheet.getCell("C3");
      cell_diy1.value = "销售额";
      cell_diy1.style = style;
      cell_diy1.font = { size: 11, bold: true, name: "宋体" };
      // 遍历合并的单元格的区域,为其余部分设置边框
      for (let row = 3; row <= 3; row++) {
        for (let col = 3; col <= years + 4; col++) {
          const cell = workSheet.getCell(row, col);
          cell.border = cell_diy1.border;
        }
      }

      workSheet.mergeCells(
        `${this.numberToMatchLetter(years + 5)}3:${this.numberToMatchLetter(
          2 * years + 5
        )}3`
      );
      const cell_diy2 = workSheet.getCell(
        `${this.numberToMatchLetter(years + 5)}3`
      );
      // 创建富文本内容
      const richText = [
        {
          text: "费用",
          font: {
            name: "宋体",
            size: 11,
            bold: true,
          },
        },
        {
          text: "(报销的费用+备案商务费)",
          font: {
            name: "宋体",
            size: 9,
            bold: true,
            color: { argb: "FF0000" },
          },
        },
      ];
      cell_diy2.value = { richText: richText };
      cell_diy2.style = style;
      // 遍历合并的单元格的区域,为其余部分设置边框
      for (let row = 3; row <= 3; row++) {
        for (let col = years + 5; col <= 2 * years + 5; col++) {
          const cell = workSheet.getCell(row, col);
          cell.border = cell_diy2.border;
        }
      }

      workSheet.mergeCells(
        `${this.numberToMatchLetter(2 * years + 6)}3:${this.numberToMatchLetter(
          3 * years + 6
        )}3`
      );
      const cell_diy3 = workSheet.getCell(
        `${this.numberToMatchLetter(2 * years + 6)}3`
      );
      cell_diy3.value = "费用占比(%)";
      cell_diy3.style = style;
      cell_diy3.font = { size: 11, bold: true, name: "宋体" };
      // 遍历合并的单元格的区域,为其余部分设置边框
      for (let row = 3; row <= 3; row++) {
        for (let col = 2 * years + 6; col <= 3 * years + 6; col++) {
          const cell = workSheet.getCell(row, col);
          cell.border = cell_diy3.border;
        }
      }

      const cell_XJ1 = workSheet.getCell(
        `${this.numberToMatchLetter(3 + years)}4`
      ); // 获取单元格
      cell_XJ1.value = "小计";
      cell_XJ1.style = style;
      cell_XJ1.font = { size: 11, bold: true, name: "宋体" };

      const cell_ZB1 = workSheet.getCell(
        `${this.numberToMatchLetter(4 + years)}4`
      ); // 获取单元格
      cell_ZB1.value = "占比(%)";
      cell_ZB1.style = style;
      cell_ZB1.font = { size: 11, bold: true, name: "宋体" };

      const cell_XJ2 = workSheet.getCell(
        `${this.numberToMatchLetter(5 + 2 * years)}4`
      ); // 获取单元格
      cell_XJ2.value = "小计";
      cell_XJ2.style = style;
      cell_XJ2.font = { size: 11, bold: true, name: "宋体" };

      const cell_XJ3 = workSheet.getCell(
        `${this.numberToMatchLetter(6 + 3 * years)}4`
      ); // 获取单元格
      cell_XJ3.value = "小计";
      cell_XJ3.style = style;
      cell_XJ3.font = { size: 11, bold: true, name: "宋体" };

      // 销售额动态年份填充
      for (let i = 1; i <= years; i++) {
        workSheet.getCell(`${this.numberToMatchLetter(2 + i)}4`).value =
          this.times[i - 1].time;
        workSheet.getCell(`${this.numberToMatchLetter(2 + i)}4`).style = style;
        workSheet.getCell(`${this.numberToMatchLetter(2 + i)}4`).font = {
          size: 11,
          bold: true,
          name: "宋体",
        };

        workSheet.getCell(`${this.numberToMatchLetter(years + 4 + i)}4`).value =
          this.times[i - 1].time;
        workSheet.getCell(`${this.numberToMatchLetter(years + 4 + i)}4`).style =
          style;
        workSheet.getCell(`${this.numberToMatchLetter(years + 4 + i)}4`).font =
          {
            size: 11,
            bold: true,
            name: "宋体",
          };

        workSheet.getCell(
          `${this.numberToMatchLetter(2 * years + 5 + i)}4`
        ).value = this.times[i - 1].time;
        workSheet.getCell(
          `${this.numberToMatchLetter(2 * years + 5 + i)}4`
        ).style = style;
        workSheet.getCell(
          `${this.numberToMatchLetter(2 * years + 5 + i)}4`
        ).font = {
          size: 11,
          bold: true,
          name: "宋体",
        };
      }
      let exportTableProp = [
        { key: "xuhao" },
        { key: "user" },
        { key: "htXj", width: 18 },
        { key: "htZb", width: 18 },
        { key: "fyXj", width: 18 },
        { key: "fyZbXj", width: 18 },
      ];
      if (this.times.length > 0) {
        this.times.forEach((item, index) => {
          exportTableProp.splice(index + 2, 0, {
            key: `htPrice${index}`,
            width: 18,
          });
        });
        this.times.forEach((item, index) => {
          exportTableProp.splice(index + years + 4, 0, {
            key: `fyPrice${index}`,
            width: 18,
          });
        });
        this.times.forEach((item, index) => {
          exportTableProp.splice(index + 2 * years + 5, 0, {
            key: `fyzb${index}`,
            width: 18,
          });
        });
      }

      workSheet.columns = exportTableProp; // 工作表添加表头
      workSheet.addRows(this.userStatistics); // 往工作表插入数据

      workSheet.eachRow((row, rowNumber) => {
        if (rowNumber > 4) {
          row.height = 20;
          row.alignment = style.alignment;
          row.eachCell(
            {
              start: 1,
              end: 3 * years + 6,
            },
            (cell, colNumber) => {
              cell.border = style.border;
            }
          );
        }
      });

      // 下载工作簿
      workbook.xlsx.writeBuffer().then((buffer) => {
        saveAs(
          new Blob([buffer], { type: "application/octet-stream" }),
          "销售员销售额及费用表格导出.xlsx"
        );
      });
      loading.close();
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/912156.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytest自动化测试框架详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Pytest是一种基于Python编程语言的自动化测试框架&#xff0c;它提供了丰富的功能和灵活的扩展性&#xff0c;可以用于单元测试、集成测试、功能测试、端到端测试…

解决com.mysql.jdbc.NonRegisteringDriver内存泄漏问题

1. 问题背景 线上出现内存报警&#xff0c;通过dump文件&#xff0c;MAT分析&#xff0c;发现mysql-connector-java 有内存泄漏问题 2.问题分析 然后看大对象列表&#xff0c;NonRegisteringDriver 对象确实占内存比较多&#xff0c;里面村的数据库连接的虚引用占比较多 3.解…

Golang | Leetcode Golang题解之第547题身份数量

题目&#xff1a; 题解&#xff1a; func findCircleNum(isConnected [][]int) (ans int) {n : len(isConnected)parent : make([]int, n)for i : range parent {parent[i] i}var find func(int) intfind func(x int) int {if parent[x] ! x {parent[x] find(parent[x])}re…

CSS实现文字渐变效果

效果图&#xff1a; 代码&#xff1a; h1 {font-size: 100px;color:linear-gradient(gold,deeppink);background-image:linear-gradient( -gold, deeppink); /*春意盎然*///背景被裁剪成文字的前景色。background-clip:text;/*兼容内核版本较低的浏览器*/-webkit-background-c…

24/11/8 算法笔记 t-SNE降维算法

t-SNE算法的核心实现涉及几个关键步骤&#xff0c;主要包括概率分布的构建、梯度计算和优化。以下是这些步骤的简要说明&#xff1a; 1. **概率分布的构建**&#xff1a; - 在高维空间中&#xff0c;t-SNE使用高斯分布&#xff08;Gaussian distribution&#xff09;来构建…

企业微信-消息推送之微信客服-接收消息和事件

一&#xff1a;企微实现和企业间的微信客服消息接收和事件原理 新版企微主要通过2个阶段实&#xff0c;第一个&#xff1a;消息推送 概述 - 文档 - 企业微信开发者中心 &#xff0c;第二个&#xff1a;微信客服 接收消息和事件 - 文档 - 企业微信开发者中心 二&#xff1a;代码…

Ascend Extension for PyTorch是个what?

1 Ascend Extension for PyTorch Ascend Extension for PyTorch 插件是基于昇腾的深度学习适配框架&#xff0c;使昇腾NPU可以支持PyTorch框架&#xff0c;为PyTorch框架的使用者提供昇腾AI处理器的超强算力。 项目源码地址请参见Ascend/Pytorch。 昇腾为基于昇腾处理器和软…

【React】React 生命周期完全指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 React 生命周期完全指南一、生命周期概述二、生命周期的三个阶段2.1 挂载阶段&a…

开源模型应用落地-glm模型小试-glm-4-9b-chat-压力测试(六)

一、前言 GLM-4是智谱AI团队于2024年1月16日发布的基座大模型&#xff0c;旨在自动理解和规划用户的复杂指令&#xff0c;并能调用网页浏览器。其功能包括数据分析、图表创建、PPT生成等&#xff0c;支持128K的上下文窗口&#xff0c;使其在长文本处理和精度召回方面表现优异&a…

程序开发时单数复数及前缀的命名规范(目录名、文件名、函数名、变量名、数据库字段等)

在程序开发中&#xff0c;我总是被单复数搞得头疼&#xff0c;以前采用了最舒服的方法&#xff0c;一刀切&#xff1a;全部单数&#xff0c;因为理由也很简单&#xff0c;单数都可以作为定语解释&#xff0c;比如/util&#xff0c;可以认为真正的名称是/util files或者/util di…

Spring Boot原理全解析:如何让开发更轻松高效(二)-起步依赖、自动装配

通过这篇博客&#xff0c;读者将能够掌握 Spring Boot 中的配置优先级和 Bean 管理的核心原理&#xff0c;为开发更加高效、可维护的 Spring Boot 应用打下坚实的基础。 目录 前言 起步依赖 自动配置 概述 常见方案 概述 方案一 方案二 总结 前言 通过这篇博客&#xf…

力扣动态规划基础版(矩阵型)

62.不同路径&#xff08;唯一路径问题&#xff09; 62. 不同路径https://leetcode.cn/problems/unique-paths/ 方法一&#xff1a;动态规划 找状态转移方程&#xff0c;也就是说它从左上角走到右下角&#xff0c;只能往右或者往下走&#xff0c;那么设置一个位置为&#xff…

Ubuntu 22 安装 Apache Doris 3.0.3 笔记

Ubuntu 22 安装 Apache Doris 3.0.3 笔记 1. 环境准备 Doris 需要 Java 17 作为运行环境&#xff0c;所以首先需要安装 Java 17。 sudo apt-get install openjdk-17-jdk -y sudo update-alternatives --config java在安装 Java 17 后&#xff0c;可以通过 sudo update-alter…

141/142题环形链表

本题返回环入口的位置。使用快慢指针&#xff0c;快指针每次移动两个&#xff0c;慢指针每次移动一个。设前一段距离是a,进入环内到slow和fast相遇的地点距离是b&#xff0c;环内剩下的距离是c&#xff0c;如图所示。 环的长度是bc 慢指针移动距离是ab 快指针移动距离是abk(bc…

leetcode25:k个一组链表反转

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

《今日制造与升级》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问&#xff1a;《今日制造与升级》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《今日制造与升级》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;中国机械工业联合会 …

【Linux第八课-进程间通信】管道、共享内存、消息队列、信号量、信号、可重入函数、volatile

目录 进程间通信为什么&#xff1f;是什么&#xff1f;怎么办&#xff1f;一般规律具体做法 匿名管道原理代码 命名管道原理代码 system V共享内存消息队列信号量信号量的接口 信号概念为什么&#xff1f;怎么办&#xff1f;准备信号的产生信号的保存概念三张表匹配的操作和系统…

C++builder中的人工智能(18):神经网络中的SoftMax函数

在这篇文章中&#xff0c;我们将探讨SoftMax函数在神经网络中的作用&#xff0c;如何在人工神经网络&#xff08;ANN&#xff09;中使用SoftMax函数&#xff0c;以及在AI技术中SoftMax的应用场景。让我们来详细解释这些概念。 SoftMax函数是什么&#xff1f; SoftMax函数是逻辑…

证件照尺寸168宽240高,如何手机自拍更换蓝底

在提供学籍照片及一些社会化考试报名时&#xff0c;会要求我们提供尺寸为168*240像素的电子版证件照&#xff0c;本文将介绍如何使用“报名电子照助手”&#xff0c;借助手机拍照功能完成证件照的拍摄和背景更换&#xff0c;特别是如何将照片尺寸调整为168像素宽和240像素高&am…

pytest+request+allure接口自动化框架搭建分享

介绍分享一个接口自动化框架搭建方法 (pytestrequestallure)&#xff0c;这个方案是由 xpcs 同学在TesterHome社区网站的分享。 写在前面 去年11月被裁&#xff0c;到现在还没上岸&#xff0c;gap 半年了。上岸无望&#xff0c;专业技能不能落下&#xff0c;花了两三天时间&…