Просмотр исходного кода

feat: 合同列设置到其他项目,应用树结构

lanjianrong 4 недель назад
Родитель
Сommit
996748c946
3 измененных файлов с 101 добавлено и 13 удалено
  1. 3 11
      app/controller/contract_controller.js
  2. 77 0
      app/service/category.js
  3. 21 2
      app/view/contract/col_set.ejs

+ 3 - 11
app/controller/contract_controller.js

@@ -369,18 +369,10 @@ module.exports = app => {
                     attribute: attributeColumnSet,
                 };
                 if (ctx.contractOptions.tid) {
-                    const tenders = await ctx.service.tender.getAllDataByCondition({
-                        where: { spid: ctx.subProject.id },
-                        columns: ['id', 'name'],
-                    });
                     renderData.contractColCopyScope = 'tender';
-                    renderData.contractColCopyTargets = [{
-                        id: 'sub-project-root', name: ctx.subProject.name, tree_level: 1, is_folder: true,
-                    }].concat(tenders.filter(item => {
-                        return Number(item.id) !== Number(ctx.contractOptions.tid);
-                    }).map(item => {
-                        return { id: item.id, name: item.name, tree_level: 2, is_folder: false };
-                    }));
+                    renderData.contractColCopyTargets = await ctx.service.category.getContractCopyTenderTreeNodes(
+                        ctx.subProject, ctx.contractOptions.tid
+                    );
                 } else {
                     const subProjects = await ctx.service.subProject.getSubProjectTreeNodes(
                         ctx.session.sessionProject.id,

+ 77 - 0
app/service/category.js

@@ -221,6 +221,83 @@ module.exports = app => {
             return data;
         }
 
+        /**
+         * 按标段自定义分类生成合同列设置的复制目标树
+         * @param {Object} subProject - 当前子项目
+         * @param {Number} currentTenderId - 当前标段id
+         * @returns {Promise<Array>} - 按树顺序排列的分类和标段
+         */
+        async getContractCopyTenderTreeNodes(subProject, currentTenderId) {
+            const category = await this.getAllCategory(subProject);
+            const levelCategory = category.filter(item => item.level && item.level > 0)
+                .sort((a, b) => a.level - b.level);
+            const tenders = await this.ctx.service.tender.getContractList('', null, 1);
+            const root = {
+                id: 'sub-project-root', name: subProject.name, tree_level: 1, is_folder: true, children: [],
+            };
+
+            for (const tender of tenders) {
+                const tenderCategory = Array.isArray(tender.category) ? tender.category : [];
+                let parent = root;
+                let categoryPath = [];
+                for (const [index, item] of levelCategory.entries()) {
+                    const selected = tenderCategory.find(value => String(value.cid) === String(item.id));
+                    if (selected) {
+                        categoryPath.push({ item, valueId: selected.value });
+                    } else {
+                        if (index === 0) {
+                            categoryPath = tenderCategory.map(value => ({
+                                item: category.find(cate => String(cate.id) === String(value.cid)), valueId: value.value,
+                            })).filter(value => value.item);
+                        }
+                        break;
+                    }
+                }
+                for (const pathItem of categoryPath) {
+                    const categoryValue = pathItem.item.value.find(value => String(value.id) === String(pathItem.valueId));
+                    if (!categoryValue) break;
+                    let folder = parent.children.find(child => child.is_folder &&
+                        String(child.cid) === String(pathItem.item.id) && String(child.vid) === String(pathItem.valueId));
+                    if (!folder) {
+                        folder = {
+                            id: `category-${pathItem.item.id}-${pathItem.valueId}`,
+                            cid: pathItem.item.id,
+                            vid: pathItem.valueId,
+                            name: categoryValue.value,
+                            sort: categoryValue.sort,
+                            tree_level: parent.tree_level + 1,
+                            is_folder: true,
+                            children: [],
+                        };
+                        parent.children.push(folder);
+                    }
+                    parent = folder;
+                }
+                parent.children.push({
+                    id: tender.id,
+                    name: tender.name,
+                    tree_level: parent.tree_level + 1,
+                    is_folder: false,
+                    current: String(tender.id) === String(currentTenderId),
+                });
+            }
+
+            const result = [];
+            const stack = [root];
+            while (stack.length) {
+                const node = stack.pop();
+                result.push(node);
+                if (!node.children) continue;
+                node.children.sort((a, b) => {
+                    if (a.is_folder !== b.is_folder) return a.is_folder ? -1 : 1;
+                    if (a.is_folder) return Number(a.sort || 0) - Number(b.sort || 0);
+                    return a.name.localeCompare(b.name, 'zh-Hans-CN');
+                });
+                for (let index = node.children.length - 1; index >= 0; index--) stack.push(node.children[index]);
+            }
+            return result;
+        }
+
     }
 
     return Category;

+ 21 - 2
app/view/contract/col_set.ejs

@@ -28,6 +28,7 @@
     #copy-contract-col-set { z-index: 1060; }
     #copy-contract-col-set .copy-target-list { max-height: 420px; overflow-y: auto; }
     #copy-contract-col-set .copy-target-row { min-height: 34px; }
+    #copy-contract-col-set .copy-target-row.copy-target-hidden { display: none !important; }
 </style>
 <div class="modal fade" id="col-set" data-backdrop="static">
     <div class="modal-dialog modal-xl" role="document">
@@ -99,13 +100,17 @@
                     <% if (!contractColCopyTargets.some(item => !item.is_folder && !item.current)) { %><div class="text-muted p-3">暂无其他可设置的<%- contractColCopyScope === 'tender' ? '标段' : '子项目' %></div><% } %>
                     <% for (const target of contractColCopyTargets) { %>
                         <div class="copy-target-row d-flex align-items-center border-bottom px-2"
+                             data-tree-level="<%- target.tree_level || 1 %>"
                              style="padding-left: <%- 12 + ((target.tree_level || 1) - 1) * 24 %>px !important;">
                             <% if (target.is_folder) { %>
-                                <i class="fa fa-folder-o text-warning mr-2"></i><span><%- target.name %></span>
+                                <% if ((target.children && target.children.length) || target.has_children) { %>
+                                    <button type="button" class="copy-target-expand btn btn-link btn-sm p-0 mr-2" aria-expanded="true" aria-label="展开或收起分类"><i class="fa fa-minus-square-o"></i></button>
+                                <% } else { %><span class="mr-3"></span><% } %>
+                                <i class="fa fa-folder-o text-warning mr-2"></i><span><%= target.name %></span>
                             <% } else { %>
                                 <label class="mb-0 w-100">
                                     <input type="checkbox" class="copy-contract-col-target mr-2" value="<%- target.id %>" <%- target.current ? 'disabled' : '' %>>
-                                    <i class="fa <%- contractColCopyScope === 'tender' ? 'fa-flag-o' : 'fa-file-o' %> text-muted mr-1"></i><%- target.name %><% if (target.current) { %><span class="text-muted ml-2">(当前)</span><% } %>
+                                    <i class="fa <%- contractColCopyScope === 'tender' ? 'fa-flag-o' : 'fa-file-o' %> text-muted mr-1"></i><%= target.name %><% if (target.current) { %><span class="text-muted ml-2">(当前)</span><% } %>
                                 </label>
                             <% } %>
                         </div>
@@ -210,4 +215,18 @@ $('#copy-contract-col-set-ok').on('click', function() {
         toastr.success(msg || ('已设置至' + data.count + '个目标'));
     });
 });
+$('#copy-contract-col-set').on('click', '.copy-target-expand', function() {
+    const $folder = $(this).closest('.copy-target-row');
+    const collapsed = $folder.toggleClass('is-collapsed').hasClass('is-collapsed');
+    $(this).attr('aria-expanded', !collapsed)
+        .find('i').toggleClass('fa-minus-square-o', !collapsed).toggleClass('fa-plus-square-o', collapsed);
+    const collapsedLevels = [];
+    $('#copy-contract-col-set .copy-target-row').each(function() {
+        const $row = $(this);
+        const level = Number($row.data('tree-level'));
+        while (collapsedLevels.length && collapsedLevels[collapsedLevels.length - 1] >= level) collapsedLevels.pop();
+        $row.toggleClass('copy-target-hidden', collapsedLevels.length > 0);
+        if ($row.hasClass('is-collapsed')) collapsedLevels.push(level);
+    });
+});
 </script>