├── .gitignore
├── README.md
├── dist
└── daodaoExcel.js
├── package-lock.json
├── package.json
├── src
├── cell.js
├── config.js
├── contextMenu.js
├── copyedCell.js
├── edit.js
├── event.js
├── icon.js
├── main.js
├── scroll.js
├── selectCell.js
├── tableHeaderCell.js
├── tableIndexCell.js
├── toolBar.js
├── uploadFile.js
└── utils.js
├── test
└── index.html
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .history
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # daodaoExcel
2 | 基于canvas的和js开发的类excel表格,支持用户自定义添加工具栏以及右键菜单
3 |
4 | 项目演示:
5 | [https://ostask.github.io/daodaoExcel/](https://ostask.github.io/daodaoExcel/)
6 |
7 | ## 基本使用
8 |
9 | ### 初始化
10 | 引入daodaoExcel dist目录中的js文件
11 | 在使用 ZRender 前需要初始化实例,具体方式是传入一个对象,对象的id属性为dom的id
12 | 示例:
13 | ```html
14 |
15 |
16 |
19 | ```
20 |
21 | ### 实例属性(实例使用 excel 命名)
22 | #### excel.cells
23 | 该excel中所有的单元格
24 |
25 | ##### 1.excel.cells[x][y].data
26 | 该单元格的数据以及格式
27 |
28 | | 属性 | 默认值 | 类型 | 含义 |
29 | | ---------- | ------ |------ |---------------------------------- |
30 | | name |(根据位置改变)| String |单元格的名字|
31 | | border | false | Boolean |是否有边框,true:有边框。false:无边框|
32 | | cellHeight | 30 | Number |单元格高度 |
33 | | cellWidth | 100 | Number |单元格宽度 |
34 | | fill | #ffffff| String |单元格颜色 |
35 | | fontFamily | 微软雅黑| String |单元格字体 |
36 | | fontSize | 14 | Number |字体大小 |
37 | | fontStyle | normal | String |字体样式,同css的fontStyle |
38 | | fontWeight | normal |String |字体粗细, 同css的fontWeight |
39 | | merge | false |Boolean |该单元格是否被合并,true:被合并, false:没有合并,注:如果merge为true,且mergeConfig为null,span为0,row为0的时候,该单元格不会显示出来|
40 | | row | 1 | Number |单元格占几行,注:单独设置row不会起作用,还要设置cellWidth,以及merge,mergeConfig,不建议自己改这个值 |
41 | | span | 1 | Number |单元格占几列,注:单独设置span不会起作用,还要设置cellHeight,以及merge,mergeConfig,不建议自己改这个值 |
42 | | text | "" | String |单元格的文字 |
43 | | textAlign |center | String |单元格的文字对齐方式(center,left,right)|
44 | | textFill |#000000 | String |单元格文字的颜色 |
45 | | x |(根据位置改变)| Number |单元格所在的横向的坐标 ,坐标从零开始 |
46 | | xPlace |(根据位置改变)| Number |单元格左上角的像素坐标 |
47 | | y |(根据位置改变)| Number |单元格所在的纵向的坐标 ,坐标从零开始 |
48 | | yPlace |(根据位置改变)| Number |单元格左上角的像素坐标 |
49 | | ltIcon |"none" | String |单元格左上角的图标 ,注:有五个默认的图标 "upArrow","downArrow","leftArrow","rightArrow","filter",暂时不支持别的图标,有需求联系作者 |
50 | | lbIcon |"none" | String |单元格左下角的图标 ,注:有五个默认的图标 "upArrow","downArrow","leftArrow","rightArrow","filter",暂时不支持别的图标,有需求联系作者 |
51 | | rtIcon |"none" | String |单元格右上角的图标 ,注:有五个默认的图标 "upArrow","downArrow","leftArrow","rightArrow","filter",暂时不支持别的图标,有需求联系作者 |
52 | | rbIcon |"none" | String |单元格右下角的图标 ,注:有五个默认的图标 "upArrow","downArrow","leftArrow","rightArrow","filter",暂时不支持别的图标,有需求联系作者 |
53 | | imgUrl |"none" | String |单元格的图片地址|
54 |
55 | ##### 2.其它属性
56 | 参考zrender的Elements属性
57 |
58 | #### excel.activeCell
59 | 当前激活的单元格
60 |
61 | #### excel.selectCells
62 | 当前选中的单元格
63 |
64 | #### excel.copyCells
65 | 当前复制在剪贴板中的数据
66 |
67 | #### excel.tableHeaderCell
68 | 最顶端A-Z的表头
69 |
70 | ##### 1.excel.tableHeaderCell.data
71 | 该表头的数据以及格式
72 |
73 | | 属性 | 默认值 | 类型 | 含义 |
74 | | ---------- | ------ |------ |---------------------------------- |
75 | | index | (根据位置改变)| Number |表头顺序 |
76 | | width | 100 | Number |表头宽度 |
77 | | xPlace | (根据位置改变)| Number |表头像素位置 |
78 |
79 | #### excel.tableIndexCell
80 | 表格最左的列头
81 |
82 | ##### 1. excel.tableIndexCell.data
83 | 该列头的数据以及格式
84 |
85 | | 属性 | 默认值 | 类型 | 含义 |
86 | | ---------- | ------ |------ |---------------------------------- |
87 | | index | (根据位置改变)| Number |列头顺序 |
88 | | height | 30 | Number |列头高度 |
89 | | yPlace | (根据位置改变)| Number |列头像素位置 |
90 |
91 | #### excel.edit
92 | 编辑框对象
93 |
94 | ##### 1. excel.edit.editEle
95 | 编辑框DOM
96 |
97 | #### excel.contextMenu
98 | 右键菜单对象
99 |
100 | ##### 1. excel.contextMenu.menuEl
101 | 右键菜单的DOM对象
102 | ##### 2. excel.contextMenu.menus
103 | 右键菜单按钮列表
104 |
105 | #### excel.uploadFile
106 | 上传图片的对象
107 |
108 | #### excel.toolBar
109 | 工具条对象
110 |
111 | ##### 1. excel.toolBar.el
112 | 工具条DOM
113 |
114 | ##### 2. excel.toolBar.parent
115 | 工具条父元素DOM
116 |
117 | ##### 3. excel.toolBar.copyButton
118 | 复制按钮
119 |
120 | ##### 4. excel.toolBar.pasteButton
121 | 粘贴按钮
122 |
123 | ##### 5. excel.toolBar.clearFormat
124 | 清除按钮
125 |
126 | ##### 6. excel.toolBar.typeFaceButton
127 | 选择字体按钮
128 |
129 | ##### 7. excel.toolBar.fontSizeButton
130 | 字体大小按钮
131 |
132 | ##### 8. excel.toolBar.fontWeightButton
133 | 加粗按钮
134 |
135 | ##### 9. excel.toolBar.fontItalicButton
136 | 斜体按钮
137 |
138 | ##### 10. excel.toolBar.textFillButton
139 | 文字颜色按钮
140 |
141 | ##### 11. excel.toolBar.fillButton
142 | 背景颜色按钮
143 |
144 | ##### 12. excel.toolBar.borderButton
145 | 边框按钮
146 |
147 | ##### 13. excel.toolBar.alignLiftButton
148 | 左对齐按钮
149 |
150 | ##### 14. excel.toolBar.alignRightButton
151 | 右对齐按钮
152 |
153 | ##### 15. excel.toolBar.alignCenterButton
154 | 居中对齐按钮
155 |
156 | ##### 16. excel.toolBar.mergeCellButton
157 | 合并单元格按钮
158 |
159 | ##### 17. excel.toolBar.splitCellButton
160 | 拆分单元格按钮
161 |
162 | ##### 18. excel.toolBar.addImageButton
163 | 添加图片按钮
164 |
165 |
166 | ### 实例方法(实例使用 excel 命名)
167 | #### 1. excel.refreshCell()
168 | 刷新视图
169 | #### 2. excel.getTableDatas()
170 | 获取全部数据
171 |
172 | #### 3. excel.setTableDatas(config)
173 | 批量填入数据
174 | config:
175 | | 属性 | 默认值 | 类型 | 含义 |
176 | | ---------- | ------ |------ |---------------------------------- |
177 | | data | 必填 | Array | 一维数组 或 二维数组,data中,x和y是必须的,其它属性参考 excel.cells[x][y].data |
178 | | clear | false | Boolean |填充数据时是否清空其他数据 true:清空 false:不清空 |
179 |
180 | #### 4. excel.cells[x][y].setData(data)
181 | 修改单元格数据,data格式见上面 cells.data
182 |
183 | #### 5. excel.cells[x][y].clear()
184 | 清除单元格数据,不清除格式
185 |
186 | #### 6. excel.cells[x][y].clearFormat()
187 | 清除单元格格式,不清除数据
188 |
189 | #### 7. excel.setSpanNum(number)
190 | 修改excel的列数
191 |
192 | #### 8. excel.setRowNum(number)
193 | 修改excel的行数
194 |
195 | #### 9. 获取某个单元格数据
196 | 这个功能我没有写。。大家可以用 excel.cells 的data (hhh,我是懒鬼,反正没人用,那我就怎么开心怎么来啦)
197 |
198 | #### 10. 添加右键菜单
199 | excel.contextMenu.addButton(text,func)
200 | 例:添加清除内容按钮
201 | ```js
202 | const clearInputBtn = excel.contextMenu.addButton('清除内容',() => {
203 | this.selectCells.forEach(cell => {
204 | cell.clear()
205 | })
206 | })
207 | ```
208 | #### 11. 添加工具菜单
209 | excel.toolBar.addButton(html,tooltip,func)
210 | 例:
211 | ```js
212 | const sayHello = excel.toolBar.addButton(
213 | '',
214 | '说你好',
215 | (e)=>{
216 | alert('你好')
217 | }
218 | )
219 | ```
220 |
221 |
222 | #### 10. excel.dispose()
223 | 移除自身。当不再需要使用该实例时,调用该方法以释放内存。
224 |
225 | #### 11. excel.setSelect(xstart,ystart,xend?,yend?)
226 | 设置选中的单元格
227 | #### 12. excel.clearTableDatasAndFormat()
228 | 初始化表格
229 |
230 | ### 事件
231 |
232 | #### 1.单元格点击事件
233 | clickCell事件
234 |
235 | 例:
236 | ```js
237 | excel.on("clickCell",(data) =>{
238 | console.log(data)
239 | })
240 |
241 | ```
242 | #### 2.上下左右切换单元格事件
243 | moveCell事件
244 |
245 | 例:
246 | ```js
247 | excel.on("moveCell",(data) =>{
248 | console.log(data)
249 | })
250 |
251 | ```
252 |
253 | #### 3.单元格数据改变事件
254 | changeCell事件
255 |
256 | 例:
257 | ```js
258 | excel.on("changeCell",(data) =>{
259 | console.log(data)
260 | })
261 |
262 | ```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "daodaoexcel",
3 | "version": "1.0.0",
4 | "description": "a web excel plugin",
5 | "main": "src/main.js",
6 | "scripts": {
7 | "dev": "webpack-dev-server --hot --open --contentBase test",
8 | "build": "webpack --config webpack.config.js --grogress --colors",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "uuid": "^7.0.2",
15 | "zrender": "^4.3.0"
16 | },
17 | "devDependencies": {
18 | "webpack": "^4.42.1",
19 | "webpack-cli": "^3.3.11",
20 | "webpack-dev-server": "^3.10.3"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/cell.js:
--------------------------------------------------------------------------------
1 | import zrender from 'zrender'
2 | import {headerHeight,indexWidth} from './config'
3 | import {generateCode } from "./utils.js"
4 |
5 | import icons from './icon'
6 |
7 | class Cell extends zrender.Group{
8 | constructor(data){
9 | let defaultCellConfig = {
10 | cursor:'default',
11 | scale:[0.5,0.5],
12 | z:1
13 | }
14 | //由于zrender的Rect描边粗细有bug,所以我的做法是先放大两倍然后再缩放0.5倍
15 | let xPlace = data.cellWidth * data.x + indexWidth
16 | let yPlace = data.cellHeight * data.y + headerHeight
17 | let countConfig = {
18 | shape:{
19 | x:1,
20 | y:1,
21 | width:data.cellWidth * 2,
22 | height:data.cellHeight * 2
23 | },
24 | style:{
25 | truncate:{
26 | outerWidth:data.cellWidth
27 | },
28 | stroke: '#aaa',
29 | fill: 'white',
30 | lineWidth:'1',
31 | fontSize:data.fontSize,
32 | fontFamily:data.fontFamily,
33 | fontWeight:data.fontWeight,
34 | fontStyle:data.fontStyle,
35 | textFill:data.textFill,
36 | fill:data.fill,
37 | textAlign:'center',
38 | textPosition:'inside',
39 | textOffset:[0,0]
40 | }
41 | }
42 | let finnalconfig = Object.assign({},defaultCellConfig,countConfig)
43 | super({
44 | position:[xPlace,yPlace]
45 | })
46 | this.data = Object.assign({},data,{
47 | xPlace:xPlace,
48 | yPlace:yPlace,
49 | name:generateCode(data.x)+(data.y+1)
50 | })
51 | this.handlers = {}
52 | this.type = 'cell'
53 | this.img = null
54 | this.ltIcon = null
55 | this.rtIcon = null
56 | this.lbIcon = null
57 | this.rbIcon = null
58 | this.cell = null
59 | this.init(finnalconfig)
60 | }
61 | //初始化单元格
62 | init(finnalconfig){
63 | this.cell = new zrender.Rect(finnalconfig)
64 | this.cell.type = 'cellborder'
65 | this.add(this.cell)
66 | }
67 | addEvent(type,handler){
68 | if(typeof this.handlers[type] === "undefined"){
69 | this.handlers[type] = []
70 | }
71 | this.handlers[type].push(handler)
72 | }
73 | emit(type,event){
74 | if(!event.target){
75 | event.target = this
76 | }
77 | if(this.handlers[type] instanceof Array){
78 | const handlers = this.handlers[type]
79 | handlers.forEach((handler)=>{
80 | handler(event)
81 | })
82 | }
83 | }
84 | removeEvent(type,handler){
85 | if(this.handlers[type] instanceof Array){
86 | const handlers = this.handlers[type]
87 | for(var i = 0,len = handlers.length; i < len; i++){
88 | if(handlers[i] === handler){
89 | break;
90 | }
91 | }
92 | handlers.splice(i,1)
93 | }
94 | }
95 | //设置选中单元格样式
96 | selectCell(){
97 | let color = 'rgba(1,136,251,0.1)'
98 | let datafill = this.data.fill
99 | let newColor = zrender.color.lerp(0.8, [color,datafill],true)
100 |
101 | this.cell.attr({style:{fill:newColor.color}})
102 | }
103 | //取消选择样式重置
104 | unSelectCell(){
105 | this.cell.attr({style:{fill:this.data.fill}})
106 | }
107 | //设置单元格文字
108 | setText(text,flag){
109 | this.cell.attr({style:{text:text}})
110 | this.data.text = text
111 | if(!flag){
112 | this.emit('change',{data:this.data})
113 | }
114 | }
115 | //设置字体
116 | setFontFamily(font,flag){
117 | this.cell.attr({style:{fontFamily:font}})
118 | this.data.fontFamily = font
119 | if(!flag){
120 | this.emit('change',{data:this.data})
121 | }
122 | }
123 | //设置字体大小
124 | setFontSize(size,flag){
125 | this.cell.attr({style:{fontSize:size}})
126 | this.data.fontSize = size
127 | if(!flag){
128 | this.emit('change',{data:this.data})
129 | }
130 | }
131 | //设置文字粗细
132 | setFontWeight(data,flag){
133 | this.cell.attr({style:{fontWeight:data}})
134 | this.data.fontWeight = data
135 | if(!flag){
136 | this.emit('change',{data:this.data})
137 | }
138 | }
139 | //设置文字倾斜
140 | setFontItalic(data,flag){
141 | this.cell.attr({style:{fontStyle:data}})
142 | this.data.fontStyle = data
143 | if(!flag){
144 | this.emit('change',{data:this.data})
145 | }
146 | }
147 | //设置字体颜色
148 | setTextFill(data,flag){
149 | this.cell.attr({style:{textFill:data}})
150 | this.data.textFill = data
151 | if(!flag){
152 | this.emit('change',{data:this.data})
153 | }
154 | }
155 | //设置背景颜色
156 | setFill(data,flag){
157 | this.cell.attr({style:{fill:data}})
158 | this.data.fill = data
159 | if(!flag){
160 | this.emit('change',{data:this.data})
161 | }
162 | }
163 | //设置边框
164 | setBorder(data,flag){
165 | if(data===true || data == 'true'){
166 | this.cell.attr({style:{stroke: '#000',lineWidth:2}})
167 | this.cell.attr({z:2})
168 | this.data.border = true
169 | }else{
170 | this.cell.attr({style:{stroke: '#aaa',lineWidth:1}})
171 | this.cell.attr({z:1})
172 | this.data.border = false
173 | }
174 | if(!flag){
175 | this.emit('change',{data:this.data})
176 | }
177 | }
178 | //设置对齐方式
179 | setTextAlign(data,flag){
180 | if(data == 'left'){
181 | this.cell.attr({style:{
182 | textAlign:'left',
183 | textPosition:'left',
184 | textOffset:[10,0]
185 | }})
186 | }else if(data == 'right'){
187 | this.cell.attr({style:{
188 | textAlign:'right',
189 | textPosition:'right',
190 | textOffset:[-10,0]
191 | }})
192 | }else if(data == 'center'){
193 | this.cell.attr({style:{
194 | textAlign:'center',
195 | textPosition:'inside',
196 | textOffset:[0,0]
197 | }})
198 | }
199 | this.data.textAlign = data
200 | if(!flag){
201 | this.emit('change',{data:this.data})
202 | }
203 | }
204 | //设置单元格data
205 | setData(data){
206 | this.data = Object.assign({},this.data,data)
207 | this.data.name = generateCode(this.data.x)+(this.data.y+1)
208 | //更改cell的大小
209 | this.cell.attr({shape:{
210 | width:this.data.cellWidth * 2,
211 | height:this.data.cellHeight * 2
212 | }})
213 | this.cell.attr({
214 | style:{
215 | truncate:{
216 | outerWidth:data.cellWidth
217 | }
218 | }
219 | })
220 | //更改图片
221 | if(this.data.imgUrl){
222 | this.addImage(this.data.imgUrl,true)
223 | }else{
224 | if(this.img){
225 | this.removeImage(true)
226 | }else{
227 |
228 | }
229 | }
230 | //更改cell的显示隐藏
231 | if(this.data.merge == true && (this.data.row == 0 || this.data.span == 0)){
232 | this.hide()
233 | }else{
234 | this.show()
235 | }
236 | this.attr('position',[this.data.xPlace,this.data.yPlace])
237 | //更改文字
238 | this.setText(this.data.text,true)
239 | //更改字体
240 | this.setFontFamily(this.data.fontFamily,true)
241 | //更改字体大小
242 | this.setFontSize(this.data.fontSize,true)
243 | //更改文字粗细
244 | this.setFontWeight(this.data.fontWeight,true)
245 | //更改文字倾斜
246 | this.setFontItalic(this.data.fontStyle,true)
247 | //更改字体颜色
248 | this.setTextFill(this.data.textFill,true)
249 | //更改背景颜色
250 | this.setFill(this.data.fill,true)
251 | //设置边框
252 | this.setBorder(this.data.border,true)
253 | //设置对齐方式
254 | this.setTextAlign(this.data.textAlign,true)
255 | //设置左上角标
256 | this.setLTIcon(this.data.ltIcon,true)
257 | //设置右上角标
258 | this.setRTIcon(this.data.rtIcon,true)
259 | //设置左下角标
260 | this.setLBIcon(this.data.lbIcon,true)
261 | //设置右下角标
262 | this.setRBIcon(this.data.rbIcon,true)
263 |
264 | this.emit('change',{data:this.data})
265 | }
266 | //设置左上角标
267 | setLTIcon(data,flag){
268 | if(data){
269 | if(data == "none"){
270 | if(this.ltIcon){
271 | this.remove(this.ltIcon)
272 | this.ltIcon = null
273 | }
274 | }else{
275 | if(this.ltIcon){
276 | this.remove(this.ltIcon)
277 | }
278 | this.ltIcon = new zrender.Image({
279 | style:{
280 | image:icons[data],
281 | x:0,
282 | y:0,
283 | width:15,
284 | height:15
285 | },
286 | z:3
287 | })
288 | this.add(this.ltIcon)
289 | }
290 | }else{
291 | if(this.ltIcon){
292 | this.remove(this.ltIcon)
293 | this.ltIcon = null
294 | }
295 | }
296 | this.data.ltIcon = data
297 | if(!flag){
298 | this.emit('change',{data:this.data})
299 | }
300 | }
301 | //设置左下角标
302 | setLBIcon(data,flag){
303 | if(data){
304 | if(data == "none"){
305 | if(this.lbIcon){
306 | this.remove(this.lbIcon)
307 | this.lbIcon = null
308 | }
309 | }else{
310 | if(this.lbIcon){
311 | this.remove(this.lbIcon)
312 | }
313 | this.lbIcon = new zrender.Image({
314 | style:{
315 | image:icons[data],
316 | x:0,
317 | y:this.data.cellHeight - 15,
318 | width:15,
319 | height:15
320 | },
321 | z:3
322 | })
323 | this.add(this.lbIcon)
324 | }
325 | }else{
326 | if(this.lbIcon){
327 | this.remove(this.lbIcon)
328 | this.lbIcon = null
329 | }
330 | }
331 | this.data.lbIcon = data
332 | if(!flag){
333 | this.emit('change',{data:this.data})
334 | }
335 | }
336 | //设置右上角标
337 | setRTIcon(data,flag){
338 | if(data){
339 | if(data == "none"){
340 | if(this.rtIcon){
341 | this.remove(this.rtIcon)
342 | this.rtIcon = null
343 | }
344 | }else{
345 | if(this.rtIcon){
346 | this.remove(this.rtIcon)
347 | }
348 | this.rtIcon = new zrender.Image({
349 | style:{
350 | image:icons[data],
351 | x:this.data.cellWidth - 15,
352 | y:0,
353 | width:15,
354 | height:15
355 | },
356 | z:3
357 | })
358 | this.add(this.rtIcon)
359 | }
360 | }else{
361 | if(this.rtIcon){
362 | this.remove(this.rtIcon)
363 | this.rtIcon = null
364 | }
365 | }
366 | this.data.rtIcon = data
367 | if(!flag){
368 | this.emit('change',{data:this.data})
369 | }
370 | }
371 | //设置右下角标
372 | setRBIcon(data,flag){
373 | if(data){
374 | if(data == "none"){
375 | if(this.rbIcon){
376 | this.remove(this.rbIcon)
377 | this.rbIcon = null
378 | }
379 | }else{
380 | if(this.rbIcon){
381 | this.remove(this.rbIcon)
382 | }
383 | this.rbIcon = new zrender.Image({
384 | style:{
385 | image:icons[data],
386 | x:this.data.cellWidth - 15,
387 | y:this.data.cellHeight - 15,
388 | width:15,
389 | height:15
390 | },
391 | z:3
392 | })
393 | this.add(this.rbIcon)
394 | }
395 | }else{
396 | if(this.rbIcon){
397 | this.remove(this.rbIcon)
398 | this.rbIcon = null
399 | }
400 | }
401 | this.data.rbIcon = data
402 | if(!flag){
403 | this.emit('change',{data:this.data})
404 | }
405 | }
406 | //添加图片
407 | addImage(url,flag){
408 | this.data.imgUrl = url
409 | if(this.img){
410 | this.remove(this.img)
411 | this.img = null
412 | }
413 | this.setText("")
414 | this.img = new zrender.Image({
415 | style:{
416 | image:url,
417 | x:0,
418 | y:0,
419 | width:this.data.cellWidth,
420 | height:this.data.cellHeight
421 | },
422 | z:2
423 | })
424 | this.add(this.img)
425 | if(!flag){
426 | this.emit('change',{data:this.data})
427 | }
428 | }
429 | removeImage(flag){
430 | console.log(this.img)
431 | if(this.img){
432 | this.remove(this.img)
433 | this.img = null
434 | this.data.imgUrl = ""
435 | }
436 | if(!flag){
437 | this.emit('change',{data:this.data})
438 | }
439 | }
440 | clear(){
441 | let data = {
442 | fontFamily:'微软雅黑',
443 | fontSize:14,
444 | fontStyle:'normal',
445 | fontWeight:'normal',
446 | textFill:'#000000',
447 | fill:'#ffffff',
448 | border:false,
449 | textAlign:'center',
450 | cellWidth:this.data.cellWidth,
451 | cellHeight:this.data.cellHeight,
452 | merge:this.data.merge,
453 | row:this.data.row,
454 | span:this.data.span,
455 | mergeConfig:this.data.mergeConfig,
456 | text:"",
457 | xPlace:this.data.xPlace,
458 | yPlace:this.data.yPlace,
459 | imgUrl:'',
460 | x:this.data.x,
461 | y:this.data.y
462 | }
463 | this.data = data
464 | this.setData(data)
465 | this.emit('change',{data:this.data})
466 | }
467 | clearFormat(){
468 | let data = {
469 | fontFamily:'微软雅黑',
470 | fontSize:14,
471 | fontStyle:'normal',
472 | fontWeight:'normal',
473 | textFill:'#000000',
474 | fill:'#ffffff',
475 | border:false,
476 | textAlign:'center',
477 | }
478 | this.setData(data)
479 | this.emit('change',{data:this.data})
480 | }
481 | }
482 |
483 | export default Cell
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | export const letter = [
2 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
3 | ]
4 |
5 | export const defaultTableConfig = {
6 | cellWidth:100,
7 | cellHeight:30,
8 | row:30,
9 | span:26
10 | }
11 |
12 | //表头高度
13 | export const headerHeight = 20
14 | //列头宽度
15 | export const indexWidth = 50
16 | //scrollWidth
17 | export const scrollWidth = 10
--------------------------------------------------------------------------------
/src/contextMenu.js:
--------------------------------------------------------------------------------
1 | class ContextMenu {
2 | constructor(parent){
3 | this.menus = [
4 |
5 | ]
6 | this.menuEl = null
7 | this.init(parent)
8 | }
9 | init(parent){
10 | //创建右键菜单dom
11 | this.menuEl = document.createElement('div')
12 | this.menuEl.id = "daodao_excel_menu"
13 | this.menuEl.style.cssText = `
14 | position:absolute;
15 | padding:2px;
16 | width:150px;
17 | display:none;
18 | background:#fff;
19 | border:1px solid #d2d2d2;
20 | `
21 | parent.appendChild(this.menuEl)
22 | }
23 | showMenu(x,y){
24 | this.menuEl.style.display = "block"
25 | this.menuEl.style.top = y + 'px'
26 | this.menuEl.style.left = x + 'px'
27 | }
28 | hideMenu(){
29 | this.menuEl.style.display = "none"
30 | }
31 | addButton(name,func){
32 | let btn = document.createElement('div')
33 | btn.innerText = name
34 | btn.style.cssText = `
35 | cursor: pointer;
36 | padding: 4px;
37 | color: #6b6b6b;
38 | font-size: 14px;
39 | `
40 | this.menuEl.appendChild(btn)
41 | this.menus.push(btn)
42 | btn.addEventListener("click",func)
43 | return btn
44 | }
45 | }
46 |
47 | export default ContextMenu
--------------------------------------------------------------------------------
/src/copyedCell.js:
--------------------------------------------------------------------------------
1 | import zrender from 'zrender'
2 | import {headerHeight,indexWidth} from './config'
3 |
4 | class CopyedCell extends zrender.Rect{
5 | constructor(cells){
6 | let originConfig = {
7 | cursor:'default',
8 | style:{
9 | stroke: '#4e9fff',
10 | lineWidth:'2',
11 | fill:'none',
12 | lineDash:[5]
13 | },
14 | z:3
15 | }
16 | let config = {
17 | shape:{
18 |
19 | }
20 | }
21 | //cells的长度为1的时候
22 | if(cells.length == 1){
23 | config.shape = {
24 | x:cells[0].data.xPlace,
25 | y:cells[0].data.yPlace,
26 | width:cells[0].data.cellWidth,
27 | height:cells[0].data.cellHeight
28 | }
29 | }else{
30 | let x = cells[0].data.xPlace
31 | let y = cells[0].data.yPlace
32 | let width = cells[cells.length - 1].data.xPlace + cells[cells.length - 1].data.cellWidth - x
33 | let height = cells[cells.length - 1].data.yPlace + cells[cells.length - 1].data.cellHeight - y
34 |
35 | //下标最小的是起点
36 | config.shape.x = x
37 | config.shape.y = y
38 | //下标最大的是终点
39 | config.shape.width = width
40 | config.shape.height = height
41 | }
42 | let finalConfig = Object.assign({},originConfig,config)
43 | super(finalConfig)
44 | }
45 | //改变选择框的位置和大小
46 | change(cells){
47 | let shape = {}
48 | if(cells.length == 1){
49 | shape = {
50 | x:cells[0].data.xPlace,
51 | y:cells[0].data.yPlace,
52 | width:cells[0].data.cellWidth,
53 | height:cells[0].data.cellHeight
54 | }
55 | }else{
56 | //求最小下标以及最大下标
57 | let x = cells[0].data.xPlace
58 | let y = cells[0].data.yPlace
59 | let width = cells[cells.length - 1].data.xPlace + cells[cells.length - 1].data.cellWidth - x
60 | let height = cells[cells.length - 1].data.yPlace + cells[cells.length - 1].data.cellHeight - y
61 |
62 | //下标最小的是起点
63 | shape.x = x
64 | shape.y = y
65 | //下标最大的是终点
66 | shape.width = width
67 | shape.height = height
68 | }
69 | this.attr('shape',shape)
70 | }
71 | }
72 |
73 | export default CopyedCell
--------------------------------------------------------------------------------
/src/edit.js:
--------------------------------------------------------------------------------
1 | import Event from './event.js'
2 |
3 | class Edit extends Event{
4 | constructor(parent){
5 | super()
6 | //编辑框的dom元素
7 | this.editEle = null
8 | this.editFlag = false
9 | this.init(parent)
10 | }
11 | init(parent){
12 | //初始化编辑框
13 | this.editEle = document.createElement('div')
14 | this.editEle.id = "daodao_excel_edit_div"
15 | this.editEle.style.cssText = `
16 | position:absolute;
17 | text-indent:2px;
18 | outline-color:transparent;
19 | display:none;
20 | background:#fff;
21 | border:1px solid #4e9fff;
22 | `
23 | this.editEle.setAttribute("contenteditable", "true");
24 | parent.appendChild(this.editEle)
25 | }
26 | //改变编辑框的位置和大小
27 | setPosition(width,height,positionX,positionY,data){
28 | this.editEle.style.width = `${width - 2}px`
29 | this.editEle.style.minHeight = `${height - 2}px`
30 | this.editEle.style.top = `${positionY}px`
31 | this.editEle.style.left = `${positionX}px`
32 | this.editEle.textContent = data
33 | this.showEdit()
34 | this.editEle.focus()
35 | }
36 | hideEdit(){
37 | if(!this.editFlag){
38 | return false
39 | }
40 | this.emit('update',{
41 | type:'text',
42 | text:this.editEle.textContent
43 | })
44 | this.editEle.style.display = "none"
45 | this.editFlag = false
46 | this.clearInput()
47 | }
48 | showEdit(){
49 | this.editEle.style.display = "block"
50 | this.editFlag = true
51 | }
52 | clearInput(){
53 | this.editEle.innerHTML = " "
54 | }
55 | }
56 |
57 | export default Edit
--------------------------------------------------------------------------------
/src/event.js:
--------------------------------------------------------------------------------
1 | class Event {
2 | constructor(){
3 | this.handlers = {}
4 | }
5 | on(type,handler){
6 | if(typeof this.handlers[type] === "undefined"){
7 | this.handlers[type] = []
8 | }
9 | this.handlers[type].push(handler)
10 | }
11 | emit(type,event){
12 | if(!event.target){
13 | event.target = this
14 | }
15 | if(this.handlers[type] instanceof Array){
16 | const handlers = this.handlers[type]
17 | handlers.forEach((handler)=>{
18 | handler(event)
19 | })
20 | }
21 | }
22 | off(type,handler){
23 | if(this.handlers[type] instanceof Array){
24 | const handlers = this.handlers[type]
25 | for(var i = 0,len = handlers.length; i < len; i++){
26 | if(handlers[i] === handler){
27 | break;
28 | }
29 | }
30 | handlers.splice(i,1)
31 | }
32 | }
33 | }
34 |
35 | export default Event
--------------------------------------------------------------------------------
/src/icon.js:
--------------------------------------------------------------------------------
1 | const icons = {
2 | "upArrow":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABuklEQVRoQ+3Xu4rVUBjF8d8gKgheGu18BJlyYHTES2Ohj2DlC2gxMI0whSDYaGdpJwha2CiC+AqCNgoWdt5A1EYYROSDE4jhmJOZb0eJ7ECak73Pt/5rrWzIkolfSxPXrwL86wRrAjWBpAO1QkkD09trAgssPIY13MWXtN1z/mDMBC7gPvbgNU7hfWmIsQDO4eFMfKP5DVbxqSTEGABn8LgjvtH8CidLQpQGCPGPsLfH5YA4js8lkigJcAJPF4hvNL+cvRNpiFIAIf4J9rVc3UTccf3EDWy0ngdEnFBfM0mUAJgn/grutI7OANiF6x2I5zidgcgCrOBZx/kQfwsH5wCE2UUhMgBRl7c43KpAIz5++hNAPLuJy619YcLZnVQpC/AOB2aD2+IXAXQhtnAI37cLkQGIWRdxCfdwuzO8L4FmabzY67iGq9sVH+uzAH0zhwDsRPNveypAj4U1gSH9qhWqFRrSk541tUK1QrVCSQdqhZIG1lPof67QfnybAf7A7mRb5m4fs0Ix8AOO4AWWpwhwFOfxAB+nCDCG5r/2RTa6+LG/iSvAEAfGPoWGaEitqQAp+wpsnnwCvwCy1lwxNkQzpAAAAABJRU5ErkJggg==",
3 | "rightArrow":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABcklEQVRoQ+2YTUoEMRCFvwEFFyK4cCOiVxBlNiKuXagXEDycKw8hgojXkFEQ0bUHkBpaaHDMTJJHfqCyTirvq1ep7mRC52PSuX4coLaD7oA7kJkBL6HMBGYvdwciU7gHnAN3wHfk2oXTSzqwBnwB28ALcAJ85kKUBFgH3oGdQbQEoiSA6Z4CD8CmCqI0gByiBoAUohaADKImwH8QZ8NhX6lB1QZYBPE2tFjrWEtHCwBZEK0A/ELcA1tD2ldyoiUA030IPMZAhACOgKulRaifcAxcjMIGnQgBzIADvb6kiE+Adac/IwTwCuwnbadf9AycxgLsAjfAhl5PMKKV7qWihArrnm8nPcSlAaLFm8BW2miS+FYAksW3AGAXnPHX125p3fzMSW5ntc6ARHytEpKJrwEgFV8aYPwuZHt396wyfheSiC/tgO1nT4vXwC3wofjU1+pCCu3zGA4gS2ViIHcgMXGyZe6ALJWJgdyBxMTJlnXvwA8QdVAx3rcJfAAAAABJRU5ErkJggg==",
4 | "downArrow":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACCklEQVRoQ+2ZsUoDQRCGZy4kryAiWFhEsrtFaosIWgm+gZ0ognZ2PoSNqJVWPoMWgoUiKQTBYhOSCCkDQiBEwUA4GDlI4AiXu927PSEwqQK52/2/mW82R4Kw4C9c8PzAAPM6qJSqENEbAPRHo1G12+0O8+h2bh2QUp4j4ukk9IHW+nbRAK4Q8TgITUQnjUbjmgEiKpCnQtwBE+W4A/OqJKVkhVghkwqwQlmqBJDfwxwPsWFn+HuAh9hQlXmXsUKsECuUsQKsUMYC8inECv2TQiilvEDEdd/3z1qt1nvSvmmeRoUQR57nXRKR9n2/1m63f5L2MZoBIcSO53kPkx+pvolos9lsfsQtbguglNonohtEnGba1VrfOwGoVCrLhULhFQDWTCFsAGbDE9HnYDCo9nq9XycAwSLlcnmlWCzWEXHVBMIUIKLyXQDY0Fp/JYUPPjdSaLqQDYQJQNbw1gDTTpRKpZcknZIAXIRPBRDcpJRaAoB6HEQcgBBiDxHvQgNrpU1YLSuFwjcmQcwDcBk+dQemIHEQUQCuw2cGiNMJEQ/Df3AQ0dCVNk4UStIJEYMvutrkyH0CgC0Xzs8eralnYHahWZ1izvDUAxu1pjOAKJ0iNnQa3skMWHTCefhcAEKdeAaA9QlgLuFzAwhBPAbvx+PxdqfT6Zs829he43QGbDd3cT0DuKhiljX+AOZNFk86C5VQAAAAAElFTkSuQmCC",
5 | "leftArrow":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAB0UlEQVRoQ+3XP6hPcRzG8ddNkjJcGQw2M6PlRiQyK6PRYjTehZgYlMVotBikpJBMBgwG/xY7SUJEJvrWufXrdq57zvc51723vmf+Pp/P836e7+/0O3M2+TO3yf1rAOvdYGugNRAm0K5QGGAsbw3EEYYDWgNhgLE8bWAvDuEWfsVuKgYkAAfwEPO4g5MV+2NJLUAx/xg7OgfvsSd2UzGgBmC5+bL2NG5W7I8lYwH6zJ/DtdhJ5YAxABvOfGEeCrCABzN3vmgv4FJlcJPJhgAU84+wfWbrZSxO5iIYtBpAn/ly38u93xDPvwAu4vwyl+V1+e4/O/+Eq3jat3clgN34MOI3stZMb7BvDMAufMSWtXY2cP5r7B8DUM6exfWZFv7gJb4OXDrVsc+4gudjAcr547iHrZ34N47hyVTu0jmrvYX6IH7iKJ6ly6fQDwHog/jRNbHuEEMBVoI4jBdTJFk7YwzAEsRdbOsWfus+aF7VGkh1YwHKviO4PwPxBQfxNjVTo68B6IMoX2YnagykmlqAJYjb2IkbOJOaqdEnAGVf+Yd6qvsm/l5jINWkAOn+WN8A4gjDAa2BMMBY3hqIIwwHtAbCAGN5ayCOMBzQGggDjOV/AZVbOTEZHY7IAAAAAElFTkSuQmCC",
6 | "filter":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC9klEQVRYR+2WS2gTURSG/zOTpGlTaxJipaGtiQ8EBV9QdVGU0oWKIoqgCFYRFdoR1y5EFB8bkboQk/oG25UI4gMRbBfiA7X1sVBEBJOmtVLQTmqiqTH3Hplo1aapSWwxC3tXM8M5///NmXPPHUKeF+XZH+MA4xUYUgGvX78EYN3fNiYDDCYtqNmbvL6+OyCqTtGKC6aVIc3eOvg8FeA2gCUAdzPobM4gDClBLSHN/sbj09eAMM/QYLBbAe1I6hGvDtQ7r6UHOB2djET8EUCVgrkupDlbcoZISShv5EKTNXyfCPOYcSCoOfb9HjKsCStOhqepgjuIYBNQa0INJfdGA+Hx6VeIsBqMloDmqEvVSrsLvP7+hQxxG0yx2ukFrQvKLGtdNtUU+8r8Lpro1mN8+kh1wcFUsaPPeRbHRaNJxfxwTAavvYx16ANSY+abQZdjFdaTyArACPI09S8Hy+uuIkVtWDQBxZahrG8/ivddUbniWLWlw4g//CSxuayIztksimrcv+j9iuZnn4zLR2y1Lw1upYF0lfzjHKj09W1SiZrdJSoMCLMyVEIy8OGzGLBbVbNZRdLYWK8/JHD+cRRC8isU0uLgVkd4pM+YcRBN8fftUUCHZk4yYcv8YigZMkJhgZPtESQE9whRUNW1y9bzpx7KCGAke336GRC2VZVbsG520Yh63f0Cp9ojiAuOgE1VAa3kVaYGzgoA+1nxlIYvG928bIYVNVOtw3R7oxL+hx8xkEBcSK4N7XTezWSeHAvZBCVjLrLF+15vM6bbxjk2zC0z/0zti0mceBBB9AtLAtYGNMfVbHWzBwDgOssTir/o7UQ0M52BlLS9c6c9pwmaE4BhWnH8k9tkij8FUPo7BIOPBBucu7N988G4nAGSTenXf5wZv+wkKSs76yfeGAcYr8D/UYHKpsgslRP+739P39c/3QWDpp4T4Q0g2UhE7rwAGCCTL7CtMKrvJUVpC9Tbb/2THsjVZNTH8Vgapmr91SgeS6C8A3wDaS4vMOrG7VAAAAAASUVORK5CYII=",
7 | }
8 |
9 | export default icons
10 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import zrender from 'zrender'
2 | import Cell from './cell.js'
3 | import SelectCell from './selectCell.js'
4 | import CopyedCell from './copyedCell.js'
5 | import TableHeaderCell from './tableHeaderCell.js'
6 | import TableIndexCell from './tableIndexCell.js'
7 | import {defaultTableConfig,headerHeight,indexWidth,scrollWidth} from './config'
8 | import Scroll from './scroll.js'
9 | import Edit from './edit.js'
10 | import Event from "./event.js"
11 | import ContextMenu from './contextMenu.js'
12 | import UploadFile from './uploadFile.js'
13 | import ToolBar from './toolBar.js'
14 | import { mouseWheelDirection, preventDefault,stopPropagation ,generateUUID} from "./utils.js"
15 |
16 | class DaoDaoExcel extends Event {
17 | constructor(obj){
18 | super()
19 | //默认配置
20 | const defaultObj = defaultTableConfig
21 | this.currentObj = {...defaultObj,...obj}
22 | //zrender实例
23 | this.canvas = null
24 | //整个表格的group
25 | this.table = null
26 | //所有单元格
27 | this.cells = new Array()
28 | //当前激活的单元格
29 | this.activeCell = null
30 | //当前选中的单元格
31 | this.selectCells = []
32 | //当前在剪贴板中的单元格
33 | this.copyCells = []
34 | //选中的时候显示的那个蓝色框框
35 | this.selectedCell = null
36 | //copyCells有数据的那个蓝色框框
37 | this.copyedCell = null
38 | //表头a-z
39 | this.tableHeader = null
40 | //表头a-z单元格
41 | this.tableHeaderCell = []
42 | //列头1-n
43 | this.tableIndex = null
44 | //列头1-n的单元格
45 | this.tableIndexCell = []
46 | //左上角的那个什么也没有的格子
47 | this.selectAllCell = null
48 | //可编辑的div
49 | this.edit = null
50 | //右键菜单
51 | this.contextMenu = null
52 | //改变宽度的控制柄
53 | this.changeWidthLine = null
54 | //上传图片的组件
55 | this.uploadFile = null
56 | //工具条
57 | this.toolBar = null
58 | //默认文字状态
59 | this.textConfig = {
60 | 'fontFamily':'微软雅黑',
61 | 'fontSize':14,
62 | 'fontStyle':'normal',
63 | 'fontWeight':'normal',
64 | 'textFill':'#000000',
65 | 'fill':'#ffffff',
66 | 'border':false,
67 | 'textAlign':'center'
68 | }
69 | //当前是否正在编辑
70 | this.isActive = false
71 | this.init()
72 | }
73 | init(){
74 | //parent是canvas的包裹元素
75 | const parent = document.getElementById(this.currentObj.id)
76 | if(!parent){
77 | //如果没有包裹元素就不在执行,并抛出错误
78 | //JavaScript引擎一旦遇到throw语句,就会停止执行后面的语句,并将throw语句的参数值,返回给用户。
79 | throw new Error("没有找到id为"+this.currentObj.id+"的元素!");
80 | }
81 | const canvasWrapper = document.createElement('div')
82 | canvasWrapper.id = generateUUID()
83 | canvasWrapper.style.width = '100%'
84 | canvasWrapper.style.height = (parent.clientHeight - 30) + 'px'
85 | parent.appendChild(canvasWrapper)
86 | //新建canvas
87 | this.canvas = zrender.init(canvasWrapper);
88 | this.initCells()
89 | this.initTableHeader()
90 | this.initTableIndex()
91 | //左上角加上一个单元格
92 | this.selectAllCell = new zrender.Rect({
93 | cursor:'default',
94 | scale:[0.5,0.5],
95 | style:{
96 | stroke: '#aaa',
97 | fill: 'white',
98 | lineWidth:'1'
99 | },
100 | shape:{
101 | x:1,
102 | y:1,
103 | width:indexWidth * 2,
104 | height:headerHeight * 2
105 | },
106 | z:1001
107 | })
108 | this.canvas.add(this.selectAllCell)
109 | //初始化滚动条
110 | this.initScroll(canvasWrapper)
111 | //初始化编辑框
112 | this.initEdit(canvasWrapper)
113 | //初始化上传文件
114 | this.initUploadFile(parent)
115 | //初始化右键菜单
116 | this.initContextMenu(canvasWrapper)
117 | //初始化工具条
118 | this.initToolBar(parent)
119 | //绑定事件
120 | this.initEvents()
121 | }
122 | initUploadFile(parent){
123 | this.uploadFile = new UploadFile(parent)
124 | this.uploadFile.on('changeImage',(event) => {
125 | this.activeCell.addImage(event.url)
126 | })
127 | }
128 | //取消选择单元格
129 | cancelSelectCell(){
130 | this.selectCells.forEach(cell => {
131 | cell.unSelectCell()
132 | })
133 | this.tableHeaderCell.forEach(cell => {
134 | cell.unSelectCell()
135 | })
136 | this.tableIndexCell.forEach(cell => {
137 | cell.unSelectCell()
138 | })
139 | }
140 | //更新选择状态
141 | updateSelectState(){
142 | this.selectCells.forEach(cell => {
143 | cell.selectCell()
144 | })
145 | //初始化选中的蓝色框框
146 | if(this.selectedCell){
147 | //如果有选择框了就更新位置
148 | this.selectedCell.change(this.selectCells)
149 | }else{
150 | //如果没有选择框就创建一个
151 | this.selectedCell = new SelectCell(this.selectCells)
152 | this.canvas.add(this.selectedCell)
153 | }
154 | this.activeCell = this.selectCells[0]
155 | if(this.activeCell){
156 | this.activeCell.unSelectCell()
157 | }
158 | this.changeHeaderAndIndexState(this.selectCells)
159 | }
160 | initCells(){
161 | //table是一个group,里面装着cells
162 | this.table = new zrender.Group()
163 | this.canvas.add(this.table)
164 | for(let x = 0;x < this.currentObj.span;x++){
165 | this.cells[x] = new Array()
166 | for(let y = 0;y < this.currentObj.row;y++){
167 | this.cells[x][y] = new Cell({...{
168 | x:x,
169 | y:y,
170 | cellWidth:this.currentObj.cellWidth,
171 | cellHeight:this.currentObj.cellHeight,
172 | row:1,
173 | span:1,
174 | merge:false,
175 | text:"",
176 | },...this.textConfig})
177 | this.cells[x][y].addEvent('change',(event)=>{
178 | this.emit("changeCell",event)
179 | })
180 | this.table.add(this.cells[x][y])
181 | }
182 | }
183 | this.table.on('mousedown',(event) => {
184 | //将工具条的状态改变得和cell的状态一致
185 | this.toolBar.setConfig(event.target.parent.data)
186 | if(event.event.button != 0){
187 | //如果点击的不是鼠标左键
188 | return false
189 | }
190 | this.isActive = true
191 | this.emit("clickCell",{data:event.target.parent.data})
192 | //隐藏输入框
193 | this.edit.hideEdit()
194 | this.cancelSelectCell()
195 | //设置选中单元格为当前单击的单元格
196 | this.selectCells = [event.target.parent]
197 | //设置激活的单元格尾当前单击的单元格
198 | this.activeCell = event.target.parent
199 | //初始化选中的蓝色框框
200 | if(this.selectedCell){
201 | //如果有选择框了就更新位置
202 | this.selectedCell.change(this.selectCells)
203 | }else{
204 | //如果没有选择框就创建一个
205 | this.selectedCell = new SelectCell(this.selectCells)
206 | this.canvas.add(this.selectedCell)
207 | }
208 | let positionX = this.table.position[0]
209 | let positionY = this.table.position[1]
210 | this.selectedCell.attr('position',[positionX,positionY])
211 | //更新一下列和行的选择状态
212 | this.changeHeaderAndIndexState(this.selectCells)
213 | this.table.on('mousemove',this.handleTableMouseMove,this)
214 | })
215 | //鼠标双击进入编辑模式
216 | this.table.on("dblclick",(event) => {
217 | this.isActive = false
218 | //计算可编辑div的位置
219 | let x = event.target.parent.data.x
220 | let y = event.target.parent.data.y
221 | let width = event.target.parent.data.cellWidth
222 | let height = event.target.parent.data.cellHeight
223 | //得到x方向和y方向的位移
224 | let movex = this.table.position[0]
225 | let movey = this.table.position[1]
226 | let positionX = event.target.parent.data.xPlace + movex
227 | let positionY = event.target.parent.data.yPlace + movey
228 | let data = event.target.parent.data.text
229 | //改变编辑框的状态
230 | this.edit.setPosition(width,height,positionX,positionY,data)
231 | })
232 | }
233 | setSelect(xstart,ystart,xend,yend){
234 | if(!xend){
235 | xend = xstart
236 | }
237 | if(!yend){
238 | yend = ystart
239 | }
240 | this.cancelSelectCell()
241 |
242 | this.selectCells = this.countSelect(xstart,xend,ystart,yend)
243 |
244 | this.updateSelectState()
245 | }
246 | handleTableMouseMove(event){
247 | //计算当前拖动到的单元格的下标
248 | const x = event.target.parent.data.x
249 | const y = event.target.parent.data.y
250 | //根据activeCell和当前的下标,动态的改变selectCells
251 | const ax = this.activeCell.data.x
252 | const ay = this.activeCell.data.y
253 |
254 | if(ax == x && ay == y){
255 | return false
256 | }
257 |
258 | let xstart
259 | let ystart
260 | let xend
261 | let yend
262 |
263 | //判断两种下标的大小,来判定到底是从哪边拖动的
264 | if(x > ax){
265 | xstart = ax
266 | xend = x
267 | }else{
268 | xstart = x
269 | xend = ax
270 | }
271 |
272 | if(y > ay){
273 | ystart = ay
274 | yend = y
275 | }else{
276 | ystart = y
277 | yend = ay
278 | }
279 |
280 | this.cancelSelectCell()
281 |
282 | this.selectCells = this.countSelect(xstart,xend,ystart,yend)
283 |
284 | this.updateSelectState()
285 | }
286 | countSelect(xs,xe,ys,ye){
287 | let xstart = xs
288 | let xend = xe
289 | let ystart = ys
290 | let yend = ye
291 | let that = this
292 | let list = []
293 | getBoundXY()
294 | function getBoundXY(){
295 | for(let x = xstart;x <= xend;x++){
296 | for(let y = ystart;y <= yend;y++){
297 | if(that.cells[x][y].data.merge == true&&that.cells[x][y].data.mergeConfig){
298 | let flag = false
299 | if(that.cells[x][y].data.mergeConfig.xstart < xstart){
300 | xstart = that.cells[x][y].data.mergeConfig.xstart
301 | flag = true
302 | }
303 | if(that.cells[x][y].data.mergeConfig.xend > xend){
304 | xend = that.cells[x][y].data.mergeConfig.xend
305 | flag = true
306 | }
307 | if(that.cells[x][y].data.mergeConfig.ystart < ystart){
308 | ystart = that.cells[x][y].data.mergeConfig.ystart
309 | flag = true
310 | }
311 | if(that.cells[x][y].data.mergeConfig.yend > yend){
312 | yend = that.cells[x][y].data.mergeConfig.yend
313 | flag = true
314 | }
315 | if(flag){
316 | getBoundXY()
317 | }
318 | }
319 | }
320 | }
321 | }
322 | for(let x = xstart;x <= xend;x++){
323 | for(let y = ystart;y <= yend;y++){
324 | list.push(this.cells[x][y])
325 | }
326 | }
327 | return list
328 | }
329 | keydownMethod(event){
330 | const keyCode = event.keyCode || event.which
331 | //获取到activeCell的下标
332 | if(!this.activeCell){
333 | return false
334 | }
335 | let x = this.activeCell.data.x
336 | let y = this.activeCell.data.y
337 | switch(keyCode){
338 | case 38:
339 | if(!this.isActive){
340 | return false
341 | }
342 | preventDefault(event)
343 | //上
344 | //如果y = 0,就阻止,否则 y - 1
345 | if(y > 0){
346 | y -= 1
347 | this.edit.hideEdit()
348 |
349 | this.activeCell = this.cells[x][y]
350 | this.cancelSelectCell()
351 | this.selectCells = [this.activeCell]
352 | this.selectedCell.change(this.selectCells,{
353 | cellWidth:this.currentObj.cellWidth,
354 | cellHeight:this.currentObj.cellHeight
355 | })
356 | this.emit("moveCell",{data:this.cells[x][y].data})
357 | }
358 | break;
359 | case 40:
360 | //下
361 | if(!this.isActive){
362 | return false
363 | }
364 | preventDefault(event)
365 | if(y < this.currentObj.row - 1){
366 | y += 1
367 | this.edit.hideEdit()
368 |
369 | this.activeCell = this.cells[x][y]
370 | this.cancelSelectCell()
371 | this.selectCells = [this.activeCell]
372 | this.selectedCell.change(this.selectCells,{
373 | cellWidth:this.currentObj.cellWidth,
374 | cellHeight:this.currentObj.cellHeight
375 | })
376 | this.emit("moveCell",{data:this.cells[x][y].data})
377 | }
378 | break;
379 | case 37:
380 | //左
381 | if(!this.isActive){
382 | return false
383 | }
384 | preventDefault(event)
385 | if(x > 0){
386 | x -= 1
387 | this.edit.hideEdit()
388 |
389 | this.activeCell = this.cells[x][y]
390 | this.cancelSelectCell()
391 | this.selectCells = [this.activeCell]
392 | this.selectedCell.change(this.selectCells,{
393 | cellWidth:this.currentObj.cellWidth,
394 | cellHeight:this.currentObj.cellHeight
395 | })
396 | this.emit("moveCell",{data:this.cells[x][y].data})
397 | }
398 | break;
399 | case 39:
400 | //右
401 | if(!this.isActive){
402 | return false
403 | }
404 | preventDefault(event)
405 | if(x < this.currentObj.span - 1){
406 | x += 1
407 | this.edit.hideEdit()
408 |
409 | this.activeCell = this.cells[x][y]
410 | this.cancelSelectCell()
411 | this.selectCells = [this.activeCell]
412 | this.selectedCell.change(this.selectCells,{
413 | cellWidth:this.currentObj.cellWidth,
414 | cellHeight:this.currentObj.cellHeight
415 | })
416 | this.emit("moveCell",{data:this.cells[x][y].data})
417 | }
418 | break;
419 | case 13:
420 | //回车
421 | this.edit.hideEdit()
422 | break;
423 | case 8:
424 | case 46:
425 | //删除
426 | if(!this.edit.editFlag){
427 | this.selectCells.forEach(cell => {
428 | cell.clear()
429 | })
430 | }
431 | break;
432 | case 67:
433 | //ctrl+c
434 | if(event.ctrlKey){
435 | this.setCopyCell()
436 | }
437 | break;
438 | case 86:
439 | //ctrl+v
440 | if(event.ctrlKey){
441 | this.pastCopyCell()
442 | }
443 | break;
444 | }
445 | }
446 | removeMethods(event){
447 | if(event.target != this.edit.editEle){
448 | this.edit.hideEdit()
449 | }
450 |
451 | if(this.handleTableMouseMove){
452 | this.table.off('mousemove',this.handleTableMouseMove)
453 | }
454 | if(this.handleHeaderMouseMove && this.canvas){
455 | this.canvas.off('mousemove',this.handleHeaderMouseMove)
456 | }
457 | if(this.handleIndexMouseMove && this.canvas){
458 | this.canvas.off('mousemove',this.handleIndexMouseMove)
459 | }
460 |
461 | this.isActive = false
462 |
463 | let list = event.composedPath()
464 | for(let i = 0;i item.x))
517 | let copyMin = Math.min(...this.copyCells.map(item => item.x))
518 | let copyYMin = Math.min(...this.copyCells.map(item => item.y))
519 | let copyYMax = Math.max(...this.copyCells.map(item => item.y))
520 | let copySpan = copyMax - copyMin + 1
521 | let copyRow = this.copyCells.length / copySpan
522 |
523 | if(this.copyCells.length == 1 && this.copyCells[0].mergeConfig){
524 | copyMax = this.copyCells[0].mergeConfig.xend
525 | copyMin = this.copyCells[0].mergeConfig.xstart
526 | copyYMin = this.copyCells[0].mergeConfig.ystart
527 | copyYMax = this.copyCells[0].mergeConfig.yend
528 | copySpan = copyMax - copyMin + 1
529 | copyRow = copyYMax - copyYMin + 1
530 |
531 | this.copyCells = []
532 | for(let i = copyMin;i<=copyMax;i++){
533 | for(let j = copyYMin;j<=copyYMax;j++){
534 | let obj = zrender.util.clone(this.cells[i][j].data)
535 | delete obj.cellHeight
536 | delete obj.cellWidth
537 | delete obj.xPlace
538 | delete obj.yPlace
539 |
540 | this.copyCells.push(obj)
541 | }
542 | }
543 | }
544 |
545 | let tempSelect = []
546 | let selectMax = Math.max(...this.selectCells.map(item => item.data.x))
547 | let selectMin = Math.min(...this.selectCells.map(item => item.data.x))
548 | let selectYMin = Math.min(...this.selectCells.map(item => item.data.y))
549 | let selectYMax = Math.max(...this.selectCells.map(item => item.data.y))
550 | let selectSpan = selectMax - selectMin + 1
551 | let selectRow = this.selectCells.length / selectSpan
552 |
553 | //如果选择的不是复制的整数倍的话,就去掉几个格子
554 | if(selectRow / copyRow < 1||selectSpan / copySpan < 1){
555 | alert('复制粘贴的位置不够,请重新选择')
556 | return false
557 | }
558 |
559 | if(selectRow % copyRow != 0){
560 | selectYMax -= selectRow % copyRow
561 | selectRow -= selectRow % copyRow
562 | }
563 | if(selectSpan % copySpan != 0){
564 | selectMax -= selectSpan % copySpan
565 | selectSpan -= selectSpan % copySpan
566 | }
567 |
568 | this.selectCells.forEach(cell => {
569 | cell.unSelectCell()
570 | })
571 | this.selectCells = []
572 | for(let i = selectMin;i<=selectMax;i++){
573 | for(let j = selectYMin;j<=selectYMax;j++){
574 | this.selectCells.push(this.cells[i][j])
575 | }
576 | }
577 |
578 | for(let i = 0;i < copyRow;i++){
579 | copyTemp.push([])
580 | for(let j = 0;j {
639 | cell.selectCell()
640 | })
641 | this.activeCell.unSelectCell()
642 | this.copyCells = []
643 | this.canvas.remove(this.copyedCell)
644 | this.copyedCell = null
645 | }
646 | addTableHeaderCell(config){
647 | let headCell = new TableHeaderCell(config)
648 | this.tableHeaderCell.splice(config.index,0,headCell)
649 | this.tableHeader.add(headCell)
650 | headCell.addEvent('dragLine',(event)=>{
651 | if(this.changeWidthLine){
652 | this.changeWidthLine.attr({shape:{
653 | x1:event.offsetX,
654 | y1:0,
655 | x2:event.offsetX,
656 | y2:this.canvas.getHeight()
657 | }})
658 | }else{
659 | this.changeWidthLine = new zrender.Line({
660 | z:1001,
661 | shape:{
662 | x1:event.offsetX,
663 | y1:0,
664 | x2:event.offsetX,
665 | y2:this.canvas.getHeight()
666 | },
667 | style:{
668 | stroke: '#4e9fff',
669 | fill: 'none',
670 | lineWidth:'1',
671 | }
672 | })
673 | this.canvas.add(this.changeWidthLine)
674 | }
675 | })
676 | headCell.addEvent('changeSize',(event) => {
677 | this.canvas.remove(this.changeWidthLine)
678 | this.changeWidthLine = null
679 | this.refreshCell()
680 | })
681 | }
682 | initTableHeader(){
683 | this.tableHeader = new zrender.Group()
684 | this.canvas.add(this.tableHeader)
685 | for(let i = 0;i {
693 | //隐藏编辑框
694 | this.edit.hideEdit()
695 | if(event.target.type != 'headerBorder'){
696 | return false
697 | }
698 | this.cancelSelectCell()
699 | //选中的是哪一列
700 | let index = event.target.parent.data.index
701 | this.selectCells = []
702 | //更新activeCell和selectCells
703 | for(let x = 0;x < this.cells.length;x++){
704 | if(x == index){
705 | this.selectCells = this.cells[x]
706 | }
707 | }
708 | //选择列头 以及 单元格
709 | //设置激活的单元格尾当前单击的单元格
710 | this.activeCell = this.selectCells[0]
711 | this.selectCells.forEach(cell => {
712 | cell.selectCell()
713 | })
714 | this.activeCell.unSelectCell()
715 | //初始化选中的蓝色框框
716 | if(this.selectedCell){
717 | //如果有选择框了就更新位置
718 | this.selectedCell.change(this.selectCells,{
719 | cellWidth:this.currentObj.cellWidth,
720 | cellHeight:this.currentObj.cellHeight
721 | })
722 | }else{
723 | //如果没有选择框就创建一个
724 | this.selectedCell = new SelectCell(this.selectCells,{
725 | cellWidth:this.currentObj.cellWidth,
726 | cellHeight:this.currentObj.cellHeight
727 | })
728 | this.canvas.add(this.selectedCell)
729 | }
730 | //更新一下列和行的选择状态
731 | this.changeHeaderAndIndexState(this.selectCells)
732 |
733 | //绑定鼠标移动事件
734 | this.canvas.on('mousemove',this.handleHeaderMouseMove,this)
735 | })
736 | }
737 | handleHeaderMouseMove(event){
738 | if(event.target){
739 | let targetIndex
740 | let activeIndex
741 | let start
742 | let end
743 | switch (event.target.type){
744 | case 'headerBorder':
745 | targetIndex = event.target.parent.data.index
746 | activeIndex = this.activeCell.data.x
747 | if(targetIndex != activeIndex){
748 | this.cancelSelectCell()
749 | this.selectCells = []
750 | if(targetIndex > activeIndex){
751 | start = activeIndex
752 | end = targetIndex
753 | }else{
754 | start = targetIndex
755 | end = activeIndex
756 | }
757 | for(let x = start;x <= end;x++){
758 | this.selectCells.push(...this.cells[x])
759 | }
760 | this.updateSelectState()
761 | }
762 | break;
763 | case 'cellborder':
764 | targetIndex = event.target.parent.data.x
765 | activeIndex = this.activeCell.data.x
766 | if(targetIndex != activeIndex){
767 | this.cancelSelectCell()
768 | this.selectCells = []
769 | if(targetIndex > activeIndex){
770 | start = activeIndex
771 | end = targetIndex
772 | }else{
773 | start = targetIndex
774 | end = activeIndex
775 | }
776 | for(let x = start;x <= end;x++){
777 | this.selectCells.push(...this.cells[x])
778 | }
779 | this.updateSelectState()
780 | }
781 | break;
782 | }
783 | }
784 | }
785 | addTableIndexCell(config){
786 | let indexCell = new TableIndexCell(config)
787 | this.tableIndexCell.splice(config.index,0,indexCell)
788 | this.tableIndex.add(indexCell)
789 |
790 | indexCell.addEvent('dragLine',(event)=>{
791 | if(this.changeWidthLine){
792 | this.changeWidthLine.attr({shape:{
793 | x1:0,
794 | y1:event.offsetY,
795 | x2:this.canvas.getWidth(),
796 | y2:event.offsetY
797 | }})
798 | }else{
799 | this.changeWidthLine = new zrender.Line({
800 | z:1001,
801 | shape:{
802 | x1:0,
803 | y1:event.offsetY,
804 | x2:this.canvas.getWidth(),
805 | y2:event.offsetY
806 | },
807 | style:{
808 | stroke: '#4e9fff',
809 | fill: 'none',
810 | lineWidth:'1',
811 | }
812 | })
813 | this.canvas.add(this.changeWidthLine)
814 | }
815 | })
816 | indexCell.addEvent('changeSize',(event) => {
817 | this.canvas.remove(this.changeWidthLine)
818 | this.changeWidthLine = null
819 | this.refreshCell()
820 | })
821 | }
822 | initTableIndex(){
823 | this.tableIndex = new zrender.Group()
824 | this.canvas.add(this.tableIndex)
825 | for(let i = 0;i < this.currentObj.row;i++){
826 | this.addTableIndexCell({
827 | cellWidth:indexWidth,
828 | cellHeight:this.currentObj.cellHeight,
829 | index:i
830 | })
831 | }
832 | this.tableIndex.on('mousedown',(event) => {
833 | //隐藏编辑框
834 | this.edit.hideEdit()
835 | if(event.target.type != 'indexBorder'){
836 | return false
837 | }
838 | this.cancelSelectCell()
839 | //选中的是哪一行
840 | let index = event.target.parent.data.index
841 | this.selectCells = []
842 | //更新activeCell和selectCells
843 | for(let x = 0;x < this.cells.length;x++){
844 | for(let y = 0;y < this.cells[x].length;y++){
845 | if(y == index){
846 | this.selectCells.push(this.cells[x][y])
847 | }
848 | }
849 | }
850 | //选择列头 以及 单元格
851 | //设置激活的单元格尾当前单击的单元格
852 | this.activeCell = this.selectCells[0]
853 | this.selectCells.forEach(cell => {
854 | cell.selectCell()
855 | })
856 | this.activeCell.unSelectCell()
857 | //初始化选中的蓝色框框
858 | if(this.selectedCell){
859 | //如果有选择框了就更新位置
860 | this.selectedCell.change(this.selectCells,{
861 | cellWidth:this.currentObj.cellWidth,
862 | cellHeight:this.currentObj.cellHeight
863 | })
864 | }else{
865 | //如果没有选择框就创建一个
866 | this.selectedCell = new SelectCell(this.selectCells,{
867 | cellWidth:this.currentObj.cellWidth,
868 | cellHeight:this.currentObj.cellHeight
869 | })
870 | this.canvas.add(this.selectedCell)
871 | }
872 | //更新一下列和行的选择状态
873 | this.changeHeaderAndIndexState(this.selectCells)
874 |
875 | //绑定鼠标移动事件
876 | this.canvas.on('mousemove',this.handleIndexMouseMove,this)
877 | })
878 | }
879 | handleIndexMouseMove(event){
880 | if(event.target){
881 | let targetIndex
882 | let activeIndex
883 | let start
884 | let end
885 | switch (event.target.type){
886 | case 'indexBorder':
887 | targetIndex = event.target.parent.data.index
888 | activeIndex = this.activeCell.data.y
889 | if(targetIndex != activeIndex){
890 | this.cancelSelectCell()
891 | this.selectCells = []
892 | if(targetIndex > activeIndex){
893 | start = activeIndex
894 | end = targetIndex
895 | }else{
896 | start = targetIndex
897 | end = activeIndex
898 | }
899 | for(let x = 0; x < this.cells.length;x++){
900 | for(let y = start;y<=end;y++){
901 | this.selectCells.push(this.cells[x][y])
902 | }
903 | }
904 | this.updateSelectState()
905 | }
906 | break;
907 | case 'cellborder':
908 | targetIndex = event.target.parent.data.y
909 | activeIndex = this.activeCell.data.y
910 | if(targetIndex != activeIndex){
911 | this.cancelSelectCell()
912 | this.selectCells = []
913 | if(targetIndex > activeIndex){
914 | start = activeIndex
915 | end = targetIndex
916 | }else{
917 | start = targetIndex
918 | end = activeIndex
919 | }
920 | for(let x = 0; x < this.cells.length;x++){
921 | for(let y = start;y<=end;y++){
922 | this.selectCells.push(this.cells[x][y])
923 | }
924 | }
925 | this.updateSelectState()
926 | }
927 | break;
928 | }
929 | }
930 | }
931 | changeHeaderAndIndexState(cells){
932 | if(cells.length == 1){
933 | this.tableHeaderCell[cells[0].data.x].selectCell()
934 | this.tableIndexCell[cells[0].data.y].selectCell()
935 | }else{
936 | //计算应该选择的行开始和结束位置
937 | let xstart = cells[0].data.x
938 | let ystart = cells[0].data.y
939 | let xend = cells[cells.length - 1].data.x
940 | let yend = cells[cells.length - 1].data.y
941 | for(let i = xstart;i <= xend;i++){
942 | this.tableHeaderCell[i].selectCell()
943 | }
944 | for(let i = ystart;i <= yend;i++){
945 | this.tableIndexCell[i].selectCell()
946 | }
947 | }
948 | }
949 | //初始化滚动条
950 | initScroll(parent){
951 | //计算纵向的高度
952 | const tableHeight = this.table.getBoundingRect().height
953 | //计算纵向显示高度
954 | const tableWrapperHeight = this.canvas.getHeight() - headerHeight
955 | //计算横向的宽度
956 | const tableWidth = this.table.getBoundingRect().width
957 | //计算横向显示宽度
958 | const tableWrapperWidth = this.canvas.getWidth() - indexWidth
959 |
960 | this.scroll = new Scroll({
961 | fullHeight:tableHeight,
962 | wrapHeight:tableWrapperHeight,
963 | fullWidth:tableWidth,
964 | wrapWidth:tableWrapperWidth,
965 | parent:parent
966 | })
967 | this.scroll.on('scrollY',(e) => {
968 | //隐藏编辑框
969 | this.edit.hideEdit()
970 | let positionX = this.table.position[0]
971 | let moveY = e.pageMove
972 | if(moveY > 0){
973 | moveY = 0
974 | }
975 | if(moveY < -(this.scroll.config.fullHeight - this.scroll.config.wrapHeight + scrollWidth)){
976 | moveY = -(this.scroll.config.fullHeight - this.scroll.config.wrapHeight + scrollWidth)
977 | }
978 | this.table.attr('position',[positionX, moveY])
979 | let positionIndexX = this.tableIndex.position[0]
980 | this.tableIndex.attr('position',[positionIndexX,moveY])
981 | if(this.selectedCell){
982 | this.selectedCell.attr('position',[positionX,moveY])
983 | }
984 | if(this.copyedCell){
985 | this.copyedCell.attr('position',[positionX,moveY])
986 | }
987 | })
988 | this.scroll.on('scrollX',(e) => {
989 | //隐藏编辑框
990 | this.edit.hideEdit()
991 | let positionY = this.table.position[1]
992 | let moveX = e.pageMove
993 | if(moveX > 0){
994 | moveX = 0
995 | }
996 | if(moveX < -(this.scroll.config.fullWidth - this.scroll.config.wrapWidth + scrollWidth)){
997 | moveX = -(this.scroll.config.fullWidth - this.scroll.config.wrapWidth + scrollWidth)
998 | }
999 | this.table.attr('position',[moveX, positionY])
1000 | let positionHeaderY = this.tableHeader.position[1]
1001 | this.tableHeader.attr('position',[moveX, positionHeaderY])
1002 | if(this.selectedCell){
1003 | this.selectedCell.attr('position',[moveX, positionY])
1004 | }
1005 | if(this.copyedCell){
1006 | this.copyedCell.attr('position',[moveX,positionY])
1007 | }
1008 | })
1009 | this.canvas.on('mousewheel',(event) => {
1010 | //判断需不需要滚动
1011 | if(this.scroll.config.fullHeight < this.scroll.config.wrapHeight){
1012 | return false
1013 | }
1014 | //隐藏编辑框
1015 | this.edit.hideEdit()
1016 | if(mouseWheelDirection(event.event)){
1017 | //table滚动
1018 | //index滚动
1019 | let positionX = this.table.position[0]
1020 | let positionY = this.table.position[1]
1021 | let moveY = positionY + 20
1022 | if(moveY > 0){
1023 | moveY = 0
1024 | }
1025 | this.scroll.scrollY(moveY)
1026 | this.table.attr('position',[positionX, moveY])
1027 | let positionIndexX = this.tableIndex.position[0]
1028 | this.tableIndex.attr('position',[positionIndexX,moveY])
1029 | if(this.selectedCell){
1030 | this.selectedCell.attr('position',[positionX,moveY])
1031 | }
1032 | if(this.copyedCell){
1033 | this.copyedCell.attr('position',[positionX,moveY])
1034 | }
1035 | //更新滚动条位置
1036 | }else{
1037 | let positionX = this.table.position[0]
1038 | let positionY = this.table.position[1]
1039 | let moveY = positionY - 20
1040 | if(moveY < -(this.scroll.config.fullHeight - this.scroll.config.wrapHeight + scrollWidth)){
1041 | moveY = -(this.scroll.config.fullHeight - this.scroll.config.wrapHeight + scrollWidth)
1042 | }
1043 | this.scroll.scrollY(moveY)
1044 | this.table.attr('position',[positionX,moveY])
1045 | let positionIndexX = this.tableIndex.position[0]
1046 | this.tableIndex.attr('position',[positionIndexX,moveY])
1047 | if(this.selectedCell){
1048 | this.selectedCell.attr('position',[positionX,moveY])
1049 | }
1050 | if(this.copyedCell){
1051 | this.copyedCell.attr('position',[positionX,moveY])
1052 | }
1053 | }
1054 | })
1055 | }
1056 | initEdit(parent){
1057 | this.edit = new Edit(parent)
1058 | this.edit.editEle.addEventListener('click',(e) => {
1059 | stopPropagation(e)
1060 | })
1061 | this.edit.on('update',(event) => {
1062 | if(event.type == 'text'){
1063 | this.activeCell.setText(event.text)
1064 | }
1065 | })
1066 | }
1067 | initContextMenu(parent){
1068 | this.contextMenu = new ContextMenu(parent)
1069 | //添加菜单
1070 |
1071 | const addSpan = this.contextMenu.addButton('插入列',()=>{
1072 | //tableHeaderCell插入列
1073 | const index = this.activeCell.data.x
1074 | this.addTableHeaderCell({
1075 | cellWidth:this.currentObj.cellWidth,
1076 | cellHeight:headerHeight,
1077 | index:index + 1
1078 | })
1079 | //更新所有tableHeaderCell的data和位置
1080 | for(let i = index;i{
1097 | this.emit("changeCell",event)
1098 | })
1099 | this.table.add(insertArr[y])
1100 | }
1101 | this.cells.splice(index+1,0,insertArr)
1102 | //更新所有cells的xy
1103 | for(let x = 0;x 0 &&this.cells[x][y].data.span > 0){
1108 | //如果index在merge的单元格之中
1109 | if(index >= this.cells[x][y].data.mergeConfig.xstart && index < this.cells[x][y].data.mergeConfig.xend){
1110 | this.cells[x][y].data.mergeConfig.xend += 1
1111 | this.cells[x][y].data.span += 1
1112 | for(let m = this.cells[x][y].data.mergeConfig.ystart;m<=this.cells[x][y].data.mergeConfig.yend;m++){
1113 | this.cells[index+1][m].setData({
1114 | row:0,
1115 | span:0,
1116 | merge:true
1117 | })
1118 | }
1119 | }
1120 | //如果index在merge之前
1121 | if(index < this.cells[x][y].data.mergeConfig.xstart){
1122 | this.cells[x][y].data.mergeConfig.xstart += 1
1123 | this.cells[x][y].data.mergeConfig.xend += 1
1124 | }
1125 | }
1126 | }
1127 | }
1128 | //更新整个视图
1129 | this.refreshCell()
1130 | })
1131 |
1132 | const addRow = this.contextMenu.addButton('插入行',()=>{
1133 | //tableHeaderCell插入列
1134 | const index = this.activeCell.data.y
1135 | this.addTableIndexCell({
1136 | cellWidth:indexWidth,
1137 | cellHeight:this.currentObj.cellHeight,
1138 | index:index + 1
1139 | })
1140 | //更新所有tableHeaderCell的data和位置
1141 | for(let i = index;i{
1157 | this.emit("changeCell",event)
1158 | })
1159 | this.cells[x].splice(index+1,0,insert)
1160 | this.table.add(insert)
1161 | }
1162 | //更新所有cells的xy
1163 | for(let x = 0;x 0 &&this.cells[x][y].data.span > 0){
1168 | //如果index在merge的单元格之中
1169 | if(index >= this.cells[x][y].data.mergeConfig.ystart && index < this.cells[x][y].data.mergeConfig.yend){
1170 | this.cells[x][y].data.mergeConfig.yend += 1
1171 | this.cells[x][y].data.row += 1
1172 | for(let m = this.cells[x][y].data.mergeConfig.xstart;m<=this.cells[x][y].data.mergeConfig.xend;m++){
1173 | this.cells[m][index + 1].setData({
1174 | row:0,
1175 | span:0,
1176 | merge:true
1177 | })
1178 | }
1179 | }
1180 | //如果index在merge之前
1181 | if(index < this.cells[x][y].data.mergeConfig.ystart){
1182 | this.cells[x][y].data.mergeConfig.ystart += 1
1183 | this.cells[x][y].data.mergeConfig.yend += 1
1184 | }
1185 | }
1186 | }
1187 | }
1188 | //更新整个视图
1189 | this.refreshCell()
1190 | })
1191 |
1192 | const addImage = this.contextMenu.addButton('插入图片',()=>{
1193 | this.uploadFile.open()
1194 | })
1195 |
1196 | const clearInputBtn = this.contextMenu.addButton('清除内容',() => {
1197 | this.selectCells.forEach(cell => {
1198 | cell.clear()
1199 | })
1200 | })
1201 |
1202 | //合并单元格
1203 | const mergeCells = this.contextMenu.addButton('合并单元格',()=>{
1204 | this.mergeCells()
1205 | })
1206 |
1207 | //取消合并单元格
1208 | const splitCell = this.contextMenu.addButton('取消合并单元格',() => {
1209 | this.splitCell()
1210 | })
1211 |
1212 | //删除列
1213 | const deleteSpan = this.contextMenu.addButton('删除列',() => {
1214 | //先把列删掉
1215 | const index = this.activeCell.data.x
1216 | for(let y = 0;y 1 &&this.cells[x][y].data.span > 1){
1226 | //如果index在merge的单元格之中
1227 | if(index >= this.cells[x][y].data.mergeConfig.xstart && index <= this.cells[x][y].data.mergeConfig.xend){
1228 | this.cells[x][y].data.mergeConfig.xend -= 1
1229 | this.cells[x][y].data.span -= 1
1230 | //如果被删掉的刚好是合并的,那就设置相邻的单元格顶替
1231 | if(index == this.cells[x][y].data.mergeConfig.xstart || index == this.cells[x][y].data.mergeConfig.xend){
1232 | this.cells[this.cells[x][y].data.mergeConfig.xstart + 1][y].setData({
1233 | row:this.cells[x][y].data.row,
1234 | span:this.cells[x][y].data.span,
1235 | mergeConfig:{
1236 | xstart:this.cells[x][y].data.mergeConfig.xstart,
1237 | xend:this.cells[x][y].data.mergeConfig.xend,
1238 | ystart:this.cells[x][y].data.mergeConfig.ystart,
1239 | yend:this.cells[x][y].data.mergeConfig.yend,
1240 | }
1241 | })
1242 | x++
1243 | }
1244 | }
1245 | //如果index在merge之前
1246 | if(index < this.cells[x][y].data.mergeConfig.xstart){
1247 | this.cells[x][y].data.mergeConfig.xstart -= 1
1248 | this.cells[x][y].data.mergeConfig.xend -= 1
1249 | }
1250 | }
1251 | }
1252 | }
1253 |
1254 | this.cells.splice(index,1)
1255 | //更新所有cells的xy
1256 | for(let x = 0;x {
1275 | //先把行删掉
1276 | const index = this.activeCell.data.y
1277 | for(let x = 0;x 1 &&this.cells[x][y].data.span > 1){
1287 | //如果index在merge的单元格之中
1288 | if(index >= this.cells[x][y].data.mergeConfig.ystart && index <= this.cells[x][y].data.mergeConfig.yend){
1289 | this.cells[x][y].data.mergeConfig.yend -= 1
1290 | this.cells[x][y].data.row -= 1
1291 | //如果被删掉的刚好是合并的,那就设置相邻的单元格顶替
1292 | if(index == this.cells[x][y].data.mergeConfig.ystart || index == this.cells[x][y].data.mergeConfig.yend){
1293 | this.cells[x][this.cells[x][y].data.mergeConfig.ystart + 1].setData({
1294 | row:this.cells[x][y].data.row,
1295 | span:this.cells[x][y].data.span,
1296 | mergeConfig:{
1297 | xstart:this.cells[x][y].data.mergeConfig.xstart,
1298 | xend:this.cells[x][y].data.mergeConfig.xend,
1299 | ystart:this.cells[x][y].data.mergeConfig.ystart,
1300 | yend:this.cells[x][y].data.mergeConfig.yend,
1301 | }
1302 | })
1303 | y++
1304 | }
1305 | }
1306 | //如果index在merge之前
1307 | if(index < this.cells[x][y].data.mergeConfig.ystart){
1308 | this.cells[x][y].data.mergeConfig.ystart -= 1
1309 | this.cells[x][y].data.mergeConfig.yend -= 1
1310 | }
1311 | }
1312 | }
1313 | }
1314 |
1315 | for(let x = 0;x {
1339 | this.setCopyCell()
1340 | })
1341 |
1342 | //粘贴
1343 | const paste = this.contextMenu.addButton('粘贴',() => {
1344 | this.pastCopyCell()
1345 | })
1346 |
1347 | //右键菜单
1348 | this.canvas.on("contextmenu",(event) => {
1349 | preventDefault(event.event)
1350 | //判断当前点的cell有没有在selectCells里面
1351 | if(event.target.parent && event.target.parent.type == 'cell'){
1352 | if(this.selectCells.includes(event.target.parent)){
1353 |
1354 | }else{
1355 | this.edit.hideEdit()
1356 | this.cancelSelectCell()
1357 | this.activeCell = event.target.parent
1358 | this.selectCells = [event.target.parent]
1359 | if(this.selectedCell){
1360 | //如果有选择框了就更新位置
1361 | this.selectedCell.change(this.selectCells,{
1362 | cellWidth:this.currentObj.cellWidth,
1363 | cellHeight:this.currentObj.cellHeight
1364 | })
1365 | }else{
1366 | //如果没有选择框就创建一个
1367 | this.selectedCell = new SelectCell(this.selectCells,{
1368 | cellWidth:this.currentObj.cellWidth,
1369 | cellHeight:this.currentObj.cellHeight
1370 | })
1371 | this.canvas.add(this.selectedCell)
1372 | }
1373 | //更新一下列和行的选择状态
1374 | this.changeHeaderAndIndexState(this.selectCells)
1375 | }
1376 | //判断选中了几个单元格,如果选中了多个就显示合并单元格按钮
1377 | if(this.selectCells.length > 1){
1378 | addImage.style.display='none'
1379 | if(this.selectCells.every(cell => {return cell.data.merge == false})){
1380 | mergeCells.style.display = "block"
1381 | splitCell.style.display = "none"
1382 | }else{
1383 | mergeCells.style.display = "none"
1384 | splitCell.style.display = "block"
1385 | }
1386 | }else{
1387 | mergeCells.style.display = "none"
1388 | addImage.style.display='block'
1389 | if(this.selectCells[0].data.merge == true){
1390 | splitCell.style.display = "block"
1391 | }else{
1392 | splitCell.style.display = "none"
1393 | }
1394 | }
1395 | }
1396 | //如果有就直接弹出menu
1397 | //如果没有就重新设置一下selectCells以及activeCell
1398 | this.contextMenu.showMenu(event.offsetX,event.offsetY)
1399 | })
1400 | this.hideMenu = this.hideMenu.bind(this)
1401 | document.addEventListener('click',this.hideMenu)
1402 | }
1403 | hideMenu(){
1404 | this.contextMenu.hideMenu()
1405 | }
1406 | //重新绘制cell
1407 | refreshCell(){
1408 | //重绘headerCell
1409 | this.refreshTableHeaderCell()
1410 | //重绘indexCell
1411 | this.refreshTableIndexCell()
1412 | //重绘tableCell
1413 | this.refreshTableCell()
1414 | //刷新选择框
1415 | if(this.selectCells.length > 0 && this.selectedCell){
1416 | this.selectedCell.change(this.selectCells)
1417 | this.selectCells.forEach(cell => {
1418 | cell.selectCell()
1419 | })
1420 | this.activeCell.unSelectCell()
1421 | }
1422 | //刷新复制框
1423 | if(this.copyedCell){
1424 | this.copyCells = []
1425 | this.canvas.remove(this.copyedCell)
1426 | this.copyedCell = null
1427 | }
1428 | //刷新滚动条
1429 | this.refreshScroll()
1430 | }
1431 | refreshTableHeaderCell(){
1432 | let x = indexWidth
1433 | this.tableHeaderCell.forEach(cell => {
1434 | cell.setData({
1435 | xPlace:x
1436 | })
1437 | cell.refresh()
1438 | x += cell.data.width
1439 | })
1440 | }
1441 | refreshTableIndexCell(){
1442 | let y = headerHeight
1443 | this.tableIndexCell.forEach(cell => {
1444 | cell.setData({
1445 | yPlace:y
1446 | })
1447 | cell.refresh()
1448 | y += cell.data.height
1449 | })
1450 | }
1451 | refreshTableCell(){
1452 | for(let x = 0;x {return cell.data.merge == false})){
1512 |
1513 | }else{
1514 | return false
1515 | }
1516 | //设置activeCell的row和span
1517 | //求最小下标以及最大下标
1518 | let xstart = this.selectCells[0].data.x
1519 | let ystart = this.selectCells[0].data.y
1520 | let xPlace = this.selectCells[0].data.xPlace
1521 | let yPlace = this.selectCells[0].data.yPlace
1522 | let xend = this.selectCells[this.selectCells.length - 1].data.x
1523 | let yend = this.selectCells[this.selectCells.length - 1].data.y
1524 |
1525 | let row = yend - ystart + 1
1526 | let span = xend - xstart + 1
1527 |
1528 | let width = 0
1529 | let height = 0
1530 |
1531 | for(let i = xstart;i<=xend;i++){
1532 | width += this.tableHeaderCell[i].data.width
1533 | }
1534 |
1535 | for(let i = ystart;i<=yend;i++){
1536 | height += this.tableIndexCell[i].data.height
1537 | }
1538 |
1539 | this.activeCell.setData({
1540 | row:row,
1541 | span:span,
1542 | merge:true,
1543 | cellWidth:width,
1544 | cellHeight:height,
1545 | xPlace:xPlace,
1546 | yPlace:yPlace,
1547 | mergeConfig:{
1548 | xstart:xstart,
1549 | ystart:ystart,
1550 | xend:xend,
1551 | yend:yend
1552 | }
1553 | })
1554 |
1555 | //其余的selectCells全部隐藏不显示,被merge
1556 | this.selectCells.forEach(cell => {
1557 | if(cell != this.activeCell){
1558 | cell.setData({
1559 | row:0,
1560 | span:0,
1561 | merge:true,
1562 | })
1563 | }
1564 | })
1565 |
1566 | //重新设置activeCell和selectCells
1567 | this.selectCells = [this.activeCell]
1568 | }
1569 | splitCell(){
1570 | //遍历一遍当前合并的单元格,重新设置他们的位置以及大小
1571 | let xstart = this.selectCells[0].data.x
1572 | let ystart = this.selectCells[0].data.y
1573 | let xend = this.selectCells[this.selectCells.length - 1].data.x
1574 | let yend = this.selectCells[this.selectCells.length - 1].data.y
1575 |
1576 | for(let x = xstart;x<=xend;x++){
1577 | for(let y=ystart;y<=yend;y++){
1578 | if(this.cells[x][y].data.merge == true && this.cells[x][y].data.row + this.cells[x][y].data.span > 2){
1579 | let x0 = this.cells[x][y].data.mergeConfig.xstart
1580 | let y0 = this.cells[x][y].data.mergeConfig.ystart
1581 | let x1 = this.cells[x][y].data.mergeConfig.xend
1582 | let y1 = this.cells[x][y].data.mergeConfig.yend
1583 | for(let i = x0;i<=x1;i++){
1584 | for(let j=y0;j<=y1;j++){
1585 | this.cells[i][j].setData({
1586 | xPlace:this.tableHeaderCell[i].data.xPlace,
1587 | yPlace:this.tableIndexCell[j].data.yPlace,
1588 | cellWidth:this.tableHeaderCell[i].data.width,
1589 | cellHeight:this.tableIndexCell[j].data.height,
1590 | merge:false,
1591 | row:1,
1592 | span:1,
1593 | mergeConfig:null
1594 | })
1595 | this.cells[i][j].show()
1596 | this.selectCells.push(this.cells[i][j])
1597 | }
1598 | }
1599 | }
1600 | }
1601 | }
1602 | this.selectedCell.change(this.selectCells)
1603 | }
1604 | initToolBar(parent){
1605 | this.toolBar = new ToolBar(parent)
1606 | this.toolBar.on('copy',(e) => {
1607 | this.setCopyCell()
1608 | })
1609 | this.toolBar.on('paste',(e) => {
1610 | this.pastCopyCell()
1611 | })
1612 | this.toolBar.on('clearFormat',(e) => {
1613 | this.selectCells.forEach(cell =>{
1614 | cell.clearFormat()
1615 | })
1616 | })
1617 | this.toolBar.on('changeTypeFace',(e) => {
1618 | //更改selectCells的fontFamily
1619 | this.selectCells.forEach(cell => {
1620 | cell.setFontFamily(e.data)
1621 | })
1622 | })
1623 | this.toolBar.on('changeFontSize',(e) => {
1624 | this.selectCells.forEach(cell => {
1625 | cell.setFontSize(e.data)
1626 | })
1627 | })
1628 | this.toolBar.on('changeFontWeight',(e) => {
1629 | this.selectCells.forEach(cell => {
1630 | cell.setFontWeight(e.data)
1631 | })
1632 | })
1633 | this.toolBar.on('changeFontItalic',(e) => {
1634 | this.selectCells.forEach(cell => {
1635 | cell.setFontItalic(e.data)
1636 | })
1637 | })
1638 | this.toolBar.on('changeTextFill',(e) => {
1639 | this.selectCells.forEach(cell => {
1640 | cell.setTextFill(e.data)
1641 | })
1642 | })
1643 | this.toolBar.on('changeFill',(e) => {
1644 | this.selectCells.forEach(cell => {
1645 | cell.setFill(e.data)
1646 | })
1647 | })
1648 | this.toolBar.on('changeBorder',(e) =>{
1649 | this.selectCells.forEach(cell => {
1650 | cell.setBorder(e.data)
1651 | })
1652 | })
1653 | this.toolBar.on('changeTextAlign',(e) => {
1654 | this.selectCells.forEach(cell => {
1655 | cell.setTextAlign(e.data)
1656 | })
1657 | })
1658 | this.toolBar.on('mergeCell',(e) => {
1659 | this.mergeCells()
1660 | })
1661 | this.toolBar.on('splitCell',(e) => {
1662 | this.splitCell()
1663 | })
1664 | this.toolBar.on('addImage',(e) => {
1665 | this.uploadFile.open()
1666 | })
1667 | }
1668 | //改变列数
1669 | setSpanNum(number){
1670 | //检测有没有这么多列,如果没有的话,就删除单元格和Header,如果没有的话就添加单元格和Header
1671 | if(number <= this.tableHeaderCell.length){
1672 | while(number < this.tableHeaderCell.length){
1673 | //tableHeaderCell
1674 | this.tableHeader.remove(this.tableHeaderCell.pop())
1675 | //删除cells最末尾几列
1676 | for(let i=0;i this.tableHeaderCell.length){
1686 | const index = this.tableHeaderCell.length
1687 | this.addTableHeaderCell({
1688 | cellWidth:this.currentObj.cellWidth,
1689 | cellHeight:headerHeight,
1690 | index:index
1691 | })
1692 | //cells插入列
1693 | let insertArr = new Array()
1694 | for(let y = 0;y < this.tableIndexCell.length;y++){
1695 | insertArr[y] = new Cell({...{
1696 | x:index,
1697 | y:y,
1698 | cellWidth:this.currentObj.cellWidth,
1699 | cellHeight:this.currentObj.cellHeight,
1700 | row:1,
1701 | span:1,
1702 | merge:false,
1703 | text:""
1704 | },...this.textConfig})
1705 | insertArr[y].addEvent('change',(event)=>{
1706 | this.emit("changeCell",event)
1707 | })
1708 | this.table.add(insertArr[y])
1709 | }
1710 | this.cells.splice(index,0,insertArr)
1711 | }
1712 | //重新绘制
1713 | this.refreshCell()
1714 | }
1715 | }
1716 | //改变行数
1717 | setRowNum(number){
1718 | //检测有没有这么多行,如果没有的话,就删除单元格和Index,如果没有的话就添加单元格和Index
1719 | if(number <= this.tableIndexCell.length){
1720 | while(number < this.tableIndexCell.length){
1721 | //删除tableIndexCell最末尾几个
1722 | this.tableIndex.remove(this.tableIndexCell.pop())
1723 | //删除cells最末尾几行
1724 | for(let i=0;i this.tableIndexCell.length){
1733 | //新增tableIndexCell
1734 | //新增cells
1735 | const index = this.tableIndexCell.length
1736 | this.addTableIndexCell({
1737 | cellWidth:indexWidth,
1738 | cellHeight:this.currentObj.cellHeight,
1739 | index:index
1740 | })
1741 | //cells插入行
1742 | for(let x = 0;x < this.cells.length;x++){
1743 | let insert = new Cell({...{
1744 | x:x,
1745 | y:index,
1746 | cellWidth:this.currentObj.cellWidth,
1747 | cellHeight:this.currentObj.cellHeight,
1748 | row:1,
1749 | span:1,
1750 | merge:false,
1751 | text:""
1752 | },...this.textConfig})
1753 | insert.addEvent('change',(event)=>{
1754 | this.emit("changeCell",event)
1755 | })
1756 | this.cells[x].splice(index,0,insert)
1757 | this.table.add(insert)
1758 | }
1759 | }
1760 | //重新绘制
1761 | this.refreshCell()
1762 | }
1763 | }
1764 | //获取全部数据
1765 | getTableDatas(){
1766 | let data = []
1767 | for(let i = 0;i {
1778 | item.setData({
1779 | width:this.currentObj.cellWidth,
1780 | })
1781 | })
1782 | this.tableIndexCell.forEach(item => {
1783 | item.setData({
1784 | height:this.currentObj.cellHeight,
1785 | })
1786 | })
1787 | for(let x = 0;x 2){
1792 | let x0 = this.cells[x][y].data.mergeConfig.xstart
1793 | let y0 = this.cells[x][y].data.mergeConfig.ystart
1794 | let x1 = this.cells[x][y].data.mergeConfig.xend
1795 | let y1 = this.cells[x][y].data.mergeConfig.yend
1796 | for(let i = x0;i<=x1;i++){
1797 | for(let j=y0;j<=y1;j++){
1798 | this.cells[i][j].setData({
1799 | xPlace:this.tableHeaderCell[i].data.xPlace,
1800 | yPlace:this.tableIndexCell[j].data.yPlace,
1801 | cellWidth:this.tableHeaderCell[i].data.width,
1802 | cellHeight:this.tableIndexCell[j].data.height,
1803 | merge:false,
1804 | row:1,
1805 | span:1,
1806 | mergeConfig:null
1807 | })
1808 | this.cells[i][j].show()
1809 | this.selectCells.push(this.cells[i][j])
1810 | }
1811 | }
1812 | }
1813 | }
1814 | }
1815 | this.refreshCell()
1816 | }
1817 | //批量填充全部数据
1818 | /*
1819 | * config = {
1820 | * data:Array 一维数组 或 二维数组,data中,x和y是必须的
1821 | * clear:Boolean 填充数据时是否清空其他数据 true:清空 false:不清空
1822 | * }
1823 | */
1824 | setTableDatas(data,clear = false){
1825 | //先检测data的长度,看有没有data,没有的话提示一下,有的话进行下一步
1826 | if(data){
1827 | if(data instanceof Array){
1828 | if(data.length > 0){
1829 | if(clear){
1830 | //先清空其他格子
1831 | this.cells.forEach(list => {
1832 | list.forEach(cell => {
1833 | cell.clear()
1834 | cell.clearFormat()
1835 | })
1836 | })
1837 | }
1838 | //判断data是一维数组还是二维数组
1839 | if(data[0] instanceof Array){
1840 | //判断data最大的x,y如果大于现在的,那就添加几行几列
1841 | let arr = data.flat()
1842 | let maxX = Math.max(...arr.map(item => item.x))
1843 | let maxY = Math.max(...arr.map(item => item.y))
1844 | if(maxX > this.tableHeaderCell.length){
1845 | this.setSpanNum(maxX+1)
1846 | }
1847 | if(maxY > this.tableIndexCell.length){
1848 | this.setRowNum(maxY+1)
1849 | }
1850 | //二维
1851 | for(let i = 0;i 0 && data[i][j].span > 0){
1855 | //计算正确的宽度,合并过的单元格宽度需要用总宽度减去被合并的宽度
1856 | let xstart = data[i][j].mergeConfig.xstart
1857 | let xend = data[i][j].mergeConfig.xend
1858 | let cellWidth = data[i][j].cellWidth
1859 | for(let x = 0;x= xstart && data[x][j].x <= xend && data[x][j].merge && data[x][j].span == 0){
1861 | cellWidth -= data[x][j].cellWidth
1862 | }
1863 | }
1864 | if(this.tableHeaderCell[data[i][j].x].data.width < cellWidth){
1865 | this.tableHeaderCell[data[i][j].x].setData({
1866 | width:cellWidth
1867 | })
1868 | }
1869 | }else{
1870 | if(this.tableHeaderCell[data[i][j].x].data.width < data[i][j].cellWidth){
1871 | this.tableHeaderCell[data[i][j].x].setData({
1872 | width:data[i][j].cellWidth
1873 | })
1874 | }
1875 | }
1876 | }
1877 | if(data[i][j].cellHeight){
1878 | if(data[i][j].merge && data[i][j].row > 0 && data[i][j].span > 0){
1879 | //计算正确的高度
1880 | let ystart = data[i][j].mergeConfig.ystart
1881 | let yend = data[i][j].mergeConfig.yend
1882 | let cellHeight = data[i][j].cellHeight
1883 | for(let y = 0;y= ystart && data[i][y].y <= yend && data[i][y].merge && data[i][y].row == 0){
1885 | cellHeight -= data[i][y].cellHeight
1886 | }
1887 | }
1888 | if(this.tableIndexCell[data[i][j].y].data.height < cellHeight){
1889 | this.tableIndexCell[data[i][j].y].setData({
1890 | height:cellHeight
1891 | })
1892 | }
1893 | }else{
1894 | if(this.tableIndexCell[data[i][j].y].data.height < data[i][j].cellHeight){
1895 | this.tableIndexCell[data[i][j].y].setData({
1896 | height:data[i][j].cellHeight
1897 | })
1898 | }
1899 | }
1900 | }
1901 | this.cells[data[i][j].x][data[i][j].y].setData({
1902 | ...this.textConfig,...data[i][j]
1903 | })
1904 | }
1905 | }
1906 | this.refreshCell()
1907 | }else{
1908 | //判断data最大的x,y如果大于现在的,那就添加几行几列
1909 | let maxX = Math.max(data.map(item => item.x))
1910 | let maxY = Math.max(data.map(item => item.y))
1911 | if(maxX > this.tableHeaderCell.length){
1912 | this.setSpanNum(maxX)
1913 | }
1914 | if(maxY > this.tableIndexCell.length){
1915 | this.setRowNum(maxY)
1916 | }
1917 | //一维
1918 | for(let i = 0;i 0 && data[i].span > 0){
1921 | //计算正确的宽度,合并过的单元格宽度需要用总宽度减去被合并的宽度
1922 | let xstart = data[i].mergeConfig.xstart
1923 | let xend = data[i].mergeConfig.xend
1924 | let cellWidth = data[i].cellWidth
1925 | for(let x = 0;x= xstart && data[x].x <= xend && data[x].merge && data[x].span == 0){
1927 | cellWidth -= data[x].cellWidth
1928 | }
1929 | }
1930 | if(this.tableHeaderCell[data[i].x].data.width < cellWidth){
1931 | this.tableHeaderCell[data[i].x].setData({
1932 | width:cellWidth
1933 | })
1934 | }
1935 | }else{
1936 | if(this.tableHeaderCell[data[i].x].data.width < data[i].cellWidth){
1937 | this.tableHeaderCell[data[i].x].setData({
1938 | width:data[i].cellWidth
1939 | })
1940 | }
1941 | }
1942 | }
1943 | if(data[i].cellHeight){
1944 | if(data[i].merge && data[i].row > 0 && data[i].span > 0){
1945 | //计算正确的高度
1946 | let ystart = data[i].mergeConfig.ystart
1947 | let yend = data[i].mergeConfig.yend
1948 | let cellHeight = data[i].cellHeight
1949 | for(let y = 0;y= ystart && data[y].y <= yend && data[y].merge && data[y].row == 0){
1951 | cellHeight -= data[y].cellHeight
1952 | }
1953 | }
1954 | if(this.tableIndexCell[data[i].y].data.height < cellHeight){
1955 | this.tableIndexCell[data[i].y].setData({
1956 | height:cellHeight
1957 | })
1958 | }
1959 | }else{
1960 | if(this.tableIndexCell[data[i].y].data.height < data[i].cellHeight){
1961 | this.tableIndexCell[data[i].y].setData({
1962 | height:data[i].cellHeight
1963 | })
1964 | }
1965 | }
1966 | }
1967 | this.cells[data[i].x][data[i].y].setData({
1968 | ...this.textConfig,...data[i]
1969 | })
1970 | }
1971 | this.refreshCell()
1972 | }
1973 | }else{
1974 | return false
1975 | }
1976 | }else{
1977 | return false
1978 | }
1979 | }else{
1980 | return false
1981 | }
1982 | }
1983 | /****
1984 | * 注销对象
1985 | *
1986 | */
1987 | dispose(){
1988 | this.canvas.dispose()
1989 | this.canvas = null
1990 | const parent = document.getElementById(this.currentObj.id)
1991 | console.log(parent)
1992 | if(parent){
1993 | parent.innerHTML = ""
1994 | }
1995 | this.scroll.dispose()
1996 | document.removeEventListener('click',this.hideMenu)
1997 | document.removeEventListener('keydown',this.keydownMethod)
1998 | document.removeEventListener('mouseup',this.removeMethods)
1999 | this.currentObj = null
2000 | this.table = null
2001 | this.cells = null
2002 | this.activeCell = null
2003 | this.selectCells = null
2004 | this.copyCells = null
2005 | this.selectedCell = null
2006 | this.copyedCell = null
2007 | this.tableHeader = null
2008 | this.tableHeaderCell = null
2009 | this.tableIndex = null
2010 | this.tableIndexCell = null
2011 | this.selectAllCell = null
2012 | this.edit = null
2013 | this.contextMenu = null
2014 | this.changeWidthLine = null
2015 | this.uploadFile = null
2016 | this.toolBar = null
2017 | this.textConfig = null
2018 | }
2019 | }
2020 |
2021 | window.DaoDaoExcel = DaoDaoExcel
2022 |
2023 | export default DaoDaoExcel
2024 |
--------------------------------------------------------------------------------
/src/scroll.js:
--------------------------------------------------------------------------------
1 | import Event from "./event.js"
2 | import {scrollWidth} from './config.js'
3 |
4 | class Scroll extends Event{
5 | constructor(config){
6 | const defaultConfig= {
7 | bgColor:'rgba(199,199,199,0.3)',
8 | hoverBgColor:'rgba(199,199,199,0.5)',
9 | scrollColor:'rgba(167,167,167,0.3)',
10 | scrollWidth:scrollWidth,
11 | hoverColor:'rgba(167,167,167,0.6)'
12 | }
13 | super()
14 | this.config = Object.assign({},defaultConfig,config)
15 | //纵向滚动条的包围盒
16 | this.scrollHeight = null
17 | //纵向滚动条的操纵条
18 | this.scrollHeightBtn = null
19 | //横向滚动条的包围盒
20 | this.scrollWidth = null
21 | //横向滚动条的操纵条
22 | this.scrollWidthBtn = null
23 | //鼠标点下去的时候的x,y坐标
24 | this.originMouseXY = [0,0]
25 | this.originScrollXY = [0,0]
26 | this.parent = config.parent
27 | this.init(config.parent)
28 | }
29 | init(parent){
30 | this.initMethod = this.initMethod.bind(this)
31 | document.addEventListener('mouseup',this.initMethod)
32 | this.addScrollY(parent)
33 | this.addScrollX(parent)
34 | }
35 | initMethod(){
36 | if(this.mouseMoveY){
37 | document.removeEventListener('mousemove',this.mouseMoveY)
38 | }
39 | if(this.mouseMoveX){
40 | document.removeEventListener('mousemove',this.mouseMoveX)
41 | }
42 | }
43 | dispose(){
44 | document.removeEventListener('mouseup',this.initMethod)
45 | }
46 | scrollY(moveY){
47 | //将进度换算成进度条的移动
48 | let moveScroll = - moveY / this.config.fullHeight * this.config.wrapHeight
49 | if(moveScroll < 0){
50 | moveScroll = 0
51 | }
52 | if(moveScroll > this.config.wrapHeight - (this.config.wrapHeight / this.config.fullHeight * this.config.wrapHeight)){
53 | moveScroll = this.config.wrapHeight - (this.config.wrapHeight / this.config.fullHeight * this.config.wrapHeight)
54 | }
55 | if(this.scrollHeight){
56 | this.scrollHeightBtn.style.top = moveScroll + 'px'
57 | }
58 | }
59 | mouseMoveY(event){
60 | //鼠标拖动的距离为
61 | let distance = event.pageY - this.originMouseXY[1]
62 | let positionY = this.originScrollXY[1] + distance
63 | if(positionY < 0){
64 | positionY = 0
65 | }
66 | if(positionY > this.config.wrapHeight - (this.config.wrapHeight / this.config.fullHeight * this.config.wrapHeight)){
67 | positionY = this.config.wrapHeight - (this.config.wrapHeight / this.config.fullHeight * this.config.wrapHeight)
68 | }
69 | this.scrollHeightBtn.style.top = positionY + 'px'
70 | //换算成页面需要挪动多少
71 | let pageMoveY = -positionY / this.config.wrapHeight * (this.config.fullHeight + this.config.scrollWidth)
72 | this.emit('scrollY',{
73 | pageMove:pageMoveY
74 | })
75 | }
76 | mouseMoveX(event){
77 | //鼠标拖动的距离为
78 | let distance = event.pageX - this.originMouseXY[0]
79 | let positionX = this.originScrollXY[0] + distance
80 | if(positionX < 0){
81 | positionX = 0
82 | }
83 | if(positionX > this.config.wrapWidth - (this.config.wrapWidth / this.config.fullWidth * this.config.wrapWidth)){
84 | positionX = this.config.wrapWidth - (this.config.wrapWidth / this.config.fullWidth * this.config.wrapWidth)
85 | }
86 | this.scrollWidthBtn.style.left = positionX + 'px'
87 | //换算成页面需要挪动多少
88 | let pageMoveX = -positionX / this.config.wrapWidth * (this.config.fullWidth + this.config.scrollWidth)
89 | this.emit('scrollX',{
90 | pageMove:pageMoveX
91 | })
92 | }
93 | addScrollY(){
94 | //判断是否需要加纵向滚动条
95 | if(this.config.wrapHeight < this.config.fullHeight){
96 | this.scrollHeight = document.createElement('div')
97 | this.scrollHeight.id = "daodao_excel_scroll_height_wrapper"
98 | this.scrollHeightBtn = document.createElement('div')
99 | this.scrollHeightBtn.id = "daodao_excel_scroll_height_btn"
100 | this.scrollHeight.appendChild(this.scrollHeightBtn)
101 | const parent = this.config.parent
102 | this.parent = parent
103 | parent.appendChild(this.scrollHeight)
104 | parent.style.position = "relative"
105 |
106 | //设置纵向滚动条的style
107 | this.scrollHeight.style.cssText = `
108 | position:absolute;
109 | bottom:0;
110 | right:0;
111 | height:${this.config.wrapHeight}px;
112 | width:${this.config.scrollWidth}px;
113 | background:${this.config.bgColor};
114 | `
115 | this.scrollHeightBtn.style.cssText = `
116 | position:absolute;
117 | cursor:pointer;
118 | border-radius:${this.config.scrollWidth / 2}px;
119 | top:0;
120 | left:1px;
121 | height:${this.config.wrapHeight / this.config.fullHeight * this.config.wrapHeight}px;
122 | width:${this.config.scrollWidth - 2}px;
123 | background:${this.config.scrollColor};
124 | `
125 | const style = document.createElement('style')
126 | style.innerHTML = `#daodao_excel_scroll_height_wrapper:hover{
127 | background:${this.config.hoverBgColor}!important;
128 | }
129 | #daodao_excel_scroll_height_btn:hover{
130 | background:${this.config.hoverColor}!important;
131 | }`
132 | this.scrollHeight.appendChild(style)
133 |
134 | //给纵向滚动条加事件
135 | this.scrollHeightBtn.addEventListener('mousedown',(event) => {
136 | this.originMouseXY = [event.pageX,event.pageY]
137 | this.originScrollXY = [parseInt(this.scrollWidthBtn.style.left),parseInt(this.scrollHeightBtn.style.top)]
138 | this.mouseMoveY = this.mouseMoveY.bind(this)
139 | document.addEventListener('mousemove',this.mouseMoveY)
140 | })
141 | }
142 | }
143 | addScrollX(){
144 | //判断是否需要加横向滚动条
145 | if(this.config.wrapWidth < this.config.fullWidth){
146 | this.scrollWidth = document.createElement('div')
147 | this.scrollWidth.id = "daodao_excel_scroll_width_wrapper"
148 | this.scrollWidthBtn = document.createElement('div')
149 | this.scrollWidthBtn.id = "daodao_excel_scroll_width_btn"
150 | this.scrollWidth.appendChild(this.scrollWidthBtn)
151 | const parent = this.config.parent
152 | parent.appendChild(this.scrollWidth)
153 | parent.style.position = "relative"
154 |
155 | //设置纵向滚动条的style
156 | this.scrollWidth.style.cssText = `
157 | position:absolute;
158 | bottom:0;
159 | right:${this.config.scrollWidth}px;
160 | height:${this.config.scrollWidth}px;
161 | width:${this.config.wrapWidth - this.config.scrollWidth}px;
162 | background:${this.config.bgColor};
163 | `
164 | this.scrollWidthBtn.style.cssText = `
165 | position:absolute;
166 | cursor:pointer;
167 | border-radius:${this.config.scrollWidth / 2}px;
168 | top:1px;
169 | left:0;
170 | width:${this.config.wrapWidth / this.config.fullWidth * (this.config.wrapWidth - this.config.scrollWidth)}px;
171 | height:${this.config.scrollWidth - 2}px;
172 | background:${this.config.scrollColor};
173 | `
174 | const style = document.createElement('style')
175 | style.innerHTML = `#daodao_excel_scroll_width_wrapper:hover{
176 | background:${this.config.hoverBgColor}!important;
177 | }
178 | #daodao_excel_scroll_width_btn:hover{
179 | background:${this.config.hoverColor}!important;
180 | }`
181 | this.scrollWidth.appendChild(style)
182 |
183 | //给横向滚动条加事件
184 | this.scrollWidthBtn.addEventListener('mousedown',(event) => {
185 | this.originMouseXY = [event.pageX,event.pageY]
186 | this.originScrollXY = [parseInt(this.scrollWidthBtn.style.left),parseInt(this.scrollHeightBtn.style.top)]
187 | this.mouseMoveX = this.mouseMoveX.bind(this)
188 | document.addEventListener('mousemove',this.mouseMoveX)
189 | })
190 | }
191 | }
192 | refresh(data){
193 | this.config = Object.assign({},this.config,data)
194 | //判断是否要纵向滚动条
195 | if(this.config.wrapHeight < this.config.fullHeight){
196 | if(this.scrollHeight){
197 | //改一下纵向滚动条
198 | //设置纵向滚动条的style
199 | this.scrollHeight.style.height = `${this.config.wrapHeight}px`
200 | this.scrollHeightBtn.style.height = `${this.config.wrapHeight / this.config.fullHeight * this.config.wrapHeight}px`
201 | }else{
202 | this.addScrollY()
203 | }
204 | }else{
205 | //移除纵向滚动条
206 | if(this.scrollHeight){
207 | this.parent.removeChild(this.scrollHeight)
208 | this.scrollHeight = null
209 | }
210 | }
211 | //判断是否要横向滚动条
212 | if(this.config.wrapWidth < this.config.fullWidth){
213 | if(this.scrollWidth){
214 | //改一下横向滚动条
215 | //设置纵向滚动条的style
216 | this.scrollWidth.style.width = `${this.config.wrapWidth - this.config.scrollWidth}px`
217 | this.scrollWidthBtn.style.width = `${this.config.wrapWidth / this.config.fullWidth * (this.config.wrapWidth - this.config.scrollWidth)}px`
218 | }else{
219 | this.addScrollX()
220 | }
221 | }else{
222 | //移除纵向滚动条
223 | if(this.scrollWidth){
224 | this.parent.removeChild(this.scrollWidth)
225 | this.scrollWidth = null
226 | }
227 | }
228 | }
229 | }
230 |
231 | export default Scroll
--------------------------------------------------------------------------------
/src/selectCell.js:
--------------------------------------------------------------------------------
1 | import zrender from 'zrender'
2 | import {headerHeight,indexWidth} from './config'
3 |
4 | class SelectCell extends zrender.Rect{
5 | constructor(cells){
6 | let originConfig = {
7 | cursor:'default',
8 | style:{
9 | stroke: '#4e9fff',
10 | lineWidth:'2',
11 | fill:'none',
12 | },
13 | z:3
14 | }
15 | let config = {
16 | shape:{
17 |
18 | }
19 | }
20 | //cells的长度为1的时候
21 | if(cells.length == 1){
22 | config.shape = {
23 | x:cells[0].data.xPlace,
24 | y:cells[0].data.yPlace,
25 | width:cells[0].data.cellWidth,
26 | height:cells[0].data.cellHeight
27 | }
28 | }else{
29 | let x = cells[0].data.xPlace
30 | let y = cells[0].data.yPlace
31 | let width = cells[cells.length - 1].data.xPlace + cells[cells.length - 1].data.cellWidth - x
32 | let height = cells[cells.length - 1].data.yPlace + cells[cells.length - 1].data.cellHeight - y
33 |
34 | //下标最小的是起点
35 | config.shape.x = x
36 | config.shape.y = y
37 | //下标最大的是终点
38 | config.shape.width = width
39 | config.shape.height = height
40 | }
41 | let finalConfig = Object.assign({},originConfig,config)
42 | super(finalConfig)
43 | }
44 | //改变选择框的位置和大小
45 | change(cells){
46 | let shape = {}
47 | if(cells.length == 1){
48 | shape = {
49 | x:cells[0].data.xPlace,
50 | y:cells[0].data.yPlace,
51 | width:cells[0].data.cellWidth,
52 | height:cells[0].data.cellHeight
53 | }
54 | }else{
55 | //求最小下标以及最大下标
56 | let x = cells[0].data.xPlace
57 | let y = cells[0].data.yPlace
58 | let width = cells[cells.length - 1].data.xPlace + cells[cells.length - 1].data.cellWidth - x
59 | let height = cells[cells.length - 1].data.yPlace + cells[cells.length - 1].data.cellHeight - y
60 |
61 | //下标最小的是起点
62 | shape.x = x
63 | shape.y = y
64 | //下标最大的是终点
65 | shape.width = width
66 | shape.height = height
67 | }
68 | this.attr('shape',shape)
69 | }
70 | }
71 |
72 | export default SelectCell
--------------------------------------------------------------------------------
/src/tableHeaderCell.js:
--------------------------------------------------------------------------------
1 | import zrender from 'zrender'
2 | import {letter,indexWidth} from './config.js'
3 | import { mouseWheelDirection, preventDefault,stopPropagation,generateCode } from "./utils.js"
4 |
5 | class TableHeaderCell extends zrender.Group{
6 | constructor(config){
7 | let defaultConfig = {
8 | cursor:'default',
9 | scale:[0.5,0.5]
10 | }
11 | //由于zrender的Rect描边粗细有bug,所以我的做法是先放大两倍然后再缩放0.5倍
12 | let xPlace = config.cellWidth * config.index + indexWidth
13 | let yPlace = 0
14 | let countConfig = {
15 | shape:{
16 | x:1,
17 | y:1,
18 | width:config.cellWidth * 2,
19 | height:config.cellHeight * 2
20 | },
21 | style:{
22 | text:generateCode(config.index),
23 | stroke: '#aaa',
24 | fill: 'white',
25 | lineWidth:'1',
26 | },
27 | z:10
28 | }
29 | let finnalconfig = Object.assign({},defaultConfig,config,countConfig)
30 | super({position:[xPlace,yPlace],z:1000})
31 | this.data = {
32 | index:config.index,
33 | width:config.cellWidth,
34 | xPlace:xPlace
35 | }
36 | this.box = new zrender.Rect(finnalconfig)
37 | this.box.type = 'headerBorder'
38 | this.add(this.box)
39 | this.line = new zrender.Line({
40 | shape:{
41 | x1:config.cellWidth,
42 | y1:0,
43 | x2:config.cellWidth,
44 | y2:config.cellHeight
45 | },
46 | cursor:'ew-resize',
47 | style:{
48 | stroke: '#fff',
49 | opacity:0,
50 | fill: 'white',
51 | lineWidth:'6',
52 | },
53 | z:11
54 | })
55 | this.add(this.line)
56 | this.type = 'tableHeaderCell'
57 | this.handlers = {}
58 | this.isDrag = false
59 | this.line.on('mousedown',(event) => {
60 | stopPropagation(event.event)
61 | let width = event.offsetX - this.data.xPlace - this.parent.position[0]
62 | this.emit('dragLine',{width:width,offsetX:event.offsetX})
63 | this.isDrag = true
64 | this.mousemove = this.mousemove.bind(this)
65 | document.addEventListener('mousemove',this.mousemove)
66 | this.mouseup = this.mouseup.bind(this)
67 | document.addEventListener('mouseup',this.mouseup)
68 | })
69 | }
70 | mouseup(event){
71 | if(this.isDrag){
72 | this.data.width = event.offsetX - this.data.xPlace - this.parent.position[0]
73 | let offsetX = event.offsetX
74 | if(this.data.width < 10){
75 | this.data.width = 10
76 | offsetX = this.data.xPlace + 10
77 | }else{
78 | offsetX = event.offsetX
79 | }
80 | this.emit('changeSize',{width:this.data.width,offsetX:offsetX})
81 | }
82 | this.isDrag = false
83 | document.removeEventListener('mousemove',this.mousemove)
84 | document.removeEventListener('mouseup',this.mouseup)
85 | }
86 | mousemove(event){
87 | let width = event.offsetX - this.data.xPlace - this.parent.position[0]
88 | let offsetX = event.offsetX
89 | if(width < 10){
90 | width = 10
91 | offsetX = this.data.xPlace + 10
92 | }else{
93 | offsetX = event.offsetX
94 | }
95 | this.emit('dragLine',{width:width,offsetX:offsetX})
96 | }
97 | selectCell(){
98 | this.box.attr({style:{fill:'rgb(160,212,255)'}})
99 | }
100 | unSelectCell(){
101 | this.box.attr({style:{fill:'#fff'}})
102 | }
103 | addEvent(type,handler){
104 | if(typeof this.handlers[type] === "undefined"){
105 | this.handlers[type] = []
106 | }
107 | this.handlers[type].push(handler)
108 | }
109 | emit(type,event){
110 | if(!event.target){
111 | event.target = this
112 | }
113 | if(this.handlers[type] instanceof Array){
114 | const handlers = this.handlers[type]
115 | handlers.forEach((handler)=>{
116 | handler(event)
117 | })
118 | }
119 | }
120 | remove(type,handler){
121 | if(this.handlers[type] instanceof Array){
122 | const handlers = this.handlers[type]
123 | for(var i = 0,len = handlers.length; i < len; i++){
124 | if(handlers[i] === handler){
125 | break;
126 | }
127 | }
128 | handlers.splice(i,1)
129 | }
130 | }
131 | refresh(){
132 | this.attr('position',[this.data.xPlace,0])
133 | //画格子
134 | this.box.attr({
135 | shape:{
136 | width:this.data.width * 2
137 | }
138 | })
139 | //画线线
140 | this.line.attr({
141 | shape:{
142 | x1:this.data.width,
143 | x2:this.data.width
144 | }
145 | })
146 | }
147 | setData(data){
148 | this.data = Object.assign({},this.data,data)
149 | this.box.attr({style:{
150 | text:generateCode(this.data.index)
151 | }})
152 | }
153 | }
154 |
155 | export default TableHeaderCell
--------------------------------------------------------------------------------
/src/tableIndexCell.js:
--------------------------------------------------------------------------------
1 | import zrender from 'zrender'
2 | import {headerHeight} from './config.js'
3 | import { mouseWheelDirection, preventDefault,stopPropagation } from "./utils.js"
4 |
5 | class TableHeaderCell extends zrender.Group{
6 | constructor(config){
7 | let defaultConfig = {
8 | cursor:'default',
9 | scale:[0.5,0.5]
10 | }
11 | //由于zrender的Rect描边粗细有bug,所以我的做法是先放大两倍然后再缩放0.5倍
12 | let xPlace = 0
13 | let yPlace = config.cellHeight * config.index + headerHeight
14 | let countConfig = {
15 | shape:{
16 | x:1,
17 | y:1,
18 | width:config.cellWidth * 2,
19 | height:config.cellHeight * 2
20 | },
21 | style:{
22 | text:config.index + 1,
23 | stroke: '#aaa',
24 | fill: 'white',
25 | lineWidth:'1'
26 | },
27 | z:10
28 | }
29 | let finnalconfig = Object.assign({},defaultConfig,config,countConfig)
30 | super({position:[xPlace,yPlace],z:1000})
31 | this.box = new zrender.Rect(finnalconfig)
32 | this.box.type = 'indexBorder'
33 | this.add(this.box)
34 | this.line = new zrender.Line({
35 | shape:{
36 | x1:0,
37 | y1:config.cellHeight,
38 | x2:config.cellWidth,
39 | y2:config.cellHeight
40 | },
41 | cursor:'ns-resize',
42 | style:{
43 | stroke: '#aaa',
44 | opacity:0,
45 | fill: 'white',
46 | lineWidth:'6',
47 | },
48 | z:11
49 | })
50 | this.add(this.line)
51 | this.type = 'tableHeaderCell'
52 | this.handlers = {}
53 | this.isDrag = false
54 | this.data = {
55 | index:config.index,
56 | height:config.cellHeight,
57 | yPlace:yPlace
58 | }
59 | this.type = 'tableIndexCell'
60 | this.line.on('mousedown',(event) => {
61 | stopPropagation(event.event)
62 | let height = event.offsetY - this.data.yPlace - this.parent.position[1]
63 | this.emit('dragLine',{height:height,offsetY:event.offsetY})
64 | this.isDrag = true
65 | this.mousemove = this.mousemove.bind(this)
66 | document.addEventListener('mousemove',this.mousemove)
67 | this.mouseup = this.mouseup.bind(this)
68 | document.addEventListener('mouseup',this.mouseup)
69 | })
70 | }
71 | mouseup(event){
72 | if(this.isDrag){
73 | this.data.height = event.offsetY - this.data.yPlace - this.parent.position[1]
74 | let offsetY = event.offsetY
75 | if(this.data.height < 10){
76 | this.data.height = 10
77 | offsetY = this.data.yPlace + 10
78 | }else{
79 | offsetY = event.offsetY
80 | }
81 | this.emit('changeSize',{height:this.data.height,offsetY:offsetY})
82 | }
83 | this.isDrag = false
84 | document.removeEventListener('mousemove',this.mousemove)
85 | document.removeEventListener('mouseup',this.mouseup)
86 | }
87 | mousemove(event){
88 | let height = event.offsetY - this.data.yPlace - this.parent.position[1]
89 | let offsetY = event.offsetY
90 | if(height < 10){
91 | height = 10
92 | offsetY = this.data.yPlace + 10
93 | }else{
94 | offsetY = event.offsetY
95 | }
96 | this.emit('dragLine',{height:height,offsetY:offsetY})
97 | }
98 | selectCell(){
99 | this.box.attr({style:{fill:'rgb(160,212,255)'}})
100 | }
101 | unSelectCell(){
102 | this.box.attr({style:{fill:'#fff'}})
103 | }
104 | addEvent(type,handler){
105 | if(typeof this.handlers[type] === "undefined"){
106 | this.handlers[type] = []
107 | }
108 | this.handlers[type].push(handler)
109 | }
110 | emit(type,event){
111 | if(!event.target){
112 | event.target = this
113 | }
114 | if(this.handlers[type] instanceof Array){
115 | const handlers = this.handlers[type]
116 | handlers.forEach((handler)=>{
117 | handler(event)
118 | })
119 | }
120 | }
121 | remove(type,handler){
122 | if(this.handlers[type] instanceof Array){
123 | const handlers = this.handlers[type]
124 | for(var i = 0,len = handlers.length; i < len; i++){
125 | if(handlers[i] === handler){
126 | break;
127 | }
128 | }
129 | handlers.splice(i,1)
130 | }
131 | }
132 | refresh(){
133 | this.attr('position',[0,this.data.yPlace])
134 | //画格子
135 | this.box.attr({
136 | shape:{
137 | height:this.data.height * 2
138 | }
139 | })
140 | //画线线
141 | this.line.attr({
142 | shape:{
143 | y1:this.data.height,
144 | y2:this.data.height
145 | }
146 | })
147 | }
148 | setData(data){
149 | this.data = Object.assign({},this.data,data)
150 | this.box.attr({style:{
151 | text:this.data.index+1
152 | }})
153 | }
154 | }
155 |
156 | export default TableHeaderCell
--------------------------------------------------------------------------------
/src/toolBar.js:
--------------------------------------------------------------------------------
1 | import Event from "./event.js"
2 | import {preventDefault,stopPropagation ,generateUUID} from "./utils.js"
3 |
4 | class ToolBar extends Event{
5 | constructor(parent){
6 | super()
7 | this.el = null
8 | this.parent = parent
9 | this.copyButton = null
10 | this.pasteButton = null
11 | this.clearFormat = null
12 | this.typeFaceButton = null
13 | this.fontSizeButton = null
14 | this.fontWeightButton = null
15 | this.fontItalicButton = null
16 | this.underLineButton = null
17 | this.textFillButton = null
18 | this.fillButton = null
19 | this.borderButton = null
20 | this.alignLiftButton = null
21 | this.alignRightButton = null
22 | this.alignCenterButton = null
23 | this.mergeCellButton = null
24 | this.splitCellButton = null
25 | this.addImageButton = null
26 | this.init(parent)
27 | }
28 | init(parent){
29 | this.el = document.createElement('div')
30 | this.el.id = 'daodao_excel_scroll_toolbar-wrapper'
31 | this.el.style.cssText = `
32 | height:30px;
33 | `
34 | const first = parent.firstChild
35 | parent.insertBefore(this.el,first)
36 | this.initStyle()
37 | this.initCopy()
38 | this.initPaste()
39 | this.initClearFormat()
40 | this.initTypeFace()
41 | this.initFontSize()
42 | this.initFontWeight()
43 | this.initFontItalic()
44 | this.initTextFill()
45 | this.initFill()
46 | this.initBorder()
47 | this.initAlignLeft()
48 | this.initAlignRight()
49 | this.initAlignCenter()
50 | this.initMergeCell()
51 | this.initSplitCell()
52 | this.initAddImage()
53 | }
54 | initStyle(){
55 | const style = document.createElement('style')
56 | style.innerHTML = `
57 | #daodao_excel_scroll_toolbar-wrapper .toolbar-item{
58 | display: inline-block;
59 | position:relative;
60 | border:1px solid transparent;
61 | vertical-align: middle;
62 | cursor: pointer;
63 | margin-right:4px;
64 | }
65 | #daodao_excel_scroll_toolbar-wrapper .toolbar-item .tooltip__down{
66 | display:none;
67 | position:absolute;
68 | margin-top:10px;
69 | left:50%;
70 | margin-left:-35px;
71 | width:70px;
72 | height:22px;
73 | line-height: 22px;
74 | font-size:12px;
75 | border:1px solid #d0d0d0;
76 | background: #fff;
77 | text-align: center;
78 | z-index:10;
79 | }
80 | #daodao_excel_scroll_toolbar-wrapper .toolbar-item .tooltip__down:before{
81 | content:'';
82 | width:0;
83 | height:0;
84 | border-bottom:8px solid #d0d0d0;
85 | border-right:5px solid transparent;
86 | border-left:5px solid transparent;
87 | position:absolute;
88 | top:-8px;
89 | left:50%;
90 | margin-left:-5px;
91 | }
92 | #daodao_excel_scroll_toolbar-wrapper .toolbar-item .tooltip__down:after{
93 | content:'';
94 | width:0;
95 | height:0;
96 | border-bottom:8px solid #ffffff;
97 | border-right:5px solid transparent;
98 | border-left:5px solid transparent;
99 | position:absolute;
100 | top:-6px;
101 | left:50%;
102 | margin-left:-5px;
103 | }
104 | #daodao_excel_scroll_toolbar-wrapper .toolbar-item:hover{
105 | border:1px solid #d0d0d0;
106 | }
107 | #daodao_excel_scroll_toolbar-wrapper .toolbar-item:hover .tooltip__down{
108 | display: block;
109 | }
110 | #daodao_excel_scroll_toolbar-wrapper .toolbar-button{
111 | width:20px;
112 | height:20px;
113 | text-align: center;
114 | background:#fff;
115 | }
116 | #daodao_excel_scroll_toolbar-wrapper .toolbar-button.active{
117 | background:#e0e0e0
118 | }
119 | `
120 | this.el.appendChild(style)
121 | }
122 | initCopy(){
123 | this.copyButton = this.addButton(
124 | '',
125 | '复制',
126 | (e)=>{
127 | this.emit('copy',{
128 | data:true
129 | })
130 | },
131 | false
132 | )
133 | }
134 | initPaste(){
135 | this.copyButton = this.addButton(
136 | '',
137 | '粘贴',
138 | (e)=>{
139 | this.emit('paste',{
140 | data:true
141 | })
142 | },
143 | false
144 | )
145 | }
146 | initClearFormat(){
147 | this.copyButton = this.addButton(
148 | '',
149 | '清除格式',
150 | (e)=>{
151 | this.emit('clearFormat',{
152 | data:true
153 | })
154 | },
155 | false
156 | )
157 | }
158 | initTypeFace(){
159 | const id = generateUUID()
160 | const html = `
161 |
168 |
169 | 选择字体
170 |
171 | `
172 | let div = document.createElement('div')
173 | div.classList = ["toolbar-item"]
174 | div.innerHTML = html
175 |
176 | const style = document.createElement('style')
177 | style.innerHTML = `
178 | #daodao_excel_scroll_toolbar-wrapper select{
179 | border:none!important;
180 | outline: none!important;
181 | }
182 | `
183 | this.el.appendChild(style)
184 |
185 | this.el.appendChild(div)
186 |
187 | this.typeFaceButton = document.getElementById(id)
188 |
189 | this.typeFaceButton.addEventListener('change',(e) => {
190 | this.emit('changeTypeFace',{
191 | data:e.target.value
192 | })
193 | })
194 | }
195 | initFontSize(){
196 | const id = generateUUID()
197 | const html = `
198 |
211 |
212 | 字体大小
213 |
214 | `
215 | let div = document.createElement('div')
216 | div.classList = ["toolbar-item"]
217 | div.innerHTML = html
218 |
219 | this.el.appendChild(div)
220 |
221 | this.fontSizeButton = document.getElementById(id)
222 |
223 | this.fontSizeButton.addEventListener('change',(e) => {
224 | this.emit('changeFontSize',{
225 | data:e.target.value
226 | })
227 | })
228 | }
229 | initFontWeight(){
230 | this.fontWeightButton = this.addButton(
231 | '',
232 | '加粗',
233 | (e)=>{
234 | if(e){
235 | this.emit('changeFontWeight',{
236 | data:'bold'
237 | })
238 | }else{
239 | this.emit('changeFontWeight',{
240 | data:'normal'
241 | })
242 | }
243 | },
244 | true
245 | )
246 | }
247 | initFontItalic(){
248 | this.fontItalicButton = this.addButton(
249 | '',
250 | '倾斜',
251 | (e)=>{
252 | if(e){
253 | this.emit('changeFontItalic',{
254 | data:'italic'
255 | })
256 | }else{
257 | this.emit('changeFontItalic',{
258 | data:'normal'
259 | })
260 | }
261 | },
262 | true
263 | )
264 | }
265 | initTextFill(){
266 | const id = generateUUID()
267 | const id1 = generateUUID()
268 | const id2 = generateUUID()
269 | const html = `
270 |
274 |
275 | 文字颜色
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 | `
332 | let div = document.createElement('div')
333 | div.classList.add("toolbar-item","toolbar-drop-down","toolbar-color")
334 | div.innerHTML = html
335 |
336 | const style = document.createElement('style')
337 | style.innerHTML = `
338 | #daodao_excel_scroll_toolbar-wrapper .dropdown-icon{
339 | height:20px;
340 | display: inline-block;
341 | }
342 | #daodao_excel_scroll_toolbar-wrapper .toolbar-icon{
343 | height:20px;
344 | display: inline-block;
345 | }
346 | #daodao_excel_scroll_toolbar-wrapper .toolbar-color .dropdown-icon .dropdown-card{
347 | display: none;
348 | position:absolute;
349 | left:50%;
350 | margin-left:-110px;
351 | width:220px;
352 | line-height: 22px;
353 | font-size:12px;
354 | border:1px solid #d0d0d0;
355 | background: #fff;
356 | text-align: center;
357 | z-index:10;
358 | }
359 | #daodao_excel_scroll_toolbar-wrapper .toolbar-color .dropdown-icon .dropdown-card:before{
360 | content:'';
361 | width:0;
362 | height:0;
363 | border-bottom:8px solid #d0d0d0;
364 | border-right:5px solid transparent;
365 | border-left:5px solid transparent;
366 | position:absolute;
367 | top:-8px;
368 | left:50%;
369 | margin-left:-5px;
370 | }
371 | #daodao_excel_scroll_toolbar-wrapper .toolbar-color .dropdown-icon .dropdown-card:after{
372 | content:'';
373 | width:0;
374 | height:0;
375 | border-bottom:8px solid #ffffff;
376 | border-right:5px solid transparent;
377 | border-left:5px solid transparent;
378 | position:absolute;
379 | top:-6px;
380 | left:50%;
381 | margin-left:-5px;
382 | }
383 | #daodao_excel_scroll_toolbar-wrapper .toolbar-color .dropdown-card .color-button{
384 | width:20px;
385 | height:20px;
386 | float:left;
387 | margin:4px 2px;
388 | }
389 | #daodao_excel_scroll_toolbar-wrapper .toolbar-color .dropdown-card .color-button:hover{
390 | box-shadow:0 0 7px 2px #d0d0d0;
391 | }
392 |
393 | `
394 | this.el.appendChild(style)
395 |
396 | this.el.appendChild(div)
397 |
398 | this.textFillButton = document.getElementById(id1)
399 |
400 | document.getElementById(id2).addEventListener('click',(event) => {
401 | stopPropagation(event)
402 | document.getElementById(id).style.display = 'block'
403 | parent.addEventListener('click',hide)
404 | })
405 |
406 | document.getElementById(id).addEventListener('click',(e) => {
407 | let color = e.target.dataset.color
408 | document.getElementById(id).style.display = 'none'
409 | this.emit('changeTextFill',{
410 | data:color
411 | })
412 | this.textFillButton.querySelector(".color-line").style.backgroundColor = color
413 | })
414 |
415 | function hide(){
416 | document.getElementById(id).style.display = 'none'
417 | parent.removeEventListener('click',hide)
418 | }
419 | }
420 | initFill(){
421 | const id = generateUUID()
422 | const id1 = generateUUID()
423 | const id2 = generateUUID()
424 | const html = `
425 |
429 |
430 | 背景颜色
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 | `
487 | let div = document.createElement('div')
488 | div.classList.add("toolbar-item","toolbar-drop-down","toolbar-color")
489 | div.innerHTML = html
490 |
491 | this.el.appendChild(div)
492 |
493 | this.fillButton = document.getElementById(id1)
494 |
495 | document.getElementById(id2).addEventListener('click',(event) => {
496 | stopPropagation(event)
497 | document.getElementById(id).style.display = 'block'
498 | parent.addEventListener('click',hide)
499 | })
500 |
501 | document.getElementById(id).addEventListener('click',(e) => {
502 | let color = e.target.dataset.color
503 | document.getElementById(id).style.display = 'none'
504 | this.emit('changeFill',{
505 | data:color
506 | })
507 | this.fillButton.querySelector(".color-line").style.backgroundColor = color
508 | })
509 | function hide(){
510 | document.getElementById(id).style.display = 'none'
511 | parent.removeEventListener('click',hide)
512 | }
513 | }
514 | initBorder(){
515 | const id = generateUUID()
516 | const id1 = generateUUID()
517 | const id2 = generateUUID()
518 | const html = `
519 |
520 |
534 | `
535 | const style = document.createElement('style')
536 | style.innerHTML = `
537 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .dropdown-card{
538 | display: none;
539 | position:absolute;
540 | left:50%;
541 | margin-left:-30px;
542 | width:60px;
543 | line-height: 22px;
544 | font-size:12px;
545 | border:1px solid #d0d0d0;
546 | background: #fff;
547 | text-align: center;
548 | z-index:10;
549 | }
550 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border:hover .dropdown-card{
551 | display: block;
552 | }
553 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .dropdown-card:before{
554 | content:'';
555 | width:0;
556 | height:0;
557 | border-bottom:8px solid #d0d0d0;
558 | border-right:5px solid transparent;
559 | border-left:5px solid transparent;
560 | position:absolute;
561 | top:-8px;
562 | left:50%;
563 | margin-left:-5px;
564 | }
565 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .dropdown-card:after{
566 | content:'';
567 | width:0;
568 | height:0;
569 | border-bottom:8px solid #ffffff;
570 | border-right:5px solid transparent;
571 | border-left:5px solid transparent;
572 | position:absolute;
573 | top:-6px;
574 | left:50%;
575 | margin-left:-5px;
576 | }
577 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .border-button{
578 | position: relative;
579 | float:left;
580 | margin:4px 2px;
581 | }
582 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .border-button .tip{
583 | display: none;
584 | position:absolute;
585 | left:50%;
586 | margin-left:-35px;
587 | width:70px;
588 | line-height: 22px;
589 | font-size:12px;
590 | border:1px solid #d0d0d0;
591 | background: #fff;
592 | text-align: center;
593 | z-index:10;
594 | }
595 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .border-button:hover .tip{
596 | display: block;
597 | }
598 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .border-button .tip:before{
599 | content:'';
600 | width:0;
601 | height:0;
602 | border-bottom:8px solid #d0d0d0;
603 | border-right:5px solid transparent;
604 | border-left:5px solid transparent;
605 | position:absolute;
606 | top:-8px;
607 | left:50%;
608 | margin-left:-5px;
609 | }
610 | #daodao_excel_scroll_toolbar-wrapper .toolbar-border .border-button .tip:after{
611 | content:'';
612 | width:0;
613 | height:0;
614 | border-bottom:8px solid #ffffff;
615 | border-right:5px solid transparent;
616 | border-left:5px solid transparent;
617 | position:absolute;
618 | top:-6px;
619 | left:50%;
620 | margin-left:-5px;
621 | }
622 | `
623 | this.el.appendChild(style)
624 |
625 | let div = document.createElement('div')
626 | div.classList.add("toolbar-item","toolbar-button","toolbar-border")
627 | div.id = id
628 | div.innerHTML = html
629 |
630 | this.el.appendChild(div)
631 |
632 | this.borderButton = document.getElementById(id)
633 |
634 | document.getElementById(id1).addEventListener('click',(event) => {
635 | this.emit('changeBorder',{
636 | data:document.getElementById(id1).dataset.type
637 | })
638 | })
639 | document.getElementById(id2).addEventListener('click',(event) => {
640 | this.emit('changeBorder',{
641 | data:document.getElementById(id2).dataset.type
642 | })
643 | })
644 | }
645 | initAlignLeft(){
646 | this.alignLeftButton = this.addButton(
647 | '',
648 | '左对齐',
649 | (e)=>{
650 | this.emit('changeTextAlign',{
651 | data:'left'
652 | })
653 | },
654 | true
655 | )
656 | }
657 | initAlignRight(){
658 | this.alignRightButton = this.addButton(
659 | '',
660 | '右对齐',
661 | (e)=>{
662 | this.emit('changeTextAlign',{
663 | data:'right'
664 | })
665 | },
666 | true
667 | )
668 | }
669 | initAlignCenter(){
670 | this.alignCenterButton = this.addButton(
671 | '',
672 | '居中对齐',
673 | (e)=>{
674 | this.emit('changeTextAlign',{
675 | data:'center'
676 | })
677 | },
678 | true
679 | )
680 | }
681 | initMergeCell(){
682 | this.mergeCellButton = this.addButton(
683 | '',
684 | '合并单元格',
685 | (e)=>{
686 | this.emit('mergeCell',{
687 | data:true
688 | })
689 | },
690 | false
691 | )
692 | }
693 | initSplitCell(){
694 | this.mergeCellButton = this.addButton(
695 | '',
696 | '取消合并',
697 | (e)=>{
698 | this.emit('splitCell',{
699 | data:true
700 | })
701 | },
702 | false
703 | )
704 | }
705 | initAddImage(){
706 | this.addImageButton = this.addButton(
707 | '',
708 | '插入图片',
709 | (e)=>{
710 | this.emit('addImage',{
711 | data:true
712 | })
713 | },
714 | false
715 | )
716 | }
717 | addButton(innerHTML,tip,func,hasStatus){
718 | let dom = document.createElement('div')
719 | dom.classList.add("toolbar-item","toolbar-button")
720 | let html = `
721 | ${innerHTML}
722 |
723 | ${tip}
724 |
725 | `
726 | dom.innerHTML = html
727 | this.el.appendChild(dom)
728 | dom.addEventListener("click",()=>{
729 | if(hasStatus){
730 | if(dom.classList.contains('active')){
731 | dom.classList.remove("active")
732 | func(false)
733 | }else{
734 | dom.classList.add("active")
735 | func(true)
736 | }
737 | }else{
738 | func()
739 | }
740 | })
741 | return dom
742 | }
743 | setConfig(config){
744 | this.typeFaceButton.value = config.fontFamily
745 | this.fontSizeButton.value = config.fontSize
746 | this.textFillButton.querySelector(".color-line").style.backgroundColor = config.textFill
747 | this.fillButton.querySelector(".color-line").style.backgroundColor = config.fill
748 | if(config.fontWeight == 'bold'){
749 | this.fontWeightButton.classList.add('active')
750 | }else{
751 | this.fontWeightButton.classList.remove('active')
752 | }
753 | if(config.fontStyle == 'italic'){
754 | this.fontItalicButton.classList.add('active')
755 | }else{
756 | this.fontItalicButton.classList.remove('active')
757 | }
758 | if(config.textAlign == 'left'){
759 | this.alignLeftButton.classList.add('active')
760 | this.alignRightButton.classList.remove('active')
761 | this.alignCenterButton.classList.remove('active')
762 | }else if(config.textAlign == 'right'){
763 | this.alignRightButton.classList.add('active')
764 | this.alignLeftButton.classList.remove('active')
765 | this.alignCenterButton.classList.remove('active')
766 | }else if(config.textAlign == 'center'){
767 | this.alignCenterButton.classList.add('active')
768 | this.alignRightButton.classList.remove('active')
769 | this.alignLeftButton.classList.remove('active')
770 | }
771 | }
772 | }
773 |
774 | export default ToolBar
--------------------------------------------------------------------------------
/src/uploadFile.js:
--------------------------------------------------------------------------------
1 | import Event from "./event.js"
2 |
3 | class UploadFile extends Event{
4 | constructor(parent){
5 | super()
6 | this.el = null
7 | this.init(parent)
8 | }
9 | init(parent){
10 | this.el = document.createElement('input')
11 | this.el.type = 'file'
12 | this.el.accept = 'image/*'
13 | this.el.style.display = 'none'
14 | parent.appendChild(this.el)
15 | this.el.addEventListener('change',(e) => {
16 | let file = this.el.files[0]
17 | let reader = new FileReader()
18 | reader.onload = () => {
19 | this.emit('changeImage',{url:reader.result})
20 | this.el.value = ''
21 | }
22 | reader.readAsDataURL(file)
23 | })
24 | }
25 | open(){
26 | this.el.click()
27 | }
28 | }
29 |
30 | export default UploadFile
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from 'uuid';
2 |
3 | /*
4 | * 生成唯一id
5 | */
6 | export function generateUUID(){
7 | let id = uuidv4()
8 | return id
9 | }
10 |
11 | /*
12 | *判断滚轮方向 return true 为正,false 为负
13 | */
14 | export function mouseWheelDirection(e){
15 | e = e || window.event;
16 | if (e.wheelDelta) { //判断浏览器IE,谷歌滑轮事件
17 | if (e.wheelDelta > 0) { //当滑轮向上滚动时
18 | return true
19 | }
20 | if (e.wheelDelta < 0) { //当滑轮向下滚动时
21 | return false
22 | }
23 | } else if (e.detail) { //Firefox滑轮事件
24 | if (e.detail> 0) { //当滑轮向上滚动时
25 | return false
26 | }
27 | if (e.detail< 0) { //当滑轮向下滚动时
28 | return true
29 | }
30 | }
31 | }
32 |
33 | /*
34 | *阻止默认事件
35 | */
36 | export function preventDefault(event){
37 | if (event.preventDefault){
38 | event.preventDefault();
39 | }else{
40 | event.returnValue=false;
41 | }
42 | }
43 |
44 | /*
45 | *阻止冒泡和捕获
46 | */
47 | export function stopPropagation(event){
48 | if(event && event.stopPropagation) {
49 | event.stopPropagation(); // W3C标准
50 | }else {
51 | event.cancelBubble = true; //ie678
52 | }
53 | }
54 |
55 | /*
56 | *生成26进制的A-F序号
57 | */
58 | export function generateCode(number){
59 | let letter = [
60 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
61 | ]
62 | let code = ''
63 | let num = number
64 | code += letter[num % 26];
65 | num = Math.floor(num/26);
66 | while(num > 0){
67 | code += letter[num % 26 - 1];
68 | num = Math.floor(num/26);
69 | }
70 | return code.split('').reverse().join('')
71 | }
72 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | web excel test
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
58 |
59 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | // mode:"development",
5 | mode:'production',
6 | entry:["./src/main"],
7 | output:{
8 | libraryTarget: 'umd', //类库加载方式
9 | path:path.resolve(__dirname,"./dist"),
10 | filename:"daodaoExcel.js"
11 | }
12 | }
--------------------------------------------------------------------------------