ソースを参照

1.修复项目工料机修改市场单价接口的bug
2.整合配合比和机械单价的前端代码

caiaolin 8 年 前
コミット
337867d69b

+ 11 - 11
modules/glj/controllers/glj_controller.js

@@ -165,7 +165,7 @@ class GLJController extends BaseController {
      *
      * @return {void}
      */
-    async getMixRatio(request, response) {
+    async getRatio(request, response) {
         let projectGLJId = request.body.id;
         let projectId = request.body.project_id;
         let responseData = {
@@ -242,13 +242,13 @@ class GLJController extends BaseController {
         let data = {
             glj_id: 802,
             project_id: 1,
-            code: '80010101',
-            name: 'C20特细砂干硬性混凝土',
-            specs: 'C20',
-            unit: '',
-            type: GLJTypeConst.CONCRETE,
-            base_price: 423.36,
-            market_price: 423.36
+            code: '85030202',
+            name: '履带式起重机 15t',
+            specs: '15t',
+            unit: '台班',
+            type: GLJTypeConst.GENERAL_MACHINE,
+            base_price: 501.64,
+            market_price: 501.64
         };
         try {
             let gljListModel = new GLJListModel();
@@ -272,9 +272,9 @@ class GLJController extends BaseController {
         let projectId = request.query.project;
         // 修改数据
         let updateData = {
-            code: '00010201',
-            market_price: '44.42',
-            name: '土石方综合工日(1)',
+            code: '36290101',
+            market_price: '13',
+            name: '水(10)',
             project_id: projectId
         };
 

+ 3 - 3
modules/glj/models/glj_list_model.js

@@ -33,7 +33,7 @@ class GLJListModel extends BaseModel {
      * @var {Array}
      */
     ownCompositionTypes = [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO,
-        GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR];
+        GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR, GLJTypeConst.GENERAL_MACHINE];
 
     /**
      * 构造函数
@@ -241,7 +241,7 @@ class GLJListModel extends BaseModel {
 
             // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据)
             if (isAddProjectGLJ && (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
-                data.type === GLJTypeConst.MIX_RATIO)) {
+                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE)) {
                 this.compositionInit(data.code, data.project_id, unitPriceFileId);
             }
 
@@ -332,7 +332,7 @@ class GLJListModel extends BaseModel {
             // 如果单价数据新增则工料机也需要新增
             if (isAdd) {
                 // 如果没有对应的记录则新增一条工料机数据,并更改name
-                let regular = /\(\d\)/;
+                let regular = /\(\d+\)/;
                 let changeString = '(' + gljCount + ')';
                 updateData.name = regular.test(updateData.name) ? updateData.name.replace(regular, changeString) :
                     updateData.name + changeString;

+ 2 - 1
modules/glj/models/unit_price_model.js

@@ -87,6 +87,7 @@ class UnitPriceModel extends BaseModel {
         let unitPriceData = await this.findDataByCondition({code: data.code, unit_price_file_id: unitPriceFileId}, null, false);
 
         // 如果有记录,判断是否存在一样的市场单价,有则直接返回数据
+        data.market_price = parseFloat(data.market_price);
         let unitPriceIndex = this.isPriceIncluded(unitPriceData, data.market_price);
         if (unitPriceData && unitPriceIndex >= 0) {
             return [unitPriceData[unitPriceIndex], false];
@@ -102,7 +103,7 @@ class UnitPriceModel extends BaseModel {
 
         // 更改名称
         if (gljCount > 0) {
-            let regular = /\(\d\)/;
+            let regular = /\(\d+\)/;
             let changeString = '(' + gljCount + ')';
             data.name = regular.test(data.name) ? data.name.replace(regular, changeString) :
                 data.name + changeString;

+ 5 - 4
modules/glj/routes/glj_router.js

@@ -1,5 +1,5 @@
 /**
- * 登录相关路由
+ * 项目工料机相关路由
  *
  * @author CaiAoLin
  * @date 2017/6/22
@@ -9,17 +9,18 @@ import Express from "express";
 import GLJController from "../controllers/glj_controller";
 
 module.exports = function (app) {
-    const router = Express.Router();
-    let gljController = new GLJController();
+const router = Express.Router();
+let gljController = new GLJController();
 
 // action定义区域
 router.get('/', gljController.init, gljController.index);
 router.post('/update', gljController.init, gljController.updateData);
-router.post('/get-ratio', gljController.init, gljController.getMixRatio);
+router.post('/get-ratio', gljController.init, gljController.getRatio);
 router.post('/delete-ratio', gljController.init, gljController.deleteMixRatio);
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);
 router.get('/testDelete', gljController.init, gljController.delete);
+
 app.use('/glj',router);
 };

+ 5 - 4
web/glj/html/glj_index.html

@@ -83,7 +83,8 @@
 <script type="text/javascript" src="/public/web/sheet/sheet_data_helper.js"></script>
 <script type="text/javascript" src="/public/web/number_util.js"></script>
 <script type="text/javascript" src="/web/glj/js/socket.js"></script>
-<script type="text/javascript" src="/web/glj/js/spread_common.js"></script>
-<script type="text/javascript" src="/web/glj/js/glj_index.js"></script>
-<script type="text/javascript" src="/web/glj/js/mix_ratio.js"></script>
-<script type="text/javascript" src="/web/glj/js/machine.js"></script>
+<script type="text/javascript" src="/web/glj/js/common_spread.js"></script>
+<script type="text/javascript" src="/web/glj/js/composition_spread.js"></script>
+<script type="text/javascript" src="/web/glj/js/project_glj_spread.js"></script>
+<script type="text/javascript" src="/web/glj/js/project_glj.js"></script>
+<script type="text/javascript" src="/web/glj/js/composition.js"></script>

+ 281 - 0
web/glj/js/common_spread.js

@@ -0,0 +1,281 @@
+/**
+ * 工料机SpreadJS类
+ *
+ * @author CaiAoLin
+ * @date 2017/7/18
+ * @version
+ */
+
+/**
+ * 构造函数
+ *
+ * @param {Object} header
+ * @return {void}
+ */
+function CommonSpreadJs (header) {
+    this.header = header;
+    this.spread = null;
+    this.sheet = null;
+    this.columnInfo = [];
+    this.isChanging = false;
+}
+
+/**
+ * 初始化
+ *
+ * @param {String} target
+ * @return {Object}
+ */
+CommonSpreadJs.prototype.init = function(target) {
+
+    let setting = {
+        header: []
+    };
+    this.columnInfo = [];
+    for(let tmp of this.header) {
+        setting.header.push({headerName: tmp.name, headerWidth: 120});
+        this.columnInfo.push({name: tmp.field, displayName: tmp.name, visible: tmp.visible, cellType: tmp.cellType, size: 120});
+    }
+
+    this.spread = sheetCommonObj.buildSheet(document.getElementById(target), setting, 10);
+    this.spread.options.scrollbarShowMax = true;
+    this.spread.options.scrollbarMaxAlign = true;
+    this.spread.options.showHorizontalScrollbar = true;
+
+    this.sheet = this.spread.getActiveSheet();
+
+    this.sheet.autoGenerateColumns = false;
+    // 设置表单不可编辑
+    this.sheet.options.isProtected = true;
+
+    return this.spread;
+};
+
+/**
+ * 获取对应字段的列号
+ *
+ * @param {String} field
+ * @return {Number}
+ */
+CommonSpreadJs.prototype.getFieldColumn = function(field) {
+    let result = -1;
+    if (this.header.length <= 0) {
+        return result;
+    }
+    for (let tmp in this.header) {
+        if (this.header[tmp].field === field) {
+            result = tmp;
+            break;
+        }
+    }
+
+    return result;
+};
+
+/**
+ * 根据列号获取对应的字段
+ *
+ * @param {Number} column
+ * @return {String}
+ */
+CommonSpreadJs.prototype.getColumnField = function(column) {
+    let field = this.header[column] !== undefined && this.header[column].field !== undefined ?
+        this.header[column].field : '';
+    return field;
+};
+
+/**
+ * 设置指定列可编辑
+ *
+ * @param {Number} column
+ * @return {void}
+ */
+CommonSpreadJs.prototype.setColumnEditable = function(column) {
+    this.sheet.getRange(-1, column, -1, 1).locked(false);
+};
+
+/**
+ * 设置指定列可编辑
+ *
+ *
+ * @param {Number} column
+ * @return {void}
+ */
+CommonSpreadJs.prototype.setColumnLock = function(row, column) {
+    this.sheet.getRange(-1, column, -1, 1).locked(true);
+};
+
+/**
+ * 设置样式
+ *
+ * @param {Number} row
+ * @param {Number} column
+ * @param {Object} styleSetting
+ */
+CommonSpreadJs.prototype.setStyle = function(row, column, styleSetting) {
+    let style = new GC.Spread.Sheets.Style();
+    style.locked = styleSetting.readOnly === undefined ? true : styleSetting.readOnly;
+    style.hAlign = styleSetting.hAlign === undefined ? GC.Spread.Sheets.HorizontalAlign.center : styleSetting.hAlign;
+
+    this.sheet.setStyle(row, column, style, GC.Spread.Sheets.SheetArea.viewport);
+};
+
+/**
+ * 获取sheet对象
+ *
+ * @return {Object}
+ */
+CommonSpreadJs.prototype.getSheet = function() {
+    return this.sheet;
+};
+
+/**
+ * 设置数据
+ *
+ * @param {Object} data
+ * @return {void}
+ */
+CommonSpreadJs.prototype.setData = function(data) {
+    this.sheet.setDataSource(data);
+    this.sheet.bindColumns(this.columnInfo);
+    this.spread.refresh();
+};
+
+/**
+ * 绑定事件
+ *
+ * @param {Number} event
+ * @param {function} callback
+ * @return {void}
+ */
+CommonSpreadJs.prototype.bind = function (event, callback) {
+    this.sheet.bind(event, callback);
+};
+
+/**
+ * 查找指定数据并返回行号
+ *
+ * @param {String} keyword
+ * @param {Number} rowStart
+ * @param {Number} columnStart
+ * @return {Number}
+ */
+CommonSpreadJs.prototype.searchKeyword = function(keyword, rowStart = 0, columnStart = 0) {
+    let condition = new GC.Spread.Sheets.Search.SearchCondition();
+    condition.searchString = keyword;
+    condition.startSheetIndex = 0;
+    condition.endSheetIndex = 0;
+    condition.searchFlags = GC.Spread.Sheets.Search.SearchFlags.ignoreCase | GC.Spread.Sheets.Search.SearchFlags.blockRange;
+    condition.searchOrder = GC.Spread.Sheets.Search.SearchOrder.nOrder;
+    condition.searchTarget = GC.Spread.Sheets.Search.SearchFoundFlags.cellText;
+    condition.sheetArea = GC.Spread.Sheets.SheetArea.viewport;
+    condition.rowStart = rowStart;
+    condition.columnStart = columnStart;
+
+    let result = this.spread.search(condition);
+    return result.foundRowIndex;
+};
+
+/**
+ * 过滤数据
+ *
+ * @param {String} field
+ * @param {Array} filterList
+ * @return {void}
+ */
+CommonSpreadJs.prototype.filterData = function(field, filterList) {
+    let fieldColumn = this.getFieldColumn(field);
+    fieldColumn = parseInt(fieldColumn);
+
+    let filter = new GC.Spread.Sheets.Filter.HideRowFilter(new GC.Spread.Sheets.Range(-1, 5, -1, 1));
+    this.sheet.rowFilter(filter);
+
+    let rowFilter = this.sheet.rowFilter();
+    rowFilter.removeFilterItems(fieldColumn);
+    for (let tmp of filterList) {
+        let condition = new GC.Spread.Sheets.ConditionalFormatting.Condition(GC.Spread.Sheets.ConditionalFormatting.ConditionType.numberCondition, {
+            compareType: GC.Spread.Sheets.ConditionalFormatting.GeneralComparisonOperators.equalsTo,
+            expected: tmp
+        });
+        rowFilter.addFilterItem(fieldColumn, condition);
+    }
+
+    rowFilter.filter(fieldColumn);
+    this.sheet.invalidateLayout();
+    this.sheet.repaint();
+};
+
+/**
+ * 选中指定行
+ *
+ * @param {Number} row
+ * @return {void}
+ */
+CommonSpreadJs.prototype.selectRow = function(row) {
+    this.sheet.setSelection(row, 0, 1, 1);
+};
+
+/**
+ * 验证数据
+ *
+ * @param {Number} column
+ * @param {String} value
+ * @return {boolean}
+ */
+CommonSpreadJs.prototype.checkData = function(column, value) {
+    let result = true;
+    let validator = this.header[column].validator !== undefined ? this.header[column].validator : null;
+    if (validator === null) {
+        return result;
+    }
+
+    switch (validator) {
+        case 'number':
+            let regular = /^\d+(\.\d+)?$/;
+            result = regular.test(value);
+            break;
+        case 'boolean':
+            let booleanValue = [true, false];
+            result = booleanValue.indexOf(value) >= 0;
+            break;
+    }
+
+    return result;
+};
+
+/**
+ * 设置对应字段数值
+ *
+ * @param {String} field
+ * @param {Number} value
+ * @param {boolean} appendMode
+ * @return {void}
+ */
+CommonSpreadJs.prototype.setCellByField = function(field, value, appendMode) {
+    let row = this.sheet.getActiveRowIndex();
+    let columnIndex = this.getFieldColumn(field);
+    if (appendMode) {
+        let oldValue = this.sheet.getValue(row, columnIndex);
+        value = (oldValue + value).toDecimal(2);
+    }
+    this.sheet.setValue(row, columnIndex, value);
+};
+
+/**
+ * 获取当前选中行指定字段的值
+ *
+ * @param {String} field
+ * @return {Mixed}
+ */
+CommonSpreadJs.prototype.getActiveDataByField = function(field) {
+    let result = null;
+    let activeGLJRow = this.sheet.getActiveRowIndex();
+
+    let column = this.getFieldColumn(field);
+    if (column < 0) {
+        return result;
+    }
+
+    result = this.sheet.getValue(activeGLJRow, column);
+    return result;
+};

+ 67 - 0
web/glj/js/composition.js

@@ -0,0 +1,67 @@
+/**
+ * 配合比相关
+ *
+ * @author CaiAoLin
+ * @date 2017/7/10
+ * @version
+ */
+let compositionSpread = null;
+let mixRatioSheet = null;
+let machineSheet = null;
+let mixRatioRightClickTarget = null;
+let isDeleting = false;
+$(document).ready(function() {
+
+    compositionSpread = new CompositionSpread();
+    compositionSpread.successCallback = mixRatioSuccess;
+
+    // 切换tab触发refresh
+    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+        currentTag = $(e.target).data('name');
+
+        // 获取工料机当前选中的行号
+        let projectGLJId = 0;
+        if (currentTag === "mix-ratio") {
+            mixRatioSheet = mixRatioSheet === null ? compositionSpread.init(currentTag) : mixRatioSheet;
+            compositionSpread.initRightClick();
+            // 筛选数据显示(显示混凝土、砂浆、配合比)
+            projectGLJSheet.filterData('unit_price.type', [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO]);
+            projectGLJSheet.selectRow(projectGLJSpread.firstMixRatioRow);
+            projectGLJId = projectGLJSheet.getActiveDataByField('id');
+
+            // 获取数据
+            compositionSpread.getRatioData(projectGLJId);
+        }
+
+        if (currentTag === "machine") {
+            machineSheet = machineSheet === null ? compositionSpread.init(currentTag) : machineSheet;
+            compositionSpread.initRightClick();
+            // 筛选数据显示(显示普通机械)
+            projectGLJSheet.filterData('unit_price.type', [GLJTypeConst.GENERAL_MACHINE]);
+            projectGLJSheet.selectRow(projectGLJSpread.firstMachineRow);
+            projectGLJId = projectGLJSheet.getActiveDataByField('id');
+
+            // 获取数据
+            compositionSpread.getRatioData(projectGLJId);
+        }
+
+    });
+
+});
+
+
+/**
+ * 成功事件
+ *
+ * @param {object} info
+ * @return {void}
+ */
+function mixRatioSuccess(info) {
+    // 成功则对相应的总消耗量进行设置
+    projectGLJSheet.setCellByField('quantity', info.change, true);
+
+    // 设置父级3个价格
+    projectGLJSheet.setCellByField('unit_price.market_price', info.parentMarketPrice, false);
+    projectGLJSheet.setCellByField('unit_price.base_price', info.parentBasePrice, false);
+    projectGLJSheet.setCellByField('adjust_price', info.parentMarketPrice, false);
+}

+ 315 - 0
web/glj/js/composition_spread.js

@@ -0,0 +1,315 @@
+/**
+ * 组成物Spread
+ *
+ * @author CaiAoLin
+ * @date 2017/7/18
+ * @version
+ */
+
+/**
+ * 构造函数
+ *
+ * @return {void}
+ */
+function CompositionSpread () {
+    this.isChanging = false;
+    this.sheetObj = null;
+    this.successCallback = null;
+}
+
+/**
+ * 初始化
+ *
+ * @param {String} target
+ * @return {Object}
+ */
+CompositionSpread.prototype.init = function(target) {
+    let name = target === 'machine' ? '用量' : '消耗量';
+    let header = [
+        {name: '编码', field: 'code', visible: true},
+        {name: '名称', field: 'name', visible: true},
+        {name: '单位', field: 'unit_price.unit', visible: true},
+        {name: 'ID', field: 'id', visible: false},
+        {name: '类型', field: 'unit_price.type', visible: false},
+        {name: '基价单价', field: "unit_price.base_price", visible: true},
+        {name: '调整基价', field: 'adjust_price', visible: true},
+        {name: '市场单价', field: "unit_price.market_price", visible: true},
+        {name: '消耗量', field: 'consumption', visible: true, validator: 'number'},
+        {name: 'CID', field: 'mix_ratio_id', visible: false},
+    ];
+
+    this.sheetObj = new CommonSpreadJs(header);
+    this.sheetObj.init(target);
+
+    // 获取列号
+    let codeColumn = this.sheetObj.getFieldColumn('code');
+    let unitColumn = this.sheetObj.getFieldColumn('unit_price.unit');
+    let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
+
+    // 居中样式
+    let centerStyleSetting = {hAlign: 1};
+    this.sheetObj.setStyle(-1, codeColumn, centerStyleSetting);
+    this.sheetObj.setStyle(-1, unitColumn, centerStyleSetting);
+
+
+    // 设置可编辑列
+    this.sheetObj.setColumnEditable(consumptionColumn);
+
+    // 绑定事件
+    let self = this;
+    this.sheetObj.bind(GC.Spread.Sheets.Events.ValueChanged, function(element, info) {
+        self.updateConsumption(info, self.successCallback);
+    });
+
+    return this.sheetObj;
+};
+
+/**
+ * 初始化右键
+ *
+ * @return {void}
+ */
+CompositionSpread.prototype.initRightClick = function() {
+    let activeSheet = this.sheetObj.getSheet();
+    let self = this;
+    $.contextMenu({
+        selector: '#mix-ratio',
+        build: function ($trigger, e) {
+            mixRatioRightClickTarget = SheetDataHelper.safeRightClickSelection($trigger, e, self.sheetObj.spread);
+            return mixRatioRightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.viewport ||
+                mixRatioRightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+        },
+        items: {
+            "deleteMixRatio": {
+                name: "删除",
+                icon: 'fa-trash-o',
+                disabled: function () {
+                    return mixRatioRightClickTarget.row === undefined;
+                },
+                callback: function (key, opt) {
+                    let row = mixRatioRightClickTarget.row;
+                    let idColumn = self.sheetObj.getFieldColumn('mix_ratio_id');
+                    let deleteId = activeSheet.getValue(row, idColumn);
+                    self.deleteComposition(deleteId, row, mixRatioSuccess);
+                }
+            },
+        }
+    });
+};
+
+/**
+ * 获取组成物数据
+ *
+ * @param {Number} projectGLJid
+ * @return {void | boolean}
+ */
+CompositionSpread.prototype.getRatioData = function(projectGLJid) {
+    projectGLJid = parseInt(projectGLJid);
+    let self = this;
+    if (isNaN(projectGLJid) || projectGLJid <= 0) {
+        this.sheetObj.setData(null);
+        return false;
+    }
+    $.ajax({
+        url: '/glj/get-ratio',
+        type: 'post',
+        data: {id: projectGLJid, project_id: 1},
+        error: function() {
+            self.sheetObj.setData(null);
+        },
+        beforeSend: function() {
+
+        },
+        success: function(response) {
+            if (response.err === 0) {
+                response.data = JSON.parse(response.data);
+                // 设置数据
+                self.sheetObj.setData(response.data);
+                self.specialColumn(response.data);
+            } else {
+                self.sheetObj.setData(null);
+                alert('不存在对应数据');
+            }
+        }
+    });
+};
+
+/**
+ * 设置特殊单元格数据
+ *
+ * @param {object} sourceData
+ * @return {void}
+ */
+CompositionSpread.prototype.specialColumn = function(sourceData) {
+    let rowCounter = 0;
+    // 获取市场单价列号
+    let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
+    let idColumn = this.sheetObj.getFieldColumn('mix_ratio_id');
+    let activeSheet = this.sheetObj.getSheet();
+    for(let data of sourceData) {
+        // 把消耗量从对象中抽离出来
+        if (data.ratio_data.consumption !== undefined) {
+            activeSheet.setValue(rowCounter, consumptionColumn, data.ratio_data.consumption);
+            activeSheet.setValue(rowCounter, idColumn, data.ratio_data.id);
+        }
+        rowCounter++;
+    }
+};
+
+/**
+ * 更新组成物消耗量或用量
+ *
+ * @param {Object} info
+ * @param {function} callback
+ * return {void}
+ */
+CompositionSpread.prototype.updateConsumption = function(info, callback) {
+    // 获取修改的数据
+    let column = info.col;
+    let row = info.row;
+    let field = this.sheetObj.getColumnField(column);
+    if (field === '') {
+        return false;
+    }
+    // 防止快速同时提交
+    if (this.isChanging) {
+        return false;
+    }
+    let activeSheet = this.sheetObj.getSheet();
+    // 校验数据
+    let value = info.newValue;
+    if (!this.sheetObj.checkData(column, value)) {
+        alert('数据格式错误,请重新输入!');
+        activeSheet.setValue(row, column, info.oldValue);
+        return false;
+    }
+
+    // 获取id
+    let idColumn = this.sheetObj.getFieldColumn('mix_ratio_id');
+    if (idColumn < 0) {
+        return false;
+    }
+    let id = activeSheet.getValue(row, idColumn);
+    let [parentMarketPrice, parentBasePrice] = this.getCompositionSumPrice('modify', row, info.newValue);
+
+    let self = this;
+    $.ajax({
+        url: '/glj/update',
+        type: 'post',
+        data: {id: id, field: 'mix_ratio.' + field, value: value, market_price: parentMarketPrice, base_price: parentBasePrice},
+        dataType: 'json',
+        error: function() {
+            alert('数据传输有误!');
+            self.isChanging = false;
+            activeSheet.setValue(row, column, info.oldValue);
+        },
+        beforeSend: function() {
+            self.isChanging = true;
+        },
+        success: function(response) {
+            self.isChanging = false;
+            // 修改失败则恢复原值
+            if (response.err !== 0) {
+                activeSheet.setValue(row, column, info.oldValue);
+                alert('更改数据失败!');
+            } else {
+                info.parentMarketPrice = parentMarketPrice;
+                info.parentBasePrice = parentBasePrice;
+                info.change = info.newValue - info.oldValue;
+                callback(info);
+            }
+        }
+    });
+};
+
+/**
+ * 获取当前所有组成物累积的市场单价和基价单价
+ * 用于同步修改父级市场单价和基价单价
+ *
+ * @param {String} scene
+ * @param {Number} affectRow
+ * @param {Number} newValue
+ * @return {Array}
+ */
+CompositionSpread.prototype.getCompositionSumPrice = function(scene, affectRow, newValue = 0) {
+    let activeSheet = this.sheetObj.getSheet();
+    // 计算父级3个价格
+    let maxRow = activeSheet.getRowCount();
+    // 获取对应列的列号
+    let marketPriceColumn = this.sheetObj.getFieldColumn('unit_price.market_price');
+    let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
+    let basePriceColumn = this.sheetObj.getFieldColumn('unit_price.base_price');
+
+    let parentMarketPrice = 0;
+    let parentBasePrice = 0;
+    for(let i = 0; i < maxRow; i++) {
+        // 获取市场单价
+        let marketPrice = activeSheet.getValue(i, marketPriceColumn);
+        // 获取基价单价
+        let basePrice = activeSheet.getValue(i, basePriceColumn);
+
+        // 如果是删除则忽略即将被删除的行数据
+        if (scene === 'delete' && affectRow === i) {
+            continue;
+        }
+
+        // 获取消耗量(如果是当前修改的行则替换数据)
+        let consumption = i === affectRow ? newValue : activeSheet.getValue(i, consumptionColumn);
+
+        parentMarketPrice += consumption * marketPrice;
+        parentBasePrice += consumption * basePrice;
+    }
+
+    parentMarketPrice = parentMarketPrice.toDecimal(2);
+    parentBasePrice = parentBasePrice.toDecimal(2);
+
+    return [parentMarketPrice, parentBasePrice]
+};
+
+/**
+ * 删除组成物
+ *
+ * @param {Number} id
+ * @param {Number} row
+ * @param {function} callback
+ * @return {void | boolean}
+ */
+CompositionSpread.prototype.deleteComposition = function (id, row, callback) {
+    id = parseInt(id);
+    if (isNaN(id) || id <= 0) {
+        alert('参数错误!');
+    }
+    if (isDeleting) {
+        return false;
+    }
+    let activeSheet = this.sheetObj.getSheet();
+    // 获取当前行的消耗量
+    let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
+    let consumption = activeSheet.getValue(row, consumptionColumn);
+    $.ajax({
+        url: '/glj/delete-ratio',
+        type: 'post',
+        data: {id: id},
+        dataType: 'json',
+        error: function() {
+            isDeleting = false;
+            alert('服务器繁忙');
+        },
+        beforeSend: function() {
+            isDeleting = true;
+        },
+        success: function(response) {
+            if (response.err === 0) {
+                // 计算同级的市场单价和基价单价
+                let [parentMarketPrice, parentBasePrice] = this.getCompositionSumPrice('delete', row);
+                let info = {
+                    parentMarketPrice: parentMarketPrice,
+                    parentBasePrice: parentBasePrice,
+                    change: -consumption
+                };
+                activeSheet.setValue(row, consumptionColumn, 0);
+                callback(info);
+            }
+        }
+    });
+};

+ 0 - 527
web/glj/js/glj_index.js

@@ -1,527 +0,0 @@
-/**
- * 工料机汇总相关
- *
- * @author CaiAoLin
- * @date 2017/6/15
- * @version
- */
-let header = [];
-let gljSheet = null;
-let sourceData = [];
-let spread = null;
-let isChanging = false;
-let firstMixRatioRow = -1;
-let firstMachineRow = -1;
-$(document).ready(function () {
-
-    // 初始化表格
-    initGLJExcel();
-
-    // 是否主动更改数据
-    $("#message").on('click', '#load-data', function() {
-        $("#notify").slideUp('fast');
-        if (changeInfo.length > 0) {
-            for (let index in changeInfo) {
-                let cell = gljSheet.getCell(changeInfo[index].row, changeInfo[index].col, GC.Spread.Sheets.SheetArea.viewport);
-                cell.value(changeInfo[index].newValue);
-            }
-        }
-        changeInfo = [];
-    });
-});
-
-/**
- * 初始化表格
- *
- */
-function initGLJExcel() {
-    header = [
-        {name: '编码', field: 'code', visible: true},
-        {name: '名称', field: 'name', visible: true},
-        {name: '规格型号', field: 'unit_price.specs', visible: true},
-        {name: '单位', field: 'unit_price.unit', visible: true},
-        {name: 'ID', field: 'id', visible: false},
-        {name: '类型', field: 'unit_price.type', visible: false},
-        {name: '总消耗量', field: 'quantity', visible: true},
-        {name: '基价单价', field: "unit_price.base_price", visible: true},
-        {name: '调整基价', field: 'adjust_price', visible: true},
-        {name: '市场单价', field: "unit_price.market_price", visible: true, validator: 'number'},
-        {name: '是否暂估', field: 'is_evaluate', visible: true, cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),  validator: 'boolean'},
-        {name: '供货方式', field: 'supply', visible: true},
-        {name: '甲供数量', field: 'supply_quantity', visible: true},
-        {name: '交货方式', field: 'delivery', visible: true},
-        {name: '送达地点', field: 'delivery_address', visible: true},
-        {name: '不调价', field: 'is_adjust_price', visible: true, cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),  validator: 'boolean'},
-        {name: 'UID', field: 'unit_price.id', visible: false},
-        {name: '工料机ID', field: 'glj_id', visible: false},
-        {name: '组成物消耗量', field: 'consumption', visible: false},
-        {name: '父级关联编码', field: 'connect_code', visible: false},
-        {name: '消耗量', field: 'ratio_data', visible: false},
-    ];
-    // 数据转换格式
-    sourceData = JSON.parse(jsonData);
-    let columnInfo = [];
-    let setting = {
-        header: []
-    };
-    for(let tmp of header) {
-        setting.header.push({headerName: tmp.name, headerWidth: 120});
-        columnInfo.push({name: tmp.field, displayName: tmp.name, visible: tmp.visible, cellType: tmp.cellType, size: 120});
-    }
-
-    spread = sheetCommonObj.buildSheet(document.getElementById("project-glj"), setting, sourceData.length);
-    spread.options.scrollbarShowMax = true;
-    spread.options.scrollbarMaxAlign = true;
-    spread.options.showHorizontalScrollbar = true;
-    gljSheet = spread.getActiveSheet();
-
-    // 设置表单不可编辑
-    gljSheet.options.isProtected = true;
-
-    // 居中样式
-    let centerStyleSetting = {hAlign: 1};
-    gljSheet.setStyle(-1, 10, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    gljSheet.setStyle(-1, 15, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    gljSheet.setStyle(-1, 3, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-
-    // 向右对齐样式
-    let rightStyleSetting = {hAlign: GC.Spread.Sheets.HorizontalAlign.right};
-    gljSheet.setStyle(-1, 6, getStyle(rightStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    gljSheet.setStyle(-1, 7, getStyle(rightStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    gljSheet.setStyle(-1, 8, getStyle(rightStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    gljSheet.setStyle(-1, 9, getStyle(rightStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-
-
-    // 设置可编辑列
-    gljSheet.getRange(-1, 9, -1, 1).locked(false);
-    gljSheet.getRange(-1, 10, -1, 1).locked(false);
-    gljSheet.getRange(-1, 15, -1, 1).locked(false);
-
-    // 设置数据
-    gljSheet.autoGenerateColumns = false;
-    gljSheet.bindColumns(columnInfo);
-    gljSheet.setDataSource(sourceData);
-
-    specialColumn(sourceData);
-
-    // 绑定事件
-    gljSheet.bind(GC.Spread.Sheets.Events.ValueChanged, updateBindEvent);
-
-    // 绑定双击事件
-    gljSheet.bind(GC.Spread.Sheets.Events.CellDoubleClick, function (element, info) {
-        let column = info.col;
-        let row = info.row;
-        let field = header[column] !== undefined && header[column].field !== undefined ?
-            header[column].field : '';
-
-        // 获取类型
-        let typeColumn = getFieldColumn(header, 'unit_price.type');
-        let type = gljSheet.getValue(row, typeColumn);
-
-        // 如果类型为混凝土、砂浆、配合比、机械,则提示
-        if (field === 'unit_price.market_price' && canNotChangeTypeId.indexOf(type + '') >= 0) {
-            alert('当前工料机的市场单价由组成物计算得出,不可直接修改');
-        }
-    });
-
-    // 绑定单击事件
-    gljSheet.bind(GC.Spread.Sheets.Events.CellClick, function (element, info) {
-        if (currentTag === 'mix-ratio') {
-            let projectGLJId = getActiveProjectGLJId();
-            // 获取数据
-            getMixRatioData(projectGLJId);
-        }
-
-    });
-
-}
-
-/**
- * 成功事件
- *
- * @param {string} field
- * @param {object} info
- * @return {void}
- */
-function successTrigger(field, info) {
-    switch (field) {
-        case 'unit_price.market_price':
-            let row = info.row;
-            // 获取类型
-            let typeColumn = getFieldColumn(header, 'unit_price.type');
-            let type = gljSheet.getValue(row, typeColumn);
-
-            // 基价单价的计算
-            basePriceCalculate(type, info);
-            // 调整基价的计算
-            adjustPriceCalculate(type, info);
-            // 市场单价的计算
-            marketPriceCalculate(type, info);
-
-            // 触发websocket通知
-            socket.emit('dataNotify', JSON.stringify(info));
-            break;
-    }
-}
-
-/**
- * 设置特殊单元格数据
- *
- * @param {object} sourceData
- * @return {void}
- */
-function specialColumn(sourceData) {
-    let rowCounter = 0;
-    // 获取是否暂估的列号
-    let isEvaluateColumn = getFieldColumn(header, 'is_evaluate');
-    // 获取市场单价列号
-    let marketPriceColumn = getFieldColumn(header, 'unit_price.market_price');
-    let connectCodeColumn = getFieldColumn(header, 'connect_code');
-    let consumptionColumn = getFieldColumn(header, 'consumption');
-    for(let data of sourceData) {
-        // 只有材料才显示是否暂估
-        if (materialIdList.indexOf(data.unit_price.type + '') < 0) {
-            let string = new GC.Spread.Sheets.CellTypes.Text();
-            gljSheet.setCellType(rowCounter, isEvaluateColumn, string, GC.Spread.Sheets.SheetArea.viewport);
-            // 锁定该单元格
-            gljSheet.getRange(rowCounter, isEvaluateColumn, 1, 1).locked(true);
-            gljSheet.setValue(rowCounter, isEvaluateColumn, '');
-        }
-
-        // 如果类型为混凝土、砂浆、配合比、机械,则市场单价不能修改
-        if (canNotChangeTypeId.indexOf(data.unit_price.type + '') >= 0) {
-            firstMixRatioRow = firstMixRatioRow === -1 && data.unit_price.type !== GLJTypeConst.GENERAL_MACHINE ?
-                rowCounter : firstMixRatioRow;
-            firstMachineRow = firstMachineRow === -1 && data.unit_price.type === GLJTypeConst.GENERAL_MACHINE ?
-                rowCounter : firstMachineRow;
-            // 锁定该单元格
-            gljSheet.getRange(rowCounter, marketPriceColumn, 1, 1).locked(true);
-        }
-        // 处理数据
-        if (data.ratio_data.length > 0) {
-            let connectCode = [];
-            let consumption = [];
-            for (let tmp of data.ratio_data) {
-                connectCode.push(tmp.connect_code);
-                consumption.push(tmp.consumption);
-            }
-            let connectCodeString = connectCode.join(',');
-            let consumptionString = consumption.join(',');
-            gljSheet.setValue(rowCounter, connectCodeColumn, connectCodeString);
-            gljSheet.setValue(rowCounter, consumptionColumn, consumptionString);
-        }
-        rowCounter++;
-    }
-}
-
-/**
- * 组成物父级价格计算
- *
- * @param {Number} row
- * @return {Object}
- */
-function compositionParentCalculate(row) {
-    // 获取关联编码
-    let connectCodeColumn = getFieldColumn(header, 'connect_code');
-    let activeConnectCode = gljSheet.getValue(row, connectCodeColumn);
-
-    // 不属于组成物则忽略
-    if (activeConnectCode === '' || activeConnectCode === undefined) {
-        return null;
-    }
-    activeConnectCode = activeConnectCode.split(',');
-
-    // 计算同级组成物的价格
-    // 遍历所有记录
-    let maxRow = gljSheet.getRowCount();
-    let consumptionColumn = getFieldColumn(header, 'consumption');
-    let marketPriceColumn = getFieldColumn(header, 'unit_price.market_price');
-    let parentMarketPrice = {};
-    for (let i = 0; i < maxRow; i++) {
-        let connectCode = gljSheet.getValue(i, connectCodeColumn);
-        if (connectCode === null) {
-            continue;
-        }
-        connectCode = connectCode.split(',');
-        // 消耗量
-        let consumption = gljSheet.getValue(i, consumptionColumn);
-        consumption = consumption.split(',');
-
-        // 获取市场价
-        let marketPrice = gljSheet.getValue(i, marketPriceColumn);
-
-        for (let active of activeConnectCode) {
-            let index = connectCode.indexOf(active);
-            if (index < 0) {
-                continue;
-            }
-            let rowConsumption = consumption[index] === undefined ? 0 : consumption[index];
-            // 计算价格
-            let rowMarketPrice = (marketPrice * rowConsumption).toDecimal(2);
-            parentMarketPrice[active] = parentMarketPrice[active] === undefined ?
-                rowMarketPrice : (parentMarketPrice[active] + rowMarketPrice).toDecimal(2);
-
-        }
-
-    }
-
-    return parentMarketPrice;
-}
-
-/**
- * 组成物父类数据更新
- *
- * @param {Object} parentMarketPrice
- * @return {void}
- */
-function compositionParentUpdate(parentMarketPrice) {
-    let marketPriceColumn = getFieldColumn(header, 'unit_price.market_price');
-    // 定位到父节点,然后更新
-    for (let code in parentMarketPrice) {
-        let changeRow = searchKeyword(code);
-        gljSheet.setValue(changeRow, marketPriceColumn, parentMarketPrice[code]);
-    }
-
-}
-
-/**
- * 查找指定数据并返回行号
- *
- * @param {String} keyword
- * @param {Number} rowStart
- * @param {Number} columnStart
- * @return {Number}
- */
-function searchKeyword(keyword, rowStart = 0, columnStart = 0) {
-    let condition = new GC.Spread.Sheets.Search.SearchCondition();
-    condition.searchString = keyword;
-    condition.startSheetIndex = 0;
-    condition.endSheetIndex = 0;
-    condition.searchFlags = GC.Spread.Sheets.Search.SearchFlags.ignoreCase | GC.Spread.Sheets.Search.SearchFlags.blockRange;
-    condition.searchOrder = GC.Spread.Sheets.Search.SearchOrder.nOrder;
-    condition.searchTarget = GC.Spread.Sheets.Search.SearchFoundFlags.cellText;
-    condition.sheetArea = GC.Spread.Sheets.SheetArea.viewport;
-    condition.rowStart = rowStart;
-    condition.columnStart = columnStart;
-
-    let result = spread.search(condition);
-    return result.foundRowIndex;
-}
-
-/**
- * 基价单价计算
- *
- * @param {Number} type
- * @param {object} info
- * @return {void}
- */
-function basePriceCalculate(type, info) {
-    let basePriceColumn = getFieldColumn(header, 'unit_price.base_price');
-    switch (type) {
-        // 主材、设备自动赋值基价单价=市场单价
-        case GLJTypeConst.MAIN_MATERIAL:
-        case GLJTypeConst.EQUIPMENT:
-            gljSheet.setValue(info.row, basePriceColumn, info.newValue);
-            break;
-    }
-}
-
-/**
- * 调整基价计算
- *
- * @param {Number} type
- * @param {object} info
- * @return {void}
- */
-function adjustPriceCalculate(type, info) {
-    let basePriceColumn = getFieldColumn(header, 'unit_price.base_price');
-    let adjustPriceColumn = getFieldColumn(header, 'adjust_price');
-    switch (type) {
-        // 材料、主材、设备 调整基价=基价单价
-        case GLJTypeConst.MAIN_MATERIAL:
-        case GLJTypeConst.EQUIPMENT:
-            let basePrice = gljSheet.getValue(info.row, basePriceColumn);
-            gljSheet.setValue(info.row, adjustPriceColumn, basePrice);
-            break;
-
-    }
-}
-
-/**
- * 市场单价计算
- *
- * @param {Number} type
- * @param {object} info
- * @return {void}
- */
-function marketPriceCalculate(type, info) {
-    switch (type) {
-        // 人工、材料(普通材料)触发 需计算混凝土、砂浆、配合比、机械的市场单价
-        case GLJTypeConst.LABOUR:
-        case GLJTypeConst.GENERAL_MATERIAL:
-            // 计算
-            compositionParentUpdate(info.parentMarketPrice);
-            break;
-    }
-}
-
-/**
- * 筛选数据
- *
- * @param {Array} typeList
- * @return {void}
- */
-function filterDataByType(typeList) {
-    let typeColumn = getFieldColumn(header, 'unit_price.type');
-    typeColumn = parseInt(typeColumn);
-    let filter = new GC.Spread.Sheets.Filter.HideRowFilter(new GC.Spread.Sheets.Range(-1, typeColumn, -1, 1));
-    gljSheet.rowFilter(filter);
-    filter.filterButtonVisible(false);
-
-    for (let type of typeList) {
-        let condition = new GC.Spread.Sheets.ConditionalFormatting.Condition(GC.Spread.Sheets.ConditionalFormatting.ConditionType.numberCondition, {
-            compareType: GC.Spread.Sheets.ConditionalFormatting.GeneralComparisonOperators.equalsTo,
-            expected: type
-        });
-
-        filter.addFilterItem(typeColumn, condition);
-    }
-
-    filter.filter(typeColumn);
-    gljSheet.invalidateLayout();
-    gljSheet.repaint();
-    let filterRow = typeList.indexOf(GLJTypeConst.GENERAL_MACHINE) >= 0 ? firstMachineRow : firstMixRatioRow;
-    // 筛选后选中第一个符合类型的单元格
-    gljSheet.setSelection(filterRow, 0, 1, 1);
-}
-
-/**
- * 绑定事件
- *
- * @param {object} element
- * @param {object} info
- * @return {void|boolean}
- */
-function updateBindEvent(element, info) {
-    // 获取修改的数据
-    let column = info.col;
-    let row = info.row;
-    let idString = 'id';
-    let field = header[column] !== undefined && header[column].field !== undefined ?
-        header[column].field : '';
-    if (field === '') {
-        return false;
-    }
-
-    // 切割字段
-    let fieldArray = field.split('.');
-    idString = fieldArray.length > 1 ? 'unit_price.id' : idString;
-
-    // 防止快速同时提交
-    if (isChanging) {
-        return false;
-    }
-    // 校验数据
-    let validator = header[column].validator !== undefined ? header[column].validator : null;
-    let value = info.newValue;
-    if (validator && !checkData(validator, value)) {
-        alert('数据格式错误,请重新输入!');
-        gljSheet.setValue(row, column, info.oldValue);
-        return false;
-    }
-
-    // 获取id
-    let idColumn = getFieldColumn(header, idString);
-    if (idColumn < 0) {
-        return false;
-    }
-    let id = gljSheet.getValue(row, idColumn);
-
-    // 直接在前端计算后传值后台改
-    let extend = {};
-    let parentMarketPrice = compositionParentCalculate(row);
-    if (Object.keys(parentMarketPrice).length > 0) {
-        for (let activeCode in parentMarketPrice) {
-            let tmpObject = {
-                market_price: parentMarketPrice[activeCode],
-            };
-            extend[activeCode] = tmpObject;
-        }
-    }
-    extend = Object.keys(extend).length > 0 ?  JSON.stringify(extend) : '';
-    $.ajax({
-        url: '/glj/update',
-        type: 'post',
-        data: {id: id, field: field, value: value, extend: extend},
-        dataType: 'json',
-        error: function() {
-            alert('数据传输有误!');
-            isChanging = false;
-        },
-        beforeSend: function() {
-            isChanging = true;
-        },
-        success: function(response) {
-            isChanging = false;
-            // 修改失败则恢复原值
-            if (response.err !== 0) {
-                gljSheet.setValue(row, column, info.oldValue);
-                alert('更改数据失败!');
-            } else {
-                // 成功则触发相应事件
-                if (parentMarketPrice !== null) {
-                    info.parentMarketPrice = parentMarketPrice;
-                }
-                successTrigger(field, info);
-            }
-        }
-    });
-}
-
-/**
- * 获取当前行的项目工料机id
- *
- * @param {Number} activeRow
- * @return {Number}
- */
-function getActiveProjectGLJId() {
-    let activeGLJRow = gljSheet.getActiveRowIndex();
-
-    // 获取当前行的工料机id
-    let gljIdColumn = getFieldColumn(header, 'id');
-    let gljId = gljSheet.getValue(activeGLJRow, gljIdColumn);
-    return gljId;
-}
-
-/**
- * 设置总消耗量
- *
- * @param {Number} row
- * @param {Number} change
- * @return {void}
- */
-function setGLJQuantity(row, change) {
-    let quantityColumn = getFieldColumn(header, 'quantity');
-    let oldValue = gljSheet.getValue(row, quantityColumn);
-
-    change = (oldValue + change).toDecimal(2);
-    gljSheet.setValue(row, quantityColumn, change);
-}
-
-/**
- * 设置对应字段数值
- *
- * @param {Number} row
- * @param {String} field
- * @param {Number} value
- * @param {boolean} appendMode
- * @return {void}
- */
-function setGLJCellByField(row, field, value, appendMode) {
-    let columnIndex = getFieldColumn(header, field);
-    if (appendMode) {
-        let oldValue = gljSheet.getValue(row, columnIndex);
-        value = (oldValue + value).toDecimal(2);
-    }
-    gljSheet.setValue(row, columnIndex, value);
-}

+ 0 - 114
web/glj/js/machine.js

@@ -1,114 +0,0 @@
-/**
- * 机械单价相关
- *
- * @author CaiAoLin
- * @date 2017/7/17
- * @version
- */
-let machineHeader = [];
-let machineSheet = null;
-let machineSpread = null;
-$(document).ready(function() {
-
-    initMachineExcel();
-
-    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
-        currentTag = $(e.target).data('name');
-
-        // 获取工料机当前选中的行号
-        let projectGLJId = 0;
-        if (currentTag === "machine" && machineSpread !== null) {
-            // 筛选数据显示(显示普通机械)
-            filterDataByType([GLJTypeConst.GENERAL_MACHINE]);
-
-            // 获取数据
-            getRatioData(projectGLJId, getMachine);
-        }
-    });
-
-});
-
-/**
- * 初始化excel
- *
- * @return {void}
- */
-function initMachineExcel() {
-    machineHeader = [
-        {name: '编码', field: 'code', visible: true},
-        {name: '名称', field: 'name', visible: true},
-        {name: '单位', field: 'unit_price.unit', visible: true},
-        {name: 'ID', field: 'id', visible: false},
-        {name: '类型', field: 'unit_price.type', visible: false},
-        {name: '基价单价', field: "unit_price.base_price", visible: true},
-        {name: '调整基价', field: 'adjust_price', visible: true},
-        {name: '市场单价', field: "unit_price.market_price", visible: true},
-        {name: '用量', field: 'consumption', visible: true, validator: 'number'},
-        {name: 'CID', field: 'mix_ratio_id', visible: false},
-    ];
-
-    let setting = {
-        header: []
-    };
-    let columnInfo = [];
-    for(let tmp of machineHeader) {
-        setting.header.push({headerName: tmp.name, headerWidth: 120});
-        columnInfo.push({name: tmp.field, displayName: tmp.name, visible: tmp.visible, cellType: tmp.cellType, size: 120});
-    }
-
-    machineSpread = sheetCommonObj.buildSheet(document.getElementById("machine"), setting, 3);
-    machineSpread.options.scrollbarShowMax = true;
-    machineSpread.options.scrollbarMaxAlign = true;
-    machineSpread.options.showHorizontalScrollbar = true;
-    machineSheet = machineSpread.getActiveSheet();
-
-    // 编码列号
-    let codeColumn = getFieldColumn(machineHeader, 'code');
-    // 单位列号
-    let unitColumn = getFieldColumn(machineHeader, 'unit_price.unit');
-    // 消耗量列号
-    let consumptionColumn = getFieldColumn(machineHeader, 'consumption');
-
-    // 居中样式
-    let centerStyleSetting = {hAlign: 1};
-    machineSheet.setStyle(-1, codeColumn, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    machineSheet.setStyle(-1, unitColumn, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-
-    // 设置表单不可编辑
-    machineSheet.options.isProtected = true;
-
-    // 设置可编辑列
-    machineSheet.getRange(-1, consumptionColumn, -1, 1).locked(false);
-
-    // 绑定数据格式
-    machineSheet.autoGenerateColumns = false;
-    machineSheet.bindColumns(columnInfo);
-
-    // 绑定事件
-    // machineSheet.bind(GC.Spread.Sheets.Events.ValueChanged, updateConsumption);
-
-}
-
-/**
- * 获取对应的机械单价成功后方法
- *
- * @param {Object} response
- * @return {void|boolean}
- */
-function getMachine(response, type) {
-    switch (type) {
-        case 'success':
-            response.data = JSON.parse(response.data);
-            // 设置数据
-            machineSheet.setDataSource(response.data);
-            ratioSpecialColumn(response.data);
-            machineSpread.refresh();
-            break;
-        case 'fail':
-            // 设置数据
-            machineSheet.setDataSource(null);
-            machineSpread.refresh();
-            alert('不存在对应数据');
-            break;
-    }
-}

+ 0 - 345
web/glj/js/mix_ratio.js

@@ -1,345 +0,0 @@
-/**
- * 配合比相关
- *
- * @author CaiAoLin
- * @date 2017/7/10
- * @version
- */
-let mixRatioHeader = [];
-let mixRatioSheet = null;
-let mixRatioSpread = null;
-let currentTag = '';
-let mixRatioRightClickTarget = null;
-let isDeleting = false;
-$(document).ready(function() {
-
-    initMixRatioExcel();
-
-    // 初始化右键相关
-    initMixRatioRightClick();
-    // 切换tab触发refresh
-    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
-        currentTag = $(e.target).data('name');
-
-        // 获取工料机当前选中的行号
-        let projectGLJId = 0;
-        if (currentTag === "mix-ratio" && mixRatioSpread !== null) {
-            // 筛选数据显示(显示混凝土、砂浆、配合比)
-            filterDataByType([GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO]);
-            projectGLJId = getActiveProjectGLJId();
-
-            // 获取数据
-            getRatioData(projectGLJId, getMixRatio);
-        }
-    });
-
-
-});
-
-/**
- * 初始化excel
- *
- * @return {void}
- */
-function initMixRatioExcel() {
-    mixRatioHeader = [
-        {name: '编码', field: 'code', visible: true},
-        {name: '名称', field: 'name', visible: true},
-        {name: '单位', field: 'unit_price.unit', visible: true},
-        {name: 'ID', field: 'id', visible: false},
-        {name: '类型', field: 'unit_price.type', visible: false},
-        {name: '基价单价', field: "unit_price.base_price", visible: true},
-        {name: '调整基价', field: 'adjust_price', visible: true},
-        {name: '市场单价', field: "unit_price.market_price", visible: true},
-        {name: '消耗量', field: 'consumption', visible: true, validator: 'number'},
-        {name: 'CID', field: 'mix_ratio_id', visible: false},
-    ];
-
-    let setting = {
-        header: []
-    };
-    let columnInfo = [];
-    for(let tmp of mixRatioHeader) {
-        setting.header.push({headerName: tmp.name, headerWidth: 120});
-        columnInfo.push({name: tmp.field, displayName: tmp.name, visible: tmp.visible, cellType: tmp.cellType, size: 120});
-    }
-
-    mixRatioSpread = sheetCommonObj.buildSheet(document.getElementById("mix-ratio"), setting, 3);
-    mixRatioSpread.options.scrollbarShowMax = true;
-    mixRatioSpread.options.scrollbarMaxAlign = true;
-    mixRatioSpread.options.showHorizontalScrollbar = true;
-    mixRatioSheet = mixRatioSpread.getActiveSheet();
-
-    // 编码列号
-    let codeColumn = getFieldColumn(mixRatioHeader, 'code');
-    // 单位列号
-    let unitColumn = getFieldColumn(mixRatioHeader, 'unit_price.unit');
-    // 消耗量列号
-    let consumptionColumn = getFieldColumn(mixRatioHeader, 'consumption');
-
-    // 居中样式
-    let centerStyleSetting = {hAlign: 1};
-    mixRatioSheet.setStyle(-1, codeColumn, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-    mixRatioSheet.setStyle(-1, unitColumn, getStyle(centerStyleSetting), GC.Spread.Sheets.SheetArea.viewport);
-
-    // 设置表单不可编辑
-    mixRatioSheet.options.isProtected = true;
-
-    // 设置可编辑列
-    mixRatioSheet.getRange(-1, consumptionColumn, -1, 1).locked(false);
-
-    // 绑定数据格式
-    mixRatioSheet.autoGenerateColumns = false;
-    mixRatioSheet.bindColumns(columnInfo);
-
-    // 绑定事件
-    mixRatioSheet.bind(GC.Spread.Sheets.Events.ValueChanged, updateConsumption);
-
-}
-
-/**
- * 设置特殊单元格数据
- *
- * @param {object} sourceData
- * @return {void}
- */
-function ratioSpecialColumn(sourceData) {
-    let rowCounter = 0;
-    // 获取市场单价列号
-    let consumptionColumn = getFieldColumn(mixRatioHeader, 'consumption');
-    let idColumn = getFieldColumn(mixRatioHeader, 'mix_ratio_id');
-    for(let data of sourceData) {
-        // 把消耗量从对象中抽离出来
-        if (data.ratio_data.consumption !== undefined) {
-            mixRatioSheet.setValue(rowCounter, consumptionColumn, data.ratio_data.consumption);
-            mixRatioSheet.setValue(rowCounter, idColumn, data.ratio_data.id);
-        }
-        rowCounter++;
-    }
-}
-
-
-/**
- * 初始化右键
- *
- * @return {void}
- */
-function initMixRatioRightClick() {
-    $.contextMenu({
-        selector: '#mix-ratio',
-        build: function ($trigger, e) {
-            mixRatioRightClickTarget = SheetDataHelper.safeRightClickSelection($trigger, e, mixRatioSpread);
-            return mixRatioRightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.viewport ||
-                mixRatioRightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
-        },
-        items: {
-            "deleteMixRatio": {
-                name: "删除",
-                icon: 'fa-trash-o',
-                disabled: function () {
-                    return mixRatioRightClickTarget.row === undefined;
-                },
-                callback: function (key, opt) {
-                    let row = mixRatioRightClickTarget.row;
-                    let idColumn = getFieldColumn(mixRatioHeader, 'mix_ratio_id');
-                    let deleteId = mixRatioSheet.getValue(row, idColumn);
-                    deleteMixRatio(deleteId, row);
-                }
-            },
-        }
-    });
-}
-
-/**
- * 获取对应的配合比方法
- *
- * @param {Object} response
- * @param {String} type
- * @return {void|boolean}
- */
-function getMixRatio(response, type) {
-    switch (type) {
-        case 'success':
-            response.data = JSON.parse(response.data);
-            // 设置数据
-            mixRatioSheet.setDataSource(response.data);
-            ratioSpecialColumn(response.data);
-            mixRatioSpread.refresh();
-            break;
-        case 'fail':
-            mixRatioSheet.setDataSource(null);
-            mixRatioSpread.refresh();
-            alert('不存在对应数据');
-            break;
-    }
-}
-
-/**
- * 删除配合比数据
- *
- * @param {Number} id
- * @param {Number} row
- * @return {void | boolean}
- */
-function deleteMixRatio(id, row) {
-    id = parseInt(id);
-    if (isNaN(id) || id <= 0) {
-        alert('参数错误!');
-    }
-    if (isDeleting) {
-        return false;
-    }
-    // 获取当前行的消耗量
-    let consumptionColumn = getFieldColumn(mixRatioHeader, 'consumption');
-    let consumption = mixRatioSheet.getValue(row, consumptionColumn);
-    $.ajax({
-        url: '/glj/delete-ratio',
-        type: 'post',
-        data: {id: id},
-        dataType: 'json',
-        error: function() {
-            isDeleting = false;
-            alert('服务器繁忙');
-        },
-        beforeSend: function() {
-            isDeleting = true;
-        },
-        success: function(response) {
-            if (response.err === 0) {
-                // 删除成功后重新计算父工料机相关信息
-                let [parentMarketPrice, parentBasePrice] = getBaseAndMarketPrice('delete', row);
-                let info = {
-                    parentMarketPrice: parentMarketPrice,
-                    parentBasePrice: parentBasePrice,
-                    change: -consumption
-                };
-                mixRatioSheet.setValue(row, consumptionColumn, 0);
-                mixRatioSuccess('', info);
-            }
-        }
-    });
-}
-
-/**
- * 更新消耗量
- *
- * @param {object} element
- * @param {object} info
- * @return {void|boolean}
- */
-function updateConsumption(element, info) {
-    // 获取修改的数据
-    let column = info.col;
-    let row = info.row;
-    let field = mixRatioHeader[column] !== undefined && mixRatioHeader[column].field !== undefined ?
-        mixRatioHeader[column].field : '';
-    if (field === '') {
-        return false;
-    }
-    // 防止快速同时提交
-    if (isChanging) {
-        return false;
-    }
-    // 校验数据
-    let validator = mixRatioHeader[column].validator !== undefined ? mixRatioHeader[column].validator : null;
-    let value = info.newValue;
-    if (validator && !checkData(validator, value)) {
-        alert('数据格式错误,请重新输入!');
-        mixRatioSheet.setValue(row, column, info.oldValue);
-        return false;
-    }
-
-    // 获取id
-    let idColumn = getFieldColumn(mixRatioHeader, 'mix_ratio_id');
-    if (idColumn < 0) {
-        return false;
-    }
-    let id = mixRatioSheet.getValue(row, idColumn);
-
-    let [parentMarketPrice, parentBasePrice] = getBaseAndMarketPrice('modify', row, info.newValue);
-    $.ajax({
-        url: '/glj/update',
-        type: 'post',
-        data: {id: id, field: 'mix_ratio.' + field, value: value, market_price: parentMarketPrice, base_price: parentBasePrice},
-        dataType: 'json',
-        error: function() {
-            alert('数据传输有误!');
-            isChanging = false;
-            mixRatioSheet.setValue(row, column, info.oldValue);
-        },
-        beforeSend: function() {
-            isChanging = true;
-        },
-        success: function(response) {
-            isChanging = false;
-            // 修改失败则恢复原值
-            if (response.err !== 0) {
-                mixRatioSheet.setValue(row, column, info.oldValue);
-                alert('更改数据失败!');
-            } else {
-                info.parentMarketPrice = parentMarketPrice;
-                info.parentBasePrice = parentBasePrice;
-                info.change = info.newValue - info.oldValue;
-                mixRatioSuccess(field, info);
-            }
-        }
-    });
-}
-/**
- * 成功事件
- *
- * @param {string} field
- * @param {object} info
- * @return {void}
- */
-function mixRatioSuccess(field, info) {
-    // 成功则对相应的总消耗量进行设置
-    let activeGLJRow = gljSheet.getActiveRowIndex();
-    setGLJCellByField(activeGLJRow, 'quantity', info.change, true);
-
-    // 设置父级3个价格
-    setGLJCellByField(activeGLJRow, 'unit_price.market_price', info.parentMarketPrice, false);
-    setGLJCellByField(activeGLJRow, 'unit_price.base_price', info.parentBasePrice, false);
-    setGLJCellByField(activeGLJRow, 'adjust_price', info.parentBasePrice, false);
-}
-
-/**
- * 计算市场单价基价单价
- *
- * @param {String} scene
- * @param {Number} affectRow
- * @param {Number} newValue
- * @return {Array}
- */
-function getBaseAndMarketPrice(scene, affectRow, newValue = 0) {
-    // 计算父级3个价格
-    let maxRow = mixRatioSheet.getRowCount();
-    // 获取对应列的列号
-    let marketPriceColumn = getFieldColumn(mixRatioHeader, 'unit_price.market_price');
-    let consumptionColumn = getFieldColumn(mixRatioHeader, 'consumption');
-    let basePriceColumn = getFieldColumn(mixRatioHeader, 'unit_price.base_price');
-
-    let parentMarketPrice = 0;
-    let parentBasePrice = 0;
-    for(let i = 0; i < maxRow; i++) {
-        // 获取市场单价
-        let marketPrice = mixRatioSheet.getValue(i, marketPriceColumn);
-        // 获取基价单价
-        let basePrice = mixRatioSheet.getValue(i, basePriceColumn);
-
-        // 如果是删除则忽略即将被删除的行数据
-        if (scene === 'delete' && affectRow === i) {
-            continue;
-        }
-
-        // 获取消耗量(如果是当前修改的行则替换数据)
-        let consumption = i === affectRow ? newValue : mixRatioSheet.getValue(i, consumptionColumn);
-
-        parentMarketPrice += consumption * marketPrice;
-        parentBasePrice += consumption * basePrice;
-    }
-
-    parentMarketPrice = parentMarketPrice.toDecimal(2);
-    parentBasePrice = parentBasePrice.toDecimal(2);
-    return [parentMarketPrice, parentBasePrice]
-}

+ 68 - 0
web/glj/js/project_glj.js

@@ -0,0 +1,68 @@
+/**
+ * 工料机汇总相关
+ *
+ * @author CaiAoLin
+ * @date 2017/6/15
+ * @version
+ */
+let projectGLJSpread = null;
+let projectGLJSheet = null;
+let currentTag = '';
+let isChanging = false;
+$(document).ready(function () {
+
+    projectGLJSpread = new ProjectGLJSpread();
+    projectGLJSpread.successCallback = successTrigger;
+    projectGLJSheet = projectGLJSpread.init();
+
+    // 绑定单击事件
+    let lastRow = 0;
+    projectGLJSheet.bind(GC.Spread.Sheets.Events.CellClick, function (element, info) {
+        let currentRow = info.row;
+        if (currentTag === 'mix-ratio' && mixRatioSheet !== null && currentRow != lastRow) {
+            let projectGLJId = projectGLJSheet.getActiveDataByField('id');
+            // 获取数据
+            compositionSpread.getRatioData(projectGLJId);
+        }
+        lastRow = currentRow;
+    });
+
+    // 切换tab触发refresh
+    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+        currentTag = $(e.target).data('name');
+        if (currentTag === 'ration') {
+            projectGLJSheet.filterData('unit_price.type', []);
+        }
+    });
+
+    // 是否主动更改数据
+    // $("#message").on('click', '#load-data', function() {
+    //     $("#notify").slideUp('fast');
+    //     if (changeInfo.length > 0) {
+    //         for (let index in changeInfo) {
+    //             let cell = gljSheet.getCell(changeInfo[index].row, changeInfo[index].col, GC.Spread.Sheets.SheetArea.viewport);
+    //             cell.value(changeInfo[index].newValue);
+    //         }
+    //     }
+    //     changeInfo = [];
+    // });
+});
+
+/**
+ * 成功事件
+ *
+ * @param {string} field
+ * @param {object} info
+ * @return {void}
+ */
+function successTrigger(field, info) {
+    switch (field) {
+        case 'unit_price.market_price':
+            // 计算价格
+            projectGLJSpread.priceCalculate(info);
+
+            // 触发websocket通知
+            socket.emit('dataNotify', JSON.stringify(info));
+            break;
+    }
+}

+ 379 - 0
web/glj/js/project_glj_spread.js

@@ -0,0 +1,379 @@
+/**
+ * 项目工料机相关spread
+ *
+ * @author CaiAoLin
+ * @date 2017/7/19
+ * @version
+ */
+
+/**
+ * 构造函数
+ *
+ * @return {void}
+ */
+function ProjectGLJSpread() {
+    this.isChanging = false;
+    this.sheetObj = null;
+    this.firstMachineRow = -1;
+    this.firstMixRatioRow = -1;
+    this.successCallback = null;
+}
+
+/**
+ * 初始化
+ *
+ * @return {object}
+ */
+ProjectGLJSpread.prototype.init = function () {
+
+    let header = [
+        {name: '编码', field: 'code', visible: true},
+        {name: '名称', field: 'name', visible: true},
+        {name: '规格型号', field: 'unit_price.specs', visible: true},
+        {name: '单位', field: 'unit_price.unit', visible: true},
+        {name: 'ID', field: 'id', visible: false},
+        {name: '类型', field: 'unit_price.type', visible: true},
+        {name: '总消耗量', field: 'quantity', visible: true},
+        {name: '基价单价', field: "unit_price.base_price", visible: true},
+        {name: '调整基价', field: 'adjust_price', visible: true},
+        {name: '市场单价', field: "unit_price.market_price", visible: true, validator: 'number'},
+        {
+            name: '是否暂估',
+            field: 'is_evaluate',
+            visible: true,
+            cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),
+            validator: 'boolean'
+        },
+        {name: '供货方式', field: 'supply', visible: true},
+        {name: '甲供数量', field: 'supply_quantity', visible: true},
+        {name: '交货方式', field: 'delivery', visible: true},
+        {name: '送达地点', field: 'delivery_address', visible: true},
+        {
+            name: '不调价',
+            field: 'is_adjust_price',
+            visible: true,
+            cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),
+            validator: 'boolean'
+        },
+        {name: 'UID', field: 'unit_price.id', visible: false},
+        {name: '工料机ID', field: 'glj_id', visible: false},
+        {name: '组成物消耗量', field: 'consumption', visible: false},
+        {name: '父级关联编码', field: 'connect_code', visible: false},
+        {name: '消耗量', field: 'ratio_data', visible: false},
+    ];
+    let sourceData = JSON.parse(jsonData);
+
+    this.sheetObj = new CommonSpreadJs(header);
+    this.sheetObj.init('project-glj');
+
+    // 获取列号
+    let isEvaluateColumn = this.sheetObj.getFieldColumn('is_evaluate');
+    let isAdjustPriceColumn = this.sheetObj.getFieldColumn('is_adjust_price');
+    let unitColumn = this.sheetObj.getFieldColumn('unit_price.unit');
+    let quantityColumn = this.sheetObj.getFieldColumn('quantity');
+    let basePriceColumn = this.sheetObj.getFieldColumn('unit_price.base_price');
+    let adjustPriceColumn = this.sheetObj.getFieldColumn('adjust_price');
+    let marketPriceColumn = this.sheetObj.getFieldColumn('unit_price.market_price');
+
+    // 居中样式
+    let centerStyleSetting = {hAlign: 1};
+    this.sheetObj.setStyle(-1, isEvaluateColumn, centerStyleSetting);
+    this.sheetObj.setStyle(-1, isAdjustPriceColumn, centerStyleSetting);
+    this.sheetObj.setStyle(-1, unitColumn, centerStyleSetting);
+
+    // 向右对齐样式
+    let rightStyleSetting = {hAlign: GC.Spread.Sheets.HorizontalAlign.right};
+    this.sheetObj.setStyle(-1, quantityColumn, rightStyleSetting);
+    this.sheetObj.setStyle(-1, basePriceColumn, rightStyleSetting);
+    this.sheetObj.setStyle(-1, adjustPriceColumn, rightStyleSetting);
+    this.sheetObj.setStyle(-1, marketPriceColumn, rightStyleSetting);
+
+    // 设置可编辑列
+    this.sheetObj.setColumnEditable(marketPriceColumn);
+    this.sheetObj.setColumnEditable(isEvaluateColumn);
+    this.sheetObj.setColumnEditable(isAdjustPriceColumn);
+
+    this.sheetObj.setData(sourceData);
+    this.specialColumn(sourceData);
+
+    // 绑定修改事件
+    let self = this;
+    this.sheetObj.bind(GC.Spread.Sheets.Events.ValueChanged, function(element, info) {
+        self.updateProjectGLJField(info, self.successCallback);
+    });
+
+    // 绑定双击事件
+    this.sheetObj.bind(GC.Spread.Sheets.Events.CellDoubleClick, function (element, info) {
+        let column = info.col;
+        let row = info.row;
+        let field = self.sheetObj.getColumnField(column);
+        let activeSheet = self.sheetObj.getSheet();
+
+        // 获取类型
+        let typeColumn = self.sheetObj.getFieldColumn('unit_price.type');
+        let type = activeSheet.getValue(row, typeColumn);
+
+        // 如果类型为混凝土、砂浆、配合比、机械,则提示
+        if (field === 'unit_price.market_price' && canNotChangeTypeId.indexOf(type + '') >= 0) {
+            alert('当前工料机的市场单价由组成物计算得出,不可直接修改');
+        }
+    });
+
+
+    return this.sheetObj;
+};
+
+/**
+ * 更新项目工料机中的数据字段
+ *
+ * @param {object} info
+ * @param {function} callback
+ * @return {void|boolean}
+ */
+ProjectGLJSpread.prototype.updateProjectGLJField = function(info, callback) {
+    // 获取修改的数据
+    let column = info.col;
+    let row = info.row;
+    let idString = 'id';
+    let field = projectGLJSheet.getColumnField(column);
+
+    if (field === '') {
+        return false;
+    }
+    let activeSheet = projectGLJSheet.getSheet();
+
+    // 切割字段
+    let fieldArray = field.split('.');
+    idString = fieldArray.length > 1 ? 'unit_price.id' : idString;
+
+    // 防止快速同时提交
+    if (isChanging) {
+        return false;
+    }
+
+    // 校验数据
+    let value = info.newValue;
+    if (!projectGLJSheet.checkData(column, value)) {
+        alert('数据格式错误,请重新输入!');
+        activeSheet.setValue(row, column, info.oldValue);
+        return false;
+    }
+
+    // 获取id
+    let idColumn = projectGLJSheet.getFieldColumn(idString);
+    if (idColumn < 0) {
+        return false;
+    }
+    let id = activeSheet.getValue(row, idColumn);
+
+    // 直接在前端计算后传值后台改
+    let extend = {};
+    let parentMarketPrice = projectGLJSpread.compositionCalculate(row);
+    if (Object.keys(parentMarketPrice).length > 0) {
+        for (let activeCode in parentMarketPrice) {
+            let tmpObject = {
+                market_price: parentMarketPrice[activeCode],
+            };
+            extend[activeCode] = tmpObject;
+        }
+    }
+    extend = Object.keys(extend).length > 0 ?  JSON.stringify(extend) : '';
+    $.ajax({
+        url: '/glj/update',
+        type: 'post',
+        data: {id: id, field: field, value: value, extend: extend},
+        dataType: 'json',
+        error: function() {
+            alert('数据传输有误!');
+            isChanging = false;
+        },
+        beforeSend: function() {
+            isChanging = true;
+        },
+        success: function(response) {
+            isChanging = false;
+            // 修改失败则恢复原值
+            if (response.err !== 0) {
+                activeSheet.setValue(row, column, info.oldValue);
+                alert('更改数据失败!');
+            } else {
+                // 成功则触发相应事件
+                if (parentMarketPrice !== null) {
+                    info.parentMarketPrice = parentMarketPrice;
+                }
+                callback(field, info);
+            }
+        }
+    });
+};
+
+/**
+ * 设置特殊单元格数据
+ *
+ * @param {object} sourceData
+ * @return {void}
+ */
+ProjectGLJSpread.prototype.specialColumn = function (sourceData) {
+    let rowCounter = 0;
+    // 获取列号
+    let isEvaluateColumn = this.sheetObj.getFieldColumn('is_evaluate');
+    let marketPriceColumn = this.sheetObj.getFieldColumn('unit_price.market_price');
+
+    let connectCodeColumn = this.sheetObj.getFieldColumn('connect_code');
+    let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
+    let activeSheet = this.sheetObj.getSheet();
+    for (let data of sourceData) {
+        // 只有材料才显示是否暂估
+        if (materialIdList.indexOf(data.unit_price.type + '') < 0) {
+            let string = new GC.Spread.Sheets.CellTypes.Text();
+            activeSheet.setCellType(rowCounter, isEvaluateColumn, string, GC.Spread.Sheets.SheetArea.viewport);
+            // 锁定该单元格
+            activeSheet.getRange(rowCounter, isEvaluateColumn, 1, 1).locked(true);
+            activeSheet.setValue(rowCounter, isEvaluateColumn, '');
+        }
+
+        // 如果类型为混凝土、砂浆、配合比、机械,则市场单价不能修改
+        if (canNotChangeTypeId.indexOf(data.unit_price.type + '') >= 0) {
+            this.firstMixRatioRow = this.firstMixRatioRow === -1 && data.unit_price.type !== GLJTypeConst.GENERAL_MACHINE ?
+                rowCounter : this.firstMixRatioRow;
+            this.firstMachineRow = this.firstMachineRow === -1 && data.unit_price.type === GLJTypeConst.GENERAL_MACHINE ?
+                rowCounter : this.firstMachineRow;
+            // 锁定该单元格
+            activeSheet.getRange(rowCounter, marketPriceColumn, 1, 1).locked(true);
+        }
+        // 处理数据
+        if (data.ratio_data.length > 0) {
+            let connectCode = [];
+            let consumption = [];
+            for (let tmp of data.ratio_data) {
+                connectCode.push(tmp.connect_code);
+                consumption.push(tmp.consumption);
+            }
+            let connectCodeString = connectCode.join(',');
+            let consumptionString = consumption.join(',');
+            activeSheet.setValue(rowCounter, connectCodeColumn, connectCodeString);
+            activeSheet.setValue(rowCounter, consumptionColumn, consumptionString);
+        }
+        rowCounter++;
+    }
+};
+
+/**
+ * 计算当前行对应组成物的市场以及基价单价价格
+ *
+ * @param {Number} row
+ * @return {Object}
+ */
+ProjectGLJSpread.prototype.compositionCalculate = function(row) {
+    let activeSheet = this.sheetObj.getSheet();
+    // 获取相关列号
+    let connectCodeColumn = this.sheetObj.getFieldColumn('connect_code');
+    let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
+    let marketPriceColumn = this.sheetObj.getFieldColumn('unit_price.market_price');
+
+    let activeConnectCode = activeSheet.getValue(row, connectCodeColumn);
+
+    // 不属于组成物则忽略
+    if (activeConnectCode === '' || activeConnectCode === undefined) {
+        return null;
+    }
+    activeConnectCode = activeConnectCode.split(',');
+
+    // 计算同级组成物的价格
+    // 遍历所有记录
+    let maxRow = activeSheet.getRowCount();
+
+    let parentMarketPrice = {};
+    for (let i = 0; i < maxRow; i++) {
+        let connectCode = activeSheet.getValue(i, connectCodeColumn);
+        if (connectCode === null) {
+            continue;
+        }
+        connectCode = connectCode.split(',');
+        // 消耗量
+        let consumption = activeSheet.getValue(i, consumptionColumn);
+        consumption = consumption.split(',');
+
+        // 获取市场价
+        let marketPrice = activeSheet.getValue(i, marketPriceColumn);
+
+        for (let active of activeConnectCode) {
+            let index = connectCode.indexOf(active);
+            if (index < 0) {
+                continue;
+            }
+            let rowConsumption = consumption[index] === undefined ? 0 : consumption[index];
+            // 计算价格
+            let rowMarketPrice = (marketPrice * rowConsumption).toDecimal(2);
+            parentMarketPrice[active] = parentMarketPrice[active] === undefined ?
+                rowMarketPrice : (parentMarketPrice[active] + rowMarketPrice).toDecimal(2);
+
+        }
+
+    }
+
+    return parentMarketPrice;
+};
+
+/**
+ * 组成物父类数据更新
+ *
+ * @param {Object} parentMarketPrice
+ * @return {void}
+ */
+ProjectGLJSpread.prototype.compositionParentUpdate = function(parentMarketPrice) {
+    let marketPriceColumn = this.sheetObj.getFieldColumn('unit_price.market_price');
+    let activeSheet = this.sheetObj.getSheet();
+    // 定位到父节点,然后更新
+    for (let code in parentMarketPrice) {
+        let changeRow = this.sheetObj.searchKeyword(code);
+        activeSheet.setValue(changeRow, marketPriceColumn, parentMarketPrice[code]);
+    }
+};
+
+/**
+ * 价格计算
+ *
+ * @param {Object} info
+ * @return {void}
+ */
+ProjectGLJSpread.prototype.priceCalculate = function(info) {
+    let row = info.row;
+    let typeColumn = this.sheetObj.getFieldColumn('unit_price.type');
+    let basePriceColumn = this.sheetObj.getFieldColumn('unit_price.base_price');
+    let adjustPriceColumn = projectGLJSheet.getFieldColumn('adjust_price');
+    let activeSheet = this.sheetObj.getSheet();
+
+    // 获取类型
+    let type = activeSheet.getValue(row, typeColumn);
+
+    // 基价单价计算
+    switch (type) {
+        // 主材、设备自动赋值基价单价=市场单价
+        case GLJTypeConst.MAIN_MATERIAL:
+        case GLJTypeConst.EQUIPMENT:
+            activeSheet.setValue(info.row, basePriceColumn, info.newValue);
+            break;
+    }
+
+    // 调整基价计算
+    switch (type) {
+        // 材料、主材、设备 调整基价=基价单价
+        case GLJTypeConst.MAIN_MATERIAL:
+        case GLJTypeConst.EQUIPMENT:
+            let basePrice = activeSheet.getValue(info.row, basePriceColumn);
+            activeSheet.setValue(info.row, adjustPriceColumn, basePrice);
+            break;
+
+    }
+
+    // 市场单价计算
+    switch (type) {
+        // 人工、材料(普通材料)触发 需计算混凝土、砂浆、配合比、机械的市场单价
+        case GLJTypeConst.LABOUR:
+        case GLJTypeConst.GENERAL_MATERIAL:
+            // 计算
+            this.compositionParentUpdate(info.parentMarketPrice);
+            break;
+    }
+};

+ 0 - 98
web/glj/js/spread_common.js

@@ -1,98 +0,0 @@
-/**
- * spread 公用函数
- *
- * @author CaiAoLin
- * @date 2017/7/12
- * @version
- */
-
-/**
- * 校验数据
- *
- * @param {string} validator
- * @param {string} value
- * @return {boolean}
- */
-function checkData(validator, value) {
-    let result = false;
-    switch (validator) {
-        case 'number':
-            let regular = /^\d+(\.\d+)?$/;
-            result = regular.test(value);
-            break;
-        case 'boolean':
-            let booleanValue = [true, false];
-            result = booleanValue.indexOf(value) >= 0;
-            break;
-    }
-
-    return result;
-}
-
-/**
- * 查找指定字段的列index
- *
- * @param {object} data
- * @param {String} field
- * @return {Number}
- */
-function getFieldColumn(data, field) {
-    let result = -1;
-    if (data.length <= 0) {
-        return result;
-    }
-    for (let tmp in data) {
-        if (data[tmp].field === field) {
-            result = tmp;
-            break;
-        }
-    }
-
-    return result;
-}
-
-/**
- * 生成样式
- *
- * @param {object} setting
- * @return {object}
- */
-function getStyle(setting) {
-    let style = new GC.Spread.Sheets.Style();
-    style.locked = setting.readOnly === undefined ? true : setting.readOnly;
-    style.hAlign = setting.hAlign === undefined ? GC.Spread.Sheets.HorizontalAlign.center : setting.hAlign;
-    return style;
-}
-
-/**
- * 根据项目工料机id获取对应的配合比或者机械单价数据
- *
- * @param {Number} projectGLJid
- * @param {function} callback
- * @return {void|boolean}
- */
-function getRatioData(projectGLJid, callback) {
-    projectGLJid = parseInt(projectGLJid);
-    if (isNaN(projectGLJid) || projectGLJid <= 0) {
-        callback(null, 'fail');
-        return false;
-    }
-    $.ajax({
-        url: '/glj/get-ratio',
-        type: 'post',
-        data: {id: projectGLJid, project_id: 1},
-        error: function() {
-            callback(null, 'fail');
-        },
-        beforeSend: function() {
-
-        },
-        success: function(response) {
-            if (response.err === 0) {
-                callback(response, 'success');
-            } else {
-                callback(null, 'fail');
-            }
-        }
-    });
-}