Browse Source

资料归集模板库相关

MaiXinRong 10 months ago
parent
commit
d2c829a441

+ 5 - 1
app/controller/file_controller.js

@@ -385,7 +385,7 @@ module.exports = app => {
                 if (!renderData.template) throw '查看的资料模板不存在';
 
                 renderData.templateData = await ctx.service.filingTemplate.getData(renderData.template.id);
-                await this.layout('file/template.ejs', renderData);
+                await this.layout('file/template.ejs', renderData, 'file/template_modal.ejs');
             } catch (err) {
                 ctx.log(err);
                 ctx.session.postError = err.toString();
@@ -450,6 +450,10 @@ module.exports = app => {
                 } else if (data.updateType === 'move') {
                     if (!data.id || !(data.tree_order >= 0)) throw '数据错误';
                     result = await ctx.service.filingTemplate.move(ctx.params.id, data);
+                } else if (data.updateType === 'import') {
+                    result = await ctx.service.filingTemplate.import(ctx.params.id, data.data);
+                } else if (data.updateType === 'multi' ) {
+                    result = await ctx.service.filingTemplate.multiUpdate(ctx.params.id, data.data);
                 }
                 ctx.body = { err: 0, msg: '', data: result };
             } catch (err) {

+ 174 - 0
app/public/js/filing_template.js

@@ -117,6 +117,13 @@ $(document).ready(function() {
                 }
             });
         }
+        batchUpdateFiling(data, callback) {
+            const self = this;
+            postData(`${window.location.pathname}/update`, data, function(result) {
+                self.analysisFiling(result);
+                if (callback) callback();
+            })
+        }
     }
     const levelTreeSetting = {
         treeId: 'filing',
@@ -262,4 +269,171 @@ $(document).ready(function() {
             '</div>');
         $(`.table-file[tempId=${tempId}]`).html(html.join(''));
     });
+
+    class MultiObj {
+        constructor(setting) {
+            this.modal = $(`#${setting.modal}`);
+            this.spread = SpreadJsObj.createNewSpread($(`#${setting.spread}`)[0]);
+            this.sheet = this.spread.getActiveSheet();
+            this.spreadSetting = {
+                cols: [
+                    { title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 250, formatter: '@', readOnly: true, cellType: 'tree' },
+                    { title: '固定', colSpan: '1', rowSpan: '1', field: 'is_fixed', hAlign: 1, width: 50, cellType: 'checkbox' },
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+            };
+            SpreadJsObj.initSheet(this.sheet, this.spreadSetting);
+            this.checkTree = createNewPathTree('base', {
+                id: 'tree_id',
+                pid: 'tree_pid',
+                order: 'order',
+                level: 'level',
+                isLeaf: 'is_leaf',
+                fullPath: 'full_path',
+                rootId: -1,
+            });
+            const self = this;
+            this.modal.bind('shown.bs.modal', function() {
+                self.spread.refresh();
+            });
+            this.spread.bind(spreadNS.Events.ButtonClicked, function(e, info) {
+                if (!info.sheet.zh_setting) return;
+                const sheet = info.sheet, cellType = sheet.getCellType(info.row, info.col);
+                if (cellType instanceof  spreadNS.CellTypes.CheckBox) {
+                    if (sheet.isEditing()) sheet.endEdit(true);
+                }
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field !== 'is_fixed') return;
+                const tree = self.checkTree;
+                const node = tree.nodes[info.row];
+                if (node.level <= 1) {
+                    toastr.warning('顶层节点不可取消固定');
+                    SpreadJsObj.reLoadRowsData(info.sheet, [info.row]);
+                    return;
+                }
+
+                if (!node.is_fixed) {
+                    node.is_fixed = true;
+                    const row = [tree.nodes.indexOf(node)];
+                    const parents = tree.getAllParents(node);
+                    for (const p of parents) {
+                        if (p.is_fixed) continue;
+                        p.is_fixed = true;
+                        row.push(tree.nodes.indexOf(p));
+                    }
+                    SpreadJsObj.reLoadRowsData(info.sheet, row);
+                } else {
+                    node.is_fixed = false;
+                    const row = [tree.nodes.indexOf(node)];
+                    const posterity = tree.getPosterity(node);
+                    for (const p of posterity) {
+                        if (!p.is_fixed) continue;
+                        p.is_fixed = false;
+                        row.push(tree.nodes.indexOf(p));
+                    }
+                    SpreadJsObj.reLoadRowsData(info.sheet, row);
+                }
+            });
+            $(`#${setting.modal}-ok`).click(function() {
+                const data = self.getMultiUpdateData();
+                filingObj.batchUpdateFiling(data, function() {
+                    self.modal.modal('hide');
+                });
+            });
+        }
+        getMultiUpdateData() {
+            const data = [];
+            for (const [i, node] of this.checkTree.nodes.entries()) {
+                node.source_filing_type = i + 1;
+                let filing_type = node.source_filing_type;
+                if (!node.is_fixed) {
+                    const parents = this.checkTree.getAllParents(node);
+                    const index = parents.lastIndexOf(x => { return x.is_fixed; });
+                    filing_type = parents[index].source_filing_type;
+                }
+                data.push({ id: node.id, is_fixed: node.is_fixed, filing_type });
+            }
+            return {updateType: 'multi', data};
+        }
+        _convertData(sourceTree) {
+            const data = [];
+            for (const node of sourceTree.nodes) {
+                const parent = node.tree_pid === '-1' ? undefined : data.find(x => { return x.id === node.tree_pid; });
+                const child = sourceTree.nodes.find(x => { return x.tree_pid === node.id; });
+                data.push({
+                    id: node.id,
+                    tree_id: data.length + 1,
+                    tree_pid: parent ? parent.tree_id : -1,
+                    order: node.tree_order + 1,
+                    level: node.tree_level,
+                    is_leaf: !child,
+                    full_path: '',
+                    name: node.name,
+                    is_fixed: node.is_fixed,
+                    filing_type: node.filing_type,
+                });
+            }
+            return data;
+        }
+        reload(sourceTree) {
+            this.checkTree.loadDatas(this._convertData(sourceTree));
+            SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.checkTree);
+        }
+        show() {
+            this.modal.modal('show');
+        }
+        exportFile(sourceTree) {
+            const exportData = sourceTree.nodes.map(node => { return {
+                id: node.id,
+                tree_pid: node.tree_pid,
+                tree_order: node.tree_order,
+                tree_level: node.tree_level,
+                name: node.name,
+                is_fixed: node.is_fixed,
+                filing_type: node.filing_type,
+            }});
+            const blob = new Blob([JSON.stringify(exportData, '', '')], { type: 'application/text'});
+            const template = templateList.find(x => { return x.id === sourceTree.nodes[0].temp_id });
+            saveAs(blob, `${template.name}.json`);
+        }
+        importFromJSON(str) {
+            try {
+                const data = JSON.parse(str);
+                filingObj.batchUpdateFiling({ updateType: 'import', data });
+            } catch(err) {
+                toastr.error('导入文件格式错误,无法解析');
+            }
+        }
+        importFile(file) {
+            if (!file) return;
+            const self = this;
+            let reader = new FileReader();
+            reader.onload = function() {
+                self.importFromJSON(this.result);
+            };
+            reader.readAsText(file);
+        }
+    }
+    let multiObj = new MultiObj({ modal: 'multi-set', spread: 'multi-spread' });
+    $('#multi-setting').click(() => {
+        multiObj.reload(filingObj.dragTree);
+        multiObj.show();
+    });
+
+    $('#export-template').click(() => {
+        multiObj.exportFile(filingObj.dragTree);
+    });
+    $('#import-template').click(() => {
+        selectFile({
+            fileType: '*.json',
+            select: function(file) {
+                multiObj.importFile(file)
+            }
+        });
+    });
 });

+ 1 - 1
app/public/js/ledger_check.js

@@ -200,7 +200,7 @@ const ledgerCheckUtil = {
                         checkZero(ZhCalc.sub(g.unit_price, child.unit_price));
                 });
                 if (!gcl) {
-                    gcl = { source: [], b_code: child.b_code || '', name: child.name || '', unit: child.unit || '', unit_price: child.unit_price || 0 }
+                    gcl = { source: [], b_code: child.b_code || '', name: child.name || '', unit: child.unit || '', unit_price: child.unit_price || 0 };
                     gatherGcl.push(gcl);
                 }
                 gcl.source.push(child);

+ 52 - 0
app/service/filing_template.js

@@ -219,6 +219,58 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        async import(templateId, data) {
+            if (!data || data.length === 0) throw '导入数据不存在';
+
+            const insertData = [];
+            for (const d of data) {
+                if (d.id === undefined || d.tree_pid === undefined || d.tree_order === undefined || d.tree_level === undefined || d.name === undefined || d.is_fixed === undefined || d.filing_type === undefined) {
+                    throw '导入数据格式有误';
+                }
+                const parent = insertData.find(x => { return x.org_id === d.tree_pid });
+                insertData.push({
+                    id: this.uuid.v4(), temp_id: templateId, add_user_id: this.ctx.session.sessionUser.accountId,
+                    tree_pid: parent ? parent.id : rootId, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: d.tree_order,
+                    name: d.name, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
+                });
+            }
+            insertData.forEach(x => { delete x.org_id; });
+
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.delete(this.tableName, { temp_id: templateId });
+                await conn.insert(this.tableName, insertData);
+                await conn.commit();
+            } catch(err) {
+                await conn.rollback();
+                throw '导入数据格式有误';
+            }
+            return await this.getData(templateId)
+        }
+
+        async multiUpdate(templateId, data) {
+            if (!data || data.length === 0) throw '提交数据格式错误';
+
+            const sourceData = await this.getData(templateId);
+
+            const validFields = ['id', 'is_fixed', 'name', 'filing_type'];
+            const updateData = [];
+            for (const d of data) {
+                if (!d.id) throw '提交数据格式错误';
+                const sd = sourceData.find(x => { return x.id === d.id; });
+                if (!sd) throw '提交数据格式错误';
+
+                const nd = {};
+                for (const prop in d) {
+                    if (validFields.indexOf(prop) < 0) continue;
+                    nd[prop] = d[prop];
+                }
+                updateData.push(nd);
+            }
+            await this.db.updateRows(this.tableName, updateData);
+            return await this.getData(templateId);
+        }
     }
 
     return FilingTemplate;

+ 5 - 0
app/view/file/template.ejs

@@ -44,6 +44,11 @@
                             <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
                             <button class="btn btn-sm btn-primary"><i class="fa fa-refresh" aria-hidden="true"></i> 初始化模板</button>
                         </form>
+                        <div class="ml-auto p-2">
+                            <a href="javascript: void(0);" class="btn btn-sm btn-primary" id="import-template">导入</a>
+                            <a href="javascript: void(0);" class="btn btn-sm btn-primary" id="export-template">导出</a>
+                            <a href="javascript: void(0);" class="btn btn-sm btn-primary" id="multi-setting">附加配置</a>
+                        </div>
                     </div>
                     <div>
                         <ul id="filing" class="ztree" style="overflow: auto"></ul>

+ 21 - 0
app/view/file/template_modal.ejs

@@ -0,0 +1,21 @@
+<% include ../shares/select_file_modal.ejs %>
+<div class="modal fade" id="multi-set" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="tender-select-title">更多配置</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="modal-height-300" id="multi-spread">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button class="btn btn-sm btn-primary" id="multi-set-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 2 - 1
app/view/ledger/explode_modal.ejs

@@ -472,7 +472,8 @@
 </div>
 <% } %>
 
-<% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && (ctx.tender.data.ledger_status === auditConst.status.uncheck || ctx.tender.data.ledger_status === auditConst.status.checkNo)) { %>
+<% console.log(ctx.session.sessionUser.accountId, ctx.tender.data.user_id, ctx.session.sessionUser.is_admin) %>
+<% if ((ctx.session.sessionUser.accountId === ctx.tender.data.user_id || ctx.session.sessionUser.is_admin) && (ctx.tender.data.ledger_status === auditConst.status.uncheck || ctx.tender.data.ledger_status === auditConst.status.checkNo)) { %>
     <script>
         const shenpi_status = <%- ctx.tender.info.shenpi.ledger %>;
         const shenpiConst =  JSON.parse('<%- JSON.stringify(shenpiConst) %>');

+ 40 - 0
app/view/shares/select_file_modal.ejs

@@ -0,0 +1,40 @@
+<div class="modal fade" id="select-file" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择文件</h5>
+            </div>
+            <div class="modal-body">
+                <p><span id="sf-hint">请选择.xls和.xlsx 文件</span><a class="ml-2" id="sf-template">示例</a></p></p>
+                <div class="form-group">
+                    <label for="sf-file">选择文件</label><i class="fa fa-spinner fa-pulse fa-lg fa-fw text-primary" id="select-excel-loading" style="display: none;"></i>
+                    <input type="file" class="form-control-file" id="sf-file" accept="*.json" name="file">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="sf-ok">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const selectFile = function(setting) {
+        if (!setting || !setting.fileType || !setting.select) return;
+
+        if (setting.template) {
+            $('#sf-template').attr('href', setting.template).show();
+        } else {
+            $('#sf-template').hide();
+        }
+        $('#sf-hint').text(setting.hint || `请选择${setting.fileType}文件`);
+        $('#sf-file').val('');
+        $('#sf-ok').off('click');
+        $('#sf-file').attr('accept', setting.fileType);
+        $('#select-file').modal('show');
+        $('#sf-ok').on("click", function () {
+            setting.select(document.getElementById('sf-file').files[0]);
+            $('#select-file').modal('hide');
+        });
+    };
+</script>

+ 7 - 1
config/web.js

@@ -1213,8 +1213,14 @@ const JsFiles = {
                 mergeFile: 'file_detail',
             },
             template: {
-                files: ['/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js',],
+                files: [
+                    '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                ],
                 mergeFiles: [
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/path_tree.js',
                     '/public/js/shares/drag_tree.js',
                     '/public/js/filing_template.js',
                 ],