Browse Source

fix(cooperative): bug

1.add username display 2.add sheet 3.delete sheet 4.change sheet not post
master
mengshukeji 5 years ago
parent
commit
9e48c72a47
  1. 5
      README-zh.md
  2. 4
      README.md
  3. 4
      docs/guide/README.md
  4. 8
      docs/guide/config.md
  5. 2
      docs/guide/data.md
  6. 2
      docs/guide/sheet.md
  7. 4
      docs/zh/guide/README.md
  8. 9
      docs/zh/guide/config.md
  9. 2
      docs/zh/guide/sheet.md
  10. 3
      gulpfile.js
  11. 22
      src/controllers/handler.js
  12. 128
      src/controllers/server.js
  13. 4
      src/controllers/sheetMove.js
  14. 4
      src/controllers/updateCell.js
  15. 3
      src/global/formula.js
  16. 3
      src/locale/en.js
  17. 3
      src/locale/zh.js
  18. 7
      src/store/index.js
  19. 19
      src/utils/util.js

5
README-zh.md

@ -18,9 +18,12 @@
| [Github](https://github.com/mengshukeji/Luckysheet)| [在线文档](https://mengshukeji.github.io/LuckysheetDocs/zh/) | [在线Demo](https://mengshukeji.github.io/LuckysheetDemo) | [导入Excel Demo](https://mengshukeji.github.io/LuckyexcelDemo/) | [中文论坛](https://support.qq.com/product/288322) | [LuckyResources](https://github.com/mengshukeji/LuckyResources) |
| [Gitee镜像](https://gitee.com/mengshukeji/Luckysheet)| [Gitee在线文档](https://mengshukeji.gitee.io/LuckysheetDocs/zh/) | [Gitee在线Demo](https://mengshukeji.gitee.io/luckysheetdemo/) | [Gitee导入Excel Demo](https://mengshukeji.gitee.io/luckyexceldemo/) | [Google Group](https://groups.google.com/g/luckysheet) |
![演示](/docs/.vuepress/public/img/LuckysheetDemo.gif)
## 在线案例
- [协同编辑Demo](http://luckysheet.lashuju.com/demo/)(注意:官方Java后台待整理后也会开源,采用OT算法。请大家别操作频繁,防止搞崩服务器)
## 插件
- excel导入导出库: [Luckyexcel](https://github.com/mengshukeji/Luckyexcel)
- 图表插件: [chartMix](https://github.com/mengshukeji/chartMix)

4
README.md

@ -23,6 +23,10 @@ English| [简体中文](./README-zh.md)
![Demo](/docs/.vuepress/public/img/LuckysheetDemo.gif)
## Online Case
- [Cooperative editing demo](http://luckysheet.lashuju.com/demo/)(Note: The official Java backend will also be open source after finishing,using OT algorithm. Please do not operate frequently to prevent the server from crashing)
## Plugins
- Excel import and export library: [Luckyexcel](https://github.com/mengshukeji/Luckyexcel)
- Chart plugin: [chartMix](https://github.com/mengshukeji/chartMix)

4
docs/guide/README.md

@ -8,6 +8,10 @@ Luckysheet is an online spreadsheet like excel that is powerful, simple to confi
![Demo](/LuckysheetDocs/img/LuckysheetDemo.gif)
## Online Case
- [Cooperative editing demo](http://luckysheet.lashuju.com/demo/)(Note: The official Java backend will also be open source after finishing,using OT algorithm. Please do not operate frequently to prevent the server from crashing)
## Features
### 🛠️Formatting

8
docs/guide/config.md

@ -919,6 +919,14 @@ The hook functions are uniformly configured under ʻoptions.hook`, and configura
- Parameter:
- {Object} [book]:Configuration of the entire workbook (options)
------------
### updateBefore
- Type: Function
- Default: null
- Usage: The method executed before each operation in collaborative editing updates the data. When undoing and redoing, it is also an operation, of course, the hook function will be triggered.
- Parameter:
- {Object} [operate]: The history information of this operation will have different history records according to different operations. Refer to the source code [History](https://github.com/mengshukeji/Luckysheet/blob/master/src/controllers/controlHistory.js )
------------
### updated
- Type: Function

2
docs/guide/data.md

@ -219,7 +219,7 @@
```
### config.borderInfo
- Type:Object
- Type:Array
- Default:{}
- Usage:The border information of the cell
- example:

2
docs/guide/sheet.md

@ -254,7 +254,7 @@ eg: options.data:
```
#### config.borderInfo
- type:Object
- type:Array
- default:{}
- usage:The border information of the cell
- example:

4
docs/zh/guide/README.md

@ -8,6 +8,10 @@ Luckysheet ,一款纯前端类似excel的在线表格,功能强大、配置
![演示](/LuckysheetDocs/img/LuckysheetDemo.gif)
## 在线案例
- [协同编辑Demo](http://luckysheet.lashuju.com/demo/)(注意:官方Java后台待整理后也会开源,采用OT算法。请大家别操作频繁,防止搞崩服务器)
## 特性
### 🛠️格式设置

9
docs/zh/guide/config.md

@ -1225,6 +1225,15 @@ Luckysheet开放了更细致的自定义配置选项,分别有
- 参数:
- {Object} [book]: 整个工作簿的配置(options)
------------
### updateBefore
(TODO)
- 类型:Function
- 默认值:null
- 作用:协同编辑中的每次操作更新数据之前执行的方法,撤销重做时因为也算一次操作,也会触发此钩子函数。
- 参数:
- {Object} [operate]: 本次操作的历史记录信息,根据不同的操作,会有不同的历史记录,参考源码 [历史记录](https://github.com/mengshukeji/Luckysheet/blob/master/src/controllers/controlHistory.js)
------------
### updated
(TODO)

2
docs/zh/guide/sheet.md

@ -256,7 +256,7 @@ options.data示例如下:
```
#### config.borderInfo
- 类型:Object
- 类型:Array
- 默认值:{}
- 作用:单元格的边框信息
- 示例:

3
gulpfile.js

@ -126,7 +126,8 @@ function serve(done) {
browserSync.init({
server: {
baseDir: paths.dist
}
},
ghostMode: false, //默认true,滚动和表单在任何设备上输入将被镜像到所有设备里,会影响本地的协同编辑消息,故关闭
}, done)
}

22
src/controllers/handler.js

@ -289,6 +289,9 @@ export default function luckysheetHandler() {
return;
}
// 协同编辑其他用户不在操作的时候,用户名框隐藏
hideUsername();
$("#luckysheet-cell-selected").find(".luckysheet-cs-fillhandle")
.css("cursor","default")
.end()
@ -1446,7 +1449,9 @@ export default function luckysheetHandler() {
}
luckysheetupdateCell(row_index, col_index, Store.flowdata);
}
});
//监听拖拽
@ -5605,4 +5610,21 @@ export default function luckysheetHandler() {
}).mousedown(function (e) {
e.stopPropagation();
});
}
// 协同编辑其他用户不在操作的时候,且已经展示了用户名10秒,则用户名框隐藏
function hideUsername(){
let $showEle = $$('.luckysheet-multipleRange-show');
if($showEle.length === undefined){
$showEle = [$showEle];
}
$showEle.forEach((ele)=>{
const id = ele.id.replace('luckysheet-multipleRange-show-','');
if(Store.cooperativeEdit.usernameTimeout['user' + id] === null){
$$('.username',ele).style.display = 'none';
}
})
}

128
src/controllers/server.js

@ -88,6 +88,11 @@ const server = {
d.i = index;
d.v = value;
//切换sheet页不发后台,TODO:改为发后台+后台不广播
if(type === 'shs'){
return;
}
if (type == "rv") { //单元格批量更新
d.range = params.range;
}
@ -176,15 +181,27 @@ const server = {
index = item.i,
value = item.v;
if(getObjType(value) != "array"){
if(getObjType(value) != "array" && getObjType(value) !== "object"){
value = JSON.parse(value);
}
if(index == Store.currentSheetIndex){//发送消息者在当前页面
let r = value[value.length - 1].row[0];
let c = value[value.length - 1].column[0];
if(index == Store.currentSheetIndex){//发送消息者在当前页面
if(getObjType(value) === "object" && value.op === 'enterEdit'){
let r = value.range[value.range.length - 1].row[0];
let c = value.range[value.range.length - 1].column[0];
_this.multipleRangeShow(id, username, r, c, value.op);
}else{
_this.multipleRangeShow(id, username, r, c);
let r = value[value.length - 1].row[0];
let c = value[value.length - 1].column[0];
_this.multipleRangeShow(id, username, r, c);
}
}
}
else if(type == 4){ //批量指令更新
@ -226,7 +243,7 @@ const server = {
let file = Store.luckysheetfile[getSheetIndex(index)];
if(file == null){
if(["v","rv","cg","all","fc","drc","arc","f","fsc","fsr","sh","c"].includes(type) && file == null){
return;
}
@ -399,7 +416,7 @@ const server = {
luckysheetrefreshgrid();
}, 1);
}
}
}
}
else if(type == "fc"){ //函数链calc
let op = item.op, pos = item.pos;
@ -614,15 +631,41 @@ const server = {
else if(type == "shd"){ //删除sheet
for(let i = 0; i < Store.luckysheetfile.length; i++){
if(Store.luckysheetfile[i].index == value.deleIndex){
server.sheetDeleSave.push(Store.luckysheetfile[i]);
// 如果删除的是当前sheet,则切换到前一个sheet页
if(Store.currentSheetIndex === value.deleIndex){
const index = value.deleIndex;
Store.luckysheetfile[sheetmanage.getSheetIndex(index)].hide = 1;
let luckysheetcurrentSheetitem = $("#luckysheet-sheets-item" + index);
luckysheetcurrentSheetitem.hide();
$("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
let indicator = luckysheetcurrentSheetitem.nextAll(":visible");
if (luckysheetcurrentSheetitem.nextAll(":visible").length > 0) {
indicator = indicator.eq(0).data("index");
}
else {
indicator = luckysheetcurrentSheetitem.prevAll(":visible").eq(0).data("index");
}
$("#luckysheet-sheets-item" + indicator).addClass("luckysheet-sheets-item-active");
sheetmanage.changeSheetExec(indicator);
}
server.sheetDeleSave.push(Store.luckysheetfile[i]);
Store.luckysheetfile.splice(i, 1);
Store.luckysheetfile.splice(i, 1);
break;
}
}
$("#luckysheet-sheets-item" + value.deleIndex).remove();
$("#luckysheet-datavisual-selection-set-" + value.deleIndex).remove();
$("#luckysheet-datavisual-selection-set-" + value.deleIndex).remove();
}
else if(type == "shr"){ //sheet位置
for(let x in value){
@ -711,7 +754,7 @@ const server = {
}
},
multipleIndex: 0,
multipleRangeShow: function(id, name, r, c) {
multipleRangeShow: function(id, name, r, c, value) {
let _this = this;
let row = Store.visibledatarow[r],
@ -726,19 +769,70 @@ const server = {
col = margeset.column[1];
col_pre = margeset.column[0];
}
}
// 超出16个字符就显示...
if(getByteLen(name) > 16){
name = getByteLen(name,16) + "...";
}
// 如果正在编辑,就显示“正在输入”
if(value === 'enterEdit'){
name += " " + locale().edit.typing;
}
if($("#luckysheet-multipleRange-show-" + id).length > 0){
$("#luckysheet-multipleRange-show-" + id).css({ "position": "absolute", "left": col_pre - 1, "width": col - col_pre - 1, "top": row_pre - 1, "height": row - row_pre - 1 });
$("#luckysheet-multipleRange-show-" + id).css({ "position": "absolute", "left": col_pre - 1, "width": col - col_pre - 1, "top": row_pre - 1, "height": row - row_pre - 1 });
$("#luckysheet-multipleRange-show-" + id + " .username").text(name);
$("#luckysheet-multipleRange-show-" + id + " .username").show();
if(Store.cooperativeEdit.usernameTimeout['user' + id] != null){
clearTimeout(Store.cooperativeEdit.usernameTimeout['user' + id])
}
Store.cooperativeEdit.usernameTimeout['user' + id] = setTimeout(()=>{
clearTimeout(Store.cooperativeEdit.usernameTimeout['user' + id]);
Store.cooperativeEdit.usernameTimeout['user' + id] = null;
},10 * 1000)
}
else{
let itemHtml = '<div id="luckysheet-multipleRange-show-'+ id +'" data-color="'+ luckyColor[_this.multipleIndex] +'" title="'+ name +'" style="position: absolute;left: '+ (col_pre - 1) +'px;width: '+ (col - col_pre - 1) +'px;top: '+ (row_pre - 1) +'px;height: '+ (row - row_pre - 1) +'px;border: 1px solid '+ luckyColor[_this.multipleIndex] +';z-index: 15;">'+
'<div style="width: 100%;height: 100%;position: absolute;top: 0;right: 0;bottom: 0;left: 0;opacity: 0.03;background-color: '+ luckyColor[_this.multipleIndex] +'"></div>'+
'</div>';
// let itemHtml = '<div id="luckysheet-multipleRange-show-'+ id +'" data-color="'+ luckyColor[_this.multipleIndex] +'" title="'+ name +'" style="position: absolute;left: '+ (col_pre - 1) +'px;width: '+ (col - col_pre - 1) +'px;top: '+ (row_pre - 1) +'px;height: '+ (row - row_pre - 1) +'px;border: 1px solid '+ luckyColor[_this.multipleIndex] +';z-index: 15;">'+
// '<div style="width: 100%;height: 100%;position: absolute;top: 0;right: 0;bottom: 0;left: 0;opacity: 0.03;background-color: '+ luckyColor[_this.multipleIndex] +'"></div>'+
// '</div>';
let itemHtml = `<div
id="luckysheet-multipleRange-show-${id}"
class="luckysheet-multipleRange-show"
data-color="${luckyColor[_this.multipleIndex]}"
title="${name}"
style="position: absolute;left: ${col_pre - 1}px;width: ${col - col_pre - 1}px;top: ${row_pre - 1}px;height: ${row - row_pre - 1}px;border: 1px solid ${luckyColor[_this.multipleIndex]};z-index: 15;">
<div class="username" style="height: 19px;line-height:19px;width: max-content;position: absolute;bottom: ${row - row_pre - 1}px;right: 0;background-color: ${luckyColor[_this.multipleIndex]};color:#ffffff;padding:0 10px;">
${name}
</div>
<div style="width: 100%;height: 100%;position: absolute;top: 0;right: 0;bottom: 0;left: 0;opacity: 0.03;background-color: ${luckyColor[_this.multipleIndex]}">
</div>
</div>`;
// 正在输入
$(itemHtml).appendTo($("#luckysheet-cell-main #luckysheet-multipleRange-show"));
_this.multipleIndex++;
_this.multipleIndex++;
// 设定允许用户名消失的定时器,10秒后用户名可隐藏
// 10秒之类,用户操作界面不会隐藏用户名;10秒之后如果用户操作了界面,则隐藏用户名,没操作就不隐藏
if(Store.cooperativeEdit.usernameTimeout['user' + id] != null){
clearTimeout(Store.cooperativeEdit.usernameTimeout['user' + id])
}
Store.cooperativeEdit.usernameTimeout['user' + id] = setTimeout(()=>{
clearTimeout(Store.cooperativeEdit.usernameTimeout['user' + id]);
Store.cooperativeEdit.usernameTimeout['user' + id] = null;
},10 * 1000)
}
},
sheetDeleSave: [], //共享编辑模式下 删除的sheet保存下来,方便恢复时取值

4
src/controllers/sheetMove.js

@ -6,6 +6,7 @@ import menuButton from './menuButton';
import { selectHightlightShow } from './select';
import pivotTable from './pivotTable';
import Store from '../store';
import server from './server';
function luckysheetMoveEndCell(postion, type, isScroll, terminal, onlyvalue) {
if (isScroll == null) {
@ -622,6 +623,9 @@ function luckysheetMoveHighlightCell(postion, index, type, isScroll) {
clearTimeout(Store.countfuncTimeout);
countfunc();
// 移动单元格通知后台
server.saveParam("mv", Store.currentSheetIndex, Store.luckysheet_select_save);
}
//ctrl + 方向键 调整单元格

4
src/controllers/updateCell.js

@ -15,6 +15,7 @@ import { luckysheetRangeLast } from '../global/cursorPos';
import cleargridelement from '../global/cleargridelement';
import {isInlineStringCell} from './inlineString';
import Store from '../store';
import server from './server';
export function luckysheetupdateCell(row_index1, col_index1, d, cover, isnotfocus) {
if(!checkProtectionLocked(row_index1, col_index1, Store.currentSheetIndex)){
@ -26,6 +27,9 @@ export function luckysheetupdateCell(row_index1, col_index1, d, cover, isnotfocu
return;
}
// 编辑单元格时发送指令到后台,通知其他单元格更新为“正在输入”状态
server.saveParam("mv", Store.currentSheetIndex, {op:"enterEdit",range:Store.luckysheet_select_save});
//数据验证
if(dataVerificationCtrl.dataVerification != null && dataVerificationCtrl.dataVerification[row_index1 + '_' + col_index1] != null){
let dataVerificationItem = dataVerificationCtrl.dataVerification[row_index1 + '_' + col_index1];

3
src/global/formula.js

@ -1606,6 +1606,9 @@ const luckysheetformula = {
}
}
// 退出编辑模式后,发送后台取消“正在输入”提示
// server.saveParam("mv", Store.currentSheetIndex, "exitEdit");
if(isRefresh){
jfrefreshgrid(d, [{ "row": [r, r], "column": [c, c] }], allParam, isRunExecFunction);
// Store.luckysheetCellUpdate.length = 0; //clear array

3
src/locale/en.js

@ -9996,6 +9996,9 @@ export default {
menuItemAreas:"Print areas",
menuItemRows:"Print title rows",
menuItemColumns:"Print title columns",
},
edit:{
typing:"typing",
}
};

3
src/locale/zh.js

@ -10237,6 +10237,9 @@ export default {
menuItemAreas:"打印区域",
menuItemRows:"打印标题行",
menuItemColumns:"打印标题列",
},
edit:{
typing:"正在输入",
}
};

7
src/store/index.js

@ -137,6 +137,13 @@ const Store = {
currentSheetView:"viewNormal",
// cooperative editing
cooperativeEdit:{
usernameTimeout:{
}
}
}
export default Store;

19
src/utils/util.js

@ -1,7 +1,7 @@
import { columeHeader_word, columeHeader_word_index, luckysheetdefaultFont } from '../controllers/constant';
import menuButton from '../controllers/menuButton';
import { isdatatype, isdatatypemulti } from '../global/datecontroll';
import { hasChinaword } from '../global/validate';
import { hasChinaword,isRealNum } from '../global/validate';
import Store from '../store';
import locale from '../locale/locale';
@ -293,8 +293,16 @@ function createABCdim(x, count) {
}
};
//计算字符串字节长度
function getByteLen(val) {
/**
* 计算字符串字节长度
* @param {*} val 字符串
* @param {*} subLen 要截取的字符串长度
*/
function getByteLen(val,subLen) {
if(subLen === 0){
return "";
}
if (val == null) {
return 0;
}
@ -309,6 +317,11 @@ function getByteLen(val) {
else {
len += 1;
}
if(isRealNum(subLen) && len === ~~subLen){
return val.substring(0,i)
}
}
return len;

Loading…
Cancel
Save