14 |
15 |
16 |
17 | 雄安智评云数字科技有限公司 18 | |
19 |
20 |
21 |
22 |
23 | Code the Future 24 | |
25 |
26 |
27 |
28 |
29 | 吉客云 30 | |
31 |
32 |
33 |
34 |
35 | 华为 36 | |
37 |
| 40 | | 41 |42 | | 43 |44 | | 45 |46 | | 47 |
14 |
15 |
16 |
17 | 雄安智评云数字科技有限公司 18 | |
19 |
20 |
21 |
22 |
23 | Code the Future 24 | |
25 |
26 |
27 |
28 |
29 | 吉客云 30 | |
31 |
32 |
33 |
34 |
35 | 华为 36 | |
37 |
| 40 | | 41 |42 | | 43 |44 | | 45 |46 | | 47 |
|
|
20 |
21 | ### Patreon和OpenCollective有什么区别?
22 |
23 | 通过Patreon捐赠的资金将直接用于支持menshshukeji在Luckysheet上的工作。 通过OpenCollective捐赠的资金由透明费用管理,将用于补偿核心团队成员的工作和费用或赞助社区活动。 通过在任一平台上捐款,您的姓名/徽标将得到适当的认可和曝光。
24 |
25 | ## 赞助者列表
26 |
27 | (按时间顺序排列)
28 | - *勇 ¥ 30
29 | - 虚我 ¥ 200
30 | - 甜党 ¥ 50
31 | - Alphabet(Google)-gcf ¥ 1
32 | - **平 ¥ 100
33 | - **东 ¥ 10
34 | - debugger ¥ 20
35 | - 烦了烦 ¥ 10
36 | - 文顶顶 ¥ 200
37 | - yangxshn ¥ 10
38 | - 爱乐 ¥ 100
39 | - 小李飞刀刀 ¥ 66
40 | - 张铭 ¥ 200
41 | - 曹治军 ¥ 1
42 | - *特 ¥ 10
43 | - **权 ¥ 9.9
44 | - **sdmq ¥ 20
45 | - *旭 ¥ 10
46 | - Quentin ¥ 20
47 | - 周宇凡 ¥ 100
48 | - *超 ¥ 10
49 | - 维宁 ¥ 100
50 | - hyy ¥ 20
51 | - 雨亭寒江月 ¥ 50
52 | - **功 ¥ 10
53 | - **光 ¥ 20
54 | - terrywan ¥ 100
55 | - 王晓洪 ¥ 10
56 | - Sun ¥ 10
57 | - 忧绣 ¥ 100
58 | - Jasonx ¥ 10
59 | - 国勇 ¥ 66.6
60 | - 郎志 ¥ 100
61 | - 匿名 ¥ 1
62 | - ni ¥ 100
63 | - 苏 ¥ 50
64 | - Mads_chan ¥ 1
65 | - LK ¥ 100
66 | - 智连方舟 李汪石 ¥ 168
67 | - **发 ¥ 260
68 | - *超 ¥ 10
69 | - *勇 ¥ 10
70 | - *腾 ¥ 15
71 | - 名字好难起 ¥ 20
72 | - 大山 ¥ 1
73 | - waiting ¥ 1000
74 | - **宇 ¥ 10.00
75 | - 刘小帅的哥哥 ¥ 20.00
76 | - 宁静致远 ¥ 10.00
77 | - Eleven ¥ 1.00
78 | - **帆 ¥ 188
79 | - henry ¥ 100
80 | - .波罗 ¥ 50
81 | - 花落有家 ¥ 50
82 | - 踏遍南水北山 ¥ 1
83 | - LC ¥ 5
84 | - **明 ¥ 8.80
85 | - *军 ¥ 20
86 | - 张彪 ¥ 50
87 | - 企业文档云@肖敏 ¥ 10
88 | - 匿名 ¥ 50
89 | - 逍遥行 ¥ 10
90 | - z.wasaki ¥ 50
91 | - Make Children ¥ 20
92 | - Foam ¥ 20
93 | - 奥特曼( o|o)ノ三 ¥ 50
94 | - **凯 ¥ 10
95 | - **兵 ¥ 20
96 | - **川 ¥ 1
97 | - 二万 ¥ 50
98 | - 蔚然成林 ¥ 10
99 | - 邹杰 ¥ 10
100 | - 张永强 ¥ 50
101 | - 鱼得水 ¥ 50
102 | - Ccther ¥ 1
103 | - Eric Cheng ¥ 10
104 | - 佚名 ¥ 1
105 | - 花叶 ¥ 50
106 | - GT ¥ 20
107 | - 菜菜心 ¥ 10
108 | - fisher ¥ 1
109 | - JC ¥ 5
110 | - 佚名 ¥ 20
111 | - 独孤一剑 ¥ 50
112 | - mxt ¥ 20
113 | - 一叶迷山 ¥ 100
114 | - Jeff ¥ 100
115 | - 八千多条狗🐶 ¥ 100
116 | - 晓峰 ¥ 10
117 | - 戒 ¥ 1
118 | - 浪里个浪 ¥ 1
119 | - 回调函数 ¥ 50
120 | - 赖瓜子 ¥ 5
121 | - Milo•J ¥ 20
122 | - 可道云 ¥ 200
123 | - *程 ¥ 10
124 | - 来一杯卡布酸奶 ¥ 5
--------------------------------------------------------------------------------
/src/plugins/js/jquery.mousewheel.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Mousewheel 3.1.13
3 | *
4 | * Copyright 2015 jQuery Foundation and other contributors
5 | * Released under the MIT license.
6 | * http://jquery.org/license
7 | */
8 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
--------------------------------------------------------------------------------
/src/plugins/jquery.sPage.css:
--------------------------------------------------------------------------------
1 | .spage-total {
2 | display: inline-block;
3 | margin-right: 10px;
4 | line-height: 29px;
5 | color: #666;
6 | font-size: 14px
7 | }
8 |
9 | .spage-number {
10 | display: inline-block;
11 | color: #666;
12 | font-size: 14px
13 | }
14 | .selectNum {
15 | font-size: 14px;
16 | height: 27px;
17 | box-sizing: border-box;
18 | vertical-align: top;
19 | line-height: 27px;
20 | border: 1px solid #ddd;
21 | margin-left: 5px;
22 | vertical-align: middle;
23 | }
24 | .spage-number button {
25 | position: relative;
26 | box-sizing: border-box;
27 | display: inline-block;
28 | margin-left: -1px;
29 | padding: 0 10px;
30 | line-height: 27px;
31 | border: 1px solid #ddd;
32 | text-align: center;
33 | transition: all .2s;
34 | cursor: pointer;
35 | outline: none;
36 | background: 0 0;
37 | user-select: none;
38 | color: #333;
39 | background: #fff;
40 | vertical-align: middle;
41 | }
42 | .prevBtn, .nextBtn {
43 | width: 16px;
44 | height: 27px;
45 | background: url(images/js.png) no-repeat center center;
46 | background-size: 100% auto;
47 | display: block;
48 | transform: rotate(180deg);
49 | }
50 | .nextBtn {
51 | transform: rotate(0);
52 | }
53 | .spage-number button.active {
54 | background: #2d98e6;
55 | color: #fff;
56 | border-color: #2d98e6;
57 | z-index: 3
58 | }
59 |
60 | .spage-number button.active:hover {
61 | background: #2d98e6;
62 | color: #fff;
63 | border-color: #2d98e6;
64 | z-index: 3
65 | }
66 |
67 | .spage-number button:hover {
68 | background-color: #eee
69 | }
70 |
71 | .spage-number button.button-disabled {
72 | cursor: not-allowed;
73 | color: #ccc
74 | }
75 |
76 | .spage-number .spage-after,
77 | .spage-before {
78 | padding: 0;
79 | width: 40px
80 | }
81 |
82 | .spage-skip {
83 | display: inline-block;
84 | margin-left: 5px;
85 | line-height: 27px;
86 | color: #666;
87 | font-size: 14px
88 | }
89 |
90 | .spage-skip input {
91 | box-sizing: border-box;
92 | display: inline-block;
93 | width: 45px;
94 | height: 29px;
95 | text-align: center;
96 | vertical-align: top;
97 | border: 1px solid #ddd;
98 | background: 0 0;
99 | outline: none;
100 | transition: all .2s
101 | }
102 |
103 | .spage-skip input:focus {
104 | border-color: #2d98e6
105 | }
106 |
107 | .spage-skip button {
108 | display: inline-block;
109 | padding: 0 14px;
110 | line-height: 27px;
111 | vertical-align: top;
112 | color: #333;
113 | border: 1px solid #ddd;
114 | cursor: pointer;
115 | transition: all .2s;
116 | outline: none;
117 | background: 0 0;
118 | user-select: none;
119 | background-color: #fff;
120 | }
121 |
122 | .spage-skip button:hover {
123 | background: #2d98e6;
124 | color: #fff;
125 | border: 1px solid #2d98e6
126 | }
127 |
128 |
129 |
--------------------------------------------------------------------------------
/src/utils/chartUtil.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 生成随机图表id
3 | */
4 | function generateRandomKey(prefix) {
5 | if (prefix == null) {
6 | prefix = 'chart'
7 | }
8 |
9 | var userAgent = window.navigator.userAgent
10 | .replace(/[^a-zA-Z0-9]/g, '')
11 | .split('')
12 | var mid = ''
13 | for (var i = 0; i < 12; i++) {
14 | mid += userAgent[Math.round(Math.random() * (userAgent.length - 1))]
15 | }
16 | var time = new Date().getTime()
17 |
18 | return prefix + '_' + mid + '_' + time
19 | }
20 | /**
21 | * 深度克隆数据,包括对象,数组,map
22 | * @param {*} obj 对象,数组,map
23 | */
24 | function deepCopy(obj) {
25 | if (!isObject(obj) && !isMap(obj)) {
26 | return obj;
27 | }
28 |
29 | let cloneObj;
30 | if (isMap(obj)) {
31 | cloneObj = new Map();
32 | for (let key of obj.keys()) {
33 | let value = obj.get(key);
34 | if (isMap(value) || isObject(value) || Array.isArray(obj)) {
35 | let copyVal = deepCopy(value);
36 | cloneObj.set(key, copyVal);
37 | } else {
38 | cloneObj.set(key, value);
39 | }
40 | }
41 | } else if (typeof obj === "function") {
42 | cloneObj = obj
43 | } else {
44 | cloneObj = Array.isArray(obj) ? [] : {};
45 | if (obj instanceof HTMLElement) {
46 | cloneObj = obj.cloneNode(true)
47 | } else {
48 | for (let key in obj) {
49 | // if (obj.hasOwnProperty(key)) {
50 | if (Object.prototype.hasOwnProperty.call(obj, key)) {
51 | cloneObj[key] =
52 | isMap(obj[key]) || isObject(obj[key])
53 | ? deepCopy(obj[key])
54 | : obj[key];
55 | }
56 | }
57 |
58 | }
59 | }
60 | return cloneObj;
61 | }
62 |
63 | /**
64 | * 判断参数是否是Object类型
65 | * @param {*} o
66 | */
67 | function isObject(o) {
68 | return (
69 | !isMap(o) &&
70 | (typeof o === 'object' || typeof o === 'function') &&
71 | o !== null
72 | );
73 | }
74 |
75 | /**
76 | * 判断参数是否是Map类型
77 | * @param {*} obj
78 | */
79 | function isMap(obj) {
80 | if (obj instanceof Map) {
81 | return true;
82 | } else {
83 | return false;
84 | }
85 | }
86 |
87 | // 替换temp中的${xxx}为指定内容 ,temp:字符串,这里指html代码,dataarry:一个对象{"xxx":"替换的内容"}
88 | // 例:luckysheet.replaceHtml("${image}",{"image":"abc","jskdjslf":"abc"}) ==> abc
89 | function replaceHtml(temp, dataarry) {
90 | return temp.replace(/\$\{([\w]+)\}/g, function (s1, s2) { var s = dataarry[s2]; if (typeof (s) != "undefined") { return s; } else { return s1; } });
91 | }
92 |
93 | function hasChinaword(s) {
94 | var patrn = /[\u4E00-\u9FA5]|[\uFE30-\uFFA0]/gi;
95 | if (!patrn.exec(s)) {
96 | return false;
97 | }
98 | else {
99 | return true;
100 | }
101 | }
102 |
103 | export {
104 | isMap,
105 | isObject,
106 | deepCopy,
107 | generateRandomKey,
108 | replaceHtml
109 | }
--------------------------------------------------------------------------------
/src/methods/get.js:
--------------------------------------------------------------------------------
1 | import { chatatABC } from '../utils/util';
2 | import Store from '../store';
3 |
4 | function getSheetIndex(index) {
5 | for (let i = 0; i < Store.luckysheetfile.length; i++) {
6 | if (Store.luckysheetfile[i]["index"] == index) {
7 | return i;
8 | }
9 | }
10 |
11 | return null;
12 | }
13 |
14 | function getRangetxt(sheetIndex, range, currentIndex) {
15 | let sheettxt = "";
16 |
17 | if (currentIndex == null) {
18 | currentIndex = Store.currentSheetIndex;
19 | }
20 |
21 | if (sheetIndex != currentIndex) {
22 | //sheet名字包含'的,引用时应该替换为''
23 | sheettxt = Store.luckysheetfile[getSheetIndex(sheetIndex)].name.replace(/'/g,"''");
24 | //如果包含除a-z、A-Z、0-9、下划线等以外的字符那么就用单引号包起来
25 | if(/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/.test(sheettxt))
26 | {
27 | sheettxt = sheettxt+"!";
28 | }
29 | else
30 | {
31 | sheettxt="'"+sheettxt+"'!";
32 | }
33 | }
34 |
35 | let row0 = range["row"][0], row1 = range["row"][1];
36 | let column0 = range["column"][0], column1 = range["column"][1];
37 |
38 | if (row0 == null && row1 == null) {
39 | return sheettxt + chatatABC(column0) + ":" + chatatABC(column1);
40 | }
41 | else if (column0 == null && column1 == null) {
42 | return sheettxt + (row0 + 1) + ":" + (row1 + 1);
43 | }
44 | else {
45 | if (column0 == column1 && row0 == row1) {
46 | return sheettxt + chatatABC(column0) + (row0 + 1);
47 | }
48 | else {
49 | return sheettxt + chatatABC(column0) + (row0 + 1) + ":" + chatatABC(column1) + (row1 + 1);
50 | }
51 | }
52 | }
53 |
54 | function getluckysheet_select_save() {
55 | return Store.luckysheet_select_save;
56 | }
57 |
58 | function getluckysheet_scroll_status() {
59 | return Store.luckysheet_scroll_status;
60 | }
61 |
62 | function getluckysheetfile(plugin) {
63 | // 获取图表数据
64 | if(plugin){
65 | Store.luckysheetfile.forEach(file => {
66 | if(!!file.chart){
67 | file.chart.forEach((chartObj)=>{
68 | const chartJson = Store.getChartJson(chartObj.chart_id);
69 | chartObj.chartOptions = chartJson;
70 | })
71 | }
72 | });
73 | }
74 |
75 | return Store.luckysheetfile;
76 | }
77 |
78 | function getconfig() {
79 | return Store.config;
80 | }
81 |
82 | function getvisibledatarow() {
83 | return Store.visibledatarow;
84 | }
85 |
86 | function getvisibledatacolumn() {
87 | return Store.visibledatacolumn;
88 | }
89 |
90 | export {
91 | getSheetIndex,
92 | getRangetxt,
93 | getluckysheet_select_save,
94 | getluckysheet_scroll_status,
95 | getluckysheetfile,
96 | getconfig,
97 | getvisibledatarow,
98 | getvisibledatacolumn,
99 | }
100 |
--------------------------------------------------------------------------------
/docs/about/sponsor.md:
--------------------------------------------------------------------------------
1 | # Sponsor
2 |
3 | ## Why sponsor
4 |
5 | If you run a business and is using Luckysheet in a revenue-generating product, it would make business sense to sponsor Luckysheet development: it ensures the project that your product relies on stays healthy and actively maintained. It can also help your exposure in the Luckysheet community and more people pay attention to your products.
6 |
7 | Of course, individual users are also welcome to buy author a glass of juice if Luckysheet has helped you in your work or personal projects 😋.
8 |
9 | ## How to sponsor
10 |
11 | Luckysheet is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [**backers**](#sponsors-list). If you'd like to join them, please consider:
12 |
13 | - [Become a backer or sponsor on Patreon](https://www.patreon.com/mengshukeji).
14 | - [Become a backer or sponsor on Open Collective](https://opencollective.com/luckysheet).
15 | - One-time donation via PayPal, WeChat or Alipay
16 |
17 | | PayPal | WeChat | Alipay |
18 | |---|---|---|
19 | | [Paypal Me](https://www.paypal.me/wbfsa) |
|
|
20 |
21 | ### What's the difference between Patreon and OpenCollective?
22 |
23 | Funds donated via Patreon go directly to support mengshukeji's work on Luckysheet. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform.
24 |
25 | ## Sponsors List
26 |
27 | (Sort by time)
28 | - *勇 ¥ 30
29 | - 虚我 ¥ 200
30 | - 甜党 ¥ 50
31 | - Alphabet(Google)-gcf ¥ 1
32 | - **平 ¥ 100
33 | - **东 ¥ 10
34 | - debugger ¥ 20
35 | - 烦了烦 ¥ 10
36 | - 文顶顶 ¥ 200
37 | - yangxshn ¥ 10
38 | - 爱乐 ¥ 100
39 | - 小李飞刀刀 ¥ 66
40 | - 张铭 ¥ 200
41 | - 曹治军 ¥ 1
42 | - *特 ¥ 10
43 | - **权 ¥ 9.9
44 | - **sdmq ¥ 20
45 | - *旭 ¥ 10
46 | - Quentin ¥ 20
47 | - 周宇凡 ¥ 100
48 | - *超 ¥ 10
49 | - 维宁 ¥ 100
50 | - hyy ¥ 20
51 | - 雨亭寒江月 ¥ 50
52 | - **功 ¥ 10
53 | - **光 ¥ 20
54 | - terrywan ¥ 100
55 | - 王晓洪 ¥ 10
56 | - Sun ¥ 10
57 | - 忧绣 ¥ 100
58 | - Jasonx ¥ 10
59 | - 国勇 ¥ 66.6
60 | - 郎志 ¥ 100
61 | - 匿名 ¥ 1
62 | - ni ¥ 100
63 | - 苏 ¥ 50
64 | - Mads_chan ¥ 1
65 | - LK ¥ 100
66 | - 智连方舟 李汪石 ¥ 168
67 | - **发 ¥ 260
68 | - *超 ¥ 10
69 | - *勇 ¥ 10
70 | - *腾 ¥ 15
71 | - 名字好难起 ¥ 20
72 | - 大山 ¥ 1
73 | - waiting ¥ 1000
74 | - **宇 ¥ 10.00
75 | - 刘小帅的哥哥 ¥ 20.00
76 | - 宁静致远 ¥ 10.00
77 | - Eleven ¥ 1.00
78 | - **帆 ¥ 188
79 | - henry ¥ 100
80 | - .波罗 ¥ 50
81 | - 花落有家 ¥ 50
82 | - 踏遍南水北山 ¥ 1
83 | - LC ¥ 5
84 | - **明 ¥ 8.80
85 | - *军 ¥ 20
86 | - 张彪 ¥ 50
87 | - 企业文档云@肖敏 ¥ 10
88 | - 匿名 ¥ 50
89 | - 逍遥行 ¥ 10
90 | - z.wasaki ¥ 50
91 | - Make Children ¥ 20
92 | - Foam ¥ 20
93 | - 奥特曼( o|o)ノ三 ¥ 50
94 | - **凯 ¥ 10
95 | - **兵 ¥ 20
96 | - **川 ¥ 1
97 | - 二万 ¥ 50
98 | - 蔚然成林 ¥ 10
99 | - 邹杰 ¥ 10
100 | - 张永强 ¥ 50
101 | - 鱼得水 ¥ 50
102 | - Ccther ¥ 1
103 | - Eric Cheng ¥ 10
104 | - 佚名 ¥ 1
105 | - 花叶 ¥ 50
106 | - GT ¥ 20
107 | - 菜菜心 ¥ 10
108 | - fisher ¥ 1
109 | - JC ¥ 5
110 | - 佚名 ¥ 20
111 | - 独孤一剑 ¥ 50
112 | - mxt ¥ 20
113 | - 一叶迷山 ¥ 100
114 | - Jeff ¥ 100
115 | - 八千多条狗🐶 ¥ 100
116 | - 晓峰 ¥ 10
117 | - 戒 ¥ 1
118 | - 浪里个浪 ¥ 1
119 | - 回调函数 ¥ 50
120 | - 赖瓜子 ¥ 5
121 | - Milo•J ¥ 20
122 | - 可道云 ¥ 200
123 | - *程 ¥ 10
124 | - 来一杯卡布酸奶 ¥ 5
--------------------------------------------------------------------------------
/src/demoData/sheetPivotTable.js:
--------------------------------------------------------------------------------
1 | window.sheetPivotTable = {
2 | "name": "PivotTable",
3 | "color": "",
4 | "config": {},
5 | "index": "7",
6 | "chart": [],
7 | "status": 0,
8 | "order": "7",
9 | "column": 18,
10 | "row": 36,
11 | "celldata": [{
12 | "r": 0,
13 | "c": 0,
14 | "v": "count:score"
15 | }, {
16 | "r": 0,
17 | "c": 1,
18 | "v": "science"
19 | }, {
20 | "r": 0,
21 | "c": 2,
22 | "v": "mathematics"
23 | }, {
24 | "r": 0,
25 | "c": 3,
26 | "v": "foreign language"
27 | }, {
28 | "r": 0,
29 | "c": 4,
30 | "v": "English"
31 | }, {
32 | "r": 0,
33 | "c": 5,
34 | "v": "total"
35 | }, {
36 | "r": 1,
37 | "c": 0,
38 | "v": "Alex"
39 | }, {
40 | "r": 1,
41 | "c": 1,
42 | "v": 1
43 | }, {
44 | "r": 1,
45 | "c": 2,
46 | "v": 1
47 | }, {
48 | "r": 1,
49 | "c": 3,
50 | "v": 1
51 | }, {
52 | "r": 1,
53 | "c": 4,
54 | "v": 1
55 | }, {
56 | "r": 1,
57 | "c": 5,
58 | "v": 4
59 | }, {
60 | "r": 2,
61 | "c": 0,
62 | "v": "Joy"
63 | }, {
64 | "r": 2,
65 | "c": 1,
66 | "v": 1
67 | }, {
68 | "r": 2,
69 | "c": 2,
70 | "v": 1
71 | }, {
72 | "r": 2,
73 | "c": 3,
74 | "v": 1
75 | }, {
76 | "r": 2,
77 | "c": 4,
78 | "v": 1
79 | }, {
80 | "r": 2,
81 | "c": 5,
82 | "v": 4
83 | }, {
84 | "r": 3,
85 | "c": 0,
86 | "v": "Tim"
87 | }, {
88 | "r": 3,
89 | "c": 1,
90 | "v": 1
91 | }, {
92 | "r": 3,
93 | "c": 2,
94 | "v": 1
95 | }, {
96 | "r": 3,
97 | "c": 3,
98 | "v": 1
99 | }, {
100 | "r": 3,
101 | "c": 4,
102 | "v": 1
103 | }, {
104 | "r": 3,
105 | "c": 5,
106 | "v": 4
107 | }, {
108 | "r": 4,
109 | "c": 0,
110 | "v": "total"
111 | }, {
112 | "r": 4,
113 | "c": 1,
114 | "v": 3
115 | }, {
116 | "r": 4,
117 | "c": 2,
118 | "v": 3
119 | }, {
120 | "r": 4,
121 | "c": 3,
122 | "v": 3
123 | }, {
124 | "r": 4,
125 | "c": 4,
126 | "v": 3
127 | }, {
128 | "r": 4,
129 | "c": 5,
130 | "v": 12
131 | }],
132 | "ch_width": 4748,
133 | "rh_height": 1790,
134 | "luckysheet_select_save": [{
135 | "row": [0, 0],
136 | "column": [0, 0]
137 | }],
138 | "luckysheet_selection_range": [],
139 | "scrollLeft": 0,
140 | "scrollTop": 0,
141 | "isPivotTable": true,
142 | "pivotTable": {
143 | "pivot_select_save": {
144 | "left": 0,
145 | "width": 73,
146 | "top": 0,
147 | "height": 19,
148 | "left_move": 0,
149 | "width_move": 369,
150 | "top_move": 0,
151 | "height_move": 259,
152 | "row": [0, 12],
153 | "column": [0, 4],
154 | "row_focus": 0,
155 | "column_focus": 0
156 | },
157 | "pivotDataSheetIndex": 6, //The sheet index where the source data is located
158 | "column": [{
159 | "index": 3,
160 | "name": "subject",
161 | "fullname": "subject"
162 | }],
163 | "row": [{
164 | "index": 1,
165 | "name": "student",
166 | "fullname": "student"
167 | }],
168 | "filter": [],
169 | "values": [{
170 | "index": 4,
171 | "name": "score",
172 | "fullname": "count:score",
173 | "sumtype": "COUNTA",
174 | "nameindex": 0
175 | }],
176 | "showType": "column",
177 | "pivotDatas": [
178 | ["count:score", "science", "mathematics", "foreign language", "English", "total"],
179 | ["Alex", 1, 1, 1, 1, 4],
180 | ["Joy", 1, 1, 1, 1, 4],
181 | ["Tim", 1, 1, 1, 1, 4],
182 | ["total", 3, 3, 3, 3, 12]
183 | ],
184 | "drawPivotTable": false,
185 | "pivotTableBoundary": [5, 6]
186 | }
187 | }
188 |
189 | // export default sheetPivotTable;
--------------------------------------------------------------------------------
/src/controllers/print.js:
--------------------------------------------------------------------------------
1 | import luckysheetConfigsetting from './luckysheetConfigsetting';
2 | import {zoomChange} from './zoom';
3 | import sheetmanage from './sheetmanage';
4 | import server from './server';
5 | import {rowLocationByIndex, colLocationByIndex,mouseposition,rowLocation,colLocation} from '../global/location';
6 | import Store from '../store';
7 |
8 | let ExcelPlaceholder = {
9 | "[tabName]":"&A",
10 | "[CurrentDate]":"&D",
11 | "[fileName]":"&F",
12 | "[background]":"&G",
13 | "[Shadow]":"&H",
14 | "[TotalPages]":"&N",
15 | "[pageNumber]":"&P",
16 | "[CurrentTime]":"&T",
17 | "[filePath]":"&Z",
18 | }
19 |
20 | // Get the pixel value per millimeter
21 | function getOneMmsPx (){
22 | let div = document.createElement("div");
23 | div.style.width = "1mm";
24 | document.querySelector("body").appendChild(div);
25 | let mm1 = div.getBoundingClientRect();
26 | let w = mm1.width;
27 | $(div).remove();
28 | return mm1.width;
29 | }
30 |
31 | export function viewChange(curType, preType){
32 | let currentSheet = sheetmanage.getSheetByIndex();
33 |
34 | if(currentSheet.config==null){
35 | currentSheet.config = {};
36 | }
37 |
38 | if(currentSheet.config.sheetViewZoom==null){
39 | currentSheet.config.sheetViewZoom = {};
40 | }
41 |
42 | let defaultZoom = 1, type="zoomScaleNormal";
43 | printLineAndNumberDelete(currentSheet);
44 | if(curType=="viewNormal"){
45 | type = "viewNormalZoomScale";
46 | }
47 | else if(curType=="viewLayout"){
48 | type = "viewLayoutZoomScale";
49 | }
50 | else if(curType=="viewPage"){
51 | type = "viewPageZoomScale";
52 | defaultZoom = 0.6;
53 | printLineAndNumberCreate(currentSheet);
54 | }
55 |
56 |
57 |
58 | let curZoom = currentSheet.config.sheetViewZoom[type];
59 | if(curZoom==null){
60 | curZoom = defaultZoom;
61 | }
62 |
63 | currentSheet.config.curentsheetView = curType;
64 |
65 | if (Store.clearjfundo) {
66 | Store.jfredo.push({
67 | "type": "viewChange",
68 | "curType": curType,
69 | "preType": preType,
70 | "sheetIndex": Store.currentSheetIndex,
71 | });
72 | }
73 |
74 | // Store.zoomRatio = curZoom;
75 | // server.saveParam("all", Store.currentSheetIndex, curZoom, { "k": "zoomRatio" });
76 | server.saveParam("cg", Store.currentSheetIndex, curType, { "k": "curentsheetView" });
77 |
78 | Store.currentSheetView = curType;
79 |
80 | zoomChange(curZoom);
81 | }
82 |
83 |
84 | function printLineAndNumberDelete(sheet){
85 |
86 | }
87 |
88 | function printLineAndNumberCreate(sheet){
89 |
90 | }
91 |
92 | function switchViewBtn($t){
93 | let $viewList = $t.parent(), preType=$viewList.find("luckysheet-print-viewBtn-active").attr("type");
94 | if($t.attr("type") == preType){
95 | return;
96 | }
97 |
98 | let curType = $t.attr("type");
99 | if(curType!=null){
100 | viewChange(curType, preType);
101 | }
102 | else{
103 | return;
104 | }
105 |
106 | $t.parent().find(".luckysheet-print-viewBtn").removeClass("luckysheet-print-viewBtn-active");
107 | $t.addClass("luckysheet-print-viewBtn-active");
108 | }
109 |
110 | export function printInitial(){
111 | let container = luckysheetConfigsetting.container;
112 | let _this = this;
113 | $("#"+container).find(".luckysheet-print-viewBtn").click(function(){
114 | switchViewBtn($(this));
115 | });
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The default luckysheet config object.
3 | */
4 | export default {
5 | container: "luckysheet", //容器的ID
6 | loading:{}, //自定义loading
7 | column: 60, //空表格默认的列数量
8 | row: 84, //空表格默认的行数据量
9 | allowCopy: true, //是否允许拷贝
10 | showtoolbar: true, //是否第二列显示工具栏
11 | showinfobar: true, //是否显示顶部名称栏
12 | showsheetbar: true, //是否显示底部表格名称区域
13 | showstatisticBar: true, //是否显示底部计数栏
14 | pointEdit: false, //是否是编辑器插入表格模式
15 | pointEditUpdate: null, //编辑器表格更新函数
16 | pointEditZoom: 1, //编辑器表格编辑时缩放比例
17 | // menu: "undo|redo|freezenrow|freezencolumn|download|share|chart|pivot",
18 | data: [{ "name": "Sheet1", color: "", "status": "1", "order": "0", "data": [], "config": {}, "index":0 }, { "name": "Sheet2", color: "", "status": "0", "order": "1", "data": [], "config": {}, "index":1 }, { "name": "Sheet3", color: "", "status": "0", "order": "2", "data": [], "config": {}, "index":2 }], //客户端sheet数据[sheet1, sheet2, sheet3]
19 | title: "Luckysheet Demo", //表格的名称
20 | userInfo:false,// 右上角的用户信息展示样式,支持 1. boolean类型:false:不展示,ture:展示默认 ' rabbit' ,2. HTML模板字符串或者普通字符串,如:' Lucky'或者'用户名', 3. 对象格式,设置 userImage:用户头像地址 和 userName:用户名 4. 不设置或者设置undefined同设置false
21 | userMenuItem: [{url:"www.baidu.com", "icon":'', "name":"我的表格"}, {url:"www.baidu.com", "icon":'', "name":"退出登陆"}], //点击右上角的用户信息弹出的菜单
22 | myFolderUrl: "www.baidu.com", //左上角<返回按钮的链接
23 | config: {}, //表格行高、列宽、合并单元格、公式等设置
24 | fullscreenmode: true, //是否全屏模式,非全屏模式下,标记框不会强制选中。
25 | devicePixelRatio: window.devicePixelRatio, //设备比例,比例越大表格分标率越高
26 | allowEdit: true, //是否允许前台编辑
27 | loadUrl: "", // 配置loadUrl的地址,luckysheet会通过ajax请求表格数据,默认载入status为1的sheet数据中的所有data,其余的sheet载入除data字段外的所有字段
28 | loadSheetUrl: "", //配置loadSheetUrl的地址,参数为gridKey(表格主键) 和 index(sheet主键合集,格式为[1,2,3]),返回的数据为sheet的data字段数据集合
29 | gridKey: "", // 表格唯一标识符
30 | updateUrl: "", //表格数据的更新地址
31 | updateImageUrl: "", //缩略图的更新地址
32 | allowUpdate: false, //是否允许编辑后的后台更新
33 | functionButton: "", //右上角功能按钮,例如' '
34 | showConfigWindowResize: true, //图表和数据透视表的配置会在右侧弹出,设置弹出后表格是否会自动缩进
35 | enableAddRow: true,//允许添加行
36 | enableAddBackTop: true,//允许回到顶部
37 | // enablePage: false,//允许加载下一页
38 | autoFormatw: false, //自动格式化超过4位数的数字为 亿万格式 例:true or "true" or "TRUE"
39 | accuracy: undefined, //设置传输来的数值的精确位数,小数点后n位 传参数为数字或数字字符串,例: "0" 或 0
40 | pageInfo:{
41 | 'queryExps':'',
42 | 'reportId':'',
43 | 'fields':'',
44 | 'mobile':'',
45 | 'frezon':'',
46 | 'currentPage':'',
47 | "totalPage":10,
48 | "pageUrl":"",
49 | },
50 | editMode: false, //是否为编辑模式
51 | beforeCreateDom: null,//表格创建之前的方法
52 | fireMousedown: null, //单元格数据下钻
53 | lang: 'en', //language
54 | plugins: [], //plugins, e.g. ['chart']
55 | forceCalculation:false,//强制刷新公式,公式较多会有性能问题,慎用
56 | rowHeaderWidth: 46,
57 | columnHeaderHeight: 20,
58 | defaultColWidth:73,
59 | defaultRowHeight:19,
60 | defaultFontSize:10,
61 | limitSheetNameLength:true, //是否限制工作表名的长度
62 | defaultSheetNameMaxLength:31, //默认工作表名称的最大长度
63 | sheetFormulaBar:true, //是否显示公式栏
64 | showtoolbarConfig:{}, //自定义工具栏
65 | showsheetbarConfig:{}, //自定义底部sheet页
66 | showstatisticBarConfig:{}, //自定义计数栏
67 | cellRightClickConfig:{}, //自定义单元格右键菜单
68 | sheetRightClickConfig:{}, //自定义底部sheet页右击菜单
69 | imageUpdateMethodConfig:{}, //自定义图片同步方式
70 | }
71 |
--------------------------------------------------------------------------------
/src/global/rhchInit.js:
--------------------------------------------------------------------------------
1 | import Store from '../store';
2 | import { computeRowlenByContent,computeColWidthByContent } from './getRowlen';
3 | import luckysheetConfigsetting from '../controllers/luckysheetConfigsetting';
4 |
5 | export default function rhchInit(rowheight, colwidth) {
6 | zoomSetting();//Zoom sheet on first load
7 | //行高
8 | if(rowheight != null){
9 | Store.visibledatarow = [];
10 | Store.rh_height = 0;
11 |
12 | for (let r = 0; r < rowheight; r++) {
13 | let rowlen = Store.defaultrowlen;
14 |
15 | if (Store.config["rowlen"] != null && Store.config["rowlen"][r] != null) {
16 | rowlen = Store.config["rowlen"][r];
17 | }
18 |
19 | if (Store.config["rowhidden"] != null && Store.config["rowhidden"][r] != null) {
20 | Store.visibledatarow.push(Store.rh_height);
21 | continue;
22 | }
23 |
24 | // 自动行高计算
25 | if (rowlen === 'auto') {
26 | rowlen = computeRowlenByContent(Store.flowdata, r);
27 | }
28 | Store.rh_height += Math.round((rowlen + 1) * Store.zoomRatio);
29 |
30 | Store.visibledatarow.push(Store.rh_height); //行的临时长度分布
31 | }
32 |
33 | // 如果增加行和回到顶部按钮隐藏,则减少底部空白区域,但是预留足够空间给单元格下拉按钮
34 | if (!luckysheetConfigsetting.enableAddRow && !luckysheetConfigsetting.enableAddBackTop) {
35 | Store.rh_height += 29;
36 | } else {
37 | Store.rh_height += 80; //最底部增加空白
38 | }
39 |
40 | }
41 |
42 | //列宽
43 | if(colwidth != null){
44 | Store.visibledatacolumn = [];
45 | Store.ch_width = 0;
46 |
47 | let maxColumnlen = 120;
48 |
49 | for (let c = 0; c < colwidth; c++) {
50 | let firstcolumnlen = Store.defaultcollen;
51 |
52 | if (Store.config["columnlen"] != null && Store.config["columnlen"][c] != null) {
53 | firstcolumnlen = Store.config["columnlen"][c];
54 | }
55 | else {
56 | if (Store.flowdata[0] != null && Store.flowdata[0][c] != null) {
57 | if (firstcolumnlen > 300) {
58 | firstcolumnlen = 300;
59 | }
60 | else if (firstcolumnlen < Store.defaultcollen) {
61 | firstcolumnlen = Store.defaultcollen;
62 | }
63 |
64 | if (firstcolumnlen != Store.defaultcollen) {
65 | if (Store.config["columnlen"] == null) {
66 | Store.config["columnlen"] = {};
67 | }
68 |
69 | Store.config["columnlen"][c] = firstcolumnlen;
70 | }
71 | }
72 | }
73 |
74 | if(Store.config["colhidden"] != null && Store.config["colhidden"][c] != null){
75 | Store.visibledatacolumn.push(Store.ch_width);
76 | continue;
77 | }
78 |
79 | // 自动行高计算
80 | if (firstcolumnlen === 'auto') {
81 | firstcolumnlen = computeColWidthByContent(Store.flowdata, c, rowheight);
82 | }
83 | Store.ch_width += Math.round((firstcolumnlen + 1)*Store.zoomRatio);
84 |
85 | Store.visibledatacolumn.push(Store.ch_width);//列的临时长度分布
86 |
87 | // if(maxColumnlen < firstcolumnlen + 1){
88 | // maxColumnlen = firstcolumnlen + 1;
89 | // }
90 | }
91 |
92 | // Store.ch_width += 120;
93 | Store.ch_width += maxColumnlen;
94 | }
95 | }
96 |
97 |
98 | export function zoomSetting(){
99 | //zoom
100 | Store.rowHeaderWidth = luckysheetConfigsetting.rowHeaderWidth * Store.zoomRatio;
101 | Store.columnHeaderHeight = luckysheetConfigsetting.columnHeaderHeight *Store.zoomRatio;
102 | $("#luckysheet-rows-h").width((Store.rowHeaderWidth-1.5));
103 | $("#luckysheet-cols-h-c").height((Store.columnHeaderHeight-1.5));
104 | $("#luckysheet-left-top").css({width:Store.rowHeaderWidth-1.5, height:Store.columnHeaderHeight-1.5});
105 | }
106 |
--------------------------------------------------------------------------------
/src/controllers/cellDatePickerCtrl.js:
--------------------------------------------------------------------------------
1 | import menuButton from './menuButton';
2 | import formula from '../global/formula';
3 | import Store from '../store';
4 | import flatpickr from 'flatpickr'
5 | import dayjs from "dayjs";
6 | import { update, datenum_local } from '../global/format';
7 | import { setCellValue, setCellFormat } from '../global/api';
8 |
9 | const fitFormat = (formatStr) => {
10 | let dateFormat = formatStr.replace(/y/g, 'Y');
11 | dateFormat = dateFormat.replace(/d/g, 'D');
12 | dateFormat = dateFormat.replace(/h/g, 'H');
13 |
14 | dateFormat = dateFormat.replace(/上午\/下午/g, 'A');
15 | dateFormat = dateFormat.replace(/上午/g, 'A');
16 | dateFormat = dateFormat.replace(/下午/g, 'A');
17 |
18 | dateFormat = dateFormat.replace(/AM\/PM/g, 'A');
19 | dateFormat = dateFormat.replace(/AM/g, 'A');
20 | dateFormat = dateFormat.replace(/PM/g, 'A');
21 | dateFormat = dateFormat.replace(/\"/g, '');
22 |
23 | if (dateFormat.includes('A')) {
24 | dateFormat = dateFormat.replace(/H/g, 'h');
25 | }
26 | return dateFormat
27 | }
28 |
29 | const cellDatePickerCtrl = {
30 | cellFocus: function (r, c, cell) {
31 | let row = Store.visibledatarow[r],
32 | row_pre = r == 0 ? 0 : Store.visibledatarow[r - 1];
33 | let col = Store.visibledatacolumn[c],
34 | col_pre = c == 0 ? 0 : Store.visibledatacolumn[c - 1];
35 |
36 | let margeset = menuButton.mergeborer(Store.flowdata, r, c);
37 | let type = cell.ct.fa || 'YYYY-MM-DD';
38 | let defaultDate = update('yyyy-MM-dd hh:mm:ss', cell.v);
39 | let dateFormat = fitFormat(type);
40 | let enableTime = false;
41 | let noCalendar = false;
42 | let enableSeconds = false;
43 | let time_24hr = true;
44 | let hasChineseTime = false;
45 |
46 |
47 | if (!!margeset) {
48 | row = margeset.row[1];
49 | row_pre = margeset.row[0];
50 |
51 | col = margeset.column[1];
52 | col_pre = margeset.column[0];
53 | }
54 |
55 | $(".cell-date-picker").show().css({
56 | width: col - col_pre + 1,
57 | height: row - row_pre + 1,
58 | left: col_pre,
59 | top: row_pre
60 | })
61 |
62 | if (/[上午下午]/.test(type)) {
63 | hasChineseTime = true
64 | }
65 | if (/[Hhms]/.test(dateFormat)) {
66 | enableTime = true;
67 | }
68 | if (!/[YMD]/.test(dateFormat)) {
69 | noCalendar = true;
70 | }
71 | if (/s/.test(dateFormat)) {
72 | enableSeconds = true;
73 | }
74 | if (/A/.test(dateFormat)) {
75 | time_24hr = false;
76 | }
77 |
78 | const fp = flatpickr('#luckysheet-input-box', {
79 | allowInput: false,
80 | noCalendar,
81 | enableSeconds,
82 | enableTime,
83 | dateFormat,
84 | time_24hr,
85 | defaultDate,
86 | onClose() {
87 | setTimeout(() => {
88 | fp.destroy()
89 | }, 0);
90 | },
91 | parseDate: (datestr, format) => {
92 | return dayjs(datestr).toDate();
93 | },
94 | formatDate: (date, format, locale) => {
95 | if (hasChineseTime) {
96 | return dayjs(date).format(format).replace('AM', '上午').replace('PM', '下午')
97 | }
98 | return dayjs(date).format(format);
99 | },
100 | onChange: function (selectedDates, dateStr) {
101 | let currentVal = datenum_local(new Date(selectedDates))
102 | $("#luckysheet-rich-text-editor").html(dateStr);
103 | setCellValue(r, c, currentVal, { isRefresh: false })
104 | setCellFormat(r, c, 'ct', cell.ct)
105 | if (!enableTime) {
106 | formula.updatecell(Store.luckysheetCellUpdate[0], Store.luckysheetCellUpdate[1]);
107 | }
108 | }
109 | });
110 |
111 | $("#luckysheet-input-box").click();
112 | },
113 | }
114 |
115 | export default cellDatePickerCtrl;
116 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | base: '/LuckysheetDocs/',
3 | locales: {
4 | // 键名是该语言所属的子路径
5 | // 作为特例,默认语言可以使用 '/' 作为其路径。
6 | '/': {
7 | lang: 'en-US', // 将会被设置为 的 lang 属性
8 | title: 'Luckysheet Document',
9 | description: 'Luckysheet is an online spreadsheet like excel that is powerful, simple to configure, and completely open source.This site contains official configuration document, API, and tutorial.'
10 | },
11 | '/zh/': {
12 | lang: 'zh-CN',
13 | title: 'Luckysheet文档',
14 | description: 'Luckysheet ,一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。本站包含官方配置文档,API,教程。'
15 | },
16 |
17 | },
18 | themeConfig: {
19 | domain: 'https://mengshukeji.github.io/LuckysheetDemo',
20 | logo: '/img/logo.png',
21 | author: 'Luckysheet',
22 | // 仓库地址
23 | repo: 'mengshukeji/Luckysheet',
24 | // 允许编辑链接文字
25 | editLinks: true,
26 | // 仓库的文档目录
27 | docsDir: 'docs',
28 | // 页面滚动
29 | smoothScroll: true,
30 | locales: {
31 | '/': {
32 | selectText: 'Languages',
33 | label: 'English',
34 | ariaLabel: 'Select language',
35 | editLinkText: 'Edit this page on GitHub',
36 | lastUpdated: 'Last Updated',
37 | serviceWorker: {
38 | updatePopup: {
39 | message: "New content is available.",
40 | buttonText: "Refresh"
41 | }
42 | },
43 | nav: [
44 | { text: 'Home', link: '/' },
45 | { text: 'Guide', link: '/guide/' },
46 | { text: 'Demo', link: 'https://mengshukeji.github.io/LuckysheetDemo/' },
47 | {
48 | text: 'More',
49 | ariaLabel: 'More',
50 | items: [
51 | { text: 'About', link: '/about/' }
52 | ]
53 | },
54 | ],
55 | // 侧边栏
56 | sidebar: {
57 | '/guide/': [
58 | '',
59 | 'config',
60 | 'sheet',
61 | 'cell',
62 | 'operate',
63 | 'api',
64 | 'resource',
65 | 'FAQ',
66 | 'contribute'
67 | ],
68 | '/about/': [
69 | '',
70 | 'sponsor',
71 | 'company'
72 | ],
73 | },
74 | },
75 | '/zh/': {
76 | // 多语言下拉菜单的标题
77 | selectText: '选择语言',
78 | // 该语言在下拉菜单中的标签
79 | label: '简体中文',
80 | ariaLabel: '选择语言',
81 | // 编辑链接文字
82 | editLinkText: '在 GitHub 上编辑此页',
83 | lastUpdated: '上次更新',
84 | // Service Worker 的配置
85 | serviceWorker: {
86 | updatePopup: {
87 | message: "发现新内容可用.",
88 | buttonText: "刷新"
89 | }
90 | },
91 | // 导航栏
92 | nav: [
93 | { text: '首页', link: '/zh/' },
94 | { text: '指南', link: '/zh/guide/' },
95 | { text: '演示', link: 'https://mengshukeji.github.io/LuckysheetDemo/' },
96 | {
97 | text: '了解更多',
98 | ariaLabel: '了解更多',
99 | items: [
100 | { text: '关于', link: '/zh/about/' }
101 | ]
102 | },
103 | ],
104 | // 侧边栏
105 | sidebar: {
106 | '/zh/guide/': [
107 | '',
108 | 'config',
109 | 'sheet',
110 | 'cell',
111 | 'operate',
112 | 'api',
113 | 'resource',
114 | 'FAQ',
115 | 'contribute'
116 | ],
117 | '/zh/about/': [
118 | '',
119 | 'sponsor',
120 | 'company'
121 | ],
122 | },
123 | },
124 |
125 | },
126 | },
127 | plugins: {
128 | 'vuepress-plugin-baidu-autopush': {},
129 | 'sitemap': {
130 | hostname: 'https://mengshukeji.github.io/LuckysheetDocs'
131 | },
132 | 'vuepress-plugin-code-copy': true,
133 | 'seo': {
134 | siteTitle: (_, $site) => $site.title,
135 | title: $page => $page.title,
136 | description: $page => $page.frontmatter.description,
137 | author: (_, $site) => $site.themeConfig.author,
138 | tags: $page => $page.frontmatter.tags,
139 | twitterCard: _ => 'summary_large_image',
140 | type: $page => ['guide'].some(folder => $page.regularPath.startsWith('/' + folder)) ? 'article' : 'website',
141 | url: (_, $site, path) => ($site.themeConfig.domain || '') + path,
142 | image: ($page, $site) => $page.frontmatter.image && (($site.themeConfig.domain && !$page.frontmatter.image.startsWith('http') || '') + $page.frontmatter.image),
143 | publishedAt: $page => $page.frontmatter.date && new Date($page.frontmatter.date),
144 | modifiedAt: $page => $page.lastUpdated && new Date($page.lastUpdated),
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/global/scroll.js:
--------------------------------------------------------------------------------
1 | import luckysheetFreezen from '../controllers/freezen';
2 | import { luckysheet_searcharray } from '../controllers/sheetSearch';
3 | import { luckysheetrefreshgrid } from '../global/refresh';
4 | import Store from '../store';
5 | import method from '../global/method'
6 |
7 | let scrollRequestAnimationFrameIni = true,scrollRequestAnimationFrame = false, scrollTimeOutCancel=null;
8 |
9 | function execScroll(){
10 | let scrollLeft = $("#luckysheet-scrollbar-x").scrollLeft(),
11 | scrollTop = $("#luckysheet-scrollbar-y").scrollTop();
12 | luckysheetrefreshgrid(scrollLeft, scrollTop);
13 | scrollRequestAnimationFrame = window.requestAnimationFrame(execScroll);
14 | }
15 |
16 | //全局滚动事件
17 | export default function luckysheetscrollevent(isadjust) {
18 | let $t = $("#luckysheet-cell-main");
19 | let scrollLeft = $("#luckysheet-scrollbar-x").scrollLeft(),
20 | scrollTop = $("#luckysheet-scrollbar-y").scrollTop(),
21 | canvasHeight = $("#luckysheetTableContent").height(); // canvas高度
22 |
23 | // clearTimeout(scrollTimeOutCancel);
24 |
25 | // scrollTimeOutCancel = setTimeout(() => {
26 | // scrollRequestAnimationFrameIni = true;
27 | // window.cancelAnimationFrame(scrollRequestAnimationFrame);
28 | // }, 500);
29 |
30 | // if (!!isadjust) {
31 | // let scrollHeight = $t.get(0).scrollHeight;
32 | // let windowHeight = $t.height();
33 | // let scrollWidth = $t.get(0).scrollWidth;
34 | // let windowWidth = $t.width();
35 |
36 | // let maxScrollLeft = scrollWidth - windowWidth;
37 | // let maxScrollTop = scrollHeight - windowHeight;
38 |
39 | // let visibledatacolumn_c = Store.visibledatacolumn, visibledatarow_c = Store.visibledatarow;
40 |
41 | // if (luckysheetFreezen.freezenhorizontaldata != null) {
42 | // visibledatarow_c = luckysheetFreezen.freezenhorizontaldata[3];
43 | // }
44 |
45 | // if (luckysheetFreezen.freezenverticaldata != null) {
46 | // visibledatacolumn_c = luckysheetFreezen.freezenverticaldata[3];
47 | // }
48 |
49 | // let col_ed = luckysheet_searcharray(visibledatacolumn_c, scrollLeft);
50 | // let row_ed = luckysheet_searcharray(visibledatarow_c, scrollTop);
51 |
52 | // let refreshLeft = scrollLeft , refreshTop = scrollTop;
53 |
54 | // if (col_ed <= 0) {
55 | // scrollLeft = 0;
56 | // }
57 | // else {
58 | // scrollLeft = visibledatacolumn_c[col_ed - 1];
59 | // }
60 |
61 | // if (row_ed <= 0) {
62 | // scrollTop = 0;
63 | // }
64 | // else {
65 | // scrollTop = visibledatarow_c[row_ed - 1];
66 | // }
67 | // }
68 |
69 | if (luckysheetFreezen.freezenhorizontaldata != null) {
70 | if (scrollTop < luckysheetFreezen.freezenhorizontaldata[2]) {
71 | scrollTop = luckysheetFreezen.freezenhorizontaldata[2];
72 | $("#luckysheet-scrollbar-y").scrollTop(scrollTop);
73 | return;
74 | }
75 | }
76 |
77 | if (luckysheetFreezen.freezenverticaldata != null) {
78 | if (scrollLeft < luckysheetFreezen.freezenverticaldata[2]) {
79 | scrollLeft = luckysheetFreezen.freezenverticaldata[2];
80 | $("#luckysheet-scrollbar-x").scrollLeft(scrollLeft);
81 | return;
82 | }
83 | }
84 |
85 | $("#luckysheet-cols-h-c").scrollLeft(scrollLeft);//列标题
86 | $("#luckysheet-rows-h").scrollTop(scrollTop);//行标题
87 |
88 | $t.scrollLeft(scrollLeft).scrollTop(scrollTop);
89 |
90 | $("#luckysheet-input-box-index").css({
91 | "left": $("#luckysheet-input-box").css("left"),
92 | "top": (parseInt($("#luckysheet-input-box").css("top")) - 20) + "px",
93 | "z-index": $("#luckysheet-input-box").css("z-index")
94 | }).show();
95 |
96 | // if(scrollRequestAnimationFrameIni && Store.scrollRefreshSwitch){
97 | // execScroll();
98 | // scrollRequestAnimationFrameIni = false;
99 | // }
100 |
101 | luckysheetrefreshgrid(scrollLeft, scrollTop);
102 |
103 |
104 | $("#luckysheet-bottom-controll-row").css("left", scrollLeft);
105 |
106 | //有选区且有冻结时,滚动适应
107 | if(luckysheetFreezen.freezenhorizontaldata != null || luckysheetFreezen.freezenverticaldata != null){
108 | luckysheetFreezen.scrollAdapt();
109 | }
110 |
111 | if(!method.createHookFunction("scroll", {scrollLeft, scrollTop, canvasHeight})){ return; }
112 |
113 | }
--------------------------------------------------------------------------------
/src/utils/math.js:
--------------------------------------------------------------------------------
1 | import { isRealNum } from '../global/validate';
2 |
3 | /*
4 | * 判断obj是否为一个整数
5 | */
6 | function isInteger(obj) {
7 | return Math.floor(obj) === obj;
8 | }
9 |
10 | /*
11 | * 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
12 | * @param floatNum {number} 小数
13 | * @return {object}
14 | * {times:100, num: 314}
15 | */
16 | function toInteger(floatNum) {
17 | var ret = { times: 1, num: 0 };
18 |
19 | if (isInteger(floatNum)) {
20 | ret.num = floatNum;
21 | return ret;
22 | }
23 |
24 | var strfi = floatNum + '';
25 | var dotPos = strfi.indexOf('.');
26 | var len = strfi.substr(dotPos + 1).length;
27 | var times = Math.pow(10, len);
28 | var intNum = parseInt(floatNum * times + 0.5, 10);
29 |
30 | ret.times = times;
31 | ret.num = intNum;
32 |
33 | return ret;
34 | }
35 |
36 | /*
37 | * 核心方法,实现加减乘除运算,确保不丢失精度
38 | * 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
39 | *
40 | * @param a {number} 运算数1
41 | * @param b {number} 运算数2
42 | * @param digits {number} 精度,保留的小数点数,比如 2, 即保留为两位小数
43 | * @param op {string} 运算类型,有加减乘除(add/subtract/multiply/divide)
44 | *
45 | */
46 | function operation(a, b, op) {
47 | var o1 = toInteger(a);
48 | var o2 = toInteger(b);
49 | var n1 = o1.num;
50 | var n2 = o2.num;
51 | var t1 = o1.times;
52 | var t2 = o2.times;
53 | var max = t1 > t2 ? t1 : t2;
54 | var result = null;
55 |
56 | switch (op) {
57 | case 'add':
58 | if (t1 === t2) { // 两个小数位数相同
59 | result = n1 + n2;
60 | }
61 | else if (t1 > t2) { // o1 小数位 大于 o2
62 | result = n1 + n2 * (t1 / t2);
63 | }
64 | else { // o1 小数位 小于 o2
65 | result = n1 * (t2 / t1) + n2;
66 | }
67 |
68 | return result / max;
69 | case 'subtract':
70 | if (t1 === t2) {
71 | result = n1 - n2;
72 | }
73 | else if (t1 > t2) {
74 | result = n1 - n2 * (t1 / t2);
75 | }
76 | else {
77 | result = n1 * (t2 / t1) - n2;
78 | }
79 |
80 | return result / max;
81 | case 'multiply':
82 | result = (n1 * n2) / (t1 * t2);
83 |
84 | return result;
85 | case 'divide':
86 | return result = function () {
87 | var r1 = n1 / n2;
88 | var r2 = t2 / t1;
89 | return operation(r1, r2, 'multiply');
90 | }();
91 | }
92 | }
93 | /**
94 | * 做小数点的四舍五入计算
95 | * @param {*} num
96 | * @param {*} precision
97 | */
98 | function fixed(num, precision) {
99 | if (!precision) {
100 | precision = 2;
101 | }
102 | if (!isRealNum(num)) return num;
103 | let s = num.toFixed(precision);
104 | let index = s.indexOf('.');
105 | let prefix = s.substring(0, index);
106 | let suffix = s.substring(index + 1, s.length);
107 | if (suffix) {
108 | for (let i = suffix.length - 1; i != 0; i--) {
109 | //最末位不为0,直接break;
110 | if (suffix.charAt(i) != '0' && i == suffix.length - 1) {
111 | break;
112 | } else {
113 | suffix = suffix.substring(0, i);
114 | }
115 | }
116 | }
117 | return Number(prefix + '.' + suffix);
118 | }
119 |
120 |
121 | /**
122 | * Calculation +-/* Solve the problem of js accuracy
123 | */
124 | Number.prototype.add = function (value) {
125 | let number = parseFloat(value);
126 | if (typeof number !== 'number' || Number.isNaN(number)) {
127 | throw new Error('请输入数字或者数字字符串~');
128 | };
129 | return operation(this, number, 'add');
130 | };
131 | Number.prototype.subtract = function (value) {
132 | let number = parseFloat(value);
133 | if (typeof number !== 'number' || Number.isNaN(number)) {
134 | throw new Error('请输入数字或者数字字符串~');
135 | }
136 | return operation(this, number, 'subtract');
137 | };
138 | Number.prototype.multiply = function (value) {
139 | let number = parseFloat(value);
140 | if (typeof number !== 'number' || Number.isNaN(number)) {
141 | throw new Error('请输入数字或者数字字符串~');
142 | }
143 | return operation(this, number, 'multiply');
144 | };
145 | Number.prototype.divide = function (value) {
146 | let number = parseFloat(value);
147 | if (typeof number !== 'number' || Number.isNaN(number)) {
148 | throw new Error('请输入数字或者数字字符串~');
149 | }
150 | return operation(this, number, 'divide');
151 | };
152 | Number.prototype.tofixed = function (value) {
153 | let precision = parseFloat(value);
154 | if (typeof precision !== 'number' || Number.isNaN(precision)) {
155 | throw new Error('请输入数字或者数字字符串~');
156 | }
157 | return fixed(this, precision);
158 | };
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | const Store = {
2 | container: null,
3 | loadingObj:{},
4 | luckysheetfile: null,
5 | defaultcolumnNum: 60,
6 | defaultrowNum: 84,
7 | fullscreenmode: true,
8 | devicePixelRatio: 1,
9 |
10 | currentSheetIndex: 0,
11 | calculateSheetIndex: 0,
12 | flowdata: [],
13 | config: {},
14 |
15 | visibledatarow: [],
16 | visibledatacolumn: [],
17 | ch_width: 0,
18 | rh_height: 0,
19 |
20 | cellmainWidth: 0,
21 | cellmainHeight: 0,
22 | toolbarHeight: 0,
23 | infobarHeight: 0,
24 | calculatebarHeight: 0,
25 | rowHeaderWidth: 46,
26 | columnHeaderHeight: 20,
27 | cellMainSrollBarSize: 12,
28 | sheetBarHeight: 31,
29 | statisticBarHeight: 23,
30 | luckysheetTableContentHW: [0, 0],
31 |
32 | defaultcollen: 73,
33 | defaultrowlen: 19,
34 |
35 | jfcountfuncTimeout: null,
36 | jfautoscrollTimeout: null,
37 |
38 | luckysheet_select_status: false,
39 | luckysheet_select_save: [{ "row": [0, 0], "column": [0, 0] }],
40 | luckysheet_selection_range: [],
41 |
42 | luckysheet_copy_save: {}, //复制粘贴
43 | luckysheet_paste_iscut: false,
44 |
45 | filterchage: true, //筛选
46 | luckysheet_filter_save: { "row": [], "column": [] },
47 |
48 | luckysheet_sheet_move_status: false,
49 | luckysheet_sheet_move_data: [],
50 | luckysheet_scroll_status: false,
51 |
52 | luckysheetisrefreshdetail: true,
53 | luckysheetisrefreshtheme: true,
54 | luckysheetcurrentisPivotTable: false,
55 |
56 | luckysheet_rows_selected_status: false, //行列标题相关参
57 | luckysheet_cols_selected_status: false,
58 | luckysheet_rows_change_size: false,
59 | luckysheet_rows_change_size_start: [],
60 | luckysheet_cols_change_size: false,
61 | luckysheet_cols_change_size_start: [],
62 | luckysheet_cols_dbclick_timeout: null,
63 | luckysheet_cols_dbclick_times: 0,
64 |
65 | luckysheetCellUpdate: [],
66 |
67 | luckysheet_shiftpositon: null,
68 |
69 | iscopyself: true,
70 |
71 | orderbyindex: 0, //排序下标
72 |
73 | luckysheet_model_move_state: false, //模态框拖动
74 | luckysheet_model_xy: [0, 0],
75 | luckysheet_model_move_obj: null,
76 |
77 | luckysheet_cell_selected_move: false, //选区拖动替换
78 | luckysheet_cell_selected_move_index: [],
79 |
80 | luckysheet_cell_selected_extend: false, //选区下拉
81 | luckysheet_cell_selected_extend_index: [],
82 | luckysheet_cell_selected_extend_time: null,
83 |
84 | clearjfundo: true,
85 | jfundo: [],
86 | jfredo: [],
87 | lang: 'en', //language
88 | createChart: '',
89 | highlightChart: '',
90 | zIndex: 15,
91 | chartparam: {
92 | luckysheetCurrentChart: null, //current chart_id
93 | luckysheetCurrentChartActive: false,
94 | luckysheetCurrentChartMove: null, // Debounce state
95 | luckysheetCurrentChartMoveTimeout: null,//拖动图表框的节流定时器
96 | luckysheetCurrentChartMoveObj: null, //chart DOM object
97 | luckysheetCurrentChartMoveXy: null, //上一次操作结束的图表信息,x,y: chart框位置,scrollLeft1,scrollTop1: 滚动条位置
98 | luckysheetCurrentChartMoveWinH: null, //左右滚动条滑动距离
99 | luckysheetCurrentChartMoveWinW: null, //上下滚动条滑动距离
100 | luckysheetCurrentChartResize: null,
101 | luckysheetCurrentChartResizeObj: null,
102 | luckysheetCurrentChartResizeXy: null,
103 | luckysheetCurrentChartResizeWinH: null,
104 | luckysheetCurrentChartResizeWinW: null,
105 | luckysheetInsertChartTosheetChange: true, // 正在执行撤销
106 | luckysheetCurrentChartZIndexRank : 100,
107 | luckysheet_chart_redo_click:false, //撤销重做时标识
108 | luckysheetCurrentChartMaxState: false, //图表全屏状态
109 | jfrefreshchartall: '',
110 | changeChartCellData: '',
111 | renderChart: '',
112 | getChartJson: ''
113 | },
114 | functionList:null, //function list explanation
115 | luckysheet_function:null,
116 | chart_selection: {},
117 | currentChart: '',
118 | scrollRefreshSwitch:true,
119 |
120 | measureTextCache:{},
121 | measureTextCellInfoCache:{},
122 | measureTextCacheTimeOut:null,
123 | cellOverflowMapCache:{},
124 |
125 | zoomRatio:1,
126 |
127 | visibledatacolumn_unique:null,
128 | visibledatarow_unique:null,
129 |
130 | showGridLines:true,
131 |
132 | toobarObject: {}, //toolbar constant
133 | inlineStringEditCache:null,
134 | inlineStringEditRange:null,
135 |
136 | fontList:[],
137 | defaultFontSize: 10,
138 |
139 | currentSheetView:"viewNormal",
140 |
141 | // cooperative editing
142 | cooperativeEdit:{
143 | usernameTimeout:{
144 |
145 | },
146 | changeCollaborationSize:[], //改变行高或者列宽时,协同提示框需要跟随改变所需数据
147 | allDataColumnlen:[],//列宽发生过改变的列
148 | merge_range:{},//合并时单元格信息
149 | checkoutData:[],//切换表格页时所需数据
150 | },
151 |
152 | // Resources that currently need to be loaded asynchronously, especially plugins. 'Core' marks the core rendering process.
153 | asyncLoad:['core'],
154 | // 默认单元格
155 | defaultCell: {
156 | bg: null,
157 | bl: 0,
158 | ct: {fa: "General", t: "n"},
159 | fc: "rgb(51, 51, 51)",
160 | ff: 0,
161 | fs: 11,
162 | ht: 1,
163 | it: 0,
164 | vt: 1,
165 | m: '',
166 | v: ''
167 | }
168 |
169 | }
170 |
171 | export default Store;
--------------------------------------------------------------------------------
/src/global/dynamicArray.js:
--------------------------------------------------------------------------------
1 | import { getObjType } from '../utils/util';
2 | import { getSheetIndex } from '../methods/get';
3 | import Store from '../store';
4 |
5 | //动态数组计算
6 | function dynamicArrayCompute(dynamicArray) {
7 | let dynamicArray_compute = {};
8 |
9 | if(getObjType(dynamicArray) == "array"){
10 | for(let i = 0; i < dynamicArray.length; i++){
11 | let d_row = dynamicArray[i].r;
12 | let d_col = dynamicArray[i].c;
13 | let d_f = dynamicArray[i].f;
14 |
15 | if(Store.flowdata[d_row][d_col] != null && Store.flowdata[d_row][d_col].f != null && Store.flowdata[d_row][d_col].f == d_f){
16 | if((d_row + "_" + d_col) in dynamicArray_compute){
17 | dynamicArray_compute = dynamicArraySpillEditCompute(dynamicArray_compute, d_row , d_col);
18 | }
19 |
20 | let d_data = dynamicArray[i].data;
21 | let d_rowlen = d_data.length;
22 | let d_collen = 1;
23 |
24 | if(getObjType(d_data[0]) == "array"){
25 | d_collen = d_data[0].length;
26 | }
27 |
28 | if(dynamicArrayRangeIsAllNull({ "row": [d_row, d_row + d_rowlen - 1], "column": [d_col, d_col + d_collen - 1] }, Store.flowdata)){
29 | for(let x = 0; x < d_rowlen; x++){
30 | for(let y = 0; y < d_collen; y++){
31 | let rowIndex = d_row + x;
32 | let colIndex = d_col + y;
33 |
34 | if(getObjType(d_data[0]) == "array"){
35 | dynamicArray_compute[rowIndex + "_" + colIndex] = {"v": d_data[x][y], "r": d_row, "c": d_col};
36 | }
37 | else{
38 | dynamicArray_compute[rowIndex + "_" + colIndex] = {"v": d_data[x], "r": d_row, "c": d_col};
39 | }
40 | }
41 | }
42 | }
43 | else{
44 | dynamicArray_compute[d_row + "_" + d_col] = {"v": "#SPILL!", "r": d_row, "c": d_col};
45 | }
46 | }
47 | }
48 | }
49 |
50 | return dynamicArray_compute;
51 | }
52 |
53 | function dynamicArraySpillEditCompute(computeObj, r, c) {
54 | let rowIndex = computeObj[r + "_" + c].r;
55 | let colIndex = computeObj[r + "_" + c].c;
56 |
57 | for(let x in computeObj){
58 | if(x == (rowIndex + "_" + colIndex)){
59 | computeObj[x].v = "#SPILL!";
60 | }
61 | else if(computeObj[x].r == rowIndex && computeObj[x].c == colIndex){
62 | delete computeObj[x];
63 | }
64 | }
65 |
66 | return computeObj;
67 | }
68 |
69 | //范围是否都是空单元格(除第一个单元格)
70 | function dynamicArrayRangeIsAllNull(range, data) {
71 | let r1 = range["row"][0], r2 = range["row"][1];
72 | let c1 = range["column"][0], c2 = range["column"][1];
73 |
74 | let isAllNull = true;
75 | for(let r = r1; r <= r2; r++){
76 | for(let c = c1; c <= c2; c++){
77 | if(!(r == r1 && c == c1) && data[r][c] != null && data[r][c].v != null && data[r][c].v.toString() != ""){
78 | isAllNull = false;
79 | break;
80 | }
81 | }
82 | }
83 |
84 | return isAllNull;
85 | }
86 |
87 | //点击表格区域是否属于动态数组区域
88 | function dynamicArrayHightShow(r, c) {
89 | let dynamicArray = Store.luckysheetfile[getSheetIndex(Store.currentSheetIndex)]["dynamicArray"] == null ? [] : Store.luckysheetfile[getSheetIndex(Store.currentSheetIndex)]["dynamicArray"];
90 | let dynamicArray_compute = dynamicArrayCompute(dynamicArray);
91 |
92 | if((r + "_" + c) in dynamicArray_compute && dynamicArray_compute[r + "_" + c].v != "#SPILL!"){
93 | let d_row = dynamicArray_compute[r + "_" + c].r;
94 | let d_col = dynamicArray_compute[r + "_" + c].c;
95 |
96 | let d_f = Store.flowdata[d_row][d_col].f;
97 |
98 | let rlen, clen;
99 | for(let i = 0; i < dynamicArray.length; i++){
100 | if(dynamicArray[i].f == d_f){
101 | rlen = dynamicArray[i].data.length;
102 |
103 | if(getObjType(dynamicArray[i].data[0]) == "array"){
104 | clen = dynamicArray[i].data[0].length;
105 | }
106 | else{
107 | clen = 1;
108 | }
109 | }
110 | }
111 |
112 | let d_row_end = d_row + rlen - 1;
113 | let d_col_end = d_col + clen - 1;
114 |
115 | let row = Store.visibledatarow[d_row_end],
116 | row_pre = d_row - 1 == -1 ? 0 : Store.visibledatarow[d_row - 1];
117 | let col = Store.visibledatacolumn[d_col_end],
118 | col_pre = d_col - 1 == -1 ? 0 : Store.visibledatacolumn[d_col - 1];
119 |
120 | $("#luckysheet-dynamicArray-hightShow").css({
121 | "left": col_pre,
122 | "width": col - col_pre - 1,
123 | "top": row_pre,
124 | "height": row - row_pre - 1,
125 | "display": "block"
126 | });
127 | }
128 | else{
129 | $("#luckysheet-dynamicArray-hightShow").hide();
130 | }
131 | }
132 |
133 | export {
134 | dynamicArrayCompute,
135 | dynamicArraySpillEditCompute,
136 | dynamicArrayRangeIsAllNull,
137 | dynamicArrayHightShow,
138 | }
--------------------------------------------------------------------------------
/docs/zh/guide/contribute.md:
--------------------------------------------------------------------------------
1 |
2 | # 贡献指南
3 |
4 | 欢迎!我们很高兴您能来到这里,并非常期待您能有兴趣参与 Luckysheet 贡献。当然,在您参与 Luckysheet 贡献之前,请确保通读以下全文:
5 |
6 | ## 我们的行为准则
7 |
8 | 1. 我们保证尊重所有参与贡献的人,不限于提出问题、文档和代码贡献、解决bug以及其它贡献的人;
9 |
10 | 2. 我们有义务遵守当地法律法规,所有的附带法律风险的行为我们都是拒绝的;
11 | 3. 我们反对任何参与者存在贬损评论、人身攻击、骚扰或侮辱他人以及其他非专业行为;
12 | 4. 我们有权并有责任删除或编辑与此行为准则不符的内容,不限于代码、Issues、wiki、文档以及其它。不遵守行为准则的参与者可能会被移除团队;
13 | 5. 我们接受任何人的监督,任何人可通过问题反馈,向我们报告发现的与此行为准则不符的事实存在。
14 |
15 | ## 如何参与贡献?
16 |
17 | * 贡献文档:浏览文档可以加深您对 Luckysheet 的了解,一旦发现文档写得不清晰或逻辑混乱的地方,可以订正、修改、补充,您可以通过 [中文论坛](https://support.qq.com/products/288322)或者 [谷歌论坛](https://groups.google.com/g/luckysheet)给予反馈
18 | * 贡献代码:欢迎大家为 Luckysheet 社区贡献代码,欢迎您认领Open状态的 [Issues](https://github.com/mengshukeji/Luckysheet/issues) 和未完成的特性,提交PR,成为贡献者之一如果您在使用过程中发现有些功能无法满足您的需求或出现问题,请在Issues中记录
19 | * 参与Issue讨论:您可以在任一 [Issues](https://github.com/mengshukeji/Luckysheet/issues) 下发表您的建议
20 | * Review代码:您可以在 [Github](https://github.com/mengshukeji/Luckysheet)上看到所有贡献者提交的PR,您可以Review他们的代码并发表您的建议
21 |
22 |
23 | ## 如何提交 Issues
24 |
25 | 在您提交特性/改进前,应该注意以下几点:
26 |
27 | * 请先确认该特性/改进是否被其他人已经提交
28 | * 一个通俗易懂的标题来阐述你提交的Bug/提交特性/改进
29 | * 如果是Bug则详细描述该bug产生的原因,如果能够复现,请尽量提供完整的重现步骤
30 | * 如果是特性,那么该特性应该有广泛的适用性,适用于大部分用户,最好能够提供详尽的设计文档
31 | * 如果是改进,尽可能描述清楚此改进所带来的益处
32 |
33 | 具体步骤:
34 |
35 | * 创建 [Issues](https://github.com/mengshukeji/Luckysheet/issues) ,描述清楚问题
36 | * 如果你要解决该issue则将issue assign到自己名下,如果你仅仅是提交Bug/特性/改进,并没有时间去贡献代码,则assignne设置为空
37 | * 如果是比较大的特性/改进,尽量先输出设计文档,走 [Luckysheet RFC](https://github.com/mengshukeji/Luckysheet-rfcs) 流程,供其他人review
38 |
39 | ## 如何认领 Issues
40 |
41 | 在 Luckysheet 的 [Issues](https://github.com/mengshukeji/Luckysheet/issues) 列表中,有很多由其他人创建的issue并未被修复,如果你感兴趣的话,可以认领这些issue。认领步骤如下:
42 |
43 | * 在该issue下留言,表达想认领该任务的想法,另注明 **@I can solve it** 即可
44 | * 如果提交者没有意见,则将该issue assign到自己名下并及时更新进度
45 | * 如果是比较大的特性,尽量先输出设计文档,走 [Luckysheet RFC](https://github.com/mengshukeji/Luckysheet-rfcs) 流程,供其他人review
46 | * 开发代码并提交代码至github
47 |
48 |
49 | ## 如何提交代码
50 |
51 | 1. fork 到自己的仓库
52 |
53 | 进入 [Luckysheet](https://github.com/mengshukeji/Luckysheet) 的Github页面 ,点击右上角按钮 Fork 进行 Fork。
54 |
55 | 2. git clone 到本地
56 |
57 | ```shell
58 | git clone https://github.com/130 | ${local_cellFormat.protectionTips} 131 |
132 | 部分选中 133 |