config_dir.js 84 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
  1. $(document).ready(function() {
  2. autoFlashHeight();
  3. class ConfigDir {
  4. constructor() {
  5. this.spread = SpreadJsObj.createNewSpread($('#config-dir-spread')[0]);
  6. this.spread.options.showHorizontalScrollbar = false;
  7. this.sheet = this.spread.getActiveSheet();
  8. this.treeIcons = {
  9. closed: new Image(),
  10. open: new Image(),
  11. leaf: new Image(),
  12. };
  13. const refreshTreeIcon = () => { this.spread.refresh(); };
  14. this.treeIcons.closed.onload = refreshTreeIcon;
  15. this.treeIcons.open.onload = refreshTreeIcon;
  16. this.treeIcons.leaf.onload = refreshTreeIcon;
  17. this.treeIcons.closed.src = '/public/images/filesjs-tree-closed.png';
  18. this.treeIcons.open.src = '/public/images/filesjs-tree-open.png';
  19. this.treeIcons.leaf.src = '/public/images/filesjs-tree-leaf.png';
  20. const treeIcons = this.treeIcons;
  21. this.filingTree = createNewPathTree('base', {
  22. id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level',
  23. isLeaf: 'is_leaf', fullPath: 'full_path', rootId: -1,
  24. });
  25. this.spreadSetting = {
  26. cols: [
  27. {
  28. title: '目录名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 260, formatter: '@', cellType: 'tree',
  29. getValue: function(data) {
  30. return data.name;
  31. },
  32. getIcon: function(data) {
  33. const fallback = data.is_leaf ? 'file' : 'folder';
  34. return {
  35. image: data.is_leaf ? treeIcons.leaf : (data.expanded ? treeIcons.open : treeIcons.closed),
  36. fallback,
  37. size: 18,
  38. };
  39. },
  40. },
  41. { title: '已传文件', colSpan: '1', rowSpan: '1', field: 'file_count', hAlign: 1, width: 75, formatter: '@' },
  42. {
  43. title: '固定目录', colSpan: '1', rowSpan: '1', field: 'is_fixed', hAlign: 1, width: 75, formatter: '@',
  44. getValue: function(data) { return data.is_fixed ? '\u{1F512}' : ''; },
  45. },
  46. { title: '归档单位', colSpan: '1', rowSpan: '1', field: 'file_company', hAlign: 0, width: 160, formatter: '@', cellType: 'autoTip', wordWrap: true },
  47. { title: '上传说明', colSpan: '1', rowSpan: '1', field: 'upload_tips', hAlign: 0, width: 200, formatter: '@', cellType: 'autoTip', wordWrap: true },
  48. { title: '授权用户', colSpan: '1', rowSpan: '1', field: 'auth_users', hAlign: 0, width: 320, formatter: '@', cellType: 'autoTip' },
  49. { title: '添加人', colSpan: '1', rowSpan: '1', field: 'add_user', hAlign: 0, width: 90, formatter: '@', cellType: 'autoTip' },
  50. ],
  51. emptyRows: 0, headRows: 1, headRowHeight: [32], defaultRowHeight: 24,
  52. headColWidth: [25], headerFont: '12px 微软雅黑', font: '12px 微软雅黑',
  53. readOnly: true, selectedBackColor: '#e8f4fd',
  54. };
  55. SpreadJsObj.initSheet(this.sheet, this.spreadSetting);
  56. this.curNode = null;
  57. this.editableTextFields = [ 'upload_tips', 'file_company' ];
  58. var editableTextFields = this.editableTextFields;
  59. this.editableTextColumnIndexes = this.spreadSetting.cols.reduce(function(indexes, col, index) {
  60. if (editableTextFields.indexOf(col.field) >= 0) indexes.push(index);
  61. return indexes;
  62. }, []);
  63. this._textCellEditing = null;
  64. this._editableTextCellType = null;
  65. this._authUserSaving = false;
  66. this._authTargetSaving = false;
  67. this._buildAuthAccountList();
  68. this._buildAuthUserMap();
  69. this._loadData();
  70. this._bindEvents();
  71. this._resizeSpreadColumns();
  72. this._refreshAuthUserButton(this._getSelectedNode());
  73. }
  74. _buildAuthAccountList() {
  75. var accountMap = {};
  76. accountList.forEach(function(account) {
  77. accountMap[Number(account.id)] = Object.assign({}, account);
  78. });
  79. permissionData.forEach(function(permission) {
  80. var userId = Number(permission.uid);
  81. if (!userId || accountMap[userId]) return;
  82. accountMap[userId] = {
  83. id: userId,
  84. name: permission.name || '',
  85. company: permission.company || '',
  86. role: permission.role || '',
  87. mobile: permission.mobile || '',
  88. selectable: false,
  89. };
  90. });
  91. this.authAccountList = Object.keys(accountMap).map(function(userId) {
  92. return accountMap[userId];
  93. });
  94. }
  95. _buildAuthUserMap() {
  96. this.authUserMap = {};
  97. if (filingPermissionData && filingPermissionData.length > 0) {
  98. for (const permission of filingPermissionData) {
  99. if (!permission.filing_id) continue;
  100. const account = this.authAccountList.find(function(x) {
  101. return Number(x.id) === Number(permission.uid);
  102. });
  103. if (!account) continue;
  104. const filingId = String(permission.filing_id);
  105. if (!this.authUserMap[filingId]) this.authUserMap[filingId] = [];
  106. if (!this.authUserMap[filingId].some(function(user) { return Number(user.id) === Number(account.id); })) {
  107. this.authUserMap[filingId].push({
  108. id: account.id, name: account.name, company: account.company, role: account.role,
  109. });
  110. }
  111. }
  112. }
  113. }
  114. _convertData(sourceData) {
  115. var data = [];
  116. var skippedIds = new Set();
  117. var sortedData = sourceData.slice().sort(function(a, b) { return Number(a.tree_level) - Number(b.tree_level); });
  118. for (var i = 0; i < sortedData.length; i++) {
  119. var node = sortedData[i];
  120. if (skippedIds.has(node.tree_pid)) {
  121. skippedIds.add(node.id);
  122. continue;
  123. }
  124. var parent = (node.tree_pid === '-1' || node.tree_pid === -1)
  125. ? undefined
  126. : data.find(function(x) { return x.id === node.tree_pid; });
  127. var hasChild = sourceData.some(function(x) { return x.tree_pid === node.id; });
  128. var authUsers = this.authUserMap[String(node.id)] || [];
  129. if (!parent && node.tree_pid !== '-1' && node.tree_pid !== -1) {
  130. skippedIds.add(node.id);
  131. continue;
  132. }
  133. data.push({
  134. id: node.id,
  135. tree_id: data.length + 1,
  136. tree_pid: parent ? parent.tree_id : -1,
  137. order: Number(node.tree_order) + 1,
  138. level: Number(node.tree_level),
  139. is_leaf: !hasChild,
  140. full_path: '',
  141. name: node.name,
  142. filing_type: Number(node.filing_type),
  143. file_count: Number(node.file_count) || 0,
  144. is_fixed: node.is_fixed,
  145. upload_tips: node.upload_tips || '',
  146. file_company: node.file_company || '',
  147. auth_users: authUsers.map(function(x) { return x.name; }).join('、'),
  148. auth_user_ids: authUsers.map(function(x) { return Number(x.id); }),
  149. add_user: node.add_user || '',
  150. spid: node.spid,
  151. });
  152. }
  153. return data;
  154. }
  155. _loadData() {
  156. var convertedData = this._convertData(filingData);
  157. this.filingTree.loadDatas(convertedData);
  158. SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.filingTree);
  159. this._applyEditableTextCellType();
  160. this._applyLastColumnRightBorder();
  161. }
  162. _reloadTree() {
  163. var convertedData = this._convertData(filingData);
  164. this.filingTree.loadDatas(convertedData);
  165. SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.filingTree, true);
  166. this._applyEditableTextCellType();
  167. this._resizeSpreadColumns();
  168. }
  169. _isEditableTextColumn(columnIndex) {
  170. return this.editableTextColumnIndexes.indexOf(columnIndex) >= 0;
  171. }
  172. _applyEditableTextCellType() {
  173. if (!this._editableTextCellType) {
  174. this._editableTextCellType = SpreadJsObj.CellType.getAutoTipCellType();
  175. var defaultIsReservedKey = this._editableTextCellType.isReservedKey;
  176. this._editableTextCellType.isReservedKey = function(event, context) {
  177. var target = event && (event.target || event.srcElement);
  178. var isEditingInput = target && target.getAttribute &&
  179. target.getAttribute('gcUIElement') === 'gcEditingInput';
  180. if (event && event.keyCode === 13 && context && context.isEditing && isEditingInput &&
  181. !event.ctrlKey && !event.altKey && !event.metaKey) {
  182. return true;
  183. }
  184. return defaultIsReservedKey ? defaultIsReservedKey.call(this, event, context) : false;
  185. };
  186. }
  187. var self = this;
  188. this.editableTextColumnIndexes.forEach(function(columnIndex) {
  189. self.sheet.getRange(-1, columnIndex, -1, 1)
  190. .cellType(self._editableTextCellType)
  191. .wordWrap(true);
  192. });
  193. }
  194. _applyLastColumnRightBorder() {
  195. var lastColumnIndex = this.spreadSetting.cols.length - 1;
  196. var borderLine = this.sheet.borderLine || new spreadNS.LineBorder('#cccccc', spreadNS.LineStyle.thin);
  197. var headerRowCount = this.sheet.getRowCount(spreadNS.SheetArea.colHeader);
  198. var dataRowCount = this.sheet.getRowCount(spreadNS.SheetArea.viewport);
  199. if (headerRowCount > 0) {
  200. this.sheet.getRange(
  201. 0, lastColumnIndex, headerRowCount, 1, spreadNS.SheetArea.colHeader
  202. ).setBorder(borderLine, { right: true });
  203. }
  204. if (dataRowCount > 0) {
  205. this.sheet.getRange(
  206. 0, lastColumnIndex, dataRowCount, 1, spreadNS.SheetArea.viewport
  207. ).setBorder(borderLine, { right: true });
  208. }
  209. }
  210. _resizeSpreadColumns() {
  211. var containerWidth = Math.floor($('#config-dir-spread').innerWidth() || 0);
  212. if (containerWidth <= 0) return;
  213. var rowHeaderWidth = 25;
  214. var fileCompanyWidth = 160;
  215. var fixedWidth = 75 + 75 + fileCompanyWidth + 113;
  216. var minimumFlexWidth = 600;
  217. var usableWidth = Math.max(containerWidth - rowHeaderWidth, fixedWidth + minimumFlexWidth);
  218. var flexWidth = usableWidth - fixedWidth;
  219. var nameWidth = Math.floor(flexWidth * 0.34);
  220. var tipsWidth = Math.floor(flexWidth * 0.24);
  221. var authUserWidth = flexWidth - nameWidth - tipsWidth;
  222. var widths = [ nameWidth, 75, 75, fileCompanyWidth, tipsWidth, authUserWidth, 90 ];
  223. for (var index = 0; index < widths.length; index++) {
  224. this.sheet.setColumnWidth(index, widths[index]);
  225. }
  226. this.spread.options.showHorizontalScrollbar = containerWidth <
  227. fixedWidth + minimumFlexWidth + rowHeaderWidth;
  228. this._applyLastColumnRightBorder();
  229. this.spread.refresh();
  230. }
  231. _getSelectedNode() { return SpreadJsObj.getSelectObject(this.sheet); }
  232. _refreshAuthUserButton(node) {
  233. var button = $('#cd-auth-user');
  234. if (button.length === 0) return;
  235. var disabled = !node;
  236. button
  237. .toggleClass('disabled btn-outline-secondary', disabled)
  238. .toggleClass('btn-outline-primary', !disabled)
  239. .attr('aria-disabled', disabled ? 'true' : 'false')
  240. .attr('tabindex', disabled ? '-1' : '0');
  241. }
  242. _findOriginalId(treeId) {
  243. var node = this.filingTree.nodes.find(function(x) { return x.tree_id === treeId; });
  244. return node ? node.id : null;
  245. }
  246. _bindEvents() {
  247. var self = this;
  248. var resizeTimer;
  249. $(window).off('resize.configDir').on('resize.configDir', function() {
  250. clearTimeout(resizeTimer);
  251. resizeTimer = setTimeout(function() { self._resizeSpreadColumns(); }, 80);
  252. });
  253. this.spread.bind(spreadNS.Events.SelectionChanged, function() {
  254. var node = self._getSelectedNode();
  255. if (node) self.curNode = node;
  256. self._refreshAuthUserButton(node);
  257. });
  258. this.spread.bind(spreadNS.Events.EditStarting, function(e, info) {
  259. var editing = self._textCellEditing;
  260. if (!editing || info.row !== editing.row || info.col !== editing.col ||
  261. !configDirLocked || !canManageConfigDir) {
  262. info.cancel = true;
  263. }
  264. });
  265. this.spread.bind(spreadNS.Events.EditEnded, function(e, info) {
  266. self._saveTextCellEdit(info);
  267. });
  268. $('#cd-add-sibling').click(function() {
  269. var node = self._getSelectedNode();
  270. if (!node) { toastr.warning('请先选择一个分类'); return; }
  271. if (node.is_fixed) { toastr.error('固定分类不可添加同级'); return; }
  272. var treePid = node.tree_pid === -1 ? '-1' : self._findOriginalId(node.tree_pid);
  273. postData('config-dir/update', { updateType: 'add', tree_pid: treePid, tree_pre_id: node.id }, function(result) {
  274. filingData.push.apply(filingData, result.create);
  275. if (result.update) {
  276. for (var i = 0; i < result.update.length; i++) {
  277. var srcNode = filingData.find(function(x) { return x.id === result.update[i].id; });
  278. if (srcNode) srcNode.tree_order = result.update[i].tree_order;
  279. }
  280. }
  281. self._reloadTree();
  282. var newIdx = self.filingTree.nodes.findIndex(function(x) { return x.id === result.create[0].id; });
  283. if (newIdx >= 0) self.sheet.setSelection(newIdx, 0, 1, 1);
  284. });
  285. });
  286. $('#cd-add-child').click(function() {
  287. var node = self._getSelectedNode();
  288. if (!node) { toastr.warning('请先选择一个分类'); return; }
  289. var children = self.filingTree.getChildren(node);
  290. var fixedChild = children.find(function(x) { return x.is_fixed; });
  291. if (fixedChild) { toastr.error('该分类下存在固定分类,不可添加子级'); return; }
  292. if (node.file_count > 0) { toastr.error('该分类下已导入文件,不可添加子级'); return; }
  293. postData('config-dir/update', { updateType: 'add', tree_pid: node.id }, function(result) {
  294. filingData.push.apply(filingData, result.create);
  295. self._reloadTree();
  296. var newIdx = self.filingTree.nodes.findIndex(function(x) { return x.id === result.create[0].id; });
  297. if (newIdx >= 0) self.sheet.setSelection(newIdx, 0, 1, 1);
  298. });
  299. });
  300. $('#cd-move-up').click(function() {
  301. var node = self._getSelectedNode();
  302. if (!node) { toastr.warning('请先选择一个分类'); return; }
  303. if (node.is_fixed) { toastr.error('固定分类不可移动'); return; }
  304. if (self._hasSubtreeFiles(node)) { toastr.error('该目录或子目录下已有文件,不可移动'); return; }
  305. var siblings = self.filingTree.nodes.filter(function(x) { return x.tree_pid === node.tree_pid; });
  306. siblings.sort(function(a, b) { return a.order - b.order; });
  307. var idx = siblings.findIndex(function(x) { return x.tree_id === node.tree_id; });
  308. if (idx <= 0) { toastr.warning('已经是第一个,无法上移'); return; }
  309. var prevNode = siblings[idx - 1];
  310. if (prevNode.is_fixed) { toastr.error('上方是固定分类,不可交换'); return; }
  311. if (self._hasSubtreeFiles(prevNode)) { toastr.error('上方目录或子目录下已有文件,不可交换'); return; }
  312. var srcId = self._findOriginalId(node.tree_id);
  313. var preId = self._findOriginalId(prevNode.tree_id);
  314. var parentOrigId = node.tree_pid === -1 ? '-1' : self._findOriginalId(node.tree_pid);
  315. var preParentOrigId = prevNode.tree_pid === -1 ? '-1' : self._findOriginalId(prevNode.tree_pid);
  316. var curSel = self.sheet.getSelections()[0];
  317. var curRow = curSel ? curSel.row : -1;
  318. postData('config-dir/update', { updateType: 'move', id: srcId, tree_pid: parentOrigId, tree_order: prevNode.order - 1 }, function() {
  319. postData('config-dir/update', { updateType: 'move', id: preId, tree_pid: preParentOrigId, tree_order: prevNode.order }, function() {
  320. var srcNode = filingData.find(function(x) { return x.id === srcId; });
  321. if (srcNode) srcNode.tree_order = prevNode.order - 1;
  322. var preNode = filingData.find(function(x) { return x.id === preId; });
  323. if (preNode) preNode.tree_order = prevNode.order;
  324. self._reloadTree();
  325. if (curRow > 0) self.sheet.setSelection(curRow - 1, 0, 1, 1);
  326. });
  327. });
  328. });
  329. $('#cd-move-down').click(function() {
  330. var node = self._getSelectedNode();
  331. if (!node) { toastr.warning('请先选择一个分类'); return; }
  332. if (node.is_fixed) { toastr.error('固定分类不可移动'); return; }
  333. if (self._hasSubtreeFiles(node)) { toastr.error('该目录或子目录下已有文件,不可移动'); return; }
  334. var siblings = self.filingTree.nodes.filter(function(x) { return x.tree_pid === node.tree_pid; });
  335. siblings.sort(function(a, b) { return a.order - b.order; });
  336. var idx = siblings.findIndex(function(x) { return x.tree_id === node.tree_id; });
  337. if (idx < 0 || idx >= siblings.length - 1) { toastr.warning('已经是最后一个,无法下移'); return; }
  338. var nextNode = siblings[idx + 1];
  339. if (nextNode.is_fixed) { toastr.error('下方是固定分类,不可交换'); return; }
  340. if (self._hasSubtreeFiles(nextNode)) { toastr.error('下方目录或子目录下已有文件,不可交换'); return; }
  341. var srcId = self._findOriginalId(node.tree_id);
  342. var nextId = self._findOriginalId(nextNode.tree_id);
  343. var parentOrigId = node.tree_pid === -1 ? '-1' : self._findOriginalId(node.tree_pid);
  344. var curSel = self.sheet.getSelections()[0];
  345. var curRow = curSel ? curSel.row : -1;
  346. postData('config-dir/update', { updateType: 'move', id: srcId, tree_pid: parentOrigId, tree_order: nextNode.order }, function() {
  347. postData('config-dir/update', { updateType: 'move', id: nextId, tree_pid: parentOrigId, tree_order: nextNode.order - 1 }, function() {
  348. var srcNode = filingData.find(function(x) { return x.id === srcId; });
  349. if (srcNode) srcNode.tree_order = nextNode.order;
  350. var nextOrigNode = filingData.find(function(x) { return x.id === nextId; });
  351. if (nextOrigNode) nextOrigNode.tree_order = nextNode.order - 1;
  352. self._reloadTree();
  353. if (curRow >= 0) self.sheet.setSelection(curRow + 1, 0, 1, 1);
  354. });
  355. });
  356. });
  357. $('#cd-move').click(function() {
  358. var node = self._getSelectedNode();
  359. if (!node) { toastr.warning('请先选择一个分类'); return; }
  360. if (!self._hasSubtreeFiles(node)) { toastr.warning('当前目录及子目录暂无可移动文件'); return; }
  361. self._showMoveModal(node);
  362. });
  363. $('#cd-delete').click(function() {
  364. var node = self._getSelectedNode();
  365. if (!node) { toastr.warning('请先选择一个分类'); return; }
  366. if (node.is_fixed) { toastr.error('固定分类不可删除'); return; }
  367. deleteAfterHint(function() {
  368. postData('config-dir/update', { updateType: 'del', id: node.id }, function(result) {
  369. if (result && result.delete) {
  370. for (var i = 0; i < result.delete.length; i++) {
  371. var idx = filingData.findIndex(function(x) { return x.id === result.delete[i]; });
  372. if (idx >= 0) filingData.splice(idx, 1);
  373. }
  374. }
  375. if (result && result.update) {
  376. for (var j = 0; j < result.update.length; j++) {
  377. var srcNode = filingData.find(function(x) { return x.id === result.update[j].id; });
  378. if (srcNode) srcNode.tree_order = result.update[j].tree_order;
  379. }
  380. }
  381. self._reloadTree();
  382. });
  383. }, '确认删除分类【' + node.name + '】及其子分类?');
  384. });
  385. $('#cd-edit-name').click(function() {
  386. var node = self._getSelectedNode();
  387. if (!node) { toastr.warning('请先选择一个分类'); return; }
  388. self._showEditModal(node);
  389. });
  390. $('#cd-edit-tips').click(function() {
  391. var node = self._getSelectedNode();
  392. if (!node) { toastr.warning('请先选择一个分类'); return; }
  393. self._showTipsModal(node);
  394. });
  395. $('#cd-auth-user').click(function() {
  396. if ($(this).attr('aria-disabled') === 'true') return;
  397. var node = self._getSelectedNode();
  398. if (!node) { toastr.warning('请先选择一个分类'); return; }
  399. self._showAuthUserModal(node);
  400. });
  401. $('#cd-auth-batch-add').click(function() {
  402. self._showAuthTargetModal('add');
  403. });
  404. $('#cd-auth-batch-cover').click(function() {
  405. self._showAuthTargetModal('cover');
  406. });
  407. $('#cd-auth-user-search, #cd-auth-target-user-search')
  408. .on('click', function(event) { event.stopPropagation(); })
  409. .on('input propertychange', function() {
  410. if (this.id === 'cd-auth-user-search') {
  411. self._renderAuthUserDropdown();
  412. } else {
  413. self._renderAuthTargetUserDropdown();
  414. }
  415. });
  416. $('[name=cd-show-level]').click(function() {
  417. self._showLevel($(this).attr('tag'));
  418. });
  419. $('#config-dir-spread').dblclick(function() {
  420. if (!configDirLocked || !canManageConfigDir) return;
  421. var node = self._getSelectedNode();
  422. if (!node) return;
  423. var selection = self.sheet.getSelections()[0];
  424. if (selection && self._isEditableTextColumn(selection.col)) {
  425. self._startTextCellEdit(selection.row, selection.col, node);
  426. return;
  427. }
  428. self._showEditModal(node);
  429. });
  430. }
  431. _startTextCellEdit(row, col, node) {
  432. if (row < 0 || !node || !this._isEditableTextColumn(col)) return;
  433. var field = this.spreadSetting.cols[col].field;
  434. this._textCellEditing = {
  435. row: row,
  436. col: col,
  437. field: field,
  438. filingId: String(node.id),
  439. originalValue: String(node[field] || ''),
  440. };
  441. this.sheet.getCell(row, col).locked(false);
  442. this.sheet.startEdit(true);
  443. }
  444. _saveTextCellEdit(info) {
  445. if (!info || !this._isEditableTextColumn(info.col)) return;
  446. info.sheet.getCell(info.row, info.col).locked(true);
  447. var editing = this._textCellEditing;
  448. this._textCellEditing = null;
  449. var node = this.filingTree.nodes[info.row];
  450. if (!editing || !node || editing.row !== info.row || editing.col !== info.col ||
  451. String(node.id) !== editing.filingId) {
  452. if (node) SpreadJsObj.reLoadRowData(info.sheet, info.row);
  453. return;
  454. }
  455. var textValue = String(info.editingText || '').trim();
  456. if (textValue === editing.originalValue) {
  457. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  458. return;
  459. }
  460. var saveData = { updateType: 'save', id: node.id };
  461. saveData[editing.field] = textValue;
  462. postData('config-dir/update', saveData, function() {
  463. var srcNode = filingData.find(function(item) { return String(item.id) === String(node.id); });
  464. if (srcNode) srcNode[editing.field] = textValue;
  465. node[editing.field] = textValue;
  466. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  467. }, function() {
  468. node[editing.field] = editing.originalValue;
  469. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  470. });
  471. }
  472. _getSubtreeTreeIds(node) {
  473. var result = new Set([node.tree_id]);
  474. var pending = [node.tree_id];
  475. while (pending.length > 0) {
  476. var parentTreeId = pending.shift();
  477. for (var i = 0; i < this.filingTree.nodes.length; i++) {
  478. var child = this.filingTree.nodes[i];
  479. if (child.tree_pid !== parentTreeId || result.has(child.tree_id)) continue;
  480. result.add(child.tree_id);
  481. pending.push(child.tree_id);
  482. }
  483. }
  484. return result;
  485. }
  486. _hasSubtreeFiles(node) {
  487. var subtreeTreeIds = this._getSubtreeTreeIds(node);
  488. return this.filingTree.nodes.some(function(item) {
  489. return subtreeTreeIds.has(item.tree_id) && Number(item.file_count) > 0;
  490. });
  491. }
  492. _showEditModal(node) {
  493. if (!configDirLocked || !canManageConfigDir) return;
  494. var self = this;
  495. var subtreeTreeIds = this._getSubtreeTreeIds(node);
  496. var currentParentId = node.tree_pid === -1 ? '-1' : String(this._findOriginalId(node.tree_pid));
  497. var hasFiles = this._hasSubtreeFiles(node);
  498. var isFixed = Boolean(Number(node.is_fixed));
  499. var $parent = $('#cd-edit-parent').empty();
  500. $parent.append($('<option></option>').val('-1').text('根目录'));
  501. for (var i = 0; i < this.filingTree.nodes.length; i++) {
  502. var target = this.filingTree.nodes[i];
  503. if (subtreeTreeIds.has(target.tree_id)) continue;
  504. if (Number(target.file_count) > 0 && String(target.id) !== currentParentId) continue;
  505. var prefix = '';
  506. for (var level = 0; level < target.level; level++) prefix += ' ';
  507. $parent.append($('<option></option>').val(String(target.id)).text(prefix + target.name));
  508. }
  509. $parent.val(currentParentId);
  510. $('#cd-edit-dir-name').val(node.name);
  511. $('#cd-edit-fixed').prop('checked', isFixed);
  512. var moveDisabled = hasFiles || isFixed;
  513. $parent.prop('disabled', moveDisabled);
  514. $('#cd-edit-move-hint')
  515. .toggleClass('d-none', !moveDisabled)
  516. .text(hasFiles ? '当前目录或子目录下已有文件,不允许移动目录。' : '固定目录不允许移动,请先取消固定并保存。');
  517. $('#cd-edit-modal').modal('show');
  518. $('#cd-edit-dir-name').trigger('focus').select();
  519. $('#cd-edit-save').off('click').on('click', function() {
  520. var name = $('#cd-edit-dir-name').val().trim();
  521. if (!name) { toastr.warning('请输入目录名称'); return; }
  522. var treePid = $parent.prop('disabled') ? currentParentId : String($parent.val());
  523. var isFixedValue = $('#cd-edit-fixed').prop('checked') ? 1 : 0;
  524. postData('config-dir/update', {
  525. updateType: 'edit', id: node.id, tree_pid: treePid,
  526. name: name, is_fixed: isFixedValue,
  527. }, function(result) {
  528. if (result && result.update) {
  529. for (var i = 0; i < result.update.length; i++) {
  530. var updateItem = result.update[i];
  531. var srcNode = filingData.find(function(item) { return item.id === updateItem.id; });
  532. if (!srcNode) continue;
  533. Object.keys(updateItem).forEach(function(key) {
  534. if (key !== 'id') srcNode[key] = updateItem[key];
  535. });
  536. }
  537. }
  538. self._reloadTree();
  539. var selectedRow = self.filingTree.nodes.findIndex(function(item) { return item.id === node.id; });
  540. if (selectedRow >= 0) self.sheet.setSelection(selectedRow, 0, 1, 1);
  541. $('#cd-edit-modal').modal('hide');
  542. });
  543. });
  544. }
  545. _showLevel(level) {
  546. var nodes = this.filingTree.nodes;
  547. var hideRows = [];
  548. if (level === 'last') {
  549. for (var i = 0; i < nodes.length; i++) { if (!nodes[i].is_leaf) hideRows.push(i); }
  550. } else {
  551. var maxLevel = parseInt(level);
  552. for (var i = 0; i < nodes.length; i++) { if (nodes[i].level > maxLevel) hideRows.push(i); }
  553. }
  554. for (var i = 0; i < nodes.length; i++) this.sheet.setRowVisible(i, true);
  555. for (var j = 0; j < hideRows.length; j++) this.sheet.setRowVisible(hideRows[j], false);
  556. }
  557. _showMoveModal(node) {
  558. var self = this;
  559. var subtreeTreeIds = this._getSubtreeTreeIds(node);
  560. $('#cd-move-source-name').text(node.name);
  561. this._moveTargetExpanded = {};
  562. this.filingTree.nodes.forEach(function(item) {
  563. if (!item.is_leaf) self._moveTargetExpanded[item.tree_id] = true;
  564. });
  565. var html = [];
  566. var hasSelectableTarget = false;
  567. for (var i = 0; i < this.filingTree.nodes.length; i++) {
  568. var n = this.filingTree.nodes[i];
  569. var hasChildren = this.filingTree.nodes.some(function(child) { return child.tree_pid === n.tree_id; });
  570. var inSourceSubtree = subtreeTreeIds.has(n.tree_id);
  571. var selectable = n.is_leaf && !inSourceSubtree;
  572. var indent = Math.max(Number(n.level) - 1, 0) * 20;
  573. if (selectable) hasSelectableTarget = true;
  574. html.push('<tr data-move-tree-id="' + n.tree_id + '" data-move-tree-pid="' + n.tree_pid + '">');
  575. html.push('<td class="text-center"><input type="radio" name="cd-move-target" value="' +
  576. self._escapeAuthUserHtml(n.id) + '" tree-id="' + n.tree_id + '"' + (selectable ? '' : ' disabled') + '></td>');
  577. html.push('<td><span style="display:inline-block;width:' + indent + 'px;"></span>');
  578. if (hasChildren) {
  579. html.push('<a href="javascript:void(0);" class="cd-move-target-toggle mr-1" data-tree-id="' + n.tree_id + '">');
  580. html.push('<i class="fa fa-minus-square-o"></i></a>');
  581. html.push('<i class="fa fa-folder-open text-warning mr-1 cd-move-target-folder" data-tree-id="' + n.tree_id + '"></i>');
  582. } else {
  583. html.push('<span class="d-inline-block mr-1" style="width:12px;"></span>');
  584. html.push('<i class="fa fa-file-o text-primary mr-1"></i>');
  585. }
  586. html.push(self._escapeAuthUserHtml(n.name));
  587. if (String(n.id) === String(node.id)) html.push('<span class="text-muted ml-1">(当前目录)</span>');
  588. html.push('</td></tr>');
  589. }
  590. if (!hasSelectableTarget) {
  591. html.push('<tr><td colspan="2" class="text-muted text-center py-3">暂无可选择的目标底层目录</td></tr>');
  592. }
  593. $('#cd-move-target-list').html(html.join(''));
  594. this._refreshMoveTargetTreeVisibility();
  595. $('#cd-move-target-list').off('click', '.cd-move-target-toggle')
  596. .on('click', '.cd-move-target-toggle', function(event) {
  597. event.preventDefault();
  598. var treeId = Number($(this).attr('data-tree-id'));
  599. self._moveTargetExpanded[treeId] = !self._moveTargetExpanded[treeId];
  600. self._refreshMoveTargetTreeVisibility();
  601. });
  602. $('#cd-move-modal').modal('show');
  603. $('#cd-move-ok').off('click').on('click', function() {
  604. var target = $('input[name=cd-move-target]:checked');
  605. if (target.length === 0) { toastr.warning('请选择目标底层目录'); return; }
  606. var targetOrigId = target.attr('value');
  607. var srcOrigId = self._findOriginalId(node.tree_id);
  608. postData('config-dir/file/move', {
  609. source_filing_id: srcOrigId,
  610. target_filing_id: targetOrigId,
  611. }, function(result) {
  612. if (result && result.filings) {
  613. for (var i = 0; i < result.filings.length; i++) {
  614. var updateItem = result.filings[i];
  615. var srcNode = filingData.find(function(x) { return String(x.id) === String(updateItem.id); });
  616. if (srcNode) srcNode.file_count = Number(updateItem.file_count) || 0;
  617. }
  618. }
  619. self._reloadTree();
  620. var selectedRow = self.filingTree.nodes.findIndex(function(item) {
  621. return String(item.id) === String(srcOrigId);
  622. });
  623. if (selectedRow >= 0) self.sheet.setSelection(selectedRow, 0, 1, 1);
  624. $('#cd-move-modal').modal('hide');
  625. toastr.success('已移动' + Number(result.moved_count || 0) + '个文件');
  626. });
  627. });
  628. }
  629. _refreshMoveTargetTreeVisibility() {
  630. var self = this;
  631. var nodeMap = {};
  632. this.filingTree.nodes.forEach(function(node) { nodeMap[node.tree_id] = node; });
  633. this.filingTree.nodes.forEach(function(node) {
  634. var visible = true;
  635. var parentTreeId = node.tree_pid;
  636. while (parentTreeId !== -1) {
  637. if (!self._moveTargetExpanded[parentTreeId]) {
  638. visible = false;
  639. break;
  640. }
  641. var parent = nodeMap[parentTreeId];
  642. if (!parent) break;
  643. parentTreeId = parent.tree_pid;
  644. }
  645. $('#cd-move-target-list tr[data-move-tree-id="' + node.tree_id + '"]').toggle(visible);
  646. var expanded = Boolean(self._moveTargetExpanded[node.tree_id]);
  647. $('#cd-move-target-list .cd-move-target-toggle[data-tree-id="' + node.tree_id + '"] i')
  648. .toggleClass('fa-minus-square-o', expanded)
  649. .toggleClass('fa-plus-square-o', !expanded);
  650. $('#cd-move-target-list .cd-move-target-folder[data-tree-id="' + node.tree_id + '"]')
  651. .toggleClass('fa-folder-open', expanded)
  652. .toggleClass('fa-folder', !expanded);
  653. });
  654. }
  655. _showTipsModal(node) {
  656. var self = this;
  657. $('#cd-tips-filing-name').text(node.name);
  658. $('#cd-tips-input').val(node.upload_tips || '');
  659. $('#cd-tips-modal').modal('show');
  660. $('#cd-tips-save').off('click').on('click', function() {
  661. var upload_tips = $('#cd-tips-input').val().trim();
  662. postData('config-dir/update', { updateType: 'save', id: node.id, upload_tips: upload_tips }, function() {
  663. var srcNode = filingData.find(function(x) { return x.id === node.id; });
  664. if (srcNode) srcNode.upload_tips = upload_tips;
  665. node.upload_tips = upload_tips;
  666. var idx = self.filingTree.nodes.indexOf(node);
  667. if (idx >= 0) SpreadJsObj.reLoadRowsData(self.sheet, [idx]);
  668. $('#cd-tips-modal').modal('hide');
  669. });
  670. });
  671. }
  672. _showAuthUserModal(node) {
  673. var self = this;
  674. this._authUserFiling = node;
  675. this._authUserIds = (node.auth_user_ids || []).map(function(id) { return Number(id); });
  676. this._authUserPermissionState = {};
  677. this._authUserIds.forEach(function(userId) {
  678. self._authUserPermissionState[userId] = self._getInitialAuthUserPermissionState(
  679. userId, node.id, node.filing_type
  680. );
  681. });
  682. $('#auth-user-filing-name').text(node.name);
  683. $('#cd-auth-user-search').val('');
  684. this._renderAuthUserDropdown();
  685. this._renderAuthUserList();
  686. this._setAuthUserSaveState(false);
  687. $('#auth-user-modal').modal('show');
  688. $('#cd-auth-user-options').off('click', '.cd-auth-unit-row').on('click', '.cd-auth-unit-row', function(event) {
  689. event.preventDefault();
  690. event.stopPropagation();
  691. var groupId = $(this).attr('data-group-id');
  692. var children = $('#cd-auth-user-options').find('[data-group-content="' + groupId + '"]');
  693. children.toggle();
  694. $('i', this).toggleClass('fa-plus-square fa-minus-square-o');
  695. });
  696. $('#auth-user-modal').off('click', '.cd-auth-list-action').on('click', '.cd-auth-list-action', function(event) {
  697. if ($(this).attr('aria-disabled') !== 'true') return;
  698. event.preventDefault();
  699. event.stopPropagation();
  700. });
  701. $('#cd-auth-user-options').off('click', '.cd-auth-add-unit').on('click', '.cd-auth-add-unit', function(event) {
  702. event.preventDefault();
  703. event.stopPropagation();
  704. var company = $(this).attr('data-company');
  705. var userIds = accountList.filter(function(account) {
  706. return (account.company || '未设置单位') === company;
  707. }).map(function(account) { return Number(account.id); });
  708. self._addAuthUsers(userIds);
  709. });
  710. $('#cd-auth-user-options').off('click', '.cd-auth-add-user').on('click', '.cd-auth-add-user', function(event) {
  711. event.preventDefault();
  712. event.stopPropagation();
  713. self._addAuthUsers([Number($(this).attr('data-user-id'))]);
  714. });
  715. $('#auth-user-list').off('click', '.cd-auth-user-remove').on('click', '.cd-auth-user-remove', function(event) {
  716. event.preventDefault();
  717. var userId = Number($(this).attr('data-user-id'));
  718. self._authUserIds = self._authUserIds.filter(function(id) { return id !== userId; });
  719. delete self._authUserPermissionState[userId];
  720. self._renderAuthUserList();
  721. self._syncAuthUserDropdownMarks();
  722. });
  723. $('#cd-auth-user-select-all').off('change').on('change', function() {
  724. var checked = this.checked;
  725. var userIds = [];
  726. $('#auth-user-list input[name=cd-auth-user-check]').prop('checked', checked).each(function() {
  727. userIds.push(Number($(this).attr('data-user-id')));
  728. });
  729. if (!checked) self._setAuthUserPermissions(userIds, false);
  730. self._syncAuthUserSelectAll();
  731. self._syncAuthPermissionHeaders();
  732. self._refreshAuthUserActionState();
  733. });
  734. $('#auth-user-list').off('change', 'input[name=cd-auth-user-check]').on('change', 'input[name=cd-auth-user-check]', function() {
  735. var userId = Number($(this).attr('data-user-id'));
  736. if (!this.checked) self._setAuthUserPermissions([userId], false);
  737. self._syncAuthUserSelectAll();
  738. self._syncAuthPermissionHeaders();
  739. self._refreshAuthUserActionState();
  740. });
  741. $('.cd-auth-permission-all').off('change').on('change', function() {
  742. var permissionKey = $(this).attr('data-permission');
  743. var checked = this.checked;
  744. $('#auth-user-list .cd-auth-permission-check[data-permission="' + permissionKey + '"]').each(function() {
  745. var userId = Number($(this).attr('data-user-id'));
  746. $(this).prop('checked', checked);
  747. self._ensureAuthUserPermissionState(userId)[permissionKey] = checked;
  748. if (checked) {
  749. $('#auth-user-list input[name=cd-auth-user-check][data-user-id="' + userId + '"]').prop('checked', true);
  750. }
  751. });
  752. self._syncAuthUserSelectAll();
  753. self._syncAuthPermissionHeaders();
  754. self._refreshAuthUserActionState();
  755. });
  756. $('#auth-user-list').off('change', '.cd-auth-permission-check').on('change', '.cd-auth-permission-check', function() {
  757. var userId = Number($(this).attr('data-user-id'));
  758. var permissionKey = $(this).attr('data-permission');
  759. self._ensureAuthUserPermissionState(userId)[permissionKey] = this.checked;
  760. if (this.checked) {
  761. $('#auth-user-list input[name=cd-auth-user-check][data-user-id="' + userId + '"]').prop('checked', true);
  762. }
  763. self._syncAuthUserSelectAll();
  764. self._syncAuthPermissionHeaders();
  765. self._refreshAuthUserActionState();
  766. });
  767. $('#cd-auth-batch-delete').off('click').on('click', function(event) {
  768. event.preventDefault();
  769. if ($(this).attr('aria-disabled') === 'true') return;
  770. var removeIds = [];
  771. $('#auth-user-list input[name=cd-auth-user-check]:checked').each(function() {
  772. removeIds.push(Number($(this).attr('data-user-id')));
  773. });
  774. self._authUserIds = self._authUserIds.filter(function(userId) {
  775. return removeIds.indexOf(userId) < 0;
  776. });
  777. removeIds.forEach(function(userId) { delete self._authUserPermissionState[userId]; });
  778. self._renderAuthUserList();
  779. self._syncAuthUserDropdownMarks();
  780. });
  781. $('#auth-user-save').off('click').on('click', function() {
  782. if (self._authUserSaving) return;
  783. var userIds = self._authUserIds.slice();
  784. var userPermissions = userIds.map(function(userId) {
  785. var state = self._ensureAuthUserPermissionState(userId);
  786. return {
  787. uid: userId,
  788. can_upload: state.upload_reference ? 1 : 0,
  789. can_edit_file: state.edit_file ? 1 : 0,
  790. can_edit_dir: state.edit_dir ? 1 : 0,
  791. can_lock_file: state.lock_file ? 1 : 0,
  792. };
  793. });
  794. self._setAuthUserSaveState(true);
  795. postData('config-dir/permission/save', {
  796. filing_id: node.id,
  797. user_permissions: userPermissions,
  798. }, function(result) {
  799. self._setAuthUserSaveState(false);
  800. var savedPermissions = result && result.permissions instanceof Array
  801. ? result.permissions : [];
  802. var affectedFilings = result && result.filings instanceof Array
  803. ? result.filings : [{ id: node.id, add_user: node.add_user || '' }];
  804. var affectedIds = affectedFilings.map(function(filing) { return String(filing.id); });
  805. for (var pIndex = filingPermissionData.length - 1; pIndex >= 0; pIndex--) {
  806. var cachedPermission = filingPermissionData[pIndex];
  807. var isAffectedNode = cachedPermission.filing_id &&
  808. affectedIds.indexOf(String(cachedPermission.filing_id)) >= 0;
  809. if (isAffectedNode) {
  810. filingPermissionData.splice(pIndex, 1);
  811. }
  812. }
  813. savedPermissions.forEach(function(permission) {
  814. filingPermissionData.push({
  815. uid: Number(permission.uid),
  816. filing_id: String(permission.filing_id),
  817. filing_type: Number(permission.filing_type),
  818. can_upload: Number(permission.can_upload) ? 1 : 0,
  819. can_edit_file: Number(permission.can_edit_file) ? 1 : 0,
  820. can_edit_dir: Number(permission.can_edit_dir) ? 1 : 0,
  821. can_lock_file: Number(permission.can_lock_file) ? 1 : 0,
  822. is_legacy: Number(permission.is_legacy) === 1 ? 1 : 0,
  823. });
  824. });
  825. self._buildAuthUserMap();
  826. affectedFilings.forEach(function(filingInfo) {
  827. var filingId = String(filingInfo.id);
  828. var authUsers = self.authUserMap[filingId] || [];
  829. var srcNode = filingData.find(function(item) { return String(item.id) === filingId; });
  830. if (!srcNode) return;
  831. srcNode.auth_users = authUsers.map(function(user) { return user.name; }).join('、');
  832. srcNode.auth_user_ids = authUsers.map(function(user) { return Number(user.id); });
  833. srcNode.add_user = filingInfo.add_user || '';
  834. });
  835. self._reloadTree();
  836. var sourceIndex = self.filingTree.nodes.findIndex(function(item) {
  837. return String(item.id) === String(node.id);
  838. });
  839. if (sourceIndex >= 0) self.sheet.setSelection(sourceIndex, 0, 1, 1);
  840. self._refreshAuthUserButton(sourceIndex >= 0 ? self.filingTree.nodes[sourceIndex] : null);
  841. $('#auth-user-modal').modal('hide');
  842. toastr.success('保存成功');
  843. }, function() {
  844. self._setAuthUserSaveState(false);
  845. });
  846. });
  847. }
  848. _setAuthUserSaveState(saving) {
  849. this._authUserSaving = Boolean(saving);
  850. $('#auth-user-save')
  851. .prop('disabled', this._authUserSaving)
  852. .text(this._authUserSaving ? '保存中...' : '确定');
  853. }
  854. _showAuthTargetModal(mode) {
  855. var self = this;
  856. var selectedNode = this._getSelectedNode();
  857. this._authTargetReturnId = selectedNode ? String(selectedNode.id) : '';
  858. this._authTargetMode = mode === 'cover' ? 'cover' : 'add';
  859. this._authTargetIds = [];
  860. this._authTargetExpanded = {};
  861. this._authTargetUserIds = [];
  862. this._authTargetPermissionState = {};
  863. this.filingTree.nodes.forEach(function(node) {
  864. if (!node.is_leaf) self._authTargetExpanded[node.tree_id] = true;
  865. });
  866. $('#cd-auth-target-modal-title').text(
  867. this._authTargetMode === 'cover' ? '批量覆盖授权' : '批量新增授权'
  868. );
  869. this._renderAuthTargetTree();
  870. $('#cd-auth-target-user-search').val('');
  871. this._renderAuthTargetUserDropdown();
  872. this._renderAuthTargetUserList();
  873. this._setAuthTargetSaveState(false);
  874. $('#cd-auth-target-list').off('click', '.cd-auth-target-toggle')
  875. .on('click', '.cd-auth-target-toggle', function(event) {
  876. event.preventDefault();
  877. var treeId = Number($(this).attr('data-tree-id'));
  878. self._authTargetExpanded[treeId] = !self._authTargetExpanded[treeId];
  879. self._refreshAuthTargetTreeVisibility();
  880. });
  881. $('#cd-auth-target-list').off('change', '.cd-auth-target-check')
  882. .on('change', '.cd-auth-target-check', function() {
  883. self._setAuthTargetBranchChecked(Number($(this).attr('data-tree-id')), this.checked);
  884. self._syncAuthTargetSelectionState();
  885. });
  886. $('#cd-auth-target-user-options').off('click', '.cd-auth-target-unit-row')
  887. .on('click', '.cd-auth-target-unit-row', function(event) {
  888. event.preventDefault();
  889. event.stopPropagation();
  890. var groupId = $(this).attr('data-group-id');
  891. var children = $('#cd-auth-target-user-options').find('[data-group-content="' + groupId + '"]');
  892. children.toggle();
  893. $('i', this).toggleClass('fa-plus-square fa-minus-square-o');
  894. });
  895. $('#cd-auth-target-user-options').off('click', '.cd-auth-target-add-unit')
  896. .on('click', '.cd-auth-target-add-unit', function(event) {
  897. event.preventDefault();
  898. event.stopPropagation();
  899. var company = $(this).attr('data-company');
  900. self._addAuthTargetUsers(accountList.filter(function(account) {
  901. return (account.company || '未设置单位') === company;
  902. }).map(function(account) { return Number(account.id); }));
  903. });
  904. $('#cd-auth-target-user-options').off('click', '.cd-auth-target-add-user')
  905. .on('click', '.cd-auth-target-add-user', function(event) {
  906. event.preventDefault();
  907. event.stopPropagation();
  908. self._addAuthTargetUsers([Number($(this).attr('data-user-id'))]);
  909. });
  910. $('#cd-auth-target-user-list').off('click', '.cd-auth-target-user-remove')
  911. .on('click', '.cd-auth-target-user-remove', function(event) {
  912. event.preventDefault();
  913. self._removeAuthTargetUsers([Number($(this).attr('data-user-id'))]);
  914. });
  915. $('#cd-auth-target-user-select-all').off('change').on('change', function() {
  916. var checked = this.checked;
  917. var userIds = [];
  918. $('#cd-auth-target-user-list input[name=cd-auth-target-user-check]')
  919. .prop('checked', checked).each(function() {
  920. userIds.push(Number($(this).attr('data-user-id')));
  921. });
  922. if (!checked) self._setAuthTargetUserPermissions(userIds, false);
  923. self._syncAuthTargetUserControls();
  924. });
  925. $('#cd-auth-target-user-list').off('change', 'input[name=cd-auth-target-user-check]')
  926. .on('change', 'input[name=cd-auth-target-user-check]', function() {
  927. var userId = Number($(this).attr('data-user-id'));
  928. if (!this.checked) self._setAuthTargetUserPermissions([userId], false);
  929. self._syncAuthTargetUserControls();
  930. });
  931. $('.cd-auth-target-permission-all').off('change').on('change', function() {
  932. var permissionKey = $(this).attr('data-permission');
  933. var checked = this.checked;
  934. $('#cd-auth-target-user-list .cd-auth-target-permission-check[data-permission="' + permissionKey + '"]')
  935. .each(function() {
  936. var userId = Number($(this).attr('data-user-id'));
  937. $(this).prop('checked', checked);
  938. self._ensureAuthTargetUserPermissionState(userId)[permissionKey] = checked;
  939. if (checked) {
  940. $('#cd-auth-target-user-list input[name=cd-auth-target-user-check][data-user-id="' + userId + '"]')
  941. .prop('checked', true);
  942. }
  943. });
  944. self._syncAuthTargetUserControls();
  945. });
  946. $('#cd-auth-target-user-list').off('change', '.cd-auth-target-permission-check')
  947. .on('change', '.cd-auth-target-permission-check', function() {
  948. var userId = Number($(this).attr('data-user-id'));
  949. var permissionKey = $(this).attr('data-permission');
  950. self._ensureAuthTargetUserPermissionState(userId)[permissionKey] = this.checked;
  951. if (this.checked) {
  952. $('#cd-auth-target-user-list input[name=cd-auth-target-user-check][data-user-id="' + userId + '"]')
  953. .prop('checked', true);
  954. }
  955. self._syncAuthTargetUserControls();
  956. });
  957. $('#cd-auth-target-user-delete').off('click').on('click', function(event) {
  958. event.preventDefault();
  959. if ($(this).attr('aria-disabled') === 'true') return;
  960. var removeIds = $('#cd-auth-target-user-list input[name=cd-auth-target-user-check]:checked')
  961. .map(function() { return Number($(this).attr('data-user-id')); }).get();
  962. self._removeAuthTargetUsers(removeIds);
  963. });
  964. $('#cd-auth-target-save').off('click').on('click', function() {
  965. if (self._authTargetSaving) return;
  966. if (self._authTargetIds.length === 0) {
  967. toastr.warning('请选择目标资料目录');
  968. return;
  969. }
  970. if (self._authTargetUserIds.length === 0) {
  971. toastr.warning('请选择需要授权的用户');
  972. return;
  973. }
  974. var isCover = self._authTargetMode === 'cover';
  975. self._setAuthTargetSaveState(true);
  976. postData(isCover ? 'config-dir/permission/cover-other' : 'config-dir/permission/add-other', {
  977. target_filing_ids: self._authTargetIds.slice(),
  978. user_permissions: self._getAuthTargetUserPermissions(),
  979. }, function(result) {
  980. self._setAuthTargetSaveState(false);
  981. self._applyAuthTargetResult(result || {});
  982. $('#cd-auth-target-modal').modal('hide');
  983. toastr.success(isCover ? '批量覆盖授权成功' : '批量新增授权成功');
  984. }, function() {
  985. self._setAuthTargetSaveState(false);
  986. });
  987. });
  988. $('#cd-auth-target-modal').modal('show');
  989. }
  990. _renderAuthTargetTree() {
  991. var self = this;
  992. var nodes = this.filingTree.nodes;
  993. var html = [];
  994. nodes.forEach(function(node) {
  995. var hasChildren = nodes.some(function(child) { return child.tree_pid === node.tree_id; });
  996. var indent = Math.max(Number(node.level) - 1, 0) * 18;
  997. html.push('<tr data-target-tree-id="' + node.tree_id + '" data-target-tree-pid="' + node.tree_pid + '">');
  998. html.push('<td class="text-center"><input type="checkbox" class="cd-auth-target-check" data-tree-id="' +
  999. node.tree_id + '" data-filing-id="' + self._escapeAuthUserHtml(node.id) + '"></td>');
  1000. html.push('<td><span style="display:inline-block;width:' + indent + 'px;"></span>');
  1001. if (hasChildren) {
  1002. html.push('<a href="javascript:void(0);" class="cd-auth-target-toggle mr-1" data-tree-id="' + node.tree_id + '">');
  1003. html.push('<i class="fa ' + (self._authTargetExpanded[node.tree_id] ? 'fa-minus-square-o' : 'fa-plus-square-o') + '"></i></a>');
  1004. html.push('<img class="mr-1 cd-auth-target-folder" data-tree-id="' + node.tree_id +
  1005. '" src="/public/images/filesjs-tree-open.png" width="18" height="18" alt="">');
  1006. } else {
  1007. html.push('<span class="d-inline-block mr-1" style="width:12px;"></span>');
  1008. html.push('<img class="mr-1" src="/public/images/filesjs-tree-leaf.png" width="18" height="18" alt="">');
  1009. }
  1010. html.push(self._escapeAuthUserHtml(node.name) + '</td></tr>');
  1011. });
  1012. if (nodes.length === 0) {
  1013. html.push('<tr><td colspan="2" class="text-muted text-center py-4">暂无可选择的目录</td></tr>');
  1014. }
  1015. $('#cd-auth-target-list').html(html.join(''));
  1016. this._refreshAuthTargetTreeVisibility();
  1017. this._syncAuthTargetSelectionState();
  1018. }
  1019. _refreshAuthTargetTreeVisibility() {
  1020. var self = this;
  1021. var nodeMap = {};
  1022. this.filingTree.nodes.forEach(function(node) { nodeMap[node.tree_id] = node; });
  1023. this.filingTree.nodes.forEach(function(node) {
  1024. var visible = true;
  1025. var parentTreeId = node.tree_pid;
  1026. while (parentTreeId !== -1) {
  1027. if (!self._authTargetExpanded[parentTreeId]) {
  1028. visible = false;
  1029. break;
  1030. }
  1031. var parent = nodeMap[parentTreeId];
  1032. if (!parent) break;
  1033. parentTreeId = parent.tree_pid;
  1034. }
  1035. $('#cd-auth-target-list tr[data-target-tree-id="' + node.tree_id + '"]').toggle(visible);
  1036. var expanded = Boolean(self._authTargetExpanded[node.tree_id]);
  1037. $('#cd-auth-target-list .cd-auth-target-toggle[data-tree-id="' + node.tree_id + '"] i')
  1038. .toggleClass('fa-minus-square-o', expanded)
  1039. .toggleClass('fa-plus-square-o', !expanded);
  1040. $('#cd-auth-target-list .cd-auth-target-folder[data-tree-id="' + node.tree_id + '"]')
  1041. .attr('src', expanded
  1042. ? '/public/images/filesjs-tree-open.png'
  1043. : '/public/images/filesjs-tree-closed.png');
  1044. });
  1045. }
  1046. _setAuthTargetBranchChecked(treeId, checked) {
  1047. var pending = [treeId];
  1048. while (pending.length > 0) {
  1049. var currentId = pending.shift();
  1050. $('#cd-auth-target-list .cd-auth-target-check[data-tree-id="' + currentId + '"]')
  1051. .prop('checked', checked).prop('indeterminate', false);
  1052. this.filingTree.nodes.filter(function(node) { return node.tree_pid === currentId; })
  1053. .forEach(function(child) { pending.push(child.tree_id); });
  1054. }
  1055. this._syncAuthTargetParentChecks();
  1056. }
  1057. _syncAuthTargetParentChecks() {
  1058. var nodes = this.filingTree.nodes.slice().sort(function(a, b) { return Number(b.level) - Number(a.level); });
  1059. nodes.forEach(function(node) {
  1060. var children = nodes.filter(function(child) { return child.tree_pid === node.tree_id; });
  1061. if (children.length === 0) return;
  1062. var childChecks = children.map(function(child) {
  1063. return $('#cd-auth-target-list .cd-auth-target-check[data-tree-id="' + child.tree_id + '"]')[0];
  1064. }).filter(Boolean);
  1065. var allChecked = childChecks.length > 0 && childChecks.every(function(check) { return check.checked; });
  1066. var hasChecked = childChecks.some(function(check) { return check.checked || check.indeterminate; });
  1067. $('#cd-auth-target-list .cd-auth-target-check[data-tree-id="' + node.tree_id + '"]')
  1068. .prop('checked', allChecked)
  1069. .prop('indeterminate', !allChecked && hasChecked);
  1070. });
  1071. }
  1072. _syncAuthTargetSelectionState() {
  1073. this._authTargetIds = $('#cd-auth-target-list .cd-auth-target-check:checked').map(function() {
  1074. return String($(this).attr('data-filing-id'));
  1075. }).get();
  1076. var hasTargets = this._authTargetIds.length > 0;
  1077. var hasUsers = (this._authTargetUserIds || []).length > 0;
  1078. $('#cd-auth-target-save').prop('disabled', this._authTargetSaving || !hasTargets || !hasUsers);
  1079. $('#cd-auth-cover-warning').toggle(this._authTargetMode === 'cover' && hasTargets);
  1080. }
  1081. _setAuthTargetSaveState(saving) {
  1082. this._authTargetSaving = Boolean(saving);
  1083. $('#cd-auth-target-save').text(this._authTargetSaving ? '保存中...' : '确定');
  1084. this._syncAuthTargetSelectionState();
  1085. }
  1086. _renderAuthTargetUserDropdown() {
  1087. var self = this;
  1088. var keyword = String($('#cd-auth-target-user-search').val() || '').trim();
  1089. if (keyword) {
  1090. $('#cd-auth-target-user-options').html(this._getAuthUserSearchHtml(keyword, true)).scrollTop(0);
  1091. this._syncAuthTargetUserDropdownMarks();
  1092. return;
  1093. }
  1094. var html = [];
  1095. this._getAuthUserGroups().forEach(function(group, groupIndex) {
  1096. html.push('<dt class="px-2 py-1 mb-0 cd-auth-target-unit-row" data-group-id="' + groupIndex + '" style="cursor:pointer;">');
  1097. html.push('<a href="javascript:void(0);"><i class="fa fa-plus-square mr-1"></i></a>');
  1098. html.push('<span>' + self._escapeAuthUserHtml(group.company) + '</span></dt>');
  1099. html.push('<div class="dd-content" data-group-content="' + groupIndex + '" style="display:none;">');
  1100. html.push('<dd class="border-bottom p-2 mb-0 cd-auth-target-add-unit" data-company="' +
  1101. self._escapeAuthUserHtml(group.company) + '"><span class="text-primary">添加单位下全部用户</span></dd>');
  1102. group.users.forEach(function(user) {
  1103. html.push(self._getAuthUserOptionHtml(user, true));
  1104. });
  1105. html.push('</div>');
  1106. });
  1107. if (html.length === 0) html.push('<div class="text-muted text-center py-3">数据范围内暂无可选用户</div>');
  1108. $('#cd-auth-target-user-options').html(html.join('')).scrollTop(0);
  1109. this._syncAuthTargetUserDropdownMarks();
  1110. }
  1111. _syncAuthTargetUserDropdownMarks() {
  1112. var selectedIds = this._authTargetUserIds || [];
  1113. $('#cd-auth-target-user-options [data-target-user-mark]').each(function() {
  1114. var userId = Number($(this).attr('data-target-user-mark'));
  1115. $(this).toggle(selectedIds.indexOf(userId) >= 0);
  1116. });
  1117. }
  1118. _addAuthTargetUsers(userIds) {
  1119. var selectableIds = accountList.map(function(account) { return Number(account.id); });
  1120. for (var index = 0; index < userIds.length; index++) {
  1121. var userId = Number(userIds[index]);
  1122. if (selectableIds.indexOf(userId) < 0 || this._authTargetUserIds.indexOf(userId) >= 0) continue;
  1123. this._authTargetUserIds.push(userId);
  1124. this._ensureAuthTargetUserPermissionState(userId);
  1125. }
  1126. this._renderAuthTargetUserList();
  1127. this._syncAuthTargetUserDropdownMarks();
  1128. }
  1129. _removeAuthTargetUsers(userIds) {
  1130. var self = this;
  1131. this._authTargetUserIds = this._authTargetUserIds.filter(function(userId) {
  1132. return userIds.indexOf(userId) < 0;
  1133. });
  1134. userIds.forEach(function(userId) { delete self._authTargetPermissionState[userId]; });
  1135. this._renderAuthTargetUserList();
  1136. this._syncAuthTargetUserDropdownMarks();
  1137. }
  1138. _ensureAuthTargetUserPermissionState(userId) {
  1139. if (!this._authTargetPermissionState[userId]) {
  1140. this._authTargetPermissionState[userId] = {
  1141. upload_reference: false,
  1142. edit_file: false,
  1143. edit_dir: false,
  1144. lock_file: false,
  1145. };
  1146. }
  1147. return this._authTargetPermissionState[userId];
  1148. }
  1149. _renderAuthTargetUserList() {
  1150. var self = this;
  1151. var html = [];
  1152. this.authAccountList.filter(function(account) {
  1153. return self._authTargetUserIds.indexOf(Number(account.id)) >= 0;
  1154. }).forEach(function(user) {
  1155. var userId = Number(user.id);
  1156. var state = self._ensureAuthTargetUserPermissionState(userId);
  1157. var selected = state.upload_reference || state.edit_file || state.edit_dir || state.lock_file;
  1158. html.push('<tr class="text-center" data-user-id="' + userId + '">');
  1159. html.push('<td><input type="checkbox" name="cd-auth-target-user-check" data-user-id="' + userId + '"' + (selected ? ' checked' : '') + '></td>');
  1160. html.push('<td>' + self._escapeAuthUserHtml(user.name) + '</td>');
  1161. html.push('<td><i class="fa fa-check text-primary"></i></td>');
  1162. [ 'upload_reference', 'edit_file', 'edit_dir', 'lock_file' ].forEach(function(key) {
  1163. html.push('<td><input type="checkbox" class="cd-auth-target-permission-check" data-user-id="' + userId +
  1164. '" data-permission="' + key + '"' + (state[key] ? ' checked' : '') + '></td>');
  1165. });
  1166. html.push('<td><a href="javascript:void(0);" class="text-danger cd-auth-target-user-remove" data-user-id="' + userId + '">移除</a></td></tr>');
  1167. });
  1168. if (html.length === 0) {
  1169. html.push('<tr><td colspan="8" class="text-muted text-center py-4">请添加需要授权的用户</td></tr>');
  1170. }
  1171. $('#cd-auth-target-user-list').html(html.join(''));
  1172. this._syncAuthTargetUserControls();
  1173. }
  1174. _setAuthTargetUserPermissions(userIds, checked) {
  1175. var self = this;
  1176. userIds.forEach(function(userId) {
  1177. $('#cd-auth-target-user-list .cd-auth-target-permission-check[data-user-id="' + userId + '"]')
  1178. .each(function() {
  1179. var key = $(this).attr('data-permission');
  1180. $(this).prop('checked', checked);
  1181. self._ensureAuthTargetUserPermissionState(userId)[key] = checked;
  1182. });
  1183. });
  1184. }
  1185. _syncAuthTargetUserControls() {
  1186. var userChecks = $('#cd-auth-target-user-list input[name=cd-auth-target-user-check]');
  1187. var checkedCount = userChecks.filter(':checked').length;
  1188. $('#cd-auth-target-user-select-all')
  1189. .prop('checked', userChecks.length > 0 && checkedCount === userChecks.length)
  1190. .prop('indeterminate', checkedCount > 0 && checkedCount < userChecks.length);
  1191. $('.cd-auth-target-permission-all').each(function() {
  1192. var key = $(this).attr('data-permission');
  1193. var permissionChecks = $('#cd-auth-target-user-list .cd-auth-target-permission-check[data-permission="' + key + '"]');
  1194. var permissionCheckedCount = permissionChecks.filter(':checked').length;
  1195. $(this)
  1196. .prop('checked', permissionChecks.length > 0 && permissionCheckedCount === permissionChecks.length)
  1197. .prop('indeterminate', permissionCheckedCount > 0 && permissionCheckedCount < permissionChecks.length);
  1198. });
  1199. $('#cd-auth-target-user-delete')
  1200. .attr('aria-disabled', checkedCount > 0 ? 'false' : 'true')
  1201. .attr('tabindex', checkedCount > 0 ? '0' : '-1')
  1202. .toggleClass('text-muted', checkedCount === 0)
  1203. .toggleClass('text-danger', checkedCount > 0);
  1204. this._syncAuthTargetSelectionState();
  1205. }
  1206. _getAuthTargetUserPermissions() {
  1207. var self = this;
  1208. return this._authTargetUserIds.map(function(userId) {
  1209. var state = self._ensureAuthTargetUserPermissionState(userId);
  1210. return {
  1211. uid: userId,
  1212. can_upload: state.upload_reference ? 1 : 0,
  1213. can_edit_file: state.edit_file ? 1 : 0,
  1214. can_edit_dir: state.edit_dir ? 1 : 0,
  1215. can_lock_file: state.lock_file ? 1 : 0,
  1216. };
  1217. });
  1218. }
  1219. _applyAuthTargetResult(result) {
  1220. var targetIds = this._authTargetIds || [];
  1221. var permissions = result.permissions instanceof Array ? result.permissions : [];
  1222. var filingInfos = result.filings instanceof Array ? result.filings : [];
  1223. for (var index = filingPermissionData.length - 1; index >= 0; index--) {
  1224. var permission = filingPermissionData[index];
  1225. if (permission.filing_id && targetIds.indexOf(String(permission.filing_id)) >= 0) {
  1226. filingPermissionData.splice(index, 1);
  1227. }
  1228. }
  1229. permissions.forEach(function(permission) { filingPermissionData.push(permission); });
  1230. this._buildAuthUserMap();
  1231. var self = this;
  1232. targetIds.forEach(function(filingId) {
  1233. var filing = filingData.find(function(item) { return String(item.id) === filingId; });
  1234. if (!filing) return;
  1235. var authUsers = self.authUserMap[filingId] || [];
  1236. filing.auth_users = authUsers.map(function(user) { return user.name; }).join('、');
  1237. filing.auth_user_ids = authUsers.map(function(user) { return Number(user.id); });
  1238. var filingInfo = filingInfos.find(function(item) { return String(item.id) === filingId; });
  1239. if (filingInfo) filing.add_user = filingInfo.add_user || '';
  1240. });
  1241. var returnId = this._authTargetReturnId;
  1242. this._reloadTree();
  1243. var returnIndex = this.filingTree.nodes.findIndex(function(node) { return String(node.id) === returnId; });
  1244. if (returnIndex >= 0) this.sheet.setSelection(returnIndex, 0, 1, 1);
  1245. this._refreshAuthUserButton(returnIndex >= 0 ? this.filingTree.nodes[returnIndex] : this._getSelectedNode());
  1246. }
  1247. _escapeAuthUserHtml(value) {
  1248. return $('<div>').text(value === undefined || value === null ? '' : value).html();
  1249. }
  1250. _getAuthUserGroups() {
  1251. var groups = [];
  1252. accountList.forEach(function(account) {
  1253. var company = account.company || '未设置单位';
  1254. var group = groups.find(function(item) { return item.company === company; });
  1255. if (!group) {
  1256. group = { company: company, users: [] };
  1257. groups.push(group);
  1258. }
  1259. group.users.push(account);
  1260. });
  1261. return groups;
  1262. }
  1263. _renderAuthUserDropdown() {
  1264. var self = this;
  1265. var keyword = String($('#cd-auth-user-search').val() || '').trim();
  1266. if (keyword) {
  1267. $('#cd-auth-user-options').html(this._getAuthUserSearchHtml(keyword, false)).scrollTop(0);
  1268. this._syncAuthUserDropdownMarks();
  1269. return;
  1270. }
  1271. var html = [];
  1272. var groups = this._getAuthUserGroups();
  1273. groups.forEach(function(group, groupIndex) {
  1274. html.push('<dt class="px-2 py-1 mb-0 cd-auth-unit-row" data-group-id="' + groupIndex + '" style="cursor:pointer;">');
  1275. html.push('<a href="javascript:void(0);" class="cd-auth-unit-toggle">');
  1276. html.push('<i class="fa fa-plus-square mr-1"></i></a>');
  1277. html.push('<span>' + self._escapeAuthUserHtml(group.company) + '</span></dt>');
  1278. html.push('<div class="dd-content" data-group-content="' + groupIndex + '" style="display:none;">');
  1279. html.push('<dd class="border-bottom p-2 mb-0 cd-auth-add-unit" data-company="' + self._escapeAuthUserHtml(group.company) + '">');
  1280. html.push('<span class="text-primary">添加单位下全部用户</span></dd>');
  1281. group.users.forEach(function(user) {
  1282. html.push(self._getAuthUserOptionHtml(user, false));
  1283. });
  1284. html.push('</div>');
  1285. });
  1286. if (groups.length === 0) html.push('<div class="text-muted text-center py-3">数据范围内暂无可选用户</div>');
  1287. $('#cd-auth-user-options').html(html.join('')).scrollTop(0);
  1288. this._syncAuthUserDropdownMarks();
  1289. }
  1290. _syncAuthUserDropdownMarks() {
  1291. var selectedIds = this._authUserIds || [];
  1292. $('#cd-auth-user-options [data-user-mark]').each(function() {
  1293. var userId = Number($(this).attr('data-user-mark'));
  1294. $(this).toggle(selectedIds.indexOf(userId) >= 0);
  1295. });
  1296. }
  1297. _addAuthUsers(userIds) {
  1298. var selectableIds = accountList.map(function(account) { return Number(account.id); });
  1299. for (var i = 0; i < userIds.length; i++) {
  1300. var userId = Number(userIds[i]);
  1301. if (selectableIds.indexOf(userId) < 0 || this._authUserIds.indexOf(userId) >= 0) continue;
  1302. this._authUserIds.push(userId);
  1303. this._ensureAuthUserPermissionState(userId);
  1304. }
  1305. this._renderAuthUserList();
  1306. this._syncAuthUserDropdownMarks();
  1307. }
  1308. _ensureAuthUserPermissionState(userId) {
  1309. if (!this._authUserPermissionState[userId]) {
  1310. this._authUserPermissionState[userId] = {
  1311. upload_reference: false,
  1312. edit_file: false,
  1313. edit_dir: false,
  1314. lock_file: false,
  1315. };
  1316. }
  1317. return this._authUserPermissionState[userId];
  1318. }
  1319. _getInitialAuthUserPermissionState(userId, filingId, filingType) {
  1320. var explicit = filingPermissionData.find(function(permission) {
  1321. return Number(permission.uid) === Number(userId) &&
  1322. Number(permission.is_legacy) !== 1 && permission.filing_id &&
  1323. String(permission.filing_id) === String(filingId);
  1324. });
  1325. if (explicit) {
  1326. return {
  1327. upload_reference: Number(explicit.can_upload) === 1,
  1328. edit_file: Number(explicit.can_edit_file) === 1,
  1329. edit_dir: Number(explicit.can_edit_dir) === 1,
  1330. lock_file: Number(explicit.can_lock_file) === 1,
  1331. };
  1332. }
  1333. var compatiblePermission = filingPermissionData.find(function(permission) {
  1334. return Number(permission.uid) === Number(userId) && Number(permission.is_legacy) === 1 &&
  1335. permission.filing_id && String(permission.filing_id) === String(filingId);
  1336. });
  1337. if (compatiblePermission) {
  1338. return {
  1339. upload_reference: Number(compatiblePermission.can_upload) === 1,
  1340. edit_file: Number(compatiblePermission.can_edit_file) === 1,
  1341. edit_dir: Number(compatiblePermission.can_edit_dir) === 1,
  1342. lock_file: Number(compatiblePermission.can_lock_file) === 1,
  1343. };
  1344. }
  1345. var legacyPermission = filingPermissionData.find(function(permission) {
  1346. return Number(permission.uid) === Number(userId) && !permission.filing_id &&
  1347. Number(permission.filing_type) === Number(filingType);
  1348. });
  1349. if (legacyPermission) {
  1350. return {
  1351. upload_reference: Number(legacyPermission.can_upload) === 1,
  1352. edit_file: Number(legacyPermission.can_edit_file) === 1,
  1353. edit_dir: Number(legacyPermission.can_edit_dir) === 1,
  1354. lock_file: Number(legacyPermission.can_lock_file) === 1,
  1355. };
  1356. }
  1357. var oldPermission = permissionData.find(function(permission) {
  1358. return Number(permission.uid) === Number(userId);
  1359. });
  1360. var oldFilingTypes = oldPermission && oldPermission.filing_type === 'all'
  1361. ? 'all'
  1362. : (oldPermission && oldPermission.filing_type instanceof Array
  1363. ? oldPermission.filing_type.map(function(value) { return Number(value); })
  1364. : []);
  1365. var oldValues = oldPermission && oldPermission.file_permission
  1366. ? (oldPermission.file_permission instanceof Array
  1367. ? oldPermission.file_permission.map(function(value) { return Number(value); })
  1368. : oldPermission.file_permission.split(',').map(function(value) { return Number(value); }))
  1369. : [];
  1370. var hasOldView = oldFilingTypes === 'all' || oldFilingTypes.indexOf(Number(filingType)) >= 0;
  1371. return {
  1372. upload_reference: hasOldView && oldValues.indexOf(2) >= 0,
  1373. edit_file: hasOldView && oldValues.indexOf(4) >= 0,
  1374. edit_dir: hasOldView && oldValues.indexOf(3) >= 0,
  1375. lock_file: false,
  1376. };
  1377. }
  1378. _setAuthUserPermissions(userIds, checked) {
  1379. var self = this;
  1380. userIds.forEach(function(userId) {
  1381. $('#auth-user-list .cd-auth-permission-check[data-user-id="' + userId + '"]').each(function() {
  1382. var permissionKey = $(this).attr('data-permission');
  1383. $(this).prop('checked', checked);
  1384. self._ensureAuthUserPermissionState(userId)[permissionKey] = checked;
  1385. });
  1386. });
  1387. }
  1388. _renderAuthUserList() {
  1389. var self = this;
  1390. var html = [];
  1391. var selectedUsers = this.authAccountList.filter(function(account) {
  1392. return self._authUserIds.indexOf(Number(account.id)) >= 0;
  1393. });
  1394. selectedUsers.forEach(function(user) {
  1395. var userId = Number(user.id);
  1396. var permissionState = self._ensureAuthUserPermissionState(userId);
  1397. var userSelected = permissionState.upload_reference || permissionState.edit_file ||
  1398. permissionState.edit_dir || permissionState.lock_file;
  1399. html.push('<tr class="text-center" data-user-id="' + userId + '">');
  1400. html.push('<td><input type="checkbox" name="cd-auth-user-check" data-user-id="' + userId + '"' + (userSelected ? ' checked' : '') + '></td>');
  1401. html.push('<td>' + self._escapeAuthUserHtml(user.name) + '</td>');
  1402. html.push('<td><i class="fa fa-check text-primary"></i></td>');
  1403. html.push('<td><input type="checkbox" class="cd-auth-permission-check" data-user-id="' + userId + '" data-permission="upload_reference"' + (permissionState.upload_reference ? ' checked' : '') + '></td>');
  1404. html.push('<td><input type="checkbox" class="cd-auth-permission-check" data-user-id="' + userId + '" data-permission="edit_file"' + (permissionState.edit_file ? ' checked' : '') + '></td>');
  1405. html.push('<td><input type="checkbox" class="cd-auth-permission-check" data-user-id="' + userId + '" data-permission="edit_dir"' + (permissionState.edit_dir ? ' checked' : '') + '></td>');
  1406. html.push('<td><input type="checkbox" class="cd-auth-permission-check" data-user-id="' + userId + '" data-permission="lock_file"' + (permissionState.lock_file ? ' checked' : '') + '></td>');
  1407. html.push('<td><a href="javascript:void(0);" class="text-danger cd-auth-user-remove" data-user-id="' + userId + '">移除</a></td>');
  1408. html.push('</tr>');
  1409. });
  1410. if (selectedUsers.length === 0) html.push('<tr><td colspan="8" class="text-muted text-center py-4">当前目录暂未添加授权用户</td></tr>');
  1411. $('#auth-user-list').html(html.join(''));
  1412. this._syncAuthUserSelectAll();
  1413. this._syncAuthPermissionHeaders();
  1414. this._refreshAuthUserActionState();
  1415. }
  1416. _syncAuthUserSelectAll() {
  1417. var rowChecks = $('#auth-user-list input[name=cd-auth-user-check]');
  1418. var checkedCount = rowChecks.filter(':checked').length;
  1419. $('#cd-auth-user-select-all')
  1420. .prop('checked', rowChecks.length > 0 && checkedCount === rowChecks.length)
  1421. .prop('indeterminate', checkedCount > 0 && checkedCount < rowChecks.length);
  1422. }
  1423. _syncAuthPermissionHeaders() {
  1424. $('.cd-auth-permission-all').each(function() {
  1425. var permissionKey = $(this).attr('data-permission');
  1426. var rowChecks = $('#auth-user-list .cd-auth-permission-check[data-permission="' + permissionKey + '"]');
  1427. var checkedCount = rowChecks.filter(':checked').length;
  1428. $(this)
  1429. .prop('checked', rowChecks.length > 0 && checkedCount === rowChecks.length)
  1430. .prop('indeterminate', checkedCount > 0 && checkedCount < rowChecks.length);
  1431. });
  1432. }
  1433. _refreshAuthUserActionState() {
  1434. var hasUsers = $('#auth-user-list input[name=cd-auth-user-check]:checked').length > 0;
  1435. $('.cd-auth-list-action')
  1436. .attr('aria-disabled', hasUsers ? 'false' : 'true')
  1437. .attr('tabindex', hasUsers ? '0' : '-1');
  1438. $('#cd-auth-batch-delete')
  1439. .toggleClass('text-muted', !hasUsers)
  1440. .toggleClass('text-danger', hasUsers);
  1441. }
  1442. _getAuthUserOptionHtml(user, isTarget) {
  1443. var userId = Number(user.id);
  1444. var addClass = isTarget ? 'cd-auth-target-add-user' : 'cd-auth-add-user';
  1445. var markAttribute = isTarget ? 'data-target-user-mark' : 'data-user-mark';
  1446. var markClass = isTarget ? '' : 'cd-auth-selected-mark ';
  1447. var html = [];
  1448. html.push('<dd class="border-bottom p-2 mb-0 ' + addClass + '" data-user-id="' + userId + '">');
  1449. html.push('<p class="mb-0 d-flex"><span class="text-primary">' + this._escapeAuthUserHtml(user.name) + '</span>');
  1450. html.push('<span class="ml-auto">' + this._escapeAuthUserHtml(user.mobile || '') + '</span>');
  1451. html.push('<span class="' + markClass + 'text-success ml-2" ' + markAttribute + '="' + userId + '"><i class="fa fa-check"></i></span></p>');
  1452. html.push('<span class="text-muted">' + this._escapeAuthUserHtml(user.role || '') + '</span></dd>');
  1453. return html.join('');
  1454. }
  1455. _getAuthUserSearchHtml(keyword, isTarget) {
  1456. var self = this;
  1457. var users = accountList.filter(function(account) {
  1458. return String(account.name || '').indexOf(keyword) >= 0 ||
  1459. String(account.mobile || '').indexOf(keyword) >= 0;
  1460. });
  1461. if (users.length === 0) return '<dd class="text-muted text-center py-3 mb-0">未找到匹配用户</dd>';
  1462. return users.map(function(user) {
  1463. return self._getAuthUserOptionHtml(user, isTarget);
  1464. }).join('');
  1465. }
  1466. }
  1467. new ConfigDir();
  1468. });