javahuang 5 years ago
parent
commit
7ec99c2cb9
  1. 9
      README-zh.md
  2. 9
      README.md
  3. 1
      docs/guide/README.md
  4. 231
      docs/guide/api.md
  5. 19
      docs/guide/config.md
  6. 1
      docs/zh/guide/README.md
  7. 257
      docs/zh/guide/api.md
  8. 118
      docs/zh/guide/config.md
  9. 2
      docs/zh/guide/operate.md
  10. 22
      src/controllers/cellFormat.js
  11. 3
      src/controllers/constant.js
  12. 64
      src/controllers/imageCtrl.js
  13. 50
      src/controllers/postil.js
  14. 4
      src/controllers/zoom.js
  15. 24
      src/demoData/demoFeature.js
  16. 751
      src/global/api.js
  17. 8
      src/global/draw.js
  18. 4
      src/global/formula.js
  19. 18
      src/global/getRowlen.js
  20. 5
      src/global/setdata.js
  21. 8
      src/index.html

9
README-zh.md

@ -10,10 +10,10 @@
🚀Luckysheet ,一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。
## 相关链接
| 源码 | 文档 | Demo | 插件Demo |
| ------ | -------- | ------ | ------ |
| [Github](https://github.com/mengshukeji/Luckysheet)| [在线文档](https://mengshukeji.github.io/LuckysheetDocs/zh/) | [在线Demo](https://mengshukeji.github.io/LuckysheetDemo) | [导入Excel Demo](https://mengshukeji.github.io/LuckyexcelDemo/) |
| [Gitee镜像](https://gitee.com/mengshukeji/Luckysheet)| [Gitee在线文档](https://mengshukeji.gitee.io/LuckysheetDocs/zh/) | [Gitee在线Demo](https://mengshukeji.gitee.io/luckysheetdemo/) | [Gitee导入Excel Demo](https://mengshukeji.gitee.io/luckyexceldemo/) |
| 源码 | 文档 | Demo | 插件Demo | 论坛 |
| ------ | -------- | ------ | ------ | ------ |
| [Github](https://github.com/mengshukeji/Luckysheet)| [在线文档](https://mengshukeji.github.io/LuckysheetDocs/zh/) | [在线Demo](https://mengshukeji.github.io/LuckysheetDemo) | [导入Excel Demo](https://mengshukeji.github.io/LuckyexcelDemo/) | [中文论坛](https://support.qq.com/product/288322) |
| [Gitee镜像](https://gitee.com/mengshukeji/Luckysheet)| [Gitee在线文档](https://mengshukeji.gitee.io/LuckysheetDocs/zh/) | [Gitee在线Demo](https://mengshukeji.gitee.io/luckysheetdemo/) | [Gitee导入Excel Demo](https://mengshukeji.gitee.io/luckyexceldemo/) | [Gitter](https://gitter.im/mengshukeji/Luckysheet) |
![演示](/docs/.vuepress/public/img/LuckysheetDemo.gif)
@ -200,6 +200,7 @@ npm run build
- [@danielcai1987](https://github.com/danielcai1987)
- [@qq6690876](https://github.com/qq6690876)
- [@javahuang](https://github.com/javahuang)
- [@TimerGang](https://github.com/TimerGang)
- [@gsw945](https://github.com/gsw945)
- [@swen-xiong](https://github.com/swen-xiong)

9
README.md

@ -16,10 +16,10 @@ English| [简体中文](./README-zh.md)
🚀Luckysheet is an online spreadsheet like excel that is powerful, simple to configure, and completely open source.
## Links
| Source Code | Documentation | Demo | Plugins Demo |
| ------ | -------- | ------ | ------ |
| [Github](https://github.com/mengshukeji/Luckysheet)| [Online Documentation](https://mengshukeji.github.io/LuckysheetDocs/) | [Online Demo](https://mengshukeji.github.io/LuckysheetDemo) | [Import Excel Demo](https://mengshukeji.github.io/LuckyexcelDemo/) |
| [Gitee Mirror](https://gitee.com/mengshukeji/Luckysheet)| [Gitee Online Documentation](https://mengshukeji.gitee.io/LuckysheetDocs/) | [Gitee Online Demo](https://mengshukeji.gitee.io/luckysheetdemo/) | [Gitee Import Excel Demo](https://mengshukeji.gitee.io/luckyexceldemo/) |
| Source Code | Documentation | Demo | Plugins Demo | Forum |
| ------ | -------- | ------ | ------ | ------ |
| [Github](https://github.com/mengshukeji/Luckysheet)| [Online Documentation](https://mengshukeji.github.io/LuckysheetDocs/) | [Online Demo](https://mengshukeji.github.io/LuckysheetDemo) | [Import Excel Demo](https://mengshukeji.github.io/LuckyexcelDemo/) | [Chinese Forum](https://support.qq.com/product/288322) |
| [Gitee Mirror](https://gitee.com/mengshukeji/Luckysheet)| [Gitee Online Documentation](https://mengshukeji.gitee.io/LuckysheetDocs/) | [Gitee Online Demo](https://mengshukeji.gitee.io/luckysheetdemo/) | [Gitee Import Excel Demo](https://mengshukeji.gitee.io/luckyexceldemo/) | [Gitter](https://gitter.im/mengshukeji/Luckysheet) |
![Demo](/docs/.vuepress/public/img/LuckysheetDemo.gif)
@ -196,6 +196,7 @@ Create a table
- [@danielcai1987](https://github.com/danielcai1987)
- [@qq6690876](https://github.com/qq6690876)
- [@javahuang](https://github.com/javahuang)
- [@TimerGang](https://github.com/TimerGang)
- [@gsw945](https://github.com/gsw945)
- [@swen-xiong](https://github.com/swen-xiong)

1
docs/guide/README.md

@ -136,6 +136,7 @@ After `npm run build`, all files in the `dist` folder are copied to the project
<link rel='stylesheet' href='./plugins/css/pluginsCss.css' />
<link rel='stylesheet' href='./plugins/plugins.css' />
<link rel='stylesheet' href='./css/luckysheet.css' />
<link rel='stylesheet' href='./assets/iconfont/iconfont.css' />
<script src="./plugins/js/plugin.js"></script>
<script src="./luckysheet.umd.js"></script>
```

231
docs/guide/api.md

@ -541,6 +541,154 @@ Use note:
------------
### setRowHeight(rowInfo [,setting])
(TODO)
- **Parameter**
- {Object} [rowInfo]: Correspondence between number of rows and height
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Set the height of the specified ~~row~~
- **Usage**:
- Set the height of the first row to 50px and the height of the second row to 60px
`luckysheet.setRowHeight({0:50,1:60})`
------------
### setColumnWidth(columnInfo [,setting])
(TODO)
- **Parameter**
- {Number} [columnInfo]: Correspondence between the number of columns and the width
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Set the width of the specified column
- **Usage**:
- Set the width of the first column to 50px and the width of the second column to 60px
`luckysheet.setColumnWidth({0:50,1:60})`
------------
### getRowHeight(rowInfo [,setting])
(TODO)
- **Parameter**
- {Array} [rowInfo]: The number of rows
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Get the height of the specified row, get the object corresponding to the number of rows and height
- **Usage**:
- The height of the first row is 50px, the height of the second row is 60px, get these values
`luckysheet.getRowHeight([0,1])`
Return to get
`{0:50,1:60}`
------------
### getColumnWidth(columnInfo [,setting])
(TODO)
- **Parameter**
- {Array} [columnInfo]: The number of columns
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Get the width of the specified column, get the object of the corresponding relationship between the number of columns and the width
- **Usage**:
- The width of the first column is 50px, the width of the second column is 60px, get these values
`luckysheet.getColumnWidth([0,1])`
Return to get
`{0:50,1:60}`
------------
### getDefaultRowHeight([,setting])
(TODO)
- **Parameter**
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Get the default row height of the specified worksheet
- **Usage**:
- Returns the default row height of the current worksheet
`luckysheet.getDefaultRowHeight()`
Return to get
`19`
------------
### getDefaultColumnWidth([,setting])
(TODO)
- **Parameter**
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Get the default column width of the specified worksheet
- **Usage**:
- Returns the default column width of the current worksheet
`luckysheet.getDefaultColumnWidth()`
Return to get
`73`
------------
## Selection operation
### getRange()
@ -2013,6 +2161,32 @@ Use note:
------------
### setSheetZoom(zoom [,setting])
[todo]
- **Parameter**
- {Number} [zoom]: Worksheet zoom ratio
- {PlainObject} [setting]: optional parameters
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Set worksheet zoom ratio
- **Usage**:
- Set the current worksheet zoom ratio to 0.5
```js
luckysheet.setSheetZoom(0.5)
```
------------
### showGridLines([setting])
- **Parameter**
@ -2295,6 +2469,63 @@ Use note:
------------
## Data Verification
### setDataVerification(option, [setting])
[todo]
- **Parameter**
- {Object} [option]: Configuration information for data verification
- {PlainObject} [setting]: optional parameters
+ {Array | Object | String} [range]: The selection area for data verification, The format of the supported selection is `"A1:B2"`, `"sheetName!A1:B2"` or `{row:[0,1] ,column:[0,1]}`, can only be a single selection; the default is the current selection
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Specify the worksheet range to set the data verification function and set the parameters
------------
### deleteDataVerification([setting])
[todo, to be confirmed whether it is reasonable]
- **Parameter**
- {PlainObject} [setting]: optional parameters
+ {Array | Object | String} [range]: The selection area for data verification, The format of the supported selection is `"A1:B2"`, `"sheetName!A1:B2"` or `{row:[0,1] ,column:[0,1]}`, can only be a single selection; the default is the current selection
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Specify the worksheet range to delete the data verification function
------------
## Worksheet Protection
### setProtection(option, [setting])
[todo]
- **Parameter**
- {Object} [option]: Configuration information for worksheet protection
- {PlainObject} [setting]: optional parameters
+ {Number} [order]: Worksheet subscript; the default value is the current worksheet subscript
+ {Function} [success]: callback function for the end of the operation
- **Explanation**
Specify the worksheet to set the worksheet protection
------------
## Public method
### transToCellData(data [,setting])<div id='transToCellData'></div>

19
docs/guide/config.md

@ -743,6 +743,25 @@ The hook functions are uniformly configured under ʻoptions.hook`, and configura
- {String} [oldColor]: Before modification, the current worksheet color
- {String} [newColor]: After modification, the current worksheet color
------------
### sheetZoomBefore
- Type: Function
- Default: null
- Usage: Before worksheet zoom
- Parameter:
- {Number} [i]: `index` of current worksheet
- {String} [zoom]: Current worksheet zoom ratio
------------
### sheetZoomAfter
- Type: Function
- Default: null
- Usage: After worksheet zoom
- Parameter:
- {Number} [i]: `index` of current worksheet
- {String} [oldZoom]: Before modification, the current worksheet zoom ratio
- {String} [newZoom]: After modification, the current worksheet zoom ratio
------------
## Workbook

1
docs/zh/guide/README.md

@ -137,6 +137,7 @@ npm run build
<link rel='stylesheet' href='./plugins/css/pluginsCss.css' />
<link rel='stylesheet' href='./plugins/plugins.css' />
<link rel='stylesheet' href='./css/luckysheet.css' />
<link rel='stylesheet' href='./assets/iconfont/iconfont.css' />
<script src="./plugins/js/plugin.js"></script>
<script src="./luckysheet.umd.js"></script>
```

257
docs/zh/guide/api.md

@ -542,6 +542,150 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
------------
### setRowHeight(rowInfo [,setting])
- **参数**
- {Object} [rowInfo]: 行数和高度对应关系
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
设置指定行的高度
- **示例**:
- 设置第一行高度为50px,第二行高度为60px
`luckysheet.setRowHeight({0:50,1:60})`
------------
### setColumnWidth(columnInfo [,setting])
- **参数**
- {Number} [columnInfo]: 列数和宽度对应关系
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
设置指定列的宽度
- **示例**:
- 设置第一列宽度为50px,第二列宽度为60px
`luckysheet.setColumnWidth({0:50,1:60})`
------------
### getRowHeight(rowInfo [,setting])
(TODO)
- **参数**
- {Array} [rowInfo]: 行数
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
获取指定行的高度,得到行数和高度对应关系的对象
- **示例**:
- 第一行高度为50px,第二行高度为60px,获取这些值
`luckysheet.getRowHeight([0,1])`
返回得到
`{0:50,1:60}`
------------
### getColumnWidth(columnInfo [,setting])
(TODO)
- **参数**
- {Array} [columnInfo]: 列数
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
获取指定列的宽度,得到列数和宽度对应关系的对象
- **示例**:
- 第一列宽度为50px,第二列宽度为60px,获取这些值
`luckysheet.getColumnWidth([0,1])`
返回得到
`{0:50,1:60}`
------------
### getDefaultRowHeight([,setting])
(TODO)
- **参数**
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
获取指定工作表的默认行高
- **示例**:
- 返回当前工作表的默认行高
`luckysheet.getDefaultRowHeight()`
返回得到
`19`
------------
### getDefaultColumnWidth([,setting])
(TODO)
- **参数**
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
获取指定工作表的默认列宽
- **示例**:
- 返回当前工作表的默认列宽
`luckysheet.getDefaultColumnWidth()`
返回得到
`73`
------------
## 选区操作
### getRange()
@ -1651,8 +1795,6 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### getAllSheets()
[todo]
- **说明**
返回所有工作表配置,格式同工作表配置,得到的结果可用于表格初始化时作为options.data使用。
@ -1922,9 +2064,6 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### setSheetOrder(orderList [,setting])
[todo]
- **参数**
- {Array} [orderList]: 工作表顺序,设置工作表的index和order来指定位置,如:
@ -1959,6 +2098,30 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
------------
### setSheetZoom(zoom [,setting])
- **参数**
- {Number} [zoom]: 工作表缩放比例,值范围为0.1 ~ 4;
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
设置工作表缩放比例
- **示例**:
- 设置当前工作表缩放比例为0.5
```js
luckysheet.setSheetZoom(0.5)
```
------------
### showGridLines([setting])
- **参数**
@ -2019,9 +2182,6 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### refresh([setting])
[todo]
- **参数**
- {PlainObject} [setting]: 可选参数
@ -2078,27 +2238,22 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### getScreenshot([setting])
[todo]
- **参数**
- {PlainObject} [setting]: 可选参数
+ {Array | Object | String} [range]: 选区范围,支持选区的格式为`"A1:B2"`、`"sheetName!A1:B2"`或者`{row:[0,1],column:[0,1]}`,只能为单个选区;默认为当前选区
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Object | String} [range]: 选区范围,支持选区的格式为`"A1:B2"`、`"sheetName!A1:B2"`或者`{row:[0,1],column:[0,1]}`,只能为单个选区;默认为当前选区
- **说明**
返回指定选区截图后生成的base64格式的图片
返回当前表格指定选区截图后生成的base64格式的图片
------------
### setWorkbookName(name [,setting])
[todo]
- **参数**
- {Number} [name]: 工作簿名称
- {String} [name]: 工作簿名称
- {PlainObject} [setting]: 可选参数
+ {Function} [success]: 操作结束的回调函数
@ -2110,9 +2265,6 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### undo([setting])
[todo]
- **参数**
- {PlainObject} [setting]: 可选参数
@ -2126,9 +2278,6 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### redo([setting])
[todo]
- **参数**
- {PlainObject} [setting]: 可选参数
@ -2238,6 +2387,67 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
------------
## 数据验证
### setDataVerification(optionItem, [setting])
- **参数**
- {Object} [optionItem]: 数据验证的配置信息
+ {String} [type]: 类型;值可为`"dropdown"`(下拉列表)、`"checkbox"`(复选框)、`"number"`(数字)、`"number_integer"`(数字-整数)、`"number_decimal"`(数字-小数)、`"text_content"`(文本-内容)、`"text_length"`(文本-长度)、`"date"`(日期)、`"validity"`(有效性);
+ {String | Null} [type2]: 条件类型;类型`type`值为`"dropdown"`、`"checkbox"`时,`type2`值可为`null`;类型`type`值为`"number"`、`"number_integer"`、`"number_decimal"`、`"text_length"`时,`type2`值可为`"bw"`(介于)、`"nb"`(不介于)、`"eq"`(等于)、`"ne"`(不等于)、`"gt"`(大于)、`"lt"`(小于)、`"gte"`(大于等于)、`"lte"`(小于等于);类型`type`值为`"text_content"`时,`type2`值可为`"include"`(包括)、`"exclude"`(不包括)、`"equal"`(等于);类型`type`值为`"date"`时,`type2`值可为`"bw"`(介于)、`"nb"`(不介于)、`"eq"`(等于)、`"ne"`(不等于)、`"bf"`(早于)、`"nbf"`(不早于)、`"af"`(晚于)、`"naf"`(不晚于);类型`type`值为`"validity"`时,`type2`值可为`"card"`(身份证号码)、`"phone"`(手机号);
+ {String | Number} [value1]: 条件值1;类型`type`值为`"dropdown"`时,`value1`值可为选区或以英文逗号隔开的字符串,如`"1,2,3"`或者`"A1:B2"`;类型`type`值为`"validity"`时,`value1`值可为空;其他类型时`value1`值为数值或字符串;
+ {String | Number} [value2]: 条件值2;类型`type`值为`"checkbox"`或者条件类型`type2`值为`"bw"`、`"nb"`时有`value2`值,条件值为数值或日期时,条件值2要大于等于条件值1;其它情况可为空;
+ {Boolean} [remote]: 自动远程获取选项;默认为`false`;
+ {Boolean} [prohibitInput]: 输入数据无效时禁止输入;默认为`false`;
+ {Boolean} [hintShow]: 选中单元格时显示提示语;默认为`false`;
+ {String} [hintText]: 提示语文本;`hintShow`为`true`时需配置;
- {PlainObject} [setting]: 可选参数
+ {Object | String} [range]: 数据验证的选区范围,支持选区的格式为`"A1:B2"`、`"sheetName!A1:B2"`或者`{row:[0,1],column:[0,1]}`,只能为单个选区;默认为当前选区
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
指定工作表范围设置数据验证功能,并设置参数
------------
### deleteDataVerification([setting])
- **参数**
- {PlainObject} [setting]: 可选参数
+ {Object | String} [range]: 数据验证的选区范围,支持选区的格式为`"A1:B2"`、`"sheetName!A1:B2"`或者`{row:[0,1],column:[0,1]}`,只能为单个选区;默认为当前选区
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
指定工作表范围删除数据验证功能
------------
## 工作表保护
### setProtection(option, [setting])
[todo]
- **参数**
- {Object} [option]: 工作表保护的配置信息
- {PlainObject} [setting]: 可选参数
+ {Number} [order]: 工作表下标;默认值为当前工作表下标
+ {Function} [success]: 操作结束的回调函数
- **说明**
指定工作表设置工作表保护功能
------------
## 公共方法
### transToCellData(data [,setting])<div id='transToCellData'></div>
@ -2257,8 +2467,6 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
### transToData(celldata [,setting])<div id='transToData'></div>
[todo]
- **参数**
- {Array} [celldata]: data数据
@ -2282,6 +2490,7 @@ Luckysheet针对常用的数据操作需求,开放了主要功能的API,开
------------
## 旧版API
::: warning

118
docs/zh/guide/config.md

@ -106,29 +106,114 @@ Luckysheet开放了更细致的自定义配置选项,分别有
### loadUrl
- 类型:String
- 默认值:""
- 作用:配置`loadUrl`的地址,与`loadSheetUrl`配合使用,一般用于大数据量的时候。也可以不用Luckysheet提供的接口参数,使用[data](#data)参数可以提前准备好所有表格数据用于初始化。
Luckysheet会通过ajax请求整个表格数据,默认载入status为1的sheet数据中的所有`celldata`,其余的sheet载入除`celldata`字段外的所有字段。但是考虑到一些公式、图表及数据透视表会引用其他sheet的数据,所以前台会加一个判断,如果该当前sheet引用了其他sheet的数据则会通过`loadSheetUrl`配置的接口地址请求数据,把引用到的sheet的数据一并补全。因为 `loadUrl`只负责当前页数据,所以还需要配置`loadSheetUrl`作为异步加载数据的接口。
- 作用:配置`loadUrl`接口地址,加载所有工作表的配置,并包含当前页单元格数据,与`loadSheetUrl`配合使用。参数为`gridKey`(表格主键)。
源码的请求写法是:
```js
$.post(loadurl, {"gridKey" : server.gridKey}, function (d) {})
```
> 参见源码 [`src/core.js`](https://github.com/mengshukeji/Luckysheet/blob/master/src/core.js)
Luckysheet会通过ajax请求(POST)整个表格的数据,默认载入status为1的sheet数据中的`celldata`,其余的sheet载入除`celldata`字段外的所有配置字段。特别是在数据量大的时候,`loadUrl`只负责当前页单元格数据,配置`loadSheetUrl`作为其它工作表异步加载单元格数据的接口,可以提高性能。
一个合格的接口返回的json字符串数据为:
```js
"[
//status为1的sheet页,重点是需要提供初始化的数据celldata
{
"name": "Cell",
"index": "sheet_001",
"order": 0,
"status": 1,
"celldata": [{"r":0,"c":0,"v":{"v":1,"m":"1","ct":{"fa":"General","t":"n"}}}]
},
//其他status为0的sheet页,无需提供celldata,只需要配置项即可
{
"name": "Data",
"index": "sheet_002",
"order": 1,
"status": 0
},
{
"name": "Picture",
"index": "sheet_003",
"order": 2,
"status": 0
}
]"
```
有几个注意点
+ 这是一个字符串,类似于JSON.stringify()处理后的json数据,压缩后的数据便于传输
+ loadUrl是一个post请求,也是为了支持大数据量
+ 考虑到一些公式、图表及数据透视表会引用其他sheet的数据,所以前台会加一个判断,如果该当前sheet引用了其他sheet的数据则会通过`loadSheetUrl`配置的接口地址请求数据,把引用到的sheet的数据一并补全,而不用等切换到其它页的时候再请求
+ 当数据量小的时候,也可以不用Luckysheet提供的此接口,直接使用[data](#data)参数可以提前准备好所有表格数据用于初始化
------------
### loadSheetUrl
- 类型:String
- 默认值:""
- 作用:配置`loadSheetUrl`的地址,参数为`gridKey`(表格主键) 和 `index`(sheet主键合集,格式为`["sheet_01","sheet_02","sheet_0"]`),返回的数据为sheet的`celldata`字段数据集合。为了加载性能考虑,除了第一次加载当前页的`celldata`数据之外,其余sheet的数据,是在切换到那个sheet页的时候,才会请求那一页的数据。
- 作用:配置`loadSheetUrl`接口地址,用于异步加载其它单元格数据。参数为`gridKey`(表格主键) 和 `index`(sheet主键合集,格式为`["sheet_01","sheet_02","sheet_0"]`)。
源码的请求写法是:
```js
$.post(loadSheetUrl, {"gridKey" : server.gridKey, "index": sheetindex.join(",")}, function (d) {})
```
> 参见源码 [`src/controllers/sheetmanage.js`](https://github.com/mengshukeji/Luckysheet/blob/master/src/controllers/sheetmanage.js)
返回的数据为sheet的`celldata`字段数据集合。
一个合格的接口返回的json字符串数据为:
```js
"{
"sheet_01": [
{
"r": 0,
"c": 0,
"v": { "v": 1, "m": "1", "ct": { "fa": "General", "t": "n" } }
}
],
"sheet_02": [
{
"r": 0,
"c": 0,
"v": { "v": 1, "m": "1", "ct": { "fa": "General", "t": "n" } }
}
],
"sheet_0": [
{
"r": 0,
"c": 0,
"v": { "v": 1, "m": "1", "ct": { "fa": "General", "t": "n" } }
}
]
}"
```
同`loadUrl`类似,`loadSheetUrl`也要注意这几点:
+ 这是一个字符串格式数据
+ 这是一个post请求
+ 这个接口会在两种情况下自动调用,一是在`loadUrl`加载的当前页数据时发现当前工作表引用了其他工作表,二是切换到一个未曾加载过数据的工作表时
------------
### allowUpdate
- 类型:Boolean
- 默认值:false
- 作用:是否允许操作表格后的后台更新,与`updateUrl`配合使用
- 作用:是否允许操作表格后的后台更新,与`updateUrl`配合使用。如果要开启共享编辑,此参数必须设置为`true`。
------------
### updateUrl
- 类型:String
- 默认值:""
- 作用:操作表格后的后台更新地址,在`allowUpdate`为`true`时才会有效,此接口也是共享编辑的接口地址。
- 作用:操作表格后,实时保存数据的websocket地址,此接口也是共享编辑的接口地址。
有个注意点,要想开启共享编辑,必须满足以下四个条件:
+ `allowUpdate`为`true`
+ 配置了`loadUrl`
+ 配置了`loadSheetUrl`
+ 配置了`updateUrl`
注意,还需要配置`loadUrl`和`loadSheetUrl`才能生效
通过共享编辑功能,可以实现Luckysheet实时保存数据和多人同步数据,每一次操作都会发送不同的参数到后台,具体的操作类型和参数参见[表格操作](/zh/guide/operate.html)
------------
### updateImageUrl
@ -744,6 +829,25 @@ Luckysheet开放了更细致的自定义配置选项,分别有
- {String} [oldColor]: 修改前当前sheet页颜色
- {String} [newColor]: 修改后当前sheet页颜色
------------
### sheetZoomBefore
- 类型:Function
- 默认值:null
- 作用:sheet缩放前
- 参数:
- {Number} [i]: sheet页的`index`
- {String} [zoom]: 当前sheet页缩放比例
------------
### sheetZoomAfter
- 类型:Function
- 默认值:null
- 作用:sheet缩放后
- 参数:
- {Number} [i]: sheet页的`index`
- {String} [oldZoom]: 修改前当前sheet页缩放比例
- {String} [newZoom]: 修改后当前sheet页缩放比例
------------
## 工作簿

2
docs/zh/guide/operate.md

@ -1,6 +1,6 @@
# 表格操作
每一次操作都会保存历史记录,用于撤销和重做,如果在表格初始化的时候设置了`allowUpdate`为`true`和`updateUrl`数据更新地址,则会通过websocket将操作实时更新到后台,并且支持共享编辑
每一次操作都会保存历史记录,用于撤销和重做,如果在表格初始化的时候开启了[共享编辑](/zh/guide/config.html#updateurl)功能,则会通过websocket将操作实时更新到后台。
> 源码 [`src/controllers/server.js`](https://github.com/mengshukeji/Luckysheet/blob/master/src/controllers/server.js) 模块实现了后台保存功能

22
src/controllers/cellFormat.js

@ -6,6 +6,7 @@ import menuButton from './menuButton';
import {checkProtectionNotEnable} from './protection';
import { jfrefreshgrid } from '../global/refresh';
import locale from '../locale/locale';
import { setcellvalue } from '../global/setdata';
let isInitialCellFormatModel = false;
@ -22,9 +23,17 @@ function initialCellFormatModelEvent(){
hidden = hidden==true?1:0;
let d = recycleSeletion(
function(cell){
cell.lo = locked;
cell.hi = hidden;
function(cell, r, c, data){
if(cell==null){
setcellvalue(r, c, data, {
lo:locked,
hi:hidden
});
}
else{
cell.lo = locked;
cell.hi = hidden;
}
},
function(){
alert(local_cellFormat.sheetDataIsNullAlert);
@ -79,7 +88,8 @@ function recycleSeletion(cycleFunction, dataIsNullFunction){
// }
// count++;
cycleFunction(cell);
cycleFunction(cell, r, c, data);
}
}
}
@ -152,12 +162,12 @@ export function openCellFormatModel(){
recycleSeletion(
function(cell){
// let cell = data[r][c];
if(cell.lo==null || cell.lo==1){
if(cell==null || cell.lo==null || cell.lo==1){
locked = true;
lockedCount++;
}
if(cell.hi==1){
if(cell!=null && cell.hi==1){
hidden = true;
hiddenCount++;
}

3
src/controllers/constant.js

@ -530,9 +530,6 @@ function rightclickHTML(){
<button id="luckysheet-matrix-delerpt-column" class="btn btn-primary luckysheet-mousedown-cancel" style="margin-left:5px;padding:2px 3px;line-height:12px;font-size:12px;">${rightclick.byCol}</button>
</div>
</div>
<div class="luckysheet-cols-menuitem luckysheet-mousedown-cancel">
<div class="luckysheet-cols-menuitem-content luckysheet-mousedown-cancel">${rightclick.generateNewMatrix}</div>
</div>
</div>`;
}

64
src/controllers/imageCtrl.js

@ -97,19 +97,19 @@ const imageCtrl = {
let src = imgItem.src;
let imgItemParam = _this.getImgItemParam(imgItem);
let width = imgItemParam.width*Store.zoomRatio;
let height = imgItemParam.height*Store.zoomRatio;
let left = imgItemParam.left*Store.zoomRatio;
let top = imgItemParam.top*Store.zoomRatio;
let width = imgItemParam.width * Store.zoomRatio;
let height = imgItemParam.height * Store.zoomRatio;
let left = imgItemParam.left * Store.zoomRatio;
let top = imgItemParam.top * Store.zoomRatio;
let position = imgItemParam.position;
let borderWidth = imgItem.border.width;
return `<div id="${id}" class="luckysheet-modal-dialog luckysheet-modal-dialog-image" style="width:${width}px;height:${height}px;padding:0;position:${position};left:${left}px;top:${top}px;z-index:200;">
<div class="luckysheet-modal-dialog-content" style="width:100%;height:100%;overflow:hidden;position:relative;">
<img src="${src}" style="position:absolute;width:${imgItem.default.width*Store.zoomRatio}px;height:${imgItem.default.height*Store.zoomRatio}px;left:${-imgItem.crop.offsetLeft*Store.zoomRatio}px;top:${-imgItem.crop.offsetTop*Store.zoomRatio}px;" />
<img src="${src}" style="position:absolute;width:${imgItem.default.width * Store.zoomRatio}px;height:${imgItem.default.height * Store.zoomRatio}px;left:${-imgItem.crop.offsetLeft * Store.zoomRatio}px;top:${-imgItem.crop.offsetTop * Store.zoomRatio}px;" />
</div>
<div class="luckysheet-modal-dialog-border" style="border:${borderWidth}px ${imgItem.border.style} ${imgItem.border.color};border-radius:${imgItem.border.radius*Store.zoomRatio}px;position:absolute;left:${-borderWidth}px;right:${-borderWidth}px;top:${-borderWidth}px;bottom:${-borderWidth}px;"></div>
<div class="luckysheet-modal-dialog-border" style="border:${borderWidth}px ${imgItem.border.style} ${imgItem.border.color};border-radius:${imgItem.border.radius * Store.zoomRatio}px;position:absolute;left:${-borderWidth}px;right:${-borderWidth}px;top:${-borderWidth}px;bottom:${-borderWidth}px;"></div>
</div>`;
},
getSliderHtml: function() {
@ -354,10 +354,10 @@ const imageCtrl = {
let item = _this.images[id];
let imgItemParam = _this.getImgItemParam(item);
let width = imgItemParam.width*Store.zoomRatio;
let height = imgItemParam.height*Store.zoomRatio;
let left = imgItemParam.left*Store.zoomRatio;
let top = imgItemParam.top*Store.zoomRatio;
let width = imgItemParam.width * Store.zoomRatio;
let height = imgItemParam.height * Store.zoomRatio;
let left = imgItemParam.left * Store.zoomRatio;
let top = imgItemParam.top * Store.zoomRatio;
let position = imgItemParam.position;
$("#luckysheet-modal-dialog-activeImage").show().css({
@ -370,19 +370,19 @@ const imageCtrl = {
$("#luckysheet-modal-dialog-activeImage .luckysheet-modal-dialog-content").css({
"background-image": "url(" + item.src + ")",
"background-size": item.default.width*Store.zoomRatio + "px " + item.default.height*Store.zoomRatio + "px",
"background-position": -item.crop.offsetLeft*Store.zoomRatio + "px " + -item.crop.offsetTop*Store.zoomRatio + "px"
"background-size": item.default.width * Store.zoomRatio + "px " + item.default.height * Store.zoomRatio + "px",
"background-position": -item.crop.offsetLeft * Store.zoomRatio + "px " + -item.crop.offsetTop * Store.zoomRatio + "px"
})
$("#luckysheet-modal-dialog-activeImage .luckysheet-modal-dialog-border").css({
"border-width": item.border.width*Store.zoomRatio,
"border-width": item.border.width * Store.zoomRatio,
"border-style": item.border.style,
"border-color": item.border.color,
"border-radius": item.border.radius*Store.zoomRatio,
"left": -item.border.width*Store.zoomRatio,
"right": -item.border.width*Store.zoomRatio,
"top": -item.border.width*Store.zoomRatio,
"bottom": -item.border.width*Store.zoomRatio,
"border-radius": item.border.radius * Store.zoomRatio,
"left": -item.border.width * Store.zoomRatio,
"right": -item.border.width * Store.zoomRatio,
"top": -item.border.width * Store.zoomRatio,
"bottom": -item.border.width * Store.zoomRatio,
})
_this.sliderHtmlShow();
@ -591,10 +591,10 @@ const imageCtrl = {
let imgItem = _this.images[_this.currentImgId];
let imgItemParam = _this.getImgItemParam(imgItem);
let width = imgItemParam.width*Store.zoomRatio;
let height = imgItemParam.height*Store.zoomRatio;
let left = imgItemParam.left*Store.zoomRatio;
let top = imgItemParam.top*Store.zoomRatio;
let width = imgItemParam.width * Store.zoomRatio;
let height = imgItemParam.height * Store.zoomRatio;
let left = imgItemParam.left * Store.zoomRatio;
let top = imgItemParam.top * Store.zoomRatio;
let position = imgItemParam.position;
$("#" + _this.currentImgId).show().css({
@ -605,20 +605,20 @@ const imageCtrl = {
"position": position
});
$("#" + _this.currentImgId + " img").css({
"width": imgItem.default.width*Store.zoomRatio,
"height": imgItem.default.height*Store.zoomRatio,
"left": -imgItem.crop.offsetLeft*Store.zoomRatio,
"top": -imgItem.crop.offsetTop*Store.zoomRatio
"width": imgItem.default.width * Store.zoomRatio,
"height": imgItem.default.height * Store.zoomRatio,
"left": -imgItem.crop.offsetLeft * Store.zoomRatio,
"top": -imgItem.crop.offsetTop * Store.zoomRatio
});
$("#" + _this.currentImgId + " .luckysheet-modal-dialog-border").css({
"border-width": imgItem.border.width*Store.zoomRatio,
"border-width": imgItem.border.width * Store.zoomRatio,
"border-style": imgItem.border.style,
"border-color": imgItem.border.color,
"border-radius": imgItem.border.radius*Store.zoomRatio,
"left": -imgItem.border.width*Store.zoomRatio,
"right": -imgItem.border.width*Store.zoomRatio,
"top": -imgItem.border.width*Store.zoomRatio,
"bottom": -imgItem.border.width*Store.zoomRatio,
"border-radius": imgItem.border.radius * Store.zoomRatio,
"left": -imgItem.border.width * Store.zoomRatio,
"right": -imgItem.border.width * Store.zoomRatio,
"top": -imgItem.border.width * Store.zoomRatio,
"bottom": -imgItem.border.width * Store.zoomRatio,
})
_this.currentImgId = null;

50
src/controllers/postil.js

@ -208,18 +208,21 @@ const luckysheetPostil = {
let toX = col;
let toY = row_pre;
let fromX = toX + 18;
let fromY = toY - 18;
let fromX = toX + 18 * Store.zoomRatio;
let fromY = toY - 18 * Store.zoomRatio;
if(fromY < 0){
fromY = 2;
}
let width = _this.defaultWidth * Store.zoomRatio
let height = _this.defaultHeight * Store.zoomRatio
let size = _this.getArrowCanvasSize(fromX, fromY, toX, toY);
let html = '<div id="luckysheet-postil-overshow">' +
'<canvas class="arrowCanvas" width="'+ size[2] +'" height="'+ size[3] +'" style="position:absolute;left:'+ size[0] +'px;top:'+ size[1] +'px;z-index:100;pointer-events:none;"></canvas>' +
'<div style="width:132px;min-height:72px;color:#000;padding:5px;border:1px solid #000;background-color:rgb(255,255,225);position:absolute;left:'+ fromX +'px;top:'+ fromY +'px;z-index:100;">'+ value +'</div>' +
'<div style="width:'+ (width - 12) +'px;min-height:'+ (height - 12) +'px;color:#000;padding:5px;border:1px solid #000;background-color:rgb(255,255,225);position:absolute;left:'+ fromX +'px;top:'+ fromY +'px;z-index:100;">'+ value +'</div>' +
'</div>';
$(html).appendTo($("#luckysheet-cell-main"));
@ -349,10 +352,10 @@ const luckysheetPostil = {
let toX = col;
let toY = row_pre;
let left = postil["left"] == null ? toX + 18 : postil["left"];
let top = postil["top"] == null ? toY - 18 : postil["top"];
let width = postil["width"] == null ? _this.defaultWidth : postil["width"];
let height = postil["height"] == null ? _this.defaultHeight : postil["height"];
let left = postil["left"] == null ? toX + 18 * Store.zoomRatio : postil["left"] * Store.zoomRatio;
let top = postil["top"] == null ? toY - 18 * Store.zoomRatio : postil["top"] * Store.zoomRatio;
let width = postil["width"] == null ? _this.defaultWidth * Store.zoomRatio : postil["width"] * Store.zoomRatio;
let height = postil["height"] == null ? _this.defaultHeight * Store.zoomRatio : postil["height"] * Store.zoomRatio;
let value = postil["value"] == null ? "" : postil["value"];
if(top < 0){
@ -418,18 +421,21 @@ const luckysheetPostil = {
let toX = col;
let toY = row_pre;
let fromX = toX + 18;
let fromY = toY - 18;
let fromX = toX + 18 * Store.zoomRatio;
let fromY = toY - 18 * Store.zoomRatio;
if(fromY < 0){
fromY = 2;
}
let width = _this.defaultWidth * Store.zoomRatio;
let height = _this.defaultHeight * Store.zoomRatio;
let size = _this.getArrowCanvasSize(fromX, fromY, toX, toY);
let html = '<div id="luckysheet-postil-show_'+ r +'_'+ c +'" class="luckysheet-postil-show luckysheet-postil-show-active">' +
'<canvas class="arrowCanvas" width="'+ size[2] +'" height="'+ size[3] +'" style="position:absolute;left:'+ size[0] +'px;top:'+ size[1] +'px;z-index:100;pointer-events:none;"></canvas>' +
'<div class="luckysheet-postil-show-main" style="width:144px;height:84px;color:#000;padding:5px;border:1px solid #000;background-color:rgb(255,255,225);position:absolute;left:'+ fromX +'px;top:'+ fromY +'px;box-sizing:border-box;z-index:100;">' +
'<div class="luckysheet-postil-show-main" style="width:'+ width +'px;height:'+ height +'px;color:#000;padding:5px;border:1px solid #000;background-color:rgb(255,255,225);position:absolute;left:'+ fromX +'px;top:'+ fromY +'px;box-sizing:border-box;z-index:100;">' +
'<div class="luckysheet-postil-dialog-move">' +
'<div class="luckysheet-postil-dialog-move-item luckysheet-postil-dialog-move-item-t" data-type="t"></div>' +
'<div class="luckysheet-postil-dialog-move-item luckysheet-postil-dialog-move-item-r" data-type="r"></div>' +
@ -507,10 +513,10 @@ const luckysheetPostil = {
let toX = col;
let toY = row_pre;
let left = postil["left"] == null ? toX + 18 : postil["left"];
let top = postil["top"] == null ? toY - 18 : postil["top"];
let width = postil["width"] == null ? _this.defaultWidth : postil["width"];
let height = postil["height"] == null ? _this.defaultHeight : postil["height"];
let left = postil["left"] == null ? toX + 18 * Store.zoomRatio : postil["left"] * Store.zoomRatio;
let top = postil["top"] == null ? toY - 18 * Store.zoomRatio : postil["top"] * Store.zoomRatio;
let width = postil["width"] == null ? _this.defaultWidth * Store.zoomRatio : postil["width"] * Store.zoomRatio;
let height = postil["height"] == null ? _this.defaultHeight * Store.zoomRatio : postil["height"] * Store.zoomRatio;
let value = postil["value"] == null ? "" : postil["value"];
if(top < 0){
@ -609,10 +615,10 @@ const luckysheetPostil = {
let toX = col;
let toY = row_pre;
let left = postil["left"] == null ? toX + 18 : postil["left"];
let top = postil["top"] == null ? toY - 18 : postil["top"];
let width = postil["width"] == null ? _this.defaultWidth : postil["width"];
let height = postil["height"] == null ? _this.defaultHeight : postil["height"];
let left = postil["left"] == null ? toX + 18 * Store.zoomRatio : postil["left"] * Store.zoomRatio;
let top = postil["top"] == null ? toY - 18 * Store.zoomRatio : postil["top"] * Store.zoomRatio;
let width = postil["width"] == null ? _this.defaultWidth * Store.zoomRatio : postil["width"] * Store.zoomRatio;
let height = postil["height"] == null ? _this.defaultHeight * Store.zoomRatio : postil["height"] * Store.zoomRatio;
let value = postil["value"] == null ? "" : postil["value"];
if(top < 0){
@ -723,10 +729,10 @@ const luckysheetPostil = {
let toX = col;
let toY = row_pre;
let left = postil["left"] == null ? toX + 18 : postil["left"];
let top = postil["top"] == null ? toY - 18 : postil["top"];
let width = postil["width"] == null ? _this.defaultWidth : postil["width"];
let height = postil["height"] == null ? _this.defaultHeight : postil["height"];
let left = postil["left"] == null ? toX + 18 * Store.zoomRatio : postil["left"] * Store.zoomRatio;
let top = postil["top"] == null ? toY - 18 * Store.zoomRatio : postil["top"] * Store.zoomRatio;
let width = postil["width"] == null ? _this.defaultWidth * Store.zoomRatio : postil["width"] * Store.zoomRatio;
let height = postil["height"] == null ? _this.defaultHeight * Store.zoomRatio : postil["height"] * Store.zoomRatio;
let value = postil["value"] == null ? "" : postil["value"];
if(top < 0){

4
src/controllers/zoom.js

@ -5,6 +5,7 @@ import sheetmanage from './sheetmanage';
import {changeSheetContainerSize} from './resize';
import { jfrefreshgrid_rhcw } from '../global/refresh';
import server from './server';
import luckysheetPostil from './postil';
import imageCtrl from './imageCtrl';
@ -31,6 +32,9 @@ export function zoomChange(ratio){
let currentSheet = sheetmanage.getSheetByIndex();
//批注
luckysheetPostil.buildAllPs(currentSheet.data);
//图片
imageCtrl.images = currentSheet.images;
imageCtrl.allImagesShow();

24
src/demoData/demoFeature.js

@ -0,0 +1,24 @@
// Features specially written for demo
(function() {
// language
function language(params) {
var lang = navigator.language||navigator.userLanguage;//常规浏览器语言和IE浏览器
lang = lang.substr(0, 2);//截取lang前2位字符
return lang;
}
// Tencent Forum Link Button
function supportButton() {
const text = language() === 'zh' ? '反馈' : 'Forum';
document.querySelector("body").insertAdjacentHTML('beforeend', `<a id="container" href="https://support.qq.com/product/288322" target="_blank" style="z-index:2;width:50px;height:50px;line-height:50px;position:fixed;right:40px;bottom:86px;border-radius:50px;cursor:pointer;background:rgb(71,133,249);color:#fff;text-align:center;text-decoration:none;">${text}</a>`);
}
supportButton()
})()

751
src/global/api.js

@ -1,5 +1,5 @@
import Store from "../store";
import { replaceHtml, getObjType, chatatABC } from "../utils/util";
import { replaceHtml, getObjType, chatatABC, luckysheetactiveCell } from "../utils/util";
import { getSheetIndex, getluckysheet_select_save, getluckysheetfile } from "../methods/get";
import locale from "../locale/locale";
@ -20,6 +20,7 @@ import { luckysheetDeleteCell, luckysheetextendtable, luckysheetdeletetable } fr
import { isRealNull, valueIsError, isRealNum, isEditMode, hasPartMC } from "./validate";
import { isdatetime, diff } from "./datecontroll";
import { getBorderInfoCompute } from './border';
import { luckysheetDrawMain } from './draw';
import server from "../controllers/server";
import menuButton from '../controllers/menuButton';
@ -31,8 +32,11 @@ import sheetmanage from '../controllers/sheetmanage';
import conditionformat from '../controllers/conditionformat';
import { luckysheet_searcharray } from "../controllers/sheetSearch";
import { selectHightlightShow, selectIsOverlap } from '../controllers/select';
import { sheetHTML } from '../controllers/constant';
import { sheetHTML, luckysheetdefaultstyle } from '../controllers/constant';
import { createFilterOptions } from '../controllers/filter';
import controlHistory from '../controllers/controlHistory';
import { zoomRefreshView, zoomNumberDomBind } from '../controllers/zoom';
import dataVerificationCtrl from "../controllers/dataVerificationCtrl";
const IDCardReg = /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i;
@ -1174,6 +1178,113 @@ export function showColumn(startIndex, endIndex, options = {}) {
showRowOrColumn('column', startIndex, endIndex, options);
}
/**
* 设置指定行的高度
* @param {Object} rowInfo 行数和高度对应关系
* @param {Object} options 可选参数
* @param {Number} options.order 工作表索引默认值为当前工作表索引
* @param {Function} options.success 操作结束的回调函数
*/
export function setRowHeight(rowInfo, options = {}) {
if(getObjType(rowInfo) != 'object'){
return tooltip.info("The rowInfo parameter is invalid.", "");
}
let {
order = getSheetIndex(Store.currentSheetIndex),
success
} = {...options}
let file = Store.luckysheetfile[order];
if(file == null){
return tooltip.info("The order parameter is invalid.", "");
}
let cfg = $.extend(true, {}, file.config);
if(cfg['rowlen'] == null){
cfg['rowlen'] = {};
}
for(let r in rowInfo){
if(parseInt(r) >= 0){
let len = rowInfo[r];
if(Number(len) >= 0){
cfg['rowlen'][parseInt(r)] = Number(len);
}
}
}
file.config = cfg;
server.saveParam("cg", file.index, cfg["rowlen"], { "k": "rowlen" });
if(file.index == Store.currentSheetIndex){
Store.config = cfg;
jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
}
if (success && typeof success === 'function') {
success()
}
}
/**
* 设置指定列的宽度
* @param {Object} columnInfo 行数和高度对应关系
* @param {Object} options 可选参数
* @param {Number} options.order 工作表索引默认值为当前工作表索引
* @param {Function} options.success 操作结束的回调函数
*/
export function setColumnWidth(columnInfo, options = {}) {
if(getObjType(columnInfo) != 'object'){
return tooltip.info("The columnInfo parameter is invalid.", "");
}
let {
order = getSheetIndex(Store.currentSheetIndex),
success
} = {...options}
let file = Store.luckysheetfile[order];
if(file == null){
return tooltip.info("The order parameter is invalid.", "");
}
let cfg = $.extend(true, {}, file.config);
if(cfg['columnlen'] == null){
cfg['columnlen'] = {};
}
for(let c in columnInfo){
if(parseInt(c) >= 0){
let len = columnInfo[c];
if(Number(len) >= 0){
cfg['columnlen'][parseInt(c)] = Number(len);
}
}
}
file.config = cfg;
server.saveParam("cg", file.index, cfg["columnlen"], { "k": "columnlen" });
if(file.index == Store.currentSheetIndex){
Store.config = cfg;
jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
}
if (success && typeof success === 'function') {
success()
}
}
/**
* 返回当前选区对象的数组可能存在多个选区
* 每个选区的格式为row/column信息组成的对象{row:[0,1],column:[0,1]}
@ -4662,6 +4773,104 @@ export function setSheetMove(type, options = {}) {
}
/**
* 重新排序所有工作表的位置指定工作表顺序的数组
* @param {Array} orderList 工作表顺序设置工作表的index和order来指定位置
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
export function setSheetOrder(orderList, options = {}) {
if(orderList == null || orderList.length == 0){
return tooltip.info("Type orderList not available", "");
}
let orderListMap = {};
orderList.forEach((item) => {
orderListMap[item.index.toString()] = item.order;
})
Store.luckysheetfile.sort((x, y) => {
let order_x = orderListMap[x.index.toString()];
let order_y = orderListMap[y.index.toString()];
if(order_x != null && order_y != null){
return order_x - order_y;
}
else if(order_x != null){
return -1;
}
else if(order_y != null){
return 1;
}
else{
return 1;
}
})
let orders = {};
Store.luckysheetfile.forEach((item, i, arr) => {
arr[i].order = i;
orders[item.index.toString()] = i;
if(i > 0){
let preIndex = arr[i - 1].index;
$("#luckysheet-sheets-item" + item.index).insertAfter($("#luckysheet-sheets-item" + preIndex));
}
})
server.saveParam("shr", null, orders);
let {
success
} = {...options}
if (success && typeof success === 'function') {
success();
}
}
/**
* 设置工作表缩放比例
* @param {Number} zoom 工作表缩放比例值范围为0.1 ~ 4
* @param {Object} options 可选参数
* @param {Number} options.order 工作表下标默认值为当前工作表下标
* @param {Function} options.success 操作结束的回调函数
*/
export function setSheetZoom(zoom, options = {}) {
if(getObjType(zoom) != 'number' && (zoom < 0.1 || zoom > 4)){
return tooltip.info("The zoom parameter is invalid.", "");
}
let {
order = getSheetIndex(Store.currentSheetIndex),
success
} = {...options}
let file = Store.luckysheetfile[order];
if(file == null){
return tooltip.info("The order parameter is invalid.", "");
}
file["zoomRatio"] = zoom;
server.saveParam("all", file.index, zoom, { "k": "zoomRatio" });
if(file.index == Store.currentSheetIndex){
Store.zoomRatio = zoom;
zoomNumberDomBind();
zoomRefreshView();
}
if (success && typeof success === 'function') {
success();
}
}
/**
* 显示指定下标工作表的网格线返回操作的工作表对象
* @param {Object} options 可选参数
@ -4738,6 +4947,24 @@ export function hideGridLines(options = {}){
}
/**
* 刷新canvas
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
export function refresh(options = {}) {
luckysheetrefreshgrid();
let {
success
} = {...options}
if (success && typeof success === 'function') {
success();
}
}
/**
* 滚动当前工作表位置
* @param {Object} options 可选参数
@ -4801,7 +5028,6 @@ export function scroll(options = {}){
/**
* 根据窗口大小自动resize画布
*
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
@ -4818,6 +5044,200 @@ export function resize(options = {}){
}
/**
* 返回指定选区截图后生成的base64格式的图片
* @param {Object} options 可选参数
* @param {Object | String} options.range 选区范围只能为单个选区默认为当前选区
*/
export function getScreenshot(options = {}) {
let {
range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
} = {...options}
if(getObjType(range) == 'string'){
if(!formula.iscelldata(range)){
return tooltip.info("The range parameter is invalid.", "");
}
let cellrange = formula.getcellrange(range);
range = {
"row": cellrange.row,
"column": cellrange.column
};
}
if(getObjType(range) != 'object' || range.row == null || range.column == null){
return tooltip.info("The range parameter is invalid.", "");
}
let str = range.row[0],
edr = range.row[1],
stc = range.column[0],
edc = range.column[1];
let has_PartMC = hasPartMC(Store.config, str, edr, stc, edc);
if(has_PartMC){
return tooltip.info('Cannot perform this operation on partially merged cells', '');
}
let visibledatarow = Store.visibledatarow;
let visibledatacolumn = Store.visibledatacolumn;
let scrollHeight, rh_height;
if (str - 1 < 0) {
scrollHeight = 0;
rh_height = visibledatarow[edr];
}
else {
scrollHeight = visibledatarow[str - 1];
rh_height = visibledatarow[edr] - visibledatarow[str - 1];
}
let scrollWidth, ch_width;
if (stc - 1 < 0) {
scrollWidth = 0;
ch_width = visibledatacolumn[edc];
}
else {
scrollWidth = visibledatacolumn[stc - 1];
ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1];
}
let newCanvas = $("<canvas>").attr({
width: Math.ceil(ch_width * Store.devicePixelRatio),
height: Math.ceil(rh_height * Store.devicePixelRatio)
}).css({ width: ch_width, height: rh_height });
luckysheetDrawMain(scrollWidth, scrollHeight, ch_width, rh_height, 1, 1, null, null, newCanvas);
let ctx_newCanvas = newCanvas.get(0).getContext("2d");
//补上 左边框和上边框
ctx_newCanvas.beginPath();
ctx_newCanvas.moveTo(
0,
0
);
ctx_newCanvas.lineTo(
0,
Store.devicePixelRatio * rh_height
);
ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
ctx_newCanvas.stroke();
ctx_newCanvas.closePath();
ctx_newCanvas.beginPath();
ctx_newCanvas.moveTo(
0,
0
);
ctx_newCanvas.lineTo(
Store.devicePixelRatio * ch_width,
0
);
ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
ctx_newCanvas.stroke();
ctx_newCanvas.closePath();
let url = newCanvas.get(0).toDataURL("image/png");
return url;
}
/**
* 设置工作簿名称
* @param {String} name 工作簿名称
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
export function setWorkbookName(name, options = {}) {
if(name == null || name.toString().length == 0){
return tooltip.info("The name parameter is invalid.", "");
}
$("#luckysheet_info_detail_input").val(name);
let {
success
} = {...options}
if (success && typeof success === 'function') {
success();
}
}
/**
* 撤销当前操作返回刚刚撤销的操作对象
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
export function undo(options = {}) {
let ctr = $.extend(true, {}, Store.jfredo[Store.jfredo.length - 1]);
controlHistory.redo(new Event('custom'));
luckysheetactiveCell();
let {
success
} = {...options}
setTimeout(() => {
if (success && typeof success === 'function') {
success();
}
}, 1);
return ctr;
}
/**
* 重做当前操作返回刚刚重做的操作对象
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
export function redo(options = {}) {
let ctr = $.extend(true, {}, Store.jfundo[Store.jfundo.length - 1]);
controlHistory.undo(new Event('custom'));
luckysheetactiveCell();
let {
success
} = {...options}
setTimeout(() => {
if (success && typeof success === 'function') {
success();
}
}, 1);
return ctr;
}
/**
* 返回所有工作表配置
*/
export function getAllSheets() {
let data = $.extend(true, [], Store.luckysheetfile);
data.forEach((item, index, arr) => {
if(item.data != null && item.data.length > 0){
item.celldata = sheetmanage.getGridData(item.data);
}
delete item.load;
})
return data;
}
/**
* 根据index获取sheet页配置
*
@ -4940,6 +5360,304 @@ export function getLuckysheetfile(){
return getluckysheetfile();
}
/**
* 指定工作表范围设置数据验证功能并设置参数
* @param {Object} optionItem 数据验证的配置信息
* @param {String} optionItem.type 类型
* @param {String | Null} optionItem.type2 条件类型
* @param {String | Number} optionItem.value1 条件值1
* @param {String | Number} optionItem.value2 条件值2
* @param {Boolean} optionItem.checked 选中状态
* @param {Boolean} optionItem.remote 自动远程获取选项
* @param {Boolean} optionItem.prohibitInput 输入数据无效时禁止输入
* @param {Boolean} optionItem.hintShow 选中单元格时显示提示语
* @param {String} optionItem.hintText 提示语文本
* @param {Object} options 可选参数
* @param {Array | Object | String} options.range 选区范围默认为当前选区
* @param {Number} options.order 工作表下标默认值为当前工作表下标
* @param {Function} options.success 操作结束的回调函数
*/
export function setDataVerification(optionItem, options = {}) {
if(getObjType(optionItem) != 'object'){
return tooltip.info("The optionItem parameter is invalid.", "");
}
let {
type,
type2 = null,
value1 = '',
value2 = '',
remote = false,
prohibitInput = false,
hintShow = false,
hintText = ''
} = {...optionItem}
let typeValues = ["dropdown", "checkbox", "number", "number_integer", "number_decimal", "text_content", "text_length", "date", "validity"];
let type2Values_1 = ["bw", "nb", "eq", "ne", "gt", "lt", "gte", "lte"];
let type2Values_2 = ["include", "exclude", "equal"];
let type2Values_3 = ["bw", "nb", "eq", "ne", "bf", "nbf", "af", "naf"];
let type2Values_4 = ["card", "phone"];
if(!typeValues.includes(type)){
return tooltip.info("The optionItem.type parameter is invalid.", "");
}
let dvText = locale().dataVerification;
if(type == 'dropdown'){
if(value1.length == 0){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo1);
return;
}
}
else if(type == 'checkbox'){
if(value1.length == 0 || value2.length == 0){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo2);
return;
}
}
else if(type == 'number' || type == 'number_integer' || type == 'number_decimal'){
if(!type2Values_1.includes(type2)){
return tooltip.info("The optionItem.type2 parameter is invalid.", "");
}
if(!isRealNum(value1)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
return;
}
if(type2 == 'bw' || type2 == 'nb'){
if(!isRealNum(value2)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
return;
}
if(Number(value2) < Number(value1)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo4);
return;
}
}
}
else if(type == 'text_content'){
if(!type2Values_2.includes(type2)){
return tooltip.info("The optionItem.type2 parameter is invalid.", "");
}
if(value1.length == 0){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo5);
return;
}
}
else if(type == 'text_length'){
if(!type2Values_1.includes(type2)){
return tooltip.info("The optionItem.type2 parameter is invalid.", "");
}
if(!isRealNum(value1)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
return;
}
if(type2 == 'bw' || type2 == 'nb'){
if(!isRealNum(value2)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
return;
}
if(Number(value2) < Number(value1)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo4);
return;
}
}
}
else if(type == 'date'){
if(!type2Values_3.includes(type2)){
return tooltip.info("The optionItem.type2 parameter is invalid.", "");
}
if(!isdatetime(value1)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo6);
return;
}
if(type2 == 'bw' || type2 == 'nb'){
if(!isdatetime(value2)){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo6);
return;
}
if(diff(value1, value2) > 0){
tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo7);
return;
}
}
}
else if(type == 'validity'){
if(!type2Values_4.includes(type2)){
return tooltip.info("The optionItem.type2 parameter is invalid.", "");
}
}
if(getObjType(remote) != 'boolean'){
return tooltip.info("The optionItem.remote parameter is invalid.", "");
}
if(getObjType(prohibitInput) != 'boolean'){
return tooltip.info("The optionItem.prohibitInput parameter is invalid.", "");
}
if(getObjType(hintShow) != 'boolean'){
return tooltip.info("The optionItem.hintShow parameter is invalid.", "");
}
let {
range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
order = getSheetIndex(Store.currentSheetIndex),
success
} = {...options}
if(getObjType(range) == 'string'){
if(!formula.iscelldata(range)){
return tooltip.info("The range parameter is invalid.", "");
}
let cellrange = formula.getcellrange(range);
range = {
"row": cellrange.row,
"column": cellrange.column
};
}
if(getObjType(range) != 'object' || range.row == null || range.column == null){
return tooltip.info("The range parameter is invalid.", "");
}
let file = Store.luckysheetfile[order];
if(file == null){
return tooltip.info("The order parameter is invalid.", "");
}
let item = {
type: type,
type2: type2,
value1: value1,
value2: value2,
checked: false,
remote: remote,
prohibitInput: prohibitInput,
hintShow: hintShow,
hintText: hintText,
}
let currentDataVerification = $.extend(true, {}, file.dataVerification);
let data = $.extend(true, [], file.data);
if(data.length == 0){
data = sheetmanage.buildGridData(file);
}
let str = range.row[0],
edr = range.row[1],
stc = range.column[0],
edc = range.column[1];
for(let r = str; r <= edr; r++){
for(let c = stc; c <= edc; c++){
currentDataVerification[r + '_' + c] = item;
if(type == 'checkbox'){
setcellvalue(r, c, data, item.value2);
}
}
}
if(file.index == Store.currentSheetIndex){
let historyDataVerification = $.extend(true, {}, file.dataVerification);
if(type == 'checkbox'){
dataVerificationCtrl.refOfCheckbox(historyDataVerification, currentDataVerification, Store.currentSheetIndex, data, range);
}
else{
dataVerificationCtrl.ref(historyDataVerification, currentDataVerification, Store.currentSheetIndex);
}
}
else{
file.dataVerification = currentDataVerification;
file.data = data;
}
if (success && typeof success === 'function') {
success();
}
}
/**
* 指定工作表范围删除数据验证功能
* @param {Object} options 可选参数
* @param {Array | Object | String} options.range 选区范围默认为当前选区
* @param {Number} options.order 工作表下标默认值为当前工作表下标
* @param {Function} options.success 操作结束的回调函数
*/
export function deleteDataVerification(options = {}) {
let {
range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
order = getSheetIndex(Store.currentSheetIndex),
success
} = {...options}
if(getObjType(range) == 'string'){
if(!formula.iscelldata(range)){
return tooltip.info("The range parameter is invalid.", "");
}
let cellrange = formula.getcellrange(range);
range = {
"row": cellrange.row,
"column": cellrange.column
};
}
if(getObjType(range) != 'object' || range.row == null || range.column == null){
return tooltip.info("The range parameter is invalid.", "");
}
let file = Store.luckysheetfile[order];
if(file == null){
return tooltip.info("The order parameter is invalid.", "");
}
let currentDataVerification = $.extend(true, {}, file.dataVerification);
let str = range.row[0],
edr = range.row[1],
stc = range.column[0],
edc = range.column[1];
for(let r = str; r <= edr; r++){
for(let c = stc; c <= edc; c++){
delete currentDataVerification[r + '_' + c];
}
}
if(file.index == Store.currentSheetIndex){
let historyDataVerification = $.extend(true, {}, file.dataVerification);
dataVerificationCtrl.ref(historyDataVerification, currentDataVerification, Store.currentSheetIndex);
}
else{
file.dataVerification = currentDataVerification;
}
if (success && typeof success === 'function') {
success();
}
}
/**
* data => celldata data二维数组数据转化成 {r, c, v}格式一维数组
*
@ -4948,18 +5666,39 @@ export function getLuckysheetfile(){
* @param {Function} options.success 操作结束的回调函数
*/
export function transToCellData(data, options = {}){
let {
success
} = {...options}
setTimeout(()=>{
if (success && typeof success === 'function') {
success();
}
},0)
return sheetmanage.getGridData(data)
}
/**
* celldata => data celldata一维数组数据转化成表格所需二维数组
*
* @param {Array} celldata 二维数组数据
* @param {Object} options 可选参数
* @param {Function} options.success 操作结束的回调函数
*/
export function transToData(celldata, options = {}){
let {
success
} = {...options}
setTimeout(()=>{
if (success && typeof success === 'function') {
success();
}
},0)
return sheetmanage.buildGridData({
celldata: celldata
})
}

8
src/global/draw.js

@ -1112,7 +1112,7 @@ let nullCellRender = function(r, c, start_r, start_c, end_r, end_c,luckysheetTab
//若单元格有批注
if(Store.flowdata[r][c] != null && Store.flowdata[r][c].ps != null){
let ps_w = 5*Store.zoomRatio, ps_h = 5*Store.zoomRatio;
let ps_w = 8*Store.zoomRatio, ps_h = 8*Store.zoomRatio;
luckysheetTableContent.beginPath();
luckysheetTableContent.moveTo( (end_c + offsetLeft - 1- ps_w), (start_r + offsetTop));
luckysheetTableContent.lineTo( (end_c + offsetLeft - 1), (start_r + offsetTop));
@ -1338,17 +1338,17 @@ let cellRender = function(r, c, start_r, start_c, end_r, end_c, value, luckyshee
let verticalAlignPos_text = (pos_y + verticalCellHeight - space_height) ; //文本垂直方向基准线
luckysheetTableContent.textBaseline = "bottom";
let verticalAlignPos_checkbox = verticalAlignPos_text - 13;
let verticalAlignPos_checkbox = verticalAlignPos_text - 13 * Store.zoomRatio;
if(verticalAlign == "0"){ //居中对齐
verticalAlignPos_text = (pos_y + verticalCellHeight / 2);
luckysheetTableContent.textBaseline = "middle";
verticalAlignPos_checkbox = verticalAlignPos_text - 6;
verticalAlignPos_checkbox = verticalAlignPos_text - 6 * Store.zoomRatio;
}
else if(verticalAlign == "1"){ //上对齐
verticalAlignPos_text = (pos_y + space_height);
luckysheetTableContent.textBaseline = "top";
verticalAlignPos_checkbox = verticalAlignPos_text + 1;
verticalAlignPos_checkbox = verticalAlignPos_text + 1 * Store.zoomRatio;
}
horizonAlignPos = horizonAlignPos / Store.zoomRatio;

4
src/global/formula.js

@ -1238,7 +1238,7 @@ const luckysheetformula = {
}
if(!checkProtectionLocked(r, c, Store.currentSheetIndex)){
return
return;
}
//数据验证 输入数据无效时禁止输入
@ -5188,6 +5188,7 @@ const luckysheetformula = {
"r": u.r,
"c": u.c,
"v": v[1],
"f": v[2],
"spe":v[3],
"index": u.index
});
@ -5226,6 +5227,7 @@ const luckysheetformula = {
}
}
updateValue.v = item.v;
updateValue.f = item.f;
setcellvalue(item.r, item.c, data, updateValue);
server.saveParam("v", item.index, item.v, {
"r": item.r,

18
src/global/getRowlen.js

@ -928,14 +928,16 @@ function getCellTextInfo(cell , ctx, option){
let height = textWidth * Math.sin(rt*Math.PI/180) + textHeight * Math.cos(rt*Math.PI/180);//consider text box wdith and line height
let lastWord = str.substr(str.length-1,1);
if(lastWord==" " || checkWordByteLength(lastWord)==2){
spaceOrTwoByte = {
index:i,
str:preStr,
width:preTextWidth,
height:preTextHeight,
asc:preMeasureText.actualBoundingBoxAscent,
desc:preMeasureText.actualBoundingBoxDescent,
};
if(preMeasureText!=null){
spaceOrTwoByte = {
index:i,
str:preStr,
width:preTextWidth,
height:preTextHeight,
asc:preMeasureText.actualBoundingBoxAscent,
desc:preMeasureText.actualBoundingBoxDescent,
};
}
}
// textW_all += textW;

5
src/global/setdata.js

@ -15,13 +15,16 @@ function setcellvalue(r, c, d, v) {
let vupdate;
if(getObjType(v) == "object"){
if(cell==null){
if(cell == null){
cell = v;
}
else{
if(v.f != null){
cell.f = v.f;
}
// else{
// delete cell.f;
// }
if(v.spl != null){
cell.spl = v.spl;

8
src/index.html

@ -16,6 +16,7 @@
<!-- rollup luckysheet.js -->
<script src="./luckysheet.umd.js"></script>
</head>
<body>
@ -55,7 +56,7 @@
],
data:
[sheetCell,sheetFormula,sheetConditionFormat,sheetSparkline,sheetTable,sheetComment,sheetPivotTableData,sheetPivotTable,sheetChart,sheetPicture,sheetDataVerification]
/*[{"name":"Sheet1","config":{"columnlen":{"0":241},"rowlen":{"0":81,"17":100}},"index":"1","status":"1","order":"0","luckysheet_select_save":[{"row":[0,0],"column":[4,4],"sheetIndex":1}],"zoomRatio":1,"showGridLines":"1","defaultColWidth":72,"defaultRowHeight":18,"celldata":[
/*[{"name":"Sheet1","config":{"columnlen":{"0":241},"rowlen":{"0":81}},"index":"1","status":"1","order":"0","luckysheet_select_save":[{"row":[0,0],"column":[4,4],"sheetIndex":1}],"zoomRatio":1,"showGridLines":"1","defaultColWidth":72,"defaultRowHeight":18,"celldata":[
{"r":0,"c":0,
"v":{
"ct":{
@ -93,7 +94,7 @@
"qp":1,
}
},
{"r":17,"c":2,"v":{"v":"Luckysheet is good project, this is true","ct":{"fa":"General","t":"g"},"bg":null,"bl":0,"it":0,"ff":0,"fs":"11","fc":"rgb(51, 51, 51)","ht":1,"vt":1,tb:2,"m":"Luckysheet"}}
{"r":17,"c":2,"v":{"v":"Luckysheet","ct":{"fa":"General","t":"g"},"bg":null,"bl":0,"it":0,"ff":0,"fs":"11","fc":"rgb(51, 51, 51)","ht":1,"vt":1,"m":"Luckysheet"}}
],"calcChain":[]}]*/
/*
@ -1787,6 +1788,9 @@
})
</script>
<!-- demo feature, non-production use -->
<script src="./demoData/demoFeature.js"></script>
</body>
</html>
Loading…
Cancel
Save