效果:
由于我本地开发环境的原因需要修改webpack服务端口
修改:x-spreadsheet\build\webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.config.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(common, {
mode: 'development',
plugins: [
new CleanWebpackPlugin(['dist']),
// you should know that the HtmlWebpackPlugin by default will generate its own index.html
new HtmlWebpackPlugin({
template: './index.html',
title: 'x-spreadsheet',
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: '[name].[contenthash].css',
// chunkFilename: devMode ? '[id].[hash].css' : '[id].css',
}),
],
output: {
filename: '[name].[contenthash].js',
},
devtool: 'inline-source-map',
devServer: {
disableHostCheck: true,
host: '0.0.0.0',
port: 3000, // 暴露端口
contentBase: '../dist',
},
});
重点来了
修改:x-spreadsheet\src\locale\en.js增加菜单重命名
export default {
toolbar: {
undo: 'Undo',
redo: 'Redo',
print: 'Print',
paintformat: 'Paint format',
clearformat: 'Clear format',
format: 'Format',
fontName: 'Font',
fontSize: 'Font size',
fontBold: 'Font bold',
fontItalic: 'Font italic',
underline: 'Underline',
strike: 'Strike',
color: 'Text color',
bgcolor: 'Fill color',
border: 'Borders',
merge: 'Merge cells',
align: 'Horizontal align',
valign: 'Vertical align',
textwrap: 'Text wrapping',
freeze: 'Freeze cell',
autofilter: 'Filter',
formula: 'Functions',
more: 'More',
},
contextmenu: {
copy: 'Copy',
cut: 'Cut',
paste: 'Paste',
pasteValue: 'Paste values only',
pasteFormat: 'Paste format only',
hide: 'Hide',
insertRow: 'Insert row',
insertColumn: 'Insert column',
deleteSheet: 'Delete',
deleteRow: 'Delete row',
deleteColumn: 'Delete column',
deleteCell: 'Delete cell',
deleteCellText: 'Delete cell text',
validation: 'Data validations',
cellprintable: 'Enable export',
cellnonprintable: 'Disable export',
celleditable: 'Enable editing',
cellnoneditable: 'Disable editing',
rename: 'Rename', // 菜单重命名
},
print: {
size: 'Paper size',
orientation: 'Page orientation',
orientations: ['Landscape', 'Portrait'],
},
format: {
normal: 'Normal',
text: 'Plain Text',
number: 'Number',
percent: 'Percent',
rmb: 'RMB',
usd: 'USD',
eur: 'EUR',
date: 'Date',
time: 'Time',
datetime: 'Date time',
duration: 'Duration',
},
formula: {
sum: 'Sum',
average: 'Average',
max: 'Max',
min: 'Min',
_if: 'IF',
and: 'AND',
or: 'OR',
concat: 'Concat',
},
validation: {
required: 'it must be required',
notMatch: 'it not match its validation rule',
between: 'it is between {} and {}',
notBetween: 'it is not between {} and {}',
notIn: 'it is not in list',
equal: 'it equal to {}',
notEqual: 'it not equal to {}',
lessThan: 'it less than {}',
lessThanEqual: 'it less than or equal to {}',
greaterThan: 'it greater than {}',
greaterThanEqual: 'it greater than or equal to {}',
},
error: {
pasteForMergedCell: 'Unable to do this for merged cells',
},
calendar: {
weeks: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
},
button: {
next: 'Next',
cancel: 'Cancel',
remove: 'Remove',
save: 'Save',
ok: 'OK',
},
sort: {
desc: 'Sort Z -> A',
asc: 'Sort A -> Z',
},
filter: {
empty: 'empty',
},
dataValidation: {
mode: 'Mode',
range: 'Cell Range',
criteria: 'Criteria',
modeType: {
cell: 'Cell',
column: 'Colun',
row: 'Row',
},
type: {
list: 'List',
number: 'Number',
date: 'Date',
phone: 'Phone',
email: 'Email',
},
operator: {
be: 'between',
nbe: 'not betwwen',
lt: 'less than',
lte: 'less than or equal to',
gt: 'greater than',
gte: 'greater than or equal to',
eq: 'equal to',
neq: 'not equal to',
},
},
};
修改:Z:\x-spreadsheet\src\locale\zh-cn.js增加中文重命名
export default {
toolbar: {
undo: '撤销',
redo: '恢复',
print: '打印',
paintformat: '格式刷',
clearformat: '清除格式',
format: '数据格式',
fontName: '字体',
fontSize: '字号',
fontBold: '加粗',
fontItalic: '倾斜',
underline: '下划线',
strike: '删除线',
color: '字体颜色',
bgcolor: '填充颜色',
border: '边框',
merge: '合并单元格',
align: '水平对齐',
valign: '垂直对齐',
textwrap: '自动换行',
freeze: '冻结',
autofilter: '自动筛选',
formula: '函数',
more: '更多',
},
contextmenu: {
copy: '复制',
cut: '剪切',
paste: '粘贴',
pasteValue: '粘贴数据',
pasteFormat: '粘贴格式',
hide: '隐藏',
insertRow: '插入行',
insertColumn: '插入列',
deleteSheet: '删除',
deleteRow: '删除行',
deleteColumn: '删除列',
deleteCell: '删除',
deleteCellText: '删除数据',
validation: '数据验证',
cellprintable: '可打印',
cellnonprintable: '不可打印',
celleditable: '可编辑',
cellnoneditable: '不可编辑',
rename: '重命名', // 增加重命名
},
print: {
size: '纸张大小',
orientation: '方向',
orientations: ['横向', '纵向'],
},
format: {
normal: '正常',
text: '文本',
number: '数值',
percent: '百分比',
rmb: '人民币',
usd: '美元',
eur: '欧元',
date: '短日期',
time: '时间',
datetime: '长日期',
duration: '持续时间',
},
formula: {
sum: '求和',
average: '求平均值',
max: '求最大值',
min: '求最小值',
concat: '字符拼接',
_if: '条件判断',
and: '和',
or: '或',
},
validation: {
required: '此值必填',
notMatch: '此值不匹配验证规则',
between: '此值应在 {} 和 {} 之间',
notBetween: '此值不应在 {} 和 {} 之间',
notIn: '此值不在列表中',
equal: '此值应该等于 {}',
notEqual: '此值不应该等于 {}',
lessThan: '此值应该小于 {}',
lessThanEqual: '此值应该小于等于 {}',
greaterThan: '此值应该大于 {}',
greaterThanEqual: '此值应该大于等于 {}',
},
error: {
pasteForMergedCell: '无法对合并的单元格执行此操作',
},
calendar: {
weeks: ['日', '一', '二', '三', '四', '五', '六'],
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
},
button: {
next: '下一步',
cancel: '取消',
remove: '删除',
save: '保存',
ok: '确认',
},
sort: {
desc: '降序',
asc: '升序',
},
filter: {
empty: '空白',
},
dataValidation: {
mode: '模式',
range: '单元区间',
criteria: '条件',
modeType: {
cell: '单元格',
column: '列模式',
row: '行模式',
},
type: {
list: '列表',
number: '数字',
date: '日期',
phone: '手机号',
email: '电子邮件',
},
operator: {
be: '在区间',
nbe: '不在区间',
lt: '小于',
lte: '小于等于',
gt: '大于',
gte: '大于等于',
eq: '等于',
neq: '不等于',
},
},
};
修改:Z:\x-spreadsheet\src\locale\locale.js切换到中文
/* global window */
import en from './en';
import zh from './zh-cn';
// Defines the fallback language as English
let $languages = ['zh'];
const $messages = {
zh
};
function translate(key, messages) {
if (messages) {
// Return the translation from the first language in the languages array
// that has a value for the provided key.
for (const lang of $languages) {
if (!messages[lang]) break;
let message = messages[lang];
// Splits the key at '.' except where escaped as '\.'
const keys = key.match(/(?:\\.|[^.])+/g);
for (let i = 0; i < keys.length; i += 1) {
const property = keys[i];
const value = message[property];
// If value doesn't exist, try next language
if (!value) break;
if (i === keys.length - 1) return value;
// Move down to the next level of the messages object
message = value;
}
}
}
return undefined;
}
function t(key) {
let v = translate(key, $messages);
if (!v && window && window.x_spreadsheet && window.x_spreadsheet.$messages) {
v = translate(key, window.x_spreadsheet.$messages);
}
return v || '';
}
function tf(key) {
return () => t(key);
}
// If clearLangList is set to false, lang will be added to the front of the
// languages array. The languages in the language array are searched in order
// to find a translation. This allows the use of other languages as a fallback
// if lang is missing some keys. The language array is preloaded with English.
// To set the languages array to only include lang, set clearLangList to true.
function locale(lang, message, clearLangList = false) {
if (clearLangList) {
$languages = [lang];
} else {
// Append to front of array.
// Translation method will use the first language in the list that has a
// matching key.
$languages.unshift(lang);
}
if (message) {
$messages[lang] = message;
}
}
export default {
t,
};
export {
locale,
t,
tf,
};
修改:x-spreadsheet\src\component\bottombar.js文件
思路:
1.保留原有双击修改sheet名称;
2.右键sheet时,激活(active)当前sheet;
3.点击菜单时判断功能,如果是重命名,则根据active获取index索引,实现重命名功能。
import { h } from './element';
import { bindClickoutside, unbindClickoutside } from './event';
import { cssPrefix } from '../config';
import Icon from './icon';
import FormInput from './form_input';
import Dropdown from './dropdown';
// Record: temp not used
// import { xtoast } from './message';
import { tf } from '../locale/locale';
class DropdownMore extends Dropdown {
constructor(click) {
const icon = new Icon('ellipsis');
super(icon, 'auto', false, 'top-left');
this.contentClick = click;
}
reset(items) {
const eles = items.map((it, i) => h('div', `${cssPrefix}-item`)
.css('width', '150px')
.css('font-weight', 'normal')
.on('click', () => {
this.contentClick(i);
this.hide();
})
.child(it));
this.setContentChildren(...eles);
}
setTitle() {}
}
const menuItems = [
{ key: 'delete', title: tf('contextmenu.deleteSheet') },
{ key: 'rename', title: tf('contextmenu.rename') }, // 增加菜单重命名
];
function buildMenuItem(item) {
return h('div', `${cssPrefix}-item`)
.child(item.title())
.on('click', () => {
this.itemClick(item.key);
this.hide();
});
}
function buildMenu() {
return menuItems.map(it => buildMenuItem.call(this, it));
}
class ContextMenu {
constructor() {
this.el = h('div', `${cssPrefix}-contextmenu`)
.css('width', '160px')
.children(...buildMenu.call(this))
.hide();
this.itemClick = () => {};
}
hide() {
const { el } = this;
el.hide();
unbindClickoutside(el);
}
setOffset(offset) {
const { el } = this;
el.offset(offset);
el.show();
bindClickoutside(el);
}
}
export default class Bottombar {
constructor(addFunc = () => {},
swapFunc = () => {},
deleteFunc = () => {},
updateFunc = () => {}) {
this.swapFunc = swapFunc;
this.updateFunc = updateFunc;
this.dataNames = [];
this.activeEl = null;
this.deleteEl = null;
this.items = [];
this.moreEl = new DropdownMore((i) => {
this.clickSwap2(this.items[i]);
});
const that = this;
this.contextMenu = new ContextMenu();
// eslint-disable-next-line func-names
this.contextMenu.itemClick = function (p) {
if (p === 'delete') { // 删除
deleteFunc();
} else if (p === 'rename') { // 重命名
// eslint-disable-next-line no-plusplus
for (let index = 0; index < that.items.length; index++) {
if (that.items[index].el.getAttribute('class').includes('active') === true) {
const item = that.items[index];
that.sheetRenameItem(item);
break;
}
}
}
};
this.el = h('div', `${cssPrefix}-bottombar`).children(
this.contextMenu.el,
this.menuEl = h('ul', `${cssPrefix}-menu`).child(
h('li', '').children(
new Icon('add').on('click', () => {
addFunc();
}),
h('span', '').child(this.moreEl),
),
),
);
}
addItem(name, active, options) {
this.dataNames.push(name);
const item = h('li', active ? 'active' : '').child(name);
item.on('click', () => {
this.clickSwap2(item);
}).on('contextmenu', (evt) => {
if (options.mode === 'read') return;
const { offsetLeft, offsetHeight } = evt.target;
this.contextMenu.setOffset({ left: offsetLeft, bottom: offsetHeight + 1 });
this.deleteEl = item;
this.clickSwap2(item); // 右键激活当前sheet
}).on('dblclick', () => {
if (options.mode === 'read') return;
this.sheetRenameItem(item); // 双击重命名
});
if (active) {
this.clickSwap(item);
}
this.items.push(item);
this.menuEl.child(item);
this.moreEl.reset(this.dataNames);
}
renameItem(index, value) {
this.dataNames.splice(index, 1, value);
this.moreEl.reset(this.dataNames);
this.items[index].html('').child(value);
this.updateFunc(index, value);
}
clear() {
this.items.forEach((it) => {
this.menuEl.removeChild(it.el);
});
this.items = [];
this.dataNames = [];
this.moreEl.reset(this.dataNames);
}
deleteItem() {
const { activeEl, deleteEl } = this;
if (this.items.length > 1) {
const index = this.items.findIndex(it => it === deleteEl);
this.items.splice(index, 1);
this.dataNames.splice(index, 1);
this.menuEl.removeChild(deleteEl.el);
this.moreEl.reset(this.dataNames);
if (activeEl === deleteEl) {
const [f] = this.items;
this.activeEl = f;
this.activeEl.toggle();
return [index, 0];
}
return [index, -1];
}
return [-1];
}
clickSwap2(item) {
const index = this.items.findIndex(it => it === item);
this.clickSwap(item);
this.activeEl.toggle();
this.swapFunc(index);
}
clickSwap(item) {
if (this.activeEl !== null) {
this.activeEl.toggle();
}
this.activeEl = item;
}
sheetRenameItem(item) {
const v = item.html();
const input = new FormInput('auto', '');
input.val(v);
input.input.on('blur', (key) => {
this.renameSwitch(key, v);
}).on('keydown', (key) => {
const code = key.keyCode || key.which || key.charCode;
if (code === 13 || code === 27) { // 回车、ESC
this.renameSwitch(key, v);
}
});
item.html('').child(input.el);
input.focus();
}
renameSwitch(key, v) {
let { value } = key.target;
const nindex = this.dataNames.findIndex(it => it === v);
if (value === '') {
value = v;
}
if (nindex > -1 && value !== '') {
this.renameItem(nindex, value);
}
}
}
该文章在 2024/6/12 12:43:12 编辑过