|
@@ -81,6 +81,7 @@ $(document).ready(function() {
|
|
|
this._isRenaming = false;
|
|
this._isRenaming = false;
|
|
|
|
|
|
|
|
this._bindEvents();
|
|
this._bindEvents();
|
|
|
|
|
+ this._initNameClipboard();
|
|
|
this._loadFilingData();
|
|
this._loadFilingData();
|
|
|
this.refreshFileOrderButton();
|
|
this.refreshFileOrderButton();
|
|
|
}
|
|
}
|
|
@@ -341,14 +342,22 @@ $(document).ready(function() {
|
|
|
$input.focus().select();
|
|
$input.focus().select();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // 右键菜单 - 删除分类
|
|
|
|
|
|
|
+ // 分类右键菜单
|
|
|
$.contextMenu({
|
|
$.contextMenu({
|
|
|
selector: '#filing-spread',
|
|
selector: '#filing-spread',
|
|
|
build: function ($trigger, e) {
|
|
build: function ($trigger, e) {
|
|
|
|
|
+ const hit = SpreadJsObj.getHitTest($trigger, e, self.sheet);
|
|
|
|
|
+ if (!hit || hit.row < 0 || hit.row >= self.filingTree.nodes.length) return false;
|
|
|
const target = SpreadJsObj.safeRightClickSelection($trigger, e, self.spread);
|
|
const target = SpreadJsObj.safeRightClickSelection($trigger, e, self.spread);
|
|
|
return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
|
|
return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
|
|
|
},
|
|
},
|
|
|
items: {
|
|
items: {
|
|
|
|
|
+ 'paste-name': {
|
|
|
|
|
+ name: '粘贴名称',
|
|
|
|
|
+ icon: 'fa-paste',
|
|
|
|
|
+ callback: function() { self._showNamePastePreview(); },
|
|
|
|
|
+ disabled: function() { return !self._referenceNameClipboard || self._namePasteSaving; },
|
|
|
|
|
+ },
|
|
|
'delete': {
|
|
'delete': {
|
|
|
name: '删除',
|
|
name: '删除',
|
|
|
icon: 'fa-trash-o',
|
|
icon: 'fa-trash-o',
|
|
@@ -981,6 +990,207 @@ $(document).ready(function() {
|
|
|
});
|
|
});
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ _initNameClipboard() {
|
|
|
|
|
+ this._referenceNameClipboard = null;
|
|
|
|
|
+ this._namePasteItems = [];
|
|
|
|
|
+ this._namePasteSaving = false;
|
|
|
|
|
+ const container = $('#filing-spread')[0];
|
|
|
|
|
+ // 先于 SpreadJS 的只读单元格处理,保留浏览器原生粘贴事件以读取本次剪贴板。
|
|
|
|
|
+ container.addEventListener('keydown', e => {
|
|
|
|
|
+ if (!(e.ctrlKey || e.metaKey) || e.altKey || e.keyCode !== 86 || this._isRenaming) return;
|
|
|
|
|
+ e.stopImmediatePropagation();
|
|
|
|
|
+ if (!this._referenceNameClipboard || this._namePasteSaving) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ if (!this._namePasteSaving) toastr.warning('请先从右侧参考文件复制名称');
|
|
|
|
|
+ }
|
|
|
|
|
+ }, true);
|
|
|
|
|
+ container.addEventListener('paste', e => {
|
|
|
|
|
+ if (this._isRenaming) return;
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopImmediatePropagation();
|
|
|
|
|
+ const text = e.clipboardData ? e.clipboardData.getData('text/plain') : '';
|
|
|
|
|
+ this._pasteReferenceText(text);
|
|
|
|
|
+ }, true);
|
|
|
|
|
+ this.sheet.bind(spreadNS.Events.ClipboardPasting, (e, info) => {
|
|
|
|
|
+ info.cancel = true;
|
|
|
|
|
+ this._pasteReferenceText(info.pasteData ? info.pasteData.text : '');
|
|
|
|
|
+ });
|
|
|
|
|
+ $('#filing-name-paste-ok').on('click.filesjsNames', () => { this._savePastedNames(); });
|
|
|
|
|
+ $('#filing-name-paste').on('hide.bs.modal.filesjsNames', e => {
|
|
|
|
|
+ if (this._namePasteSaving) e.preventDefault();
|
|
|
|
|
+ }).on('hidden.bs.modal.filesjsNames', () => {
|
|
|
|
|
+ this._namePasteItems = [];
|
|
|
|
|
+ this.spread.focus();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ initReferenceNameClipboard(spread) {
|
|
|
|
|
+ const container = $('#std-reference-spread')[0];
|
|
|
|
|
+ const sheet = spread.getActiveSheet();
|
|
|
|
|
+ container.addEventListener('keydown', e => {
|
|
|
|
|
+ if (!(e.ctrlKey || e.metaKey) || e.altKey || e.keyCode !== 67 || sheet.isEditing()) return;
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopImmediatePropagation();
|
|
|
|
|
+ this._copyReferenceNames(spread);
|
|
|
|
|
+ }, true);
|
|
|
|
|
+ container.addEventListener('copy', e => {
|
|
|
|
|
+ if (sheet.isEditing()) return;
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopImmediatePropagation();
|
|
|
|
|
+ this._copyReferenceNames(spread, e.clipboardData);
|
|
|
|
|
+ }, true);
|
|
|
|
|
+ $.contextMenu({
|
|
|
|
|
+ selector: '#std-reference-spread',
|
|
|
|
|
+ build: function($trigger, e) {
|
|
|
|
|
+ const hit = SpreadJsObj.getHitTest($trigger, e, sheet);
|
|
|
|
|
+ if (!hit || hit.row < 0 || !sheet.zh_tree || hit.row >= sheet.zh_tree.nodes.length) return false;
|
|
|
|
|
+ if (hit.hitTestType !== spreadNS.SheetArea.viewport && hit.hitTestType !== spreadNS.SheetArea.rowHeader) return false;
|
|
|
|
|
+ SpreadJsObj.safeRightClickSelection($trigger, e, spread);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ },
|
|
|
|
|
+ items: {
|
|
|
|
|
+ 'copy-name': {
|
|
|
|
|
+ name: '复制名称',
|
|
|
|
|
+ icon: 'fa-copy',
|
|
|
|
|
+ callback: () => { this._copyReferenceNames(spread); },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _copyReferenceNames(spread, clipboardData) {
|
|
|
|
|
+ const sheet = spread.getActiveSheet();
|
|
|
|
|
+ const tree = sheet.zh_tree;
|
|
|
|
|
+ const rows = new Set();
|
|
|
|
|
+ for (const selection of sheet.getSelections()) {
|
|
|
|
|
+ if (selection.col > 0) continue;
|
|
|
|
|
+ const first = Math.max(0, selection.row);
|
|
|
|
|
+ const last = selection.row < 0 ? sheet.getRowCount() : first + selection.rowCount;
|
|
|
|
|
+ for (let row = first; row < last; row++) {
|
|
|
|
|
+ if (tree && tree.nodes[row] && sheet.getRowVisible(row)) rows.add(row);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!rows.size) {
|
|
|
|
|
+ this._referenceNameClipboard = null;
|
|
|
|
|
+ toastr.warning('请先选中参考文件的名称列');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const names = [...rows].sort((a, b) => a - b).map(row => String(tree.nodes[row].name || ''));
|
|
|
|
|
+ const text = names.join('\r\n');
|
|
|
|
|
+ // 保留原始数组,名称内的换行仍属于同一个分类。
|
|
|
|
|
+ this._referenceNameClipboard = { names, text };
|
|
|
|
|
+ SpreadJsObj.Clipboard.setCopyData(text);
|
|
|
|
|
+ if (clipboardData) {
|
|
|
|
|
+ clipboardData.setData('text/plain', text);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ SpreadJsObj.Clipboard.setSysClipboard(text);
|
|
|
|
|
+ }
|
|
|
|
|
+ spread.focus();
|
|
|
|
|
+ toastr.success(`已复制 ${names.length} 条名称`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _pasteReferenceText(text) {
|
|
|
|
|
+ const clipboard = this._referenceNameClipboard;
|
|
|
|
|
+ if (!clipboard || String(text || '').replace(/\r\n?/g, '\n') !== clipboard.text.replace(/\r\n?/g, '\n')) {
|
|
|
|
|
+ toastr.warning('请重新从右侧参考文件复制名称后粘贴');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this._showNamePastePreview();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _buildNamePasteItems(names, startRow) {
|
|
|
|
|
+ const targets = [];
|
|
|
|
|
+ for (let row = startRow; row < this.filingTree.nodes.length && targets.length < names.length; row++) {
|
|
|
|
|
+ if (this.sheet.getRowVisible(row)) targets.push({ row, node: this.filingTree.nodes[row] });
|
|
|
|
|
+ }
|
|
|
|
|
+ return names.map((value, index) => {
|
|
|
|
|
+ const target = targets[index];
|
|
|
|
|
+ const node = target && target.node;
|
|
|
|
|
+ const name = String(value).trim();
|
|
|
|
|
+ const errors = [];
|
|
|
|
|
+ if (!node) {
|
|
|
|
|
+ errors.push('目标行数不足');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const permission = this._getFilingPermission(node.id);
|
|
|
|
|
+ if (projectFileLocked) errors.push('项目资料已锁定');
|
|
|
|
|
+ if (Number(node.is_fixed)) errors.push('固定分类不可改名');
|
|
|
|
|
+ if (!Number(permission.can_view) || !Number(permission.can_edit_dir)) errors.push('无编辑目录权限');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!name) errors.push('名称不能为空');
|
|
|
|
|
+ if (name.length > 100) errors.push('名称不能超过100个字符');
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: node ? node.id : null,
|
|
|
|
|
+ row: target ? target.row : null,
|
|
|
|
|
+ old_name: node ? String(node.name || '') : '',
|
|
|
|
|
+ name,
|
|
|
|
|
+ errors,
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _showNamePastePreview() {
|
|
|
|
|
+ if (this._namePasteSaving || $('#filing-name-paste').hasClass('show')) return;
|
|
|
|
|
+ const clipboard = this._referenceNameClipboard;
|
|
|
|
|
+ if (!clipboard) { toastr.warning('请先从右侧参考文件复制名称'); return; }
|
|
|
|
|
+ if (this._isRenaming) return;
|
|
|
|
|
+ const row = this.sheet.getActiveRowIndex();
|
|
|
|
|
+ if (row < 0 || !this.filingTree.nodes[row] || !this.sheet.getRowVisible(row)) {
|
|
|
|
|
+ toastr.warning('请选择粘贴的起始分类');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.sheet.getActiveColumnIndex() > 0) {
|
|
|
|
|
+ toastr.warning('请在分类名称列粘贴');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this._namePasteItems = this._buildNamePasteItems(clipboard.names, row);
|
|
|
|
|
+ const tbody = $('#filing-name-paste-rows').empty();
|
|
|
|
|
+ for (const item of this._namePasteItems) {
|
|
|
|
|
+ const tr = $('<tr>').toggleClass('table-danger', item.errors.length > 0);
|
|
|
|
|
+ $('<td>').text(item.row === null ? '—' : item.row + 1).appendTo(tr);
|
|
|
|
|
+ $('<td>').text(item.old_name).appendTo(tr);
|
|
|
|
|
+ $('<td>').text(item.name).toggleClass('filesjs-name-paste-new', !item.errors.length && item.name !== item.old_name).appendTo(tr);
|
|
|
|
|
+ $('<td>').text(item.errors.length ? item.errors.join(';') : (item.name === item.old_name ? '名称相同,不修改' : '可替换')).appendTo(tr);
|
|
|
|
|
+ tbody.append(tr);
|
|
|
|
|
+ }
|
|
|
|
|
+ const invalid = this._namePasteItems.filter(item => item.errors.length).length;
|
|
|
|
|
+ const changed = this._namePasteItems.filter(item => item.name !== item.old_name).length;
|
|
|
|
|
+ $('#filing-name-paste-summary').text(`共 ${this._namePasteItems.length} 条,${changed} 条名称有变化,${invalid} 条校验未通过。`);
|
|
|
|
|
+ $('#filing-name-paste-error').text(invalid ? '请调整复制范围或起始分类后重新粘贴,校验全部通过后才能替换。' : '');
|
|
|
|
|
+ $('#filing-name-paste-ok').prop('disabled', invalid > 0 || changed === 0);
|
|
|
|
|
+ $('#filing-name-paste').modal('show');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _savePastedNames() {
|
|
|
|
|
+ if (this._namePasteSaving || !this._namePasteItems.length || this._namePasteItems.some(item => item.errors.length)) return;
|
|
|
|
|
+ const items = this._namePasteItems.map(item => ({ id: item.id, old_name: item.old_name, name: item.name }));
|
|
|
|
|
+ if (!items.some(item => item.name !== item.old_name)) return;
|
|
|
|
|
+ const modal = $('#filing-name-paste');
|
|
|
|
|
+ this._namePasteSaving = true;
|
|
|
|
|
+ $('button', modal).prop('disabled', true);
|
|
|
|
|
+ $('#filing-name-paste-error').text('');
|
|
|
|
|
+ postData('filing/names/save', { items }, result => {
|
|
|
|
|
+ const rows = [];
|
|
|
|
|
+ for (const update of result.update) {
|
|
|
|
|
+ const source = filing.find(node => node.id === update.id);
|
|
|
|
|
+ if (source) source.name = update.name;
|
|
|
|
|
+ const row = this.filingTree.nodes.findIndex(node => node.id === update.id);
|
|
|
|
|
+ if (row >= 0) {
|
|
|
|
|
+ this.filingTree.nodes[row].name = update.name;
|
|
|
|
|
+ rows.push(row);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (rows.length) SpreadJsObj.reLoadRowsData(this.sheet, rows);
|
|
|
|
|
+ this._namePasteSaving = false;
|
|
|
|
|
+ $('button', modal).prop('disabled', false);
|
|
|
|
|
+ modal.modal('hide');
|
|
|
|
|
+ toastr.success(`已替换 ${result.update.length} 条分类名称`);
|
|
|
|
|
+ }, error => {
|
|
|
|
|
+ this._namePasteSaving = false;
|
|
|
|
|
+ $('button', modal).prop('disabled', false);
|
|
|
|
|
+ $('#filing-name-paste-error').text(error || '保存失败,请重试;若名称已变化,请刷新页面后重新粘贴。');
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 初始化
|
|
// 初始化
|
|
@@ -2035,6 +2245,7 @@ $(document).ready(function() {
|
|
|
},
|
|
},
|
|
|
page: 'file',
|
|
page: 'file',
|
|
|
});
|
|
});
|
|
|
|
|
+ filingSjs.initReferenceNameClipboard(fileReference.spread);
|
|
|
}
|
|
}
|
|
|
fileReference.spread.refresh();
|
|
fileReference.spread.refresh();
|
|
|
}
|
|
}
|