移动端的luckysheet需要有类似钉钉写作文档中的选中一块区域后弹出复制、剪切等按钮。
操作:
luckysheet滑动选中一块区域后会在左上角和右下角有小圆点,点击小圆点或者滑动选择区域时弹出操作框(类似钉钉文档)
CTRL+P找到Moblie.js文件,在document的touchend回调的末尾加上自己的函数,开始处理。
ShowMobileOperation函数内容如下:
function ShowMobileOperation(event){
// nby 第一步、判断触摸完毕是否显示复制块
if(event.target.contains($('.luckysheet-cs-touchhandle-btn')[1]) && $("#mobileOperation").length==0){
// nby 移动端操作区域dom
if(Store.isMobile){//这个是Store.isMobile是在全局新加的变量,用以表示是移动端,因为luckysheet本身判断有问题,只能手动填写
$("body").append(`
全复制
粘贴
复制文本
`);
let col = luckysheet_touchmove_startPos.x
let row = luckysheet_touchmove_startPos.y
// let col = Store.touchCoord.x+'px'
// let row = Store.touchCoord.y+'px'
$("#mobileOperation").css({"left": col, "top": row}); //定位操作区域,这几个变量上面有计算
// nby 第二步,对具体的按钮进行处理
// 监听复制文本的按钮,就是只复制单元格的文本,不包括批注、背景等
$('#copyShowVal').on('touchend',function(event){
// $("#luckysheet-copy-array2").click()
// 先得到cell中的m字段二维数组
// let copyData=luckysheet.getRangeValue()
// copyData = copyData.map(arrItem=>arrItem.map(cell=>cell?.m))
// let htmlData='' //构建html数据
// let tr_Str = copyData.reduce((preVal,cur)=>{
// let str=''
// cur.forEach((strItem)=>{
// str += `${strItem} `
// })
// return preVal+`${str} `
// },'')
// htmlData = `${tr_Str}
`
// selection.copybyformat(event,htmlData)
// 上面是之前的代码,本想用html标签的形式作为复制的内容,发现不行,但是在键盘操作cv是行的。
let copyData=luckysheet.getRangeValue()
// 生成luckysheet的二维数组copyData,因为只复制文本值,所以取单元格对象的m字段
// {fa: 'General', t: 'n'}
copyData=copyData.map((arrCell)=>arrCell.map(cell=>({v:cell?.m,m:cell?.m,ct:{fa: 'General', t: 'n'}})))
selection.copybyformat(event,JSON.stringify(copyData))
Store.isCopyText = true //判定是点击的仅复制文本还是全复制按钮
event.stopPropagation();
event.stopImmediatePropagation()
setTimeout(()=>{
$('#mobileOperation').remove()
},200)//这里可以封装为一个函数,下面经常用。
})
// 监听粘贴
$('#mobilePasteBtn').on('touchend',function(event){
// nby 以下会对粘贴做一些限制,直接从luckysheet本身的粘贴按钮复制过来
if (isEditMode() || Store.allowEdit === false) {//此模式下禁用粘贴
return;
}
if ($(event.target).hasClass("formulaInputFocus")) {
return;
}
if (Store.luckysheet_select_save.length > 1) {
if (isEditMode()) {
alert(locale_drag.noPaste);
}
else {
tooltip.info(locale_drag.noPaste, "");
}
return;
}
selection.isPasteAction = true;
selection.paste(event,'btn',Store.isCopyText) // nby Store.isCopyText全局变量用来
luckysheetactiveCell();
event.stopPropagation();
event.stopImmediatePropagation()
setTimeout(()=>{
$('#mobileOperation').remove()
},200)
})
// 监听全复制按钮
$('#mobileCopyBtn').on('touchend',function(event){
//复制时存在格式刷状态,取消格式刷
if (menuButton.luckysheetPaintModelOn) {
menuButton.cancelPaintModel();
}
if (Store.luckysheet_select_save.length == 0) {
return;
}
//复制范围内包含部分合并单元格,提示
if (Store.config["merge"] != null) {
let has_PartMC = false;
for (let s = 0; s < Store.luckysheet_select_save.length; s++) {
let r1 = Store.luckysheet_select_save[s].row[0],
r2 = Store.luckysheet_select_save[s].row[1];
let c1 = Store.luckysheet_select_save[s].column[0],
c2 = Store.luckysheet_select_save[s].column[1];
has_PartMC = hasPartMC(Store.config, r1, r2, c1, c2);
if (has_PartMC) {
break;
}
}
if (has_PartMC) {
if (isEditMode()) {
alert(locale_drag.noMerge);
}
else {
tooltip.info(locale_drag.noMerge, "");
}
return;
}
}
//多重选区 有条件格式时 提示
let cdformat = Store.luckysheetfile[getSheetIndex(Store.currentSheetIndex)].luckysheet_conditionformat_save;
if (Store.luckysheet_select_save.length > 1 && cdformat != null && cdformat.length > 0) {
let hasCF = false;
let cf_compute = conditionformat.getComputeMap();
label:
for (let s = 0; s < Store.luckysheet_select_save.length; s++) {
if (hasCF) {
break;
}
let r1 = Store.luckysheet_select_save[s].row[0],
r2 = Store.luckysheet_select_save[s].row[1];
let c1 = Store.luckysheet_select_save[s].column[0],
c2 = Store.luckysheet_select_save[s].column[1];
for (let r = r1; r <= r2; r++) {
for (let c = c1; c <= c2; c++) {
if (conditionformat.checksCF(r, c, cf_compute) != null) {
hasCF = true;
continue label;
}
}
}
}
if (hasCF) {
if (isEditMode()) {
alert(locale_drag.noMulti);
}
else {
tooltip.info(locale_drag.noMulti, "");
}
return;
}
}
//多重选区 行不一样且列不一样时 提示
if (Store.luckysheet_select_save.length > 1) {
let isSameRow = true,
str_r = Store.luckysheet_select_save[0].row[0],
end_r = Store.luckysheet_select_save[0].row[1];
let isSameCol = true,
str_c = Store.luckysheet_select_save[0].column[0],
end_c = Store.luckysheet_select_save[0].column[1];
for (let s = 1; s < Store.luckysheet_select_save.length; s++) {
if (Store.luckysheet_select_save[s].row[0] != str_r || Store.luckysheet_select_save[s].row[1] != end_r) {
isSameRow = false;
}
if (Store.luckysheet_select_save[s].column[0] != str_c || Store.luckysheet_select_save[s].column[1] != end_c) {
isSameCol = false;
}
}
if ((!isSameRow && !isSameCol) || selectIsOverlap()) {
if (isEditMode()) {
alert(locale_drag.noMulti);
}
else {
tooltip.info(locale_drag.noMulti, "");
}
return;
}
}
// nby 全复制也需要做数据自适应
// selection.copy(event);
selection.copybyformat(event,JSON.stringify(luckysheet.getRangeValue()))
// Store.isCopyText = false//这是全复制,故需要将复制文本关闭
Store.isCopyText = true; //这是全复制,故需要将复制文本关闭
Store.luckysheet_paste_iscut = false;
luckysheetactiveCell();
event.stopPropagation();
event.stopImmediatePropagation()
setTimeout(()=>{
$('#mobileOperation').remove()
},200)
})
}
}
}
上面函数的简易思路:
(1)首先用luckysheet.getRangeValue()获取复制区域的单元格对象数据(返回结果是二维数组),然后构建出需要的符合单元格对象格式的二维数组。(就是把m也赋值给v,其他批注啥的都不要,这样去粘贴构建出来的数据就都是显示值m了)
(2)之后将构建好的数据交给selection.copybyformat(event,JSON.stringify(copyData))去处理。 最后隐藏操作区域。
同上
(1)调用selection.paste(event,'btn',Store.isCopyText) 方法,由于需要做批量粘贴,还需要对粘贴的数据进行处理。
(2)进入paste后,获取textarea的内容data(实现复制粘贴功能时clipboard不支持就用这种方法),之后交给pasteHandler方法,在pasteHandler方法里对数据进行自适应处理。自适应数据处理函数代码如下:
// nby 实现批量粘贴数据的函数
function selfAdaptionData(data){
let primData = $.extend(true, [], data);
// 判断当前选区,实现批量粘贴
let range = luckysheet.getRange()//获取需要粘贴的区域
let pasteRange = [range[0].row[1] - range[0].row[0] + 1 , range[0].column[1] - range[0].column[0] + 1 ]
let copyRange = [data.length,data[0].length]
let residueColumnNum = pasteRange[1] - copyRange[1] //粘贴区域的列数是否比已复制的数据列数多
let residueRowNum = pasteRange[0] - copyRange[0]
if(residueColumnNum > 0 || residueRowNum > 0){
// 粘贴区域更大,扩大粘贴的数据
// 先扩大列数
let columnCount = parseInt(pasteRange[1]/copyRange[1])
for(let i = 1 ; i < columnCount ; i++){
data = data.map((arr,index)=>{
return [...arr,...primData[index]]
//这里新添加的每行数据需要用对应的那一行,并且需要是之前的数据
})
}
// 扩大行数
let primDataArr = $.extend(true, [], data);//先存下,一次粘贴的数据
let rowCount = parseInt(pasteRange[0]/copyRange[0])
for(let i = 1 ; i < rowCount ; i++){
data = [...data,...primDataArr]
}
}
return data
}
selfAdaptionData函数说明:
通过luckysheet.getRange()获取粘贴的区域,计算出粘贴区域的行数和列数pasteRange,又通过入参data计算出拷贝区域的行列数copyRange,通过行列相减,判断粘贴区域是否大于拷贝区域。
如果大于,则需要扩大数据。计算出最小整数倍,然后循环添加。先把列处理好之后就是行。
我这里的批量粘贴是啥?
比如我复制一个格子,在需要粘贴之前又框选4x4的格子,如果支持批量粘贴就共有4x4=16个格子都有数据,不然只有第一个格子才有粘贴的数据。
最后附上效果图:
由于移动端操作的局限性,后续还需要加上其他功能按钮 。