--- x-spreadsheet

--- 文档 https://hondrytravis.com/x-spreadsheet-doc/

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="com.base.util.WebUtil"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; String id=WebUtil.getParam("id");
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>starting page</title> <meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
<link href="<%=basePath%>x-spreadsheet/xspreadsheet.css" rel="stylesheet"></head>
<link rel="stylesheet" href="<%=basePath %>base/layuiadmin/layui/css/layui.css" media="all">
<link rel="stylesheet" href="<%=basePath %>base/layuiadmin/style/admin.css" media="all"> </head> <body onload="load()">
<div style="position: fixed; right: 0; top: .3em;">
<input type="text" id="id" name="id" value="<%=id %>" lay-verify="required" autocomplete="off" class="layui-input" style="display: none;" >
<button id="save" name="save" class="layui-btn site-demo-active" data-type="sjglTabChange" lay-filter="demo" onclick="save()">保存</button>
</div>
<div id="x-spreadsheet-demo"></div>
</body>
<script src="<%=basePath %>base/layuiadmin/layui/layui.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript" > function save(){ // 保存
const d = xs.getData();
//console.log(d[0]);
var merges = JSON.stringify(d[0].merges);
var rows = JSON.stringify(d[0].rows);
var styles = JSON.stringify(d[0].styles);
//console.log( merges);
//console.log( rows);
//console.log( styles);
var id = "<%=id%>";
$.ajax({
url:' ',
data:{merges:merges,rows:rows,styles:styles,id:id},
type:'post',
dataType: "json",
success:function(obj){
console.log("---");
//console.log(obj);            
}
});
} var htmlsourse = "";
var arr = "";
var excel_styles = "";
function getExcel(){
$.ajax({
url:' ',
type:'post',
dataType: "json",
async:false,
success:function(obj){
//console.log("---");  
//console.log(obj);
if(obj.length>0){
//rows={obj[0].htmlsourse};
htmlsourse = obj[0].excel_rows;
htmlsourse = $.parseJSON( htmlsourse ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象
//JSON.parse(jsonstr); //可以将json字符串转换成json对象
//console.log( htmlsourse);  
arr = obj[0].excel_merges
arr = arr.split(',');
//console.log( arr); 
excel_styles = obj[0].excel_styles;
excel_styles = excel_styles.replace(/\s+/g,""); // 去空格
excel_styles = $.parseJSON( excel_styles );
//console.log( excel_styles);
}        
}
});
}
var xs = "";
function load(){
console.log('---');
getExcel();
const rows = htmlsourse;
console.log('****');
/*const rows10 = { len: 1000 };
for (let i = 0; i < 1000; i += 1) {
rows10[i] = {
cells: {
0: { text: 'A-' + i },
1: { text: 'B-' + i },
2: { text: 'C-' + i },
3: { text: 'D-' + i },
4: { text: 'E-' + i },
5: { text: 'F-' + i },
}
};
}*/
//x_spreadsheet.locale('zh-cn');
var saveIcon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNTc3MTc3MDkyOTg4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI2NzgiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+PC9zdHlsZT48L2RlZnM+PHBhdGggZD0iTTIxMy4zMzMzMzMgMTI4aDU5Ny4zMzMzMzRhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMSA4NS4zMzMzMzMgODUuMzMzMzMzdjU5Ny4zMzMzMzRhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMS04NS4zMzMzMzMgODUuMzMzMzMzSDIxMy4zMzMzMzNhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMS04NS4zMzMzMzMtODUuMzMzMzMzVjIxMy4zMzMzMzNhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMSA4NS4zMzMzMzMtODUuMzMzMzMzeiBtMzY2LjkzMzMzNCAxMjhoMzQuMTMzMzMzYTI1LjYgMjUuNiAwIDAgMSAyNS42IDI1LjZ2MTE5LjQ2NjY2N2EyNS42IDI1LjYgMCAwIDEtMjUuNiAyNS42aC0zNC4xMzMzMzNhMjUuNiAyNS42IDAgMCAxLTI1LjYtMjUuNlYyODEuNmEyNS42IDI1LjYgMCAwIDEgMjUuNi0yNS42ek0yMTMuMzMzMzMzIDIxMy4zMzMzMzN2NTk3LjMzMzMzNGg1OTcuMzMzMzM0VjIxMy4zMzMzMzNIMjEzLjMzMzMzM3ogbTEyOCAwdjI1NmgzNDEuMzMzMzM0VjIxMy4zMzMzMzNoODUuMzMzMzMzdjI5OC42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMS00Mi42NjY2NjcgNDIuNjY2NjY3SDI5OC42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMS00Mi42NjY2NjctNDIuNjY2NjY3VjIxMy4zMzMzMzNoODUuMzMzMzMzek0yNTYgMjEzLjMzMzMzM2g4NS4zMzMzMzMtODUuMzMzMzMzeiBtNDI2LjY2NjY2NyAwaDg1LjMzMzMzMy04NS4zMzMzMzN6IG0wIDU5Ny4zMzMzMzR2LTEyOEgzNDEuMzMzMzMzdjEyOEgyNTZ2LTE3MC42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMSA0Mi42NjY2NjctNDIuNjY2NjY3aDQyNi42NjY2NjZhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMSA0Mi42NjY2NjcgNDIuNjY2NjY3djE3MC42NjY2NjdoLTg1LjMzMzMzM3ogbTg1LjMzMzMzMyAwaC04NS4zMzMzMzMgODUuMzMzMzMzek0zNDEuMzMzMzMzIDgxMC42NjY2NjdIMjU2aDg1LjMzMzMzM3oiIHAtaWQ9IjI2NzkiIGZpbGw9IiMyYzJjMmMiPjwvcGF0aD48L3N2Zz4='
var previewEl = document.createElement('img')
previewEl.src = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjIxMzI4NTkxMjQzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjU2NjMiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PGRlZnM+PHN0eWxlIHR5cGU9InRleHQvY3NzIj48L3N0eWxlPjwvZGVmcz48cGF0aCBkPSJNNTEyIDE4Ny45MDRhNDM1LjM5MiA0MzUuMzkyIDAgMCAwLTQxOC41NiAzMTUuNjQ4IDQzNS4zMjggNDM1LjMyOCAwIDAgMCA4MzcuMTIgMEE0MzUuNDU2IDQzNS40NTYgMCAwIDAgNTEyIDE4Ny45MDR6TTUxMiAzMjBhMTkyIDE5MiAwIDEgMSAwIDM4NCAxOTIgMTkyIDAgMCAxIDAtMzg0eiBtMCA3Ni44YTExNS4yIDExNS4yIDAgMSAwIDAgMjMwLjQgMTE1LjIgMTE1LjIgMCAwIDAgMC0yMzAuNHpNMTQuMDggNTAzLjQ4OEwxOC41NiA0ODUuNzZsNC44NjQtMTYuMzg0IDQuOTI4LTE0Ljg0OCA4LjA2NC0yMS41NjggNC4wMzItOS43OTIgNC43MzYtMTAuODggOS4zNDQtMTkuNDU2IDEwLjc1Mi0yMC4wOTYgMTIuNjA4LTIxLjMxMkE1MTEuNjE2IDUxMS42MTYgMCAwIDEgNTEyIDExMS4xMDRhNTExLjQ4OCA1MTEuNDg4IDAgMCAxIDQyNC41MTIgMjI1LjY2NGwxMC4yNCAxNS42OGMxMS45MDQgMTkuMiAyMi41OTIgMzkuMTA0IDMyIDU5Ljc3NmwxMC40OTYgMjQuOTYgNC44NjQgMTMuMTg0IDYuNCAxOC45NDQgNC40MTYgMTQuODQ4IDQuOTkyIDE5LjM5Mi0zLjIgMTIuODY0LTMuNTg0IDEyLjgtNi40IDIwLjA5Ni00LjQ4IDEyLjYwOC00Ljk5MiAxMi45MjhhNTExLjM2IDUxMS4zNiAwIDAgMS0xNy4yOCAzOC40bC0xMi4wMzIgMjIuNC0xMS45NjggMjAuMDk2QTUxMS41NTIgNTExLjU1MiAwIDAgMSA1MTIgODk2YTUxMS40ODggNTExLjQ4OCAwIDAgMS00MjQuNDQ4LTIyNS42bC0xMS4zMjgtMTcuNTM2YTUxMS4yMzIgNTExLjIzMiAwIDAgMS0xOS44NC0zNS4wMDhMNTMuMzc2IDYxMS44NGwtOC42NC0xOC4yNC0xMC4xMTItMjQuMTI4LTcuMTY4LTE5LjY0OC04LjMyLTI2LjYyNC0yLjYyNC05Ljc5Mi0yLjQ5Ni05LjkyeiIgcC1pZD0iNTY2NCI+PC9wYXRoPjwvc3ZnPg=='
previewEl.width = 16
previewEl.height = 16 xs = x_spreadsheet('#x-spreadsheet-demo', {
showToolbar: true,
showGrid: true,
showBottomBar: true,
extendToolbar: {
left: [
{
tip: 'Save',
icon: saveIcon,
onClick: (data, sheet) => {
console.log('click save button:', data, sheet)
}
}
],
right: [
{
tip: 'Preview',
el: previewEl,
onClick: (data, sheet) => {
console.log('click preview button:', data)
}
}
],
}
})
.loadData([{ // 加载数据
freeze: 'A1',
styles: excel_styles // 样式
/*{
bgcolor: '#f4f5f8',
textwrap: true,
color: '#900b09',
border: {
top: ['thin', '#0366d6'],
bottom: ['thin', '#0366d6'],
right: ['thin', '#0366d6'],
left: ['thin', '#0366d6'],
},
},{align: "center"}*/
,
merges: arr, // 合并单元格 位置
//["C3:D4", "G3:H4", "E4:F5", "B7:E9", "F7:H9", "B10:C11", "D10:E11"],
cols: { // 显示的个数
len: 50//, 2: { width: 200 },
},
rows, // 数据
},
// 多个 sheet
/*{ name: 'sheet-test', rows: rows10 }*/] ).change((cdata) => {
//console.log(cdata);
console.log('>>>', xs.getData());
}); xs.on('cell-selected', (cell, ri, ci) => {
console.log('selected>cell:', cell, ', ri:', ri, ', ci:', ci);
}).on('cell-edited', (text, ri, ci) => {
console.log('edited>text:', text, ', ri: ', ri, ', ci:', ci);
}); /*setTimeout(() => { // 隐藏 单元格
// xs.loadData([{ rows }]);
xs.cellText(14, 3, 'cell-text').reRender();
console.log('cell(8, 8):', xs.cell(8, 8));
console.log('cellStyle(8, 8):', xs.cellStyle(8, 8));
}, 5000);*/
} </script>
<script type="text/javascript" src="<%=basePath%>x-spreadsheet/xspreadsheet.js"></script>
</html>

---

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress; import net.sf.json.JSONObject; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet; /**
*
* @author mysterious
*
*/ public class ExcelFinal { // 最终
// https://blog.csdn.net/lianzhang861/article/details/86234515
/**
* 获取单元格的值
* @param cell
* @return
*/
public static String getCellValue(Cell cell){ if(cell == null) {
cell.setCellType(Cell.CELL_TYPE_STRING);
return cell.getStringCellValue();
} if(cell.getCellType() == Cell.CELL_TYPE_STRING){ return cell.getStringCellValue(); }else if(cell.getCellType() == Cell.CELL_TYPE_BOOLEAN){ return String.valueOf(cell.getBooleanCellValue()); }else if(cell.getCellType() == Cell.CELL_TYPE_FORMULA){ return cell.getCellFormula() ; }else if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){ return String.valueOf(cell.getNumericCellValue()); }
return "";
} private static boolean is(HSSFSheet sheet,int i,int j) {
int MergeCount = sheet.getNumMergedRegions();
for(int l = 0 ; l < MergeCount ; l++){
CellRangeAddress ca = sheet.getMergedRegion(l);
String fa = ca.formatAsString() ;
//System.out.println(">>>"+fa);
int firstColumn = ca.getFirstColumn(); // 左上角的列号
int lastColumn = ca.getLastColumn(); // 右下角的列号
int firstRow = ca.getFirstRow(); // 左上角的行号
int lastRow = ca.getLastRow(); // 右下角的行号
//System.out.println("左上角的列号:"+firstColumn+",右下角的列号:"+lastColumn+",左上角的行号:"+firstRow+",右下角的行号:"+lastRow);
if(i >= firstRow && i <= lastRow){
if(j >= firstColumn && j <= lastColumn){
//Row fRow = sheet.getRow(firstRow);
//Cell fCell = fRow.getCell(firstColumn);
//System.out.println("此单元格在合并单元格内..."+getCellValue(fCell));
return true;
}
}
}
return false;
} private static String excel_styles = "[{\"border\":{\"bottom\":[\"thin\",\"#000\"],\"left\":[\"thin\",\"#000\"],\"right\":[\"thin\",\"#000\"],\"top\":[\"thin\",\"#000\"]}},{\"align\":\"center\"},{\"align\":\"center\",\"border\":{\"bottom\":[\"thin\",\"#000\"],\"left\":[\"thin\",\"#000\"],\"right\":[\"thin\",\"#000\"],\"top\":[\"thin\",\"#000\"]}}]"; public static Map<String, Object> readExcelToObj(String path) throws Exception {
File file = new File(path);
InputStream is = new FileInputStream(file);
HSSFWorkbook wb = new HSSFWorkbook(is);
Map<String, Object>map = new HashMap<String, Object>();
map = readExcel(wb);
map.put("excel_styles",excel_styles);
return map;
}
private static Map<String, Object> readExcel(HSSFWorkbook wb) {
Map<String, Object>map = new HashMap<String, Object>();
JSONObject rowJosn = new JSONObject(); // 行
//JSONObject cellsJosn = new JSONObject(); // cells
//JSONObject gridJson = new JSONObject(); // 格
//JSONObject gridValue = new JSONObject(); // 格数据
//int [] mergesArray = new int[2]; // 合并单元格
List<String> merges = new ArrayList<String>();// 合并单元格
String Emerges = ""; HSSFSheet sheet = wb.getSheetAt(0);
int trLength = sheet.getLastRowNum(); // 获取工作表上的最后一行
for(int i=0; i<trLength; i++) {
HSSFRow row = sheet.getRow(i);
int tdLength = row.getLastCellNum(); // 获取此行中最后一个单元格的索引加1 JSONObject cellsJosn = new JSONObject(); // cells
JSONObject gridJson = new JSONObject(); // 格
for(int j = 0;j<tdLength;j++) {
//得到Excel工作表指定行的单元格
HSSFCell c = row.getCell(j,row.CREATE_NULL_AS_BLANK);
//int cc = c.getColumnIndex();
//System.out.println("返回此单元格的列索引:"+cc);
System.out.println("行:"+i+",列"+j);
//---------------------------------------------boolean isMerge = is(sheet, i, j);
JSONObject gridValue = new JSONObject(); // 格数据
int [] mergesArray = new int[2]; // 合并单元格
if(isMerge) {
// "4":{ "cells":{ "1":{"text":"","merge":[0,21]} }
int MergeCount = sheet.getNumMergedRegions();
int lastColumn = 0;
for(int l = 0 ; l < MergeCount ; l++){
CellRangeAddress ca = sheet.getMergedRegion(l);
String fa = ca.formatAsString() ;
System.out.println(">>>"+fa);
int firstColumn = ca.getFirstColumn(); // 左上角的列号
lastColumn = ca.getLastColumn(); // 右下角的列号
int firstRow = ca.getFirstRow(); // 左上角的行号
int lastRow = ca.getLastRow(); // 右下角的行号
//System.out.println("左上角的列号:"+firstColumn+",右下角的列号:"+lastColumn+",左上角的行号:"+firstRow+",右下角的行号:"+lastRow);
if(i == firstRow) { // 表示新
if(j == firstColumn ){ // 表示新 // 表示 传入的 单元格 是符合的单元格
// 左上角的列号:1,右下角的列号:2,左上角的行号:4,右下角的行号:7
//添加列数据 ( 列:{text:‘数据’,merge[( 左下角行号 - h ),( -l)]} )
gridValue.put("text", c.getStringCellValue());
gridValue.put("style", 1);
mergesArray[0]=lastRow-firstRow; // 右上角列号 - 左上角列号
mergesArray[1]=lastColumn-firstColumn; // 右上角列号 - 左上角列号
gridValue.put("merge", mergesArray); merges.add(fa);
Emerges+=fa+",";
break;
}else{
if(j > firstColumn && j<= lastColumn){
//j = lastColumn; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
break;
}else{// 一定是一个单独的 单元格 因为合并单元格也是 按着循序循环的
// 添加数据
}
} }else{ // 旧 // 表示 这次的 合并单元格 于此次传入的 单元格不在一个位置
// 判断是否是一列的 合并单元格
if(i > firstRow && i<= lastRow){ // 表示 该行 存在合并单元格
if(j >= firstColumn && j<= lastColumn){ // 是否 为当前遍历出来的 单元格内
//j = lastColumn; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
break;
}else{// 一定是一个单独的 单元格 因为合并单元格也是 按着循序循环的
// 添加数据
}
}
}
}
gridJson.put(j, gridValue); // 放入格内
j = lastColumn; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
}else{
c.setCellType(Cell.CELL_TYPE_STRING);
gridValue.put("text", c.getStringCellValue());
gridValue.put("style", 1);
gridJson.put(j, gridValue); // 放入格内
}
}
cellsJosn.put("cells", gridJson);
rowJosn.put(i, cellsJosn); }
System.out.println(">>>"+rowJosn);
System.out.println(">>>"+merges.toString());
map.put("excel_rows", rowJosn);
//map.put("excel_merges", merges);
Emerges = Emerges.substring(0, Emerges.length()-1);
map.put("excel_merges", Emerges);
return map;
} public static void main(String[] args) {
// TODO Auto-generated method stub // mysterious
try {
String widz = "D:\\Book1.xls";
readExcelToObj(widz);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

---

/*左下角的行号 = 右下角的行号
左下角的列号 = 左上角的列号
右上角的行号 = 左上角的行号
右上角的列号 = 右下角的列号*/ int firstColumn = ca.getFirstColumn(); // 左上角的列号
int lastColumn = ca.getLastColumn(); // 右下角的列号
int firstRow = ca.getFirstRow(); // 左上角的行号
int lastRow = ca.getLastRow(); // 右下角的行号
a(s,h,l){ // sheet,行,列
if(h = 左上角的行号) { // 表示新
//if(){// 当前行是否已经有数据存在 一格一格遍历无需判断是否存在 //--- 添加行数据 ( 行:{cells: { ) }
if(l = 左上角的列号 ){ // 表示新 // 表示 传入的 单元格 是符合的单元格
// 左上角的列号:1,右下角的列号:2,左上角的行号:4,右下角的行号:7
添加列数据 ( 列:{text:‘数据’,merge[( 左下角行号 - h ),( -l)]} )
}else{
if(l > 左上角的列号 && l<= 右上角的列号){
l = 右上角的列号+1; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
return l;
}else{// 一定是一个单独的 单元格 因为合并单元格也是 按着循序循环的
// 添加数据
}
} }esle{ // 旧 // 表示 这次的 合并单元格 于此次传入的 单元格不在一个位置
// 判断是否是一列的 合并单元格
if(h > 左上角的行号 && h<= 左下角的行号){ // 表示 该行 存在合并单元格
if(l ){ // 是否 为当前遍历出来的 单元格内
l = 右上角的列号+1; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
}else{// 一定是一个单独的 单元格 因为合并单元格也是 按着循序循环的
// 添加数据
}
}
}
}

--- 多页---------------------------------------------------------------------------------------------------------------------------

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="com.base.util.WebUtil"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; String id=WebUtil.getParam("id");
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>starting page</title> <meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
<link href="<%=basePath%>x-spreadsheet/xspreadsheet.css" rel="stylesheet"></head>
<link rel="stylesheet" href="<%=basePath %>base/layuiadmin/layui/css/layui.css" media="all">
<link rel="stylesheet" href="<%=basePath %>base/layuiadmin/style/admin.css" media="all"> </head> <body onload="load()">
<div style="position: fixed; right: 0; top: .3em;">
<input type="text" id="id" name="id" value="<%=id %>" lay-verify="required" autocomplete="off" class="layui-input" style="display: none;" >
<button id="save" name="save" class="layui-btn site-demo-active" data-type="sjglTabChange" lay-filter="demo" onclick="save()">保存</button>
</div>
<div id="x-spreadsheet-demo"></div>
</body>
<script src="<%=basePath %>base/layuiadmin/layui/layui.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript" > function save(){
const d = xs.getData();
console.log(d[0]);
var merges = JSON.stringify(d[0].merges);
var rows = JSON.stringify(d[0].rows);
var styles = JSON.stringify(d[0].styles);
var id = "<%=id%>";
$.ajax({
url:'<%=basePath%>gzlc/saveExcel.do',
data:{merges:merges,rows:rows,styles:styles,id:id},
type:'post',
dataType: "json",
success:function(obj){
console.log("---");
console.log(obj);            
}
});
} var excel_styles = "";
var mycars=new Array();
function getExcel(){
$.ajax({
url:'<%=basePath%>gzlc/getExcel.do?id=<%=id%>',
type:'post',
dataType: "json",
async:false,
success:function(obj){
console.log("---");  
//console.log(obj);
if(obj.length>0){
excel_styles = obj[0].excel_styles;
excel_styles = $.parseJSON( excel_styles );
//console.log( excel_styles);
var htmlsourse = obj[0].excel_rows;
console.log("-*-------------------------------------------------------");
htmlsourse = $.parseJSON( htmlsourse );
console.log( htmlsourse); var i = 0;
$.each(htmlsourse, function(j) {
var xx = htmlsourse[j];
console.log(htmlsourse[j]);
//console.log(j);
var person = {
freeze : ""+j,
styles : excel_styles,
merges:j,
cols: {len: 50}, // 显示的个数
rows:j
};
var excel_rows = xx.excel_rows;
var excel_merges = xx.excel_merges;
excel_merges = excel_merges.replace(/\s+/g,""); // 去空格
excel_merges = excel_merges.split(',');
//console.log(excel_rows);
//console.log(excel_merges);
//console.log("////////////////////");
//console.log(i);
person.merges = excel_merges;
person.rows = excel_rows;
mycars[i]=person;
i++;
});
console.log(mycars);
}
}
});
}
var xs = "";
function load(){
console.log('---');
getExcel();
console.log('****');
//x_spreadsheet.locale('zh-cn');
var saveIcon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNTc3MTc3MDkyOTg4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI2NzgiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+PC9zdHlsZT48L2RlZnM+PHBhdGggZD0iTTIxMy4zMzMzMzMgMTI4aDU5Ny4zMzMzMzRhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMSA4NS4zMzMzMzMgODUuMzMzMzMzdjU5Ny4zMzMzMzRhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMS04NS4zMzMzMzMgODUuMzMzMzMzSDIxMy4zMzMzMzNhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMS04NS4zMzMzMzMtODUuMzMzMzMzVjIxMy4zMzMzMzNhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMSA4NS4zMzMzMzMtODUuMzMzMzMzeiBtMzY2LjkzMzMzNCAxMjhoMzQuMTMzMzMzYTI1LjYgMjUuNiAwIDAgMSAyNS42IDI1LjZ2MTE5LjQ2NjY2N2EyNS42IDI1LjYgMCAwIDEtMjUuNiAyNS42aC0zNC4xMzMzMzNhMjUuNiAyNS42IDAgMCAxLTI1LjYtMjUuNlYyODEuNmEyNS42IDI1LjYgMCAwIDEgMjUuNi0yNS42ek0yMTMuMzMzMzMzIDIxMy4zMzMzMzN2NTk3LjMzMzMzNGg1OTcuMzMzMzM0VjIxMy4zMzMzMzNIMjEzLjMzMzMzM3ogbTEyOCAwdjI1NmgzNDEuMzMzMzM0VjIxMy4zMzMzMzNoODUuMzMzMzMzdjI5OC42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMS00Mi42NjY2NjcgNDIuNjY2NjY3SDI5OC42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMS00Mi42NjY2NjctNDIuNjY2NjY3VjIxMy4zMzMzMzNoODUuMzMzMzMzek0yNTYgMjEzLjMzMzMzM2g4NS4zMzMzMzMtODUuMzMzMzMzeiBtNDI2LjY2NjY2NyAwaDg1LjMzMzMzMy04NS4zMzMzMzN6IG0wIDU5Ny4zMzMzMzR2LTEyOEgzNDEuMzMzMzMzdjEyOEgyNTZ2LTE3MC42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMSA0Mi42NjY2NjctNDIuNjY2NjY3aDQyNi42NjY2NjZhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMSA0Mi42NjY2NjcgNDIuNjY2NjY3djE3MC42NjY2NjdoLTg1LjMzMzMzM3ogbTg1LjMzMzMzMyAwaC04NS4zMzMzMzMgODUuMzMzMzMzek0zNDEuMzMzMzMzIDgxMC42NjY2NjdIMjU2aDg1LjMzMzMzM3oiIHAtaWQ9IjI2NzkiIGZpbGw9IiMyYzJjMmMiPjwvcGF0aD48L3N2Zz4='
var previewEl = document.createElement('img')
previewEl.src = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjIxMzI4NTkxMjQzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjU2NjMiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PGRlZnM+PHN0eWxlIHR5cGU9InRleHQvY3NzIj48L3N0eWxlPjwvZGVmcz48cGF0aCBkPSJNNTEyIDE4Ny45MDRhNDM1LjM5MiA0MzUuMzkyIDAgMCAwLTQxOC41NiAzMTUuNjQ4IDQzNS4zMjggNDM1LjMyOCAwIDAgMCA4MzcuMTIgMEE0MzUuNDU2IDQzNS40NTYgMCAwIDAgNTEyIDE4Ny45MDR6TTUxMiAzMjBhMTkyIDE5MiAwIDEgMSAwIDM4NCAxOTIgMTkyIDAgMCAxIDAtMzg0eiBtMCA3Ni44YTExNS4yIDExNS4yIDAgMSAwIDAgMjMwLjQgMTE1LjIgMTE1LjIgMCAwIDAgMC0yMzAuNHpNMTQuMDggNTAzLjQ4OEwxOC41NiA0ODUuNzZsNC44NjQtMTYuMzg0IDQuOTI4LTE0Ljg0OCA4LjA2NC0yMS41NjggNC4wMzItOS43OTIgNC43MzYtMTAuODggOS4zNDQtMTkuNDU2IDEwLjc1Mi0yMC4wOTYgMTIuNjA4LTIxLjMxMkE1MTEuNjE2IDUxMS42MTYgMCAwIDEgNTEyIDExMS4xMDRhNTExLjQ4OCA1MTEuNDg4IDAgMCAxIDQyNC41MTIgMjI1LjY2NGwxMC4yNCAxNS42OGMxMS45MDQgMTkuMiAyMi41OTIgMzkuMTA0IDMyIDU5Ljc3NmwxMC40OTYgMjQuOTYgNC44NjQgMTMuMTg0IDYuNCAxOC45NDQgNC40MTYgMTQuODQ4IDQuOTkyIDE5LjM5Mi0zLjIgMTIuODY0LTMuNTg0IDEyLjgtNi40IDIwLjA5Ni00LjQ4IDEyLjYwOC00Ljk5MiAxMi45MjhhNTExLjM2IDUxMS4zNiAwIDAgMS0xNy4yOCAzOC40bC0xMi4wMzIgMjIuNC0xMS45NjggMjAuMDk2QTUxMS41NTIgNTExLjU1MiAwIDAgMSA1MTIgODk2YTUxMS40ODggNTExLjQ4OCAwIDAgMS00MjQuNDQ4LTIyNS42bC0xMS4zMjgtMTcuNTM2YTUxMS4yMzIgNTExLjIzMiAwIDAgMS0xOS44NC0zNS4wMDhMNTMuMzc2IDYxMS44NGwtOC42NC0xOC4yNC0xMC4xMTItMjQuMTI4LTcuMTY4LTE5LjY0OC04LjMyLTI2LjYyNC0yLjYyNC05Ljc5Mi0yLjQ5Ni05LjkyeiIgcC1pZD0iNTY2NCI+PC9wYXRoPjwvc3ZnPg=='
previewEl.width = 16
previewEl.height = 16 xs = x_spreadsheet('#x-spreadsheet-demo', {
showToolbar: true,
showGrid: true,
showBottomBar: true,
extendToolbar: {
left: [ { tip: 'Save', icon: saveIcon,
onClick: (data, sheet) => {
console.log('click save button:', data, sheet)
}
} ],
right: [ { tip: 'Preview', el: previewEl,
onClick: (data, sheet) => {
console.log('click preview button:', data)
}
} ],
}
});
xs.loadData(mycars).change((cdata) => {
console.log(cdata);
console.log('>>>', xs.getData());
}); xs.on('cell-selected', (cell, ri, ci) => {
console.log('selected>cell:', cell, ', ri:', ri, ', ci:', ci);
}).on('cell-edited', (text, ri, ci) => {
console.log('edited>text:', text, ', ri: ', ri, ', ci:', ci);
});
} </script>
<script type="text/javascript" src="<%=basePath%>x-spreadsheet/xspreadsheet.js"></script>
</html>

---

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress; import net.sf.json.JSONObject; /**
*
* @author mysterious
*
*/ public class ExcelFinal { // 最终
// https://blog.csdn.net/lianzhang861/article/details/86234515
private static Workbook wb;
private static String excel_styles = "[{\"border\":{\"bottom\":[\"thin\",\"#000\"],\"left\":[\"thin\",\"#000\"],\"right\":[\"thin\",\"#000\"],\"top\":[\"thin\",\"#000\"]}},{\"align\":\"center\"},{\"align\":\"center\",\"border\":{\"bottom\":[\"thin\",\"#000\"],\"left\":[\"thin\",\"#000\"],\"right\":[\"thin\",\"#000\"],\"top\":[\"thin\",\"#000\"]}}]"; /**
* 获取单元格的值
* @param cell
* @return
*/
public static String getCellValue(Cell cell){ if(cell == null) {
cell.setCellType(Cell.CELL_TYPE_STRING);
return cell.getStringCellValue();
} if(cell.getCellType() == Cell.CELL_TYPE_STRING){ return cell.getStringCellValue(); }else if(cell.getCellType() == Cell.CELL_TYPE_BOOLEAN){ return String.valueOf(cell.getBooleanCellValue()); }else if(cell.getCellType() == Cell.CELL_TYPE_FORMULA){ return cell.getCellFormula() ; }else if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){ return String.valueOf(cell.getNumericCellValue()); }
return "";
} private static boolean is(Sheet sheet,int i,int j) {
int MergeCount = sheet.getNumMergedRegions();
for(int l = 0 ; l < MergeCount ; l++){
CellRangeAddress ca = sheet.getMergedRegion(l);
String fa = ca.formatAsString() ;
//System.out.println(">>>"+fa);
int firstColumn = ca.getFirstColumn(); // 左上角的列号
int lastColumn = ca.getLastColumn(); // 右下角的列号
int firstRow = ca.getFirstRow(); // 左上角的行号
int lastRow = ca.getLastRow(); // 右下角的行号
//System.out.println("左上角的列号:"+firstColumn+",右下角的列号:"+lastColumn+",左上角的行号:"+firstRow+",右下角的行号:"+lastRow);
if(i >= firstRow && i <= lastRow){
if(j >= firstColumn && j <= lastColumn){
//Row fRow = sheet.getRow(firstRow);
//Cell fCell = fRow.getCell(firstColumn);
System.out.println("此单元格在合并单元格内...");
return true;
}
}
}
return false;
} private static Map<String, Object> readExcel(Workbook wb) {
Map<String, Object>map = new HashMap<String, Object>();
JSONObject JO = new JSONObject(); int sheetCount = wb.getNumberOfSheets(); //Sheet的数量
for(int s = 0;s<sheetCount;s++) {
JSONObject sheetMap = new JSONObject(); // 第二页时
Sheet sheet = wb.getSheetAt(s);
String sheetName = sheet.getSheetName();
int trLength = sheet.getLastRowNum(); // 获取工作表上的最后一行
JSONObject rowJosn = new JSONObject(); // 行
String Emerges = "";// 合并单元格 for(int i=0; i<trLength; i++) {
Row row = sheet.getRow(i);
int tdLength = row.getLastCellNum(); // 获取此行中最后一个单元格的索引加1
JSONObject cellsJosn = new JSONObject(); // cells
JSONObject gridJson = new JSONObject(); // 格 for(int j = 0;j<tdLength;j++) {
Cell c = row.getCell(j,row.CREATE_NULL_AS_BLANK);
System.out.println("sheet:"+s+",行:"+i+",列"+j); boolean isMerge = is(sheet, i, j);
JSONObject gridValue = new JSONObject(); // 格数据
int [] mergesArray = new int[2]; // 合并单元格
if(isMerge) {
int MergeCount = sheet.getNumMergedRegions();
int lastColumn = 0;
for(int l = 0 ; l < MergeCount ; l++){
CellRangeAddress ca = sheet.getMergedRegion(l);
String fa = ca.formatAsString() ;
System.out.println(">>>"+fa);
int firstColumn = ca.getFirstColumn(); // 左上角的列号
lastColumn = ca.getLastColumn(); // 右下角的列号
int firstRow = ca.getFirstRow(); // 左上角的行号
int lastRow = ca.getLastRow(); // 右下角的行号
System.out.println("左上角的列号:"+firstColumn+",右下角的列号:"+lastColumn+",左上角的行号:"+firstRow+",右下角的行号:"+lastRow);
if(i == firstRow) { // 表示新
if(j == firstColumn ){ // 表示新 // 表示 传入的 单元格 是符合的单元格
// 左上角的列号:1,右下角的列号:2,左上角的行号:4,右下角的行号:7
//添加列数据 ( 列:{text:‘数据’,merge[( 左下角行号 - h ),( -l)]} )
gridValue.put("text", c.getStringCellValue());
gridValue.put("style", 1);
mergesArray[0]=lastRow-firstRow; // 右上角列号 - 左上角列号
mergesArray[1]=lastColumn-firstColumn; // 右上角列号 - 左上角列号
gridValue.put("merge", mergesArray); Emerges+=fa+",";
break;
}else{
if(j > firstColumn && j<= lastColumn){
//j = lastColumn; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
break;
}else{// 一定是一个单独的 单元格 因为合并单元格也是 按着循序循环的
// 添加数据
}
} }else{ // 旧 // 表示 这次的 合并单元格 于此次传入的 单元格不在一个位置
// 判断是否是一列的 合并单元格
if(i > firstRow && i<= lastRow){ // 表示 该行 存在合并单元格
if(j >= firstColumn && j<= lastColumn){ // 是否 为当前遍历出来的 单元格内
//j = lastColumn; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
break;
}else{// 一定是一个单独的 单元格 因为合并单元格也是 按着循序循环的
// 添加数据
}
}
}
}
gridJson.put(j, gridValue); // 放入格内
j = lastColumn; // 表示跳出 这一行 内符合 行号的 合并单元格 ,
}else{
c.setCellType(Cell.CELL_TYPE_STRING);
gridValue.put("text", c.getStringCellValue());
gridValue.put("style", 1);
gridJson.put(j, gridValue); // 放入格内
}
} cellsJosn.put("cells", gridJson);
rowJosn.put(i, cellsJosn);
}
sheetMap.put("excel_rows", rowJosn);
Emerges = Emerges.substring(0, Emerges.length()-1);
sheetMap.put("excel_merges", Emerges);
JO.put(sheetName, sheetMap);
//map.put(sheetName, sheetMap);
}
System.out.println(">>>"+JO);
map.put("excel_rows", JO);
map.put("excel_styles", excel_styles);
return map;
}
public static Map<String, Object> readExcelToObj(String path) throws Exception {
// 获得文件所在地
File file = new File(path); List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
FileInputStream is = new FileInputStream(file); //文件流
wb = WorkbookFactory.create(is); //这种方式 Excel 2003/2007/2010 都是可以处理的
return readExcel(wb); } public static void main(String[] args) {
// TODO Auto-generated method stub
String path = "设计交底记录.xls"; // 文件在服务器的地址
try {
readExcelToObj(path);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

--- mysterious

java web 在线编辑Excel -- x-spreadsheet的更多相关文章

  1. handsontable在线编辑excel扩展功能-踩坑篇

    简述 先说一下背景,之所以封装handsontable插件,是因为公司要实现在线编辑导入excel文件的功能,然后我就找到了这个功能强大的插件handsontable. 具体功能 除了handsont ...

  2. java web 在线聊天的基本实现

    随着互联网的发展,http的协议有些时候不能满足需求,比如在现聊天的实现.如果使用http协议必须轮训,或者使用长链接.必须要一个request,这样后台才能发送信息到前端. 后台不能主动找客户端通信 ...

  3. [转载] Java开发在线编辑Word同时实现全文检索

    一.背景介绍 Word文档与日常办公密不可分,在实际应用中,当某一文档服务器中有很多Word文档,假如有成千上万个文档时,用户查找打开包含某些指定关键字的文档就变得很困难,一般情况下能想到的解决方案是 ...

  4. [原创]Java开发在线编辑Word同时实现全文检索

    一.背景介绍 Word文档与日常办公密不可分,在实际应用中,当某一文档服务器中有很多Word文档,假如有成千上万个文档时,用户查找打开包含某些指定关键字的文档就变得很困难,一般情况下能想到的解决方案是 ...

  5. Java web的读取Excel简单Demo

    目录结构: Data.xls数据:   后台页面: GetExcelData.java       public void doGet(HttpServletRequest request, Http ...

  6. java web实现在线编辑word,并将word导出(一)

    前段时间领导交代了一个需求:客户需要一个能够web在线编辑文字,如同编辑word文档一样,同时能够将编辑完成的内容导出为word文档并下载到本地. 我们选择了前台使用富文本插件的形式用于编辑内容,使用 ...

  7. java 网站源码 在线编辑模版 代码编辑器 兼容手机平板PC freemaker 静态引擎

    前台: 支持四套模版, 可以在后台切换   系统介绍: 1.网站后台采用主流的 SSM 框架 jsp JSTL,网站后台采用freemaker静态化模版引擎生成html 2.因为是生成的html,所以 ...

  8. Atitit.office word  excel  ppt pdf 的web在线预览方案与html转换方案 attilax 总结

    Atitit.office word  excel  ppt pdf 的web在线预览方案与html转换方案 attilax 总结 1. office word  excel pdf 的web预览要求 ...

  9. Office word excel电子表格在线编辑的实现方法

    Office xp之后的版本支持通过webdav协议(http的扩展)直接编辑服务器上的文件. IIS(6.0)支持webdav,这在IIS管理器的web服务扩展中可以看到.利用IIS作为webdav ...

随机推荐

  1. C# 显示、隐藏窗口对应的任务栏

    WPF中全屏窗口,会自动隐藏任务栏. 那非全屏窗口如何隐藏任务栏?甚至有没有一种场景,隐藏任务后自定义一套系统任务栏来显示? 以下会分阶段讲述一些概念 1. 主屏任务栏 任务栏,其实也是一个窗口,主屏 ...

  2. 题解 [NOI2019]弹跳

    题目传送门 题目大意 给出 \(n\) 做城市,每座城市都有横纵坐标 \(x,y\).现在给出 \(m\) 个限制 \(p,t,l,r,d,u\),表示从 \(p\) 城市出发,可以花费 \(t\) ...

  3. Netty-FastThreadLocal快在哪里呢?

    来源于:https://www.wangdaye.net/archives/n-e-t-t-y-zhi-f-a-s-t-t-h-r-e-a-d-l-o-c-a-l 前言 netty的concurren ...

  4. 工作3年的Java程序员,轻松拿到阿里P6Offer,只因为他搞明白了Redis这几个问题!!

    Redis中的多路复用模型 Redis6用到了多线程?那多线程应用在哪些地方,引入多线程后,又改如何保证线程安全性呢? 同时,如何在性能和线程安全性方面做好平衡? 关于Redis的单线程模型 在Red ...

  5. Visual Studio 重置窗口布局

    Visual Studio 重置窗口布局

  6. css3鼠标悬停图片边框线条动画特效

    css3鼠标经过内容区时,边框线条特效效果制作.   html: <div class="strength grWidth hidden"> <div class ...

  7. 工厂模式--摆脱你日复一日new对象却依旧单身的苦恼!

    前言 每每谈及到Java,就不免会想到一个悲伤的事实:你是否每天都在new对象,却依然坚守在单身岗上屹立不倒.(所谓面向对象编程hhh),这篇来学一下工厂模式,摆脱new对象的苦恼! 知识点 传统工厂 ...

  8. Spring session redis ERR unknown command 'CONFIG'

    部署线上服务启动报错 redis.clients.jedis.exceptions.JedisDataException: ERR unknown command 'CONFIG' Redis CON ...

  9. [对对子队]会议记录5.20(Scrum Meeting7)

    今天已完成的工作 马嘉 ​ 工作内容:录制新手引导视频 ​ 相关issue:优化顺序关卡新手引导功能 ​ 相关签入:feat: 录制了新的新手引导视频 吴昭邦 ​ 工作内容:增加加速功能 ​ 相关is ...

  10. Unity 3D手游对不同分辨率屏幕的UI自适应

    目前安卓手机的屏幕大小各异,没有统一的标准,因此用Unity 3D制作的手游需要做好对不同分辨率屏幕的UI自适应,否则就会出现UI大小不一和位置错位等问题. 我们的项目在开发时的参照分辨率(Refer ...