|
|
@@ -11,6 +11,7 @@ $(document).ready(function() {
|
|
|
// 初始化 SpreadJS
|
|
|
this.spread = SpreadJsObj.createNewSpread($('#filing-spread')[0]);
|
|
|
this.spread.options.showHorizontalScrollbar = false; // 隐藏水平滚动条
|
|
|
+ this.spread.options.allowUndo = false; // 名称逐次保存到服务端,不使用仅修改单元格的撤销。
|
|
|
this.sheet = this.spread.getActiveSheet();
|
|
|
this.treeIcons = {
|
|
|
closed: new Image(),
|
|
|
@@ -54,8 +55,9 @@ $(document).ready(function() {
|
|
|
};
|
|
|
},
|
|
|
getTip: function(data) { return _.escape(data.name || ''); },
|
|
|
+ readOnly: data => !this._canEditFilingName(data),
|
|
|
},
|
|
|
- { title: '文件数', colSpan: '1', rowSpan: '1', field: 'total_file_count', hAlign: 1, width: 70, formatter: '@' },
|
|
|
+ { title: '文件数', colSpan: '1', rowSpan: '1', field: 'total_file_count', hAlign: 1, width: 70, formatter: '@', readOnly: true },
|
|
|
],
|
|
|
emptyRows: 0,
|
|
|
headRows: 1,
|
|
|
@@ -64,7 +66,7 @@ $(document).ready(function() {
|
|
|
headColWidth: [25],
|
|
|
headerFont: '12px 微软雅黑',
|
|
|
font: '12px 微软雅黑',
|
|
|
- readOnly: true,
|
|
|
+ readOnly: false,
|
|
|
selectedBackColor: '#e8f4fd',
|
|
|
};
|
|
|
|
|
|
@@ -82,6 +84,8 @@ $(document).ready(function() {
|
|
|
this.fileOrder = getLocalCache(this.fileOrderKey) || 'create_time|desc';
|
|
|
this.curFilingKey = 'cur-filing-sjs-' + window.location.pathname.split('/')[2];
|
|
|
this._isRenaming = false;
|
|
|
+ this._nameEdit = null;
|
|
|
+ this._nameSaving = false;
|
|
|
|
|
|
this._bindEvents();
|
|
|
this._initNameClipboard();
|
|
|
@@ -285,66 +289,7 @@ $(document).ready(function() {
|
|
|
self.nextPage();
|
|
|
});
|
|
|
|
|
|
- // 双击行内重命名分类
|
|
|
- $('#filing-spread').dblclick(function() {
|
|
|
- if (!canFiling) return;
|
|
|
- if (self._isRenaming || self._directorySaving) return;
|
|
|
- const node = SpreadJsObj.getSelectObject(self.sheet);
|
|
|
- if (!node || node.is_fixed) return;
|
|
|
-
|
|
|
- self._isRenaming = true;
|
|
|
- const rowIndex = self.filingTree.nodes.indexOf(node);
|
|
|
- const cellRect = self.sheet.getCellRect(rowIndex, 0);
|
|
|
- const containerRect = $('#filing-spread')[0].getBoundingClientRect();
|
|
|
-
|
|
|
- const inputLeft = containerRect.left + window.scrollX + cellRect.x + 2;
|
|
|
- const inputTop = containerRect.top + window.scrollY + cellRect.y + 2;
|
|
|
- const inputWidth = cellRect.width - 4;
|
|
|
- const inputHeight = cellRect.height - 4;
|
|
|
-
|
|
|
- const $input = $('<input type="text" class="form-control form-control-sm">')
|
|
|
- .val(node.name)
|
|
|
- .css({
|
|
|
- position: 'absolute',
|
|
|
- left: inputLeft,
|
|
|
- top: inputTop,
|
|
|
- width: inputWidth,
|
|
|
- height: inputHeight,
|
|
|
- zIndex: 1000,
|
|
|
- fontSize: '12px',
|
|
|
- padding: '0 4px',
|
|
|
- border: '2px solid #4a90d9',
|
|
|
- borderRadius: '2px',
|
|
|
- outline: 'none',
|
|
|
- boxShadow: '0 0 4px rgba(74,144,217,0.4)',
|
|
|
- });
|
|
|
-
|
|
|
- let handled = false;
|
|
|
- const finishEdit = async (save) => {
|
|
|
- if (handled) return;
|
|
|
- handled = true;
|
|
|
- const newName = $input.val().trim();
|
|
|
- $input.remove();
|
|
|
- self._isRenaming = false;
|
|
|
- if (save && newName && newName !== node.name) {
|
|
|
- await self.renameFiling(node, newName);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- $input.on('keydown', function(e) {
|
|
|
- if (e.keyCode === 13) { // Enter
|
|
|
- e.preventDefault();
|
|
|
- finishEdit(true);
|
|
|
- } else if (e.keyCode === 27) { // Escape
|
|
|
- e.preventDefault();
|
|
|
- finishEdit(false);
|
|
|
- }
|
|
|
- });
|
|
|
- $input.on('blur', function() { finishEdit(true); });
|
|
|
-
|
|
|
- $('body').append($input);
|
|
|
- $input.focus().select();
|
|
|
- });
|
|
|
+ this._bindFilingNameEditing();
|
|
|
|
|
|
// 分类右键菜单
|
|
|
$.contextMenu({
|
|
|
@@ -360,7 +305,7 @@ $(document).ready(function() {
|
|
|
name: '粘贴名称',
|
|
|
icon: 'fa-paste',
|
|
|
callback: function() { self._showNamePastePreview(); },
|
|
|
- disabled: function() { return !self._referenceNameClipboard || self._namePasteSaving; },
|
|
|
+ disabled: function() { return !self._referenceNameClipboard || self._namePasteSaving || self._nameSaving; },
|
|
|
},
|
|
|
'delete': {
|
|
|
name: '删除',
|
|
|
@@ -370,7 +315,7 @@ $(document).ready(function() {
|
|
|
},
|
|
|
disabled: function () {
|
|
|
const nodes = self._getSelectedFilings();
|
|
|
- return self._directorySaving || projectFileLocked || !nodes.length || nodes.some(node => {
|
|
|
+ return self._directorySaving || self._nameSaving || projectFileLocked || !nodes.length || nodes.some(node => {
|
|
|
const permission = self._getFilingPermission(node.id);
|
|
|
return Number(node.is_fixed) || !permission.can_view || !permission.can_edit_dir;
|
|
|
});
|
|
|
@@ -636,8 +581,19 @@ $(document).ready(function() {
|
|
|
this.filingTree.refreshNodeVisible();
|
|
|
SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.filingTree);
|
|
|
this.resizeSpreadColumns();
|
|
|
+ const highlightedRows = this.sheet.getSelections().map(selection => selection.row)
|
|
|
+ .filter(row => row >= 0 && row < this.sheet.getRowCount());
|
|
|
this.sheet.clearSelection();
|
|
|
- if (currentNode) this.sheet.setSelection(this.filingTree.nodes.indexOf(currentNode), 0, 1, 1);
|
|
|
+ if (currentNode) {
|
|
|
+ const selectedRow = this.filingTree.nodes.indexOf(currentNode);
|
|
|
+ this.sheet.setSelection(selectedRow, 0, 1, 1);
|
|
|
+ // 程序定位选区后,单独刷新组件绘制的整行选中背景。
|
|
|
+ SpreadJsObj.reloadRowsBackColor(this.sheet, [...new Set(highlightedRows.concat(selectedRow))]);
|
|
|
+ } else {
|
|
|
+ highlightedRows.forEach(row => {
|
|
|
+ this.sheet.getRange(row, 0, 1, this.sheet.getColumnCount()).backColor(this.sheet.getDefaultStyle().backColor);
|
|
|
+ });
|
|
|
+ }
|
|
|
const targetRow = focusId && currentNode ? this.filingTree.nodes.indexOf(currentNode)
|
|
|
: this.filingTree.nodes.findIndex(node => topNode && node.id === topNode.id);
|
|
|
if (targetRow >= 0) this.sheet.showRow(targetRow, focusId ? spreadNS.VerticalPosition.center : spreadNS.VerticalPosition.top);
|
|
|
@@ -648,6 +604,7 @@ $(document).ready(function() {
|
|
|
this.curFiling = null;
|
|
|
if (currentNode) {
|
|
|
this.setCurFiling(currentNode).catch(() => { $('#file-view').hide(); });
|
|
|
+ if (focusId) this.spread.focus();
|
|
|
} else {
|
|
|
canFiling = false;
|
|
|
$('.js-can-edit-dir').hide();
|
|
|
@@ -659,7 +616,7 @@ $(document).ready(function() {
|
|
|
* 删除分类
|
|
|
*/
|
|
|
delFiling(nodes, callback) {
|
|
|
- if (this._directorySaving || !nodes.length) return;
|
|
|
+ if (this._directorySaving || this._nameSaving || !nodes.length) return;
|
|
|
const self = this;
|
|
|
const modal = $('#filesjs-del-filing');
|
|
|
this._directorySaving = true;
|
|
|
@@ -697,24 +654,20 @@ $(document).ready(function() {
|
|
|
/**
|
|
|
* 重命名分类
|
|
|
*/
|
|
|
- async renameFiling(node, newName) {
|
|
|
- const result = await postDataAsync('filing/save', { id: node.id, name: newName });
|
|
|
- const srcNode = filing.find(x => x.id === node.id || x.id == node.id);
|
|
|
- if (srcNode) srcNode.name = newName;
|
|
|
- node.name = newName;
|
|
|
- const countHint = node.total_file_count > 0 ? `(${node.total_file_count})` : '';
|
|
|
- node.name = newName;
|
|
|
- const rowIndex = this.filingTree.nodes.indexOf(node);
|
|
|
- if (rowIndex >= 0) {
|
|
|
- SpreadJsObj.reLoadRowsData(this.sheet, [rowIndex]);
|
|
|
- }
|
|
|
- let parent = this.filingTree.getParent(node);
|
|
|
- while (parent) {
|
|
|
- const pIdx = this.filingTree.nodes.indexOf(parent);
|
|
|
- if (pIdx >= 0) SpreadJsObj.reLoadRowsData(this.sheet, [pIdx]);
|
|
|
- parent = this.filingTree.getParent(parent);
|
|
|
- }
|
|
|
- return result;
|
|
|
+ renameFiling(node, newName) {
|
|
|
+ this._nameSaving = true;
|
|
|
+ const finishSave = () => {
|
|
|
+ this._nameSaving = false;
|
|
|
+ const row = this.filingTree.nodes.findIndex(item => item.id === node.id);
|
|
|
+ if (row >= 0) SpreadJsObj.reLoadRowsData(this.sheet, [row]);
|
|
|
+ };
|
|
|
+ postData('filing/save', { id: node.id, name: newName }, () => {
|
|
|
+ const source = filing.find(item => item.id === node.id);
|
|
|
+ if (source) source.name = newName;
|
|
|
+ const currentNode = this.filingTree.nodes.find(item => item.id === node.id);
|
|
|
+ if (currentNode) currentNode.name = newName;
|
|
|
+ finishSave();
|
|
|
+ }, finishSave, false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -913,6 +866,9 @@ $(document).ready(function() {
|
|
|
const differ = count - (node.file_count || 0);
|
|
|
node.file_count = count;
|
|
|
node.total_file_count = count;
|
|
|
+ // 分类增删后会从 filing 重建树,同步原始数据以保留最新文件数。
|
|
|
+ const source = filing.find(item => item.id === node.id);
|
|
|
+ if (source) source.file_count = count;
|
|
|
// 更新 SpreadJS 显示
|
|
|
const rowIndex = this.filingTree.nodes.indexOf(node);
|
|
|
if (rowIndex >= 0) {
|
|
|
@@ -1054,6 +1010,7 @@ $(document).ready(function() {
|
|
|
this._pasteReferenceText(text);
|
|
|
}, true);
|
|
|
this.sheet.bind(spreadNS.Events.ClipboardPasting, (e, info) => {
|
|
|
+ if (this._isRenaming || this.sheet.isEditing()) return;
|
|
|
info.cancel = true;
|
|
|
this._pasteReferenceText(info.pasteData ? info.pasteData.text : '');
|
|
|
});
|
|
|
@@ -1171,7 +1128,7 @@ $(document).ready(function() {
|
|
|
}
|
|
|
|
|
|
_showNamePastePreview() {
|
|
|
- if (this._namePasteSaving || $('#filing-name-paste').hasClass('show')) return;
|
|
|
+ if (this._namePasteSaving || this._nameSaving || $('#filing-name-paste').hasClass('show')) return;
|
|
|
const clipboard = this._referenceNameClipboard;
|
|
|
if (!clipboard) { toastr.warning('请先从右侧参考文件复制名称'); return; }
|
|
|
if (this._isRenaming) return;
|
|
|
@@ -1203,7 +1160,7 @@ $(document).ready(function() {
|
|
|
}
|
|
|
|
|
|
_savePastedNames() {
|
|
|
- if (this._namePasteSaving || !this._namePasteItems.length || this._namePasteItems.some(item => item.errors.length)) return;
|
|
|
+ if (this._namePasteSaving || this._nameSaving || !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');
|
|
|
@@ -1234,7 +1191,7 @@ $(document).ready(function() {
|
|
|
}
|
|
|
|
|
|
_addFiling(data) {
|
|
|
- if (this._directorySaving || !canFiling || this._isRenaming) return;
|
|
|
+ if (this._directorySaving || this._nameSaving || !canFiling || this._isRenaming) return;
|
|
|
this._directorySaving = true;
|
|
|
postData('filing/add', data, result => {
|
|
|
for (const update of result.update || []) {
|
|
|
@@ -1265,7 +1222,7 @@ $(document).ready(function() {
|
|
|
}
|
|
|
|
|
|
_showDeletePreview() {
|
|
|
- if (this._directorySaving || projectFileLocked) return;
|
|
|
+ if (this._directorySaving || this._nameSaving || this._isRenaming || projectFileLocked) return;
|
|
|
const nodes = this._getSelectedFilings();
|
|
|
if (!nodes.length) return;
|
|
|
const included = new Set(nodes.map(node => node.id));
|
|
|
@@ -1297,6 +1254,54 @@ $(document).ready(function() {
|
|
|
$('#filesjs-del-filing-error').text('');
|
|
|
$('#filesjs-del-filing').modal('show');
|
|
|
}
|
|
|
+
|
|
|
+ _canEditFilingName(node) {
|
|
|
+ if (!node || projectFileLocked || Number(node.is_fixed)) return false;
|
|
|
+ const permission = this._getFilingPermission(node.id);
|
|
|
+ return Boolean(Number(permission.can_view) && Number(permission.can_edit_dir));
|
|
|
+ }
|
|
|
+
|
|
|
+ _bindFilingNameEditing() {
|
|
|
+ this.sheet.bind(spreadNS.Events.EditStarting, (e, info) => {
|
|
|
+ const node = this.filingTree.nodes[info.row];
|
|
|
+ if (info.col !== 0 || !this._canEditFilingName(node) || this._directorySaving
|
|
|
+ || this._nameSaving || this._namePasteSaving || this._reloadingFiling) {
|
|
|
+ info.cancel = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this._isRenaming = true;
|
|
|
+ this._nameEdit = { node, row: info.row, oldName: String(node.name || '') };
|
|
|
+ });
|
|
|
+ this.sheet.bind(spreadNS.Events.EditEnded, (e, info) => { this._finishFilingNameEdit(info); });
|
|
|
+ $('#filing-spread')[0].addEventListener('keydown', e => {
|
|
|
+ if (e.isComposing || e.keyCode === 229) return;
|
|
|
+ // 不允许快捷键绕过单个名称保存,批量替换继续走粘贴确认弹窗。
|
|
|
+ if (!this.sheet.isEditing() && (e.keyCode === 46 || e.keyCode === 8 || ((e.ctrlKey || e.metaKey) && e.keyCode === 88))) {
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopImmediatePropagation();
|
|
|
+ } else if ((e.ctrlKey || e.metaKey) && e.keyCode === 13) {
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopImmediatePropagation();
|
|
|
+ if (this.sheet.isEditing()) this.sheet.endEdit();
|
|
|
+ }
|
|
|
+ }, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ _finishFilingNameEdit(info) {
|
|
|
+ const editing = this._nameEdit;
|
|
|
+ this._nameEdit = null;
|
|
|
+ this._isRenaming = false;
|
|
|
+ if (!editing || info.col !== 0 || info.row !== editing.row) return;
|
|
|
+ // Esc 取消时 editingText 仍是输入内容;读取实际单元格值,避免误保存。
|
|
|
+ const value = String(this.sheet.getValue(info.row, 0) || '');
|
|
|
+ if (value === editing.oldName) return;
|
|
|
+ const newName = value.trim();
|
|
|
+ if (!newName || newName === editing.oldName) {
|
|
|
+ this.sheet.setValue(info.row, 0, editing.oldName);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.renameFiling(editing.node, newName);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 初始化
|