瀏覽代碼

自定义分类:删除值、删除分类相关

MaiXinRong 6 年之前
父節點
當前提交
c83bf7cda3

+ 2 - 0
app.js

@@ -11,6 +11,7 @@ const Uglyfy = require('uglify-es');
 const fs = require('fs');
 const moment = require('moment');
 const uuid = require('node-uuid');
+const _ = require('lodash');
 
 const BaseService = require('./app/base/base_service');
 const BaseController = require('./app/base/base_controller');
@@ -18,6 +19,7 @@ const BaseController = require('./app/base/base_controller');
 module.exports = app => {
     app.uuid = uuid;
     app.moment = moment;
+    app._ = _;
     // 数据模型基类
     app.BaseService = BaseService;
 

+ 1 - 1
app/base/base_service.js

@@ -25,7 +25,7 @@ class BaseService extends Service {
         this.uuid = this.app.uuid;
         this.transaction = null;
         this.sqlBuilder = null;
-        this._ = require('lodash');
+        this._ = this.app._;
     }
 
     /**

+ 2 - 5
app/controller/setting_controller.js

@@ -186,7 +186,7 @@ module.exports = app => {
                 if (!data.id) {
                     throw '提交数据错误';
                 }
-                await ctx.service.categoryValue.setCategoryValue(data.id, data.value);
+                await ctx.service.categoryValue.setCategoryValue(data.id, data.updateValue);
 
                 responseData.data.category = await ctx.service.category.getCategory(data.id);
                 responseData.data.tenders = await ctx.service.tender.getList();
@@ -216,10 +216,7 @@ module.exports = app => {
                     throw '提交数据错误';
                 }
 
-                const result = await ctx.service.category.deleteById(data.id);
-                if (!result) {
-                    throw '提交数据失败'
-                }
+                await ctx.service.category.deleteCategory(projectId, data.id);
 
                 ctx.body = responseData;
             } catch(err) {

+ 116 - 33
app/public/js/category.js

@@ -7,15 +7,15 @@
  * @date 2018/9/26
  * @version
  */
-
+let editCate;
+// 初始化分类数据
 function InitCategoryData(category) {
     const data = category instanceof Array ? category : [category];
     for (const d of data) {
-       // d.valueArr = d.value && d.value !== '' ? JSON.parse(d.value) : [];
         d.typeStr = cType.text[d.type];
     }
 }
-
+// 获取自定义分类可选类型Html(下拉、单选)
 function getTypeHtml() {
     const html = [];
     for (const k in cType.key) {
@@ -27,7 +27,7 @@ function getTypeHtml() {
     }
     return html.join('');
 }
-
+// 获取自定义分类html
 function getCategoryHtml(category) {
     const data = category instanceof Array ? category : [category];
     const html = [];
@@ -53,36 +53,19 @@ function getCategoryHtml(category) {
     }
     return html.join('');
 }
-
-function getValueTenderCount(value) {
-    function findTenderCate(tender) {
-        for (const c of tender.category) {
-            if (c.cid == value.cid) {
-                return c;
-            }
-        }
-    }
-    const valueTender = tenders.filter(function (t) {
-        const cate = findTenderCate(t);
-        console.log(cate);
-        return cate ? cate.value == value.id : false;
-    });
-    return valueTender ? valueTender.length : 0;
-}
-
+// 获取分类值html
 function getValueHtml(value) {
     const html = [];
     for (const v of value) {
-        html.push('<tr name="value">');
+        html.push('<tr name="value" vid="' + v.id + '">');
         html.push('<td><input class="form-control form-control-sm" name="value" placeholder="请输入值" value="' + v.value + '" vid ="' + v.id +  '"></td>');
-        console.log(v);
-        html.push('<td>', getValueTenderCount(v) ,'</td>');
-        html.push('<td><a href="javasrcipt: void(0);" class="text-danger">删除</a></td>');
+        html.push('<td>', v.relaTenders.length + v.newTenders.length ,'</td>');
+        html.push('<td><a href="javasrcipt: void(0);" class="text-danger" name="del-value" vid="' + v.id + '">删除</a></td>');
         html.push('</tr>');
     }
     return html.join('');
 }
-
+// 根据分类id查找分类
 function findCategory(cid) {
     for (const c of cData) {
         if (c.id === cid) {
@@ -90,19 +73,107 @@ function findCategory(cid) {
         }
     }
 }
+// 删除值 获取其他可用值的html
+function getValidValueHtml(vid) {
+    const html = [];
+    const values = $('tr:not(.table-danger)[name=value]', '#value-list');
+    for (const value of values) {
+        const v = $(value);
+        if (v.attr('vid') === vid || $('input', v).val() === '') { continue }
+        html.push('<div class="form-check form-check-inline">');
+        html.push('<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio' + v.attr('vid') + '" value="' + v.attr('vid') +'">');
+        html.push('<label class="form-check-label" for="inlineRadio' + v.attr('vid') + '">' + $('input', v).val() + '</label>');
+        html.push('</div>');
+    }
+    return html.join('');
+}
+// 初始化 添加值 编辑数据
+function getEditCate(category) {
+    function findTenderCate(tender, value) {
+        for (const c of tender.category) {
+            if (c.cid == value.cid) {
+                return c;
+            }
+        }
+    }
+
+    editCate = JSON.parse(JSON.stringify(category));
+    for (const v of editCate.value) {
+        v.relaTenders = tenders.filter(function (t) {
+            const cate = findTenderCate(t, v);
+            return cate ? cate.value == v.id : false;
+        });
+        v.newTenders = [];
+    }
+}
+// 绑定编辑值、删除值
+function bindCategoryValueControl() {
+    $('a[name="del-value"]').click(function () {
+        const vid = $(this).attr('vid');
+        const value = _.find(editCate.value, function (v) {
+            return v.id == vid;
+        });
+        if (value.delete) { return; }
 
+        if (value.relaTenders.length > 0) {
+            // 提示用户转移标段
+            $('#tender-count').text(value.relaTenders.length).attr('vid', vid);
+            $('#tender-target').html(getValidValueHtml(vid));
+            $('input[type=radio]', '#tender-target')[0].checked = true;
+            $('#del-cate-value-ok').click(function () {
+                const delVid = $('#tender-count').attr('vid');
+                const tarVid = $('input:checked', '#tender-target').val();
+                const delValue = _.find(editCate.value, function (v) {
+                    return v.id == delVid;
+                });
+                const tarValue = _.find(editCate.value, function (v) {
+                    return v.id == tarVid;
+                });
+                tarValue.newTenders = tarValue.newTenders.concat(delValue.relaTenders, delValue.newTenders);
+                delValue.relaTenders = [];
+                delValue.newTenders = [];
+                delValue.delete = true;
+                const delTr = $('tr[vid=' + delVid + ']');
+                delTr.addClass('table-danger');
+                $('input', delTr).attr('disabled', '');
+                delTr.children()[1].innerText = '移动到其他项';
+                $('tr[vid=' + tarVid + ']').children()[1].innerText = tarValue.relaTenders.length + tarValue.newTenders.length;
+                $('#del-cate-value').modal('hide');
+            });
+            $('#del-cate-value').modal('show');
+        } else {
+            value.delete = true;
+            const tr = $('tr[vid=' + vid + ']');
+            tr.addClass('table-danger');
+            $('input', tr).attr('disabled', '');
+        }
+    });
+}
+// 绑定分类控制等(添加值、编辑、删除)
 function bindCategoryControl() {
     // 弹出添加值
     $('a[name=add]').bind('click', function () {
         const id = parseInt($(this).attr('cid'));
         const category = findCategory(id);
+        getEditCate(category);
         $('#add-ok').attr('cid', id);
-        if (category) {
+        if (editCate) {
             const list = $('#value-list');
-            list.html(getValueHtml(category.value) + '<tr id="add-value-row"><td colspan="3"><a href="javascript: void(0);">添加新值</a></td></tr>');
+            list.html(getValueHtml(editCate.value) + '<tr id="add-value-row"><td colspan="3"><a href="javascript: void(0);">添加新值</a></td></tr>');
             $('#add-value-row').click(function () {
-                $(this).before('<tr><td><input class="form-control form-control-sm" name="value" placeholder="请输入值"></td><td>0</td><td><a href="javasrcipt: void(0);" class="text-danger">删除</a></td></tr>');
+                const newID = _.maxBy(editCate.value, function (v) { return v.id; }).id + 1;
+                const newValue = {
+                    isNew: true,
+                    id: newID,
+                    value: '',
+                    relaTenders: [],
+                    newTenders: [],
+                };
+                editCate.value.push(newValue);
+                $(this).before(getValueHtml([newValue]));
+                bindCategoryValueControl();
             });
+            bindCategoryValueControl();
             $('#add').modal('show');
         }
     });
@@ -154,16 +225,28 @@ $(document).ready(() => {
         const value = $('input[name=value]');
         for (const v of value) {
             if (v.value !== '') {
-                valueArr.push({
-                    id: $(v).attr('id'),
-                    value: $(v).val(),
+                const cateValue = _.find(editCate.value, function (ec) {
+                    return $(v).attr('vid') == ec.id;
                 });
+                let newValue = {};
+                if (cateValue.delete) {
+                    newValue = { id: cateValue.id, delete: true, };
+                } else if (cateValue.isNew) {
+                    newValue = { value: $(v).val(), new: true, };
+                } else {
+                    newValue = { id: cateValue.id, value: $(v).val()};
+                }
+                if (!cateValue.delete && cateValue.newTenders.length > 0) {
+                    newValue.newTenders = _.map(cateValue.newTenders, 'id');
+                }
+                valueArr.push(newValue);
             }
         }
         const data = {
             id: parseInt($(this).attr('cid')),
-            value: valueArr,
+            updateValue: valueArr,
         };
+        console.log(data);
         postData('/setting/category/value', data, function (data) {
             tenders = data.tenders;
             const category = findCategory(data.category.id);

文件差異過大導致無法顯示
+ 134 - 26
app/public/js/lodash.js


+ 90 - 2
app/service/category.js

@@ -23,6 +23,14 @@ module.exports = app => {
             this.tableName = 'category';
         }
 
+        /**
+         * 新增分类
+         *
+         * @param {Number} pid - 项目id
+         * @param {String} name - 分类名称
+         * @param {Number} type - 分类类型
+         * @returns {Promise<{pid: *, name: *, type: *}>}
+         */
         async addCategory(pid, name, type) {
             const count = await this.count({pid: pid, name: name});
             if (count > 0) {
@@ -45,14 +53,88 @@ module.exports = app => {
             return category;
         }
 
+        /**
+         * 大于level的分类level - 1
+         *
+         * @param {Number} pid - 项目id
+         * @param {Number} level - level
+         * @returns {Promise<*>}
+         * @private
+         */
+        async _upLevelCategory(pid, level) {
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('pid', {
+                value: pid,
+                operate: '='
+            });
+            this.sqlBuilder.setAndWhere('level', {
+                value: level,
+                operate: '>',
+            });
+            this.sqlBuilder.setUpdateData('level', {
+                value: 1,
+                selfOperate: '-',
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
+            const data = await this.transaction.query(sql, sqlParam);
+
+            return data;
+        }
+        /**
+         * 删除分类
+         * @param {Number} pid - 项目id
+         * @param {Number} cid - 分类id
+         * @returns {Promise<void>}
+         */
+        async deleteCategory(pid, cid) {
+            const category = await this.getDataById(cid);
+            if (category.pid !== pid) {
+                throw '提交数据错误'
+            }
+            this.transaction = await this.db.beginTransaction();
+            try {
+                // 调整已用分类排序
+                if (category.level >= 1) {
+                    await this._upLevelCategory(pid, category.level);
+                }
+                // 删除标段分类数据
+                const tenders = await this.ctx.service.tender.getList();
+                for (const t of tenders) {
+                    if (t.category && t.category.length > 0) {
+                        this._.remove(t.category, function (c) {
+                            return c.cid === cid;
+                        });
+                    }
+                    console.log(t.category);
+                    await this.transaction.update(this.ctx.service.tender.tableName, {
+                        id: t.id,
+                        category: JSON.stringify(t.category),
+                    });
+                }
+                // 删除分类数据
+                await this.transaction.delete(this.tableName, {id: cid});
+                // 删除分类值
+                await this.transaction.delete(this.ctx.service.categoryValue.tableName, {cid: cid});
+                await this.transaction.commit();
+            } catch(err) {
+                await this.transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 获取分类数据
+         *
+         * @param {Number} id - 分类id
+         * @returns {Promise<*>}
+         */
         async getCategory(id) {
             const data = await this.getDataByCondition({id: id});
             data.value = await this.ctx.service.categoryValue.getAllDataByCondition({ where: {cid: id} });
             return data;
         }
-
         /**
-         * 获取标段分类数据
+         * 获取项目下全部分类数据
          *
          * @param {Number} pid - 标段id
          * @returns {Promise<*>}
@@ -68,6 +150,12 @@ module.exports = app => {
             return data;
         }
 
+        /**
+         * 重置分类已用排序
+         *
+         * @param {Array} data - 分类已用排序数据(TODO未做验证)
+         * @returns {Promise<void>}
+         */
         async resetCategoryLevel(data) {
             this.transaction = await this.db.beginTransaction();
             try {

+ 20 - 24
app/service/category_value.js

@@ -65,35 +65,31 @@ module.exports = app => {
         }
 
         async setCategoryValue(cid, value) {
-            const orgValue = await this.getAllDataByCondition({ where: {cid: cid} });
             const tenders = await this.ctx.service.tender.getList();
 
             this.transaction = await this.db.beginTransaction();
             try {
-                // 删除值 & 更新值
-                await this._deleteAndUpdateValue(orgValue, value);
-                // 新增值
-                const newValue = value.filter(function (v) {
-                    return !v.id;
-                });
-                for (const nv of newValue) {
-                    nv.cid = cid;
-                    nv.pid = this.ctx.session.sessionProject.id;
-                    const result = await this.transaction.insert(this.tableName, nv);
-                    nv.id = result.insertId;
-                }
-                // 更新标段属性
-                for (const t of tenders) {
-                    const category = t.category ? t.category : [];
-                    const cate = _.find(t.category, function (c) {
-                        return c.cid === cid;
-                    });
-                    const tenderValue = cate ? _.find(value, function (v) {return v.id === cate.value;}) : null;
-                    if (!tenderValue) {
-                        category.push({cid: cid, value: newValue[0].id});
+                for (const v of value) {
+                    if (v.delete) {
+                        await this.transaction.delete(this.tableName, {id: v.id});
+                    } else if (v.new) {
+                        const result = await this.transaction.insert(this.tableName, {
+                            cid: cid,
+                            pid: this.ctx.session.sessionProject.id,
+                            value: v.value,
+                        });
+                        v.id = result.insertId;
+                    } else {
+                        await this.transaction.update(this.tableName, {id: v.id, value: v.value});
+                    }
+                    if (!v.newTenders) { continue }
+                    for (const nt of v.newTenders) {
+                        const tender = this._.find(tenders, function (t) { return t.id === nt; });
+                        const cate = this._.find(tender.category, function (c) { return c.cid === cid; });
+                        cate.value = v.id;
                         await this.transaction.update(this.service.tender.tableName, {
-                            id: t.id,
-                            category: JSON.stringify(category),
+                            id: tender.id,
+                            category: JSON.stringify(tender.category),
                         });
                     }
                 }

+ 33 - 0
app/view/setting/category_modal.ejs

@@ -63,6 +63,9 @@
                     <tbody id="value-list">
                     </tbody>
                 </table>
+                <div class="alert alert-warning mt-3" role="alert">
+                    新建类别,如项目中已有标段,所有标段默认都是用第一个值。
+                </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
@@ -87,4 +90,34 @@
             </div>
         </div>
     </div>
+</div>
+<!--弹出删除值 如果值下有标段-->
+<div class="modal fade" id="del-cate-value" 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">
+                <h6>该值下有 <span id="tender-count">5</span> 个标段,请设置删除后归属值。</h6>
+                <div class="form-group">
+                    <label>选择目标值</label>
+                    <div id="tender-target">
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
+                            <label class="form-check-label" for="inlineRadio1">2018</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
+                            <label class="form-check-label" for="inlineRadio2">2017</label>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-danger" id="del-cate-value-ok">确认删除</button>
+            </div>
+        </div>
+    </div>
 </div>