diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json
index 47f342e..10dd5d3 100644
--- a/api/.vscode/launch.json
+++ b/api/.vscode/launch.json
@@ -56,8 +56,8 @@
             // "--qndmn http://resources.anxinyun.cn",
             // "--qndmn http://rhvqdivo5.hn-bkt.clouddn.com",
             // click 开发
-            // "--clickHouseUrl http://10.8.30.95",
-            // "--clickHousePort 30123",
+            "--clickHouseUrl http://10.8.30.95",
+            "--clickHousePort 30123",
             // click 测试
             // "--clickHouseUrl http://10.8.30.161",
             // "--clickHousePort 30123",
@@ -71,20 +71,20 @@
             //
             //
             // click 商用
-            "--clickHouseUrl http://218.3.126.49",
-            "--clickHousePort 18123",
-            "--clickHouseUser default",
-            "--clickHousePassword Wbo@hi1I",
-            "--clickHouseAnxincloud anxinyun",
-            "--clickHousePepEmis pepca",
-            "--clickHouseProjectManage peppm",
-            "--clickHouseVcmp video_access",
-            "--clickHouseDataAlarm alarm",
-            "--clickHouseIot iota",
-            "--clickHouseCamworkflow camundaworkflow",
-            "--confirmAlarmAnxinUserId 1",
-            "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
-            "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
+            // "--clickHouseUrl http://218.3.126.49",
+            // "--clickHousePort 18123",
+            // "--clickHouseUser default",
+            // "--clickHousePassword Wbo@hi1I",
+            // "--clickHouseAnxincloud anxinyun",
+            // "--clickHousePepEmis pepca",
+            // "--clickHouseProjectManage peppm",
+            // "--clickHouseVcmp video_access",
+            // "--clickHouseDataAlarm alarm",
+            // "--clickHouseIot iota",
+            // "--clickHouseCamworkflow camundaworkflow",
+            // "--confirmAlarmAnxinUserId 1",
+            // "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
+            // "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
             //
             //
             // 似乎不能传空 先注释 * 2
@@ -98,17 +98,17 @@
             // "--clickHouseDataAlarm default",
             // "--clickHouseIot iot",
             // 测试
-            // "--clickHouseAnxincloud anxinyun",
-            // "--clickHousePepEmis pepca",
-            // "--clickHouseProjectManage peppm",
-            // "--clickHouseVcmp video_access_dev",
-            // "--clickHouseDataAlarm default",
-            // "--clickHouseDataAlarmLocal default",
-            // "--clickHouseIot iota",
-            // "--clickHouseCamworkflow camworkflow",
-            // "--confirmAlarmAnxinUserId 1",
-            // "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
-            // "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
+            "--clickHouseAnxincloud anxinyun",
+            "--clickHousePepEmis pepca",
+            "--clickHouseProjectManage peppm",
+            "--clickHouseVcmp video_access_dev",
+            "--clickHouseDataAlarm alarm",
+            "--clickHouseDataAlarmLocal default",
+            "--clickHouseIot iota",
+            "--clickHouseCamworkflow camworkflow",
+            "--confirmAlarmAnxinUserId 1",
+            "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
+            "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
             "--apiCrawUrl http://218.3.126.49:30555/v1" //设备升级接口
          ]
       },
diff --git a/api/app/lib/controllers/dataCacl/index.js b/api/app/lib/controllers/dataCacl/index.js
new file mode 100644
index 0000000..21eca58
--- /dev/null
+++ b/api/app/lib/controllers/dataCacl/index.js
@@ -0,0 +1,636 @@
+
+const moment = require('moment');
+//获取异常识别算法
+async function findAbnMethods(ctx, next) {
+    let rslt = null;
+    let error = { name: 'FindError', message: '异常识别算法获取失败' };
+    try {
+        const models = ctx.fs.dc.models;
+        let abnMethods = await models.AbnTypes.findAll();
+        rslt = abnMethods.map(s => ({
+            id: s.id,
+            name: s.name,
+            des: s.des
+        }));
+        error = null;
+    } catch (err) {
+        ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+    }
+    if (error) {
+        ctx.status = 400;
+        ctx.body = error;
+    } else {
+        ctx.status = 200;
+        ctx.body = rslt;
+    }
+}
+//获取异常参数配置
+async function findAbnParamList(ctx) {
+    const { sensorId } = ctx.query
+    const id=sensorId.split(',')
+    let rslt = null;
+    let error = { name: 'FindError', message: '异常参数配置获取失败' };
+    try {
+        const models = ctx.fs.dc.models;
+        let abnParamList = await models.AbnReportParams.findAll({
+            where: { sensorId:{$in:id} }
+        })
+        rslt = abnParamList.map(s => ({
+            key: s.id,
+            id: s.id,
+            sensorId: s.sensorId,
+            sensorName: s.sensorLocationDescription,
+            abnType: s.abnTypeId,
+            enabled: s.enabled,
+            factorId: s.factorId,
+            factorName: s.factor,
+            params: s.params,
+            itemIndex:s.itemIndex
+        }));
+        error = null;
+    } catch (err) {
+        ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+    }
+    if (error) {
+        ctx.status = 400;
+        ctx.body = error;
+    } else {
+        ctx.status = 200;
+        ctx.body = rslt;
+    }
+}
+
+//新增异常参数配置
+async function createAbnParam(ctx) {
+    let error = { name: 'CreateError', message: '异常参数配置新增失败' };
+    const models = ctx.fs.dc.models
+    const data = ctx.request.body
+
+    try {
+        for (let i = 0; i < data.length; i++) {
+            let dataItem = data[i];
+            if (dataItem && dataItem.params && dataItem.abnType && dataItem.enabled != null && dataItem.sensorId && dataItem.factorId) {
+                let dataToSave = {
+                    sensorId: dataItem.sensorId,
+                    sensorLocationDescription:dataItem.sensorName,
+                    enabled: dataItem.enabled,
+                    abnTypeId: dataItem.abnType,
+                    factorId: dataItem.factorId,
+                    factor: dataItem.factorName,
+                    itemIndex: dataItem.itemId,
+                    params: dataItem.params
+                };
+                await models.AbnReportParams.create(dataToSave)
+
+                error = null
+                // // 日志信息
+                // ctx.fs.api = ctx.fs.api || {}
+                // ctx.fs.api.actionParameter = JSON.stringify(data)
+                // ctx.fs.api.actionParameterShow = `新增异常推送配置id:${newId}`
+            }
+        }
+    }
+    catch (err) {
+        ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`)
+    }
+    if (error) {
+        ctx.status = 400
+        ctx.body = error
+    } else {
+        ctx.status = 204
+    }
+}
+//
+async function batchSwitch(ctx, next) {
+    const ids = ctx.params.ids.split(',')
+    const data = ctx.request.body
+    let error = { name: 'UpdateError', message: data ? '批量启用异常参数配置失败' : '批量禁用异常参数配置失败' };
+    try {
+        for (let i = 0; i < ids.length; i++) {
+            let id = ids[i];
+            const models = ctx.fs.dc.models;
+            let abnParam = await models.AbnReportParams.findOne({ where: { id: id } });
+            if (abnParam) {
+                let dataToSave = {};
+                if (data.use == 'switch') {
+                    dataToSave.enabled = data.enabled;//批量启用or禁用
+                } else {
+                    dataToSave.params = data.paramJson;//批量改参数
+                }
+                if (Object.keys(dataToSave).length) {
+                    await models.AbnReportParams.update(dataToSave, { where: { id } });
+                }
+                error = null;
+                // // 日志信息
+                // ctx.fs.api = ctx.fs.api || {};
+                // ctx.fs.api.actionParameter = JSON.stringify(data);
+                // ctx.fs.api.actionParameterShow = `异常参数配置id:${id}`;
+            } else {
+                error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` };
+            }
+        }
+    } catch (err) {
+        ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+    }
+    if (error) {
+        ctx.status = 400;
+        ctx.body = error;
+    } else {
+        ctx.status = 204;
+    }
+}
+
+//删除异常参数配置
+async function deleteAbnParam(ctx, next) {
+    let error = { name: 'DeleteError', message: '异常参数配置删除失败' };
+    const ids = ctx.params.ids.split(',');
+    //const { id } = ctx.params;
+    try {
+        for (let i = 0; i < ids.length; i++) {
+            let id = ids[i];
+            const models = ctx.fs.dc.models;
+            let abnParam = await models.AbnReportParams.findOne({ where: { id } });
+            if (abnParam) {
+                await models.AbnReportParams.destroy({ where: { id } });
+                error = null;
+            } else {
+                error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` };
+            }
+        }
+    } catch (err) {
+        ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+    }
+    if (error) {
+        ctx.status = 400;
+        ctx.body = error;
+    } else {
+        ctx.status = 204;
+    }
+}
+
+//修改异常推送配置
+async function updateAbnParam(ctx) {
+    let error = { name: 'UpdateError', message: '异常参数配置修改失败' }
+    const ids = ctx.params.ids.split(',')
+    const data = ctx.request.body
+
+    if (data && Object.keys(data).length) {
+        try {
+            for (let i = 0; i < ids.length; i++) {
+                let id = ids[i];
+                const models = ctx.fs.dc.models;
+                let abnParam = await models.AbnReportParams.findOne({ where: { id: id } });
+                if (abnParam) {
+                    let dataToSave = {};
+                    const { abnType, params, enabled } = data;
+                    if (enabled != null && enabled != abnParam.enabled)
+                        dataToSave.enabled = enabled;
+                    //中断
+                    if (abnType == 1) {
+                        if (params != null && params.thr_int !== abnParam.params.thr_int) {
+                            dataToSave.params = params;
+                        }
+                    }
+                    //毛刺
+                    if (abnType == 2) {
+                        if (params != null && params.thr_burr !== abnParam.params.thr_burr) {
+                            dataToSave.params = params;
+                        }
+                    }
+                    //趋势
+                    if (abnType == 3) {
+                        if (params != null &&
+                            (params.thr_burr !== abnParam.params.thr_burr || params.win_med !== abnParam.params.win_med
+                                || params.win_avg !== abnParam.params.win_avg || params.win_grad !== abnParam.params.win_grad
+                                || params.thr_grad !== abnParam.params.thr_grad || params.thr_der !== abnParam.params.thr_der
+                                || params.days_Last !== abnParam.params.days_Last)) {
+                            dataToSave.params = params;
+                        }
+                    }
+                    if (Object.keys(dataToSave).length) {
+                        await models.AbnReportParams.update(dataToSave, { where: { id } });
+                    }
+                    error = null;
+                } else {
+                    error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` };
+                }
+            }
+        } catch (err) {
+            ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+        }
+    }
+    if (error) {
+        ctx.status = 400;
+        ctx.body = error;
+    } else {
+        ctx.status = 204;
+    }
+}
+//异常数据对比
+async function getAbnTaskResult(ctx, next) {
+    let error = { name: 'TaskError', message: '异常数据对比失败' }
+    const models = ctx.fs.dc.models
+    const structId = ctx.params.id
+    const startTime = ctx.params.start
+    const endTime = ctx.params.end
+    const data = ctx.request.body
+    const stationId = data.station
+    let factorProto = await models.Factor.findOne({
+        where: { id: data.factorId },
+        attributes: ['id', 'proto']
+    });
+    let protoItems = await models.FactorProtoItem.findAll({
+        where: { proto: factorProto.proto },
+        attributes: ['id', 'name']
+    });
+    let itemName = await models.FactorProtoItem.findOne({
+        where: { id: data.itemId ? data.itemId : protoItems[0].id },
+        attributes: ['id', 'field_name', 'name']
+    });
+    try {
+        const itemsObj = await findThemeItems(models, data.factorId)
+        const filter = {
+            query: {
+                bool: {
+                    must: [
+                        { match: { "sensor": stationId } }
+                    ]
+                }
+            }
+        }
+        if (startTime && endTime) {
+            filter.query.bool.must.push({ range: { "collect_time": { gte: moment(startTime).toISOString(), lte: moment(endTime).toISOString() } } });
+        }
+        const esThemeData = await findThemeDataFromES(ctx.app.fs.esclient, filter)
+        const stationsData = esThemeData.reduce((p, c) => {
+            const { sensor, data, collect_time } = c._source;
+            p.unshift(Object.assign({}, data, { time: moment(collect_time).format('YYYY-MM-DD HH:mm:ss') }));
+            return p;
+        }, []);
+        //获取前一天的最后一条数据
+        const preFilter = {
+            query: {
+                bool: {
+                    must: [
+                        { match: { "sensor": stationId } },
+                        { range: { "collect_time": { gte: moment(startTime).add('days', -1).toISOString(), lte: moment(startTime).toISOString() } } }
+                    ]
+                }
+            }
+        }
+        const esPreData = await findThemeDataFromES(ctx.app.fs.esclient, preFilter);
+        const preOneData = esPreData.reduce((p, c) => {
+            const { data, collect_time } = c._source;
+            p.unshift(Object.assign({}, data, { time: moment(collect_time).format('YYYY-MM-DD HH:mm:ss') }));
+            return p;
+        }, []);
+        let one = preOneData && preOneData.length > 0 ? preOneData[preOneData.length - 1] : null;
+
+        let itemKey = itemName.get({ plain: true }).field_name;//监测项名称
+        let itemn = itemName.get({ plain: true }).name;
+        let calcResult = calcAlgorithm(one, stationsData, data, itemKey);//计算
+        ctx.status = 200;
+        ctx.body = {
+            unit: itemsObj[itemKey].unit,
+            method: data.abnType,
+            itemKey: itemKey,
+            itemName: itemn,
+            stationData: stationsData,
+            resultArray: calcResult
+        };
+    } catch (err) {
+        ctx.fs.logger.error(err);
+        ctx.status = 400;
+        ctx.body = {
+            name: "FindError",
+            message: "异常数据识别-数据对比失败"
+        }
+    }
+}
+
+let calcAlgorithm = function (dataOne, dataSource, params, itemKey) {
+    let result;
+    switch (params.abnType) {
+        case "interrupt":
+            result = interrupt(dataOne, dataSource, params, itemKey);
+            break;
+        case "burr":
+            result = burr(dataOne, dataSource, params, itemKey).result;
+            break;
+        case "trend":
+            result = trend(null, dataSource, params, itemKey);
+            break;
+    }
+    return result;
+};
+//中断
+let interrupt = function (dataOne, dataSource, params, key) {
+    let result = [];
+    if (dataSource.length != 0) {
+        if (dataOne == null) {
+            result.push({
+                type: "interrupt",
+                hour: 24.00,
+                time: dataSource[0].time,
+                value: dataSource[0][key]
+            });//第一个点中断
+        } else {
+            dataSource.unshift(dataOne);
+        }
+        for (let i = 0; i < dataSource.length - 1; i++) {
+            if (dataSource[i] == null || dataSource[i + 1] == null) continue;
+            let hour = getHour(dataSource[i + 1].time, dataSource[i].time);
+            if (hour >= params.params.thr_int) {
+                result.push({
+                    type: "interrupt",
+                    hour: hour,
+                    time: dataSource[i + 1].time,
+                    value: dataSource[i + 1][key]
+                });
+            }
+        }
+    }
+    return result;
+}
+//毛刺
+let burr = function (dataOne, dataSource, params, key) {
+    let burrTv = params.params.thr_burr;
+    let result = [];//毛刺点
+    let dataSAfterBurr = [];//去掉毛刺点的数组
+    if (dataSource.length != 0) {
+        if (dataOne != null) {
+            dataSource.unshift(dataOne);
+        }
+        for (let i = 1; i < dataSource.length - 1; i++) {
+            if (dataSource[i - 1] == null || dataSource[i] == null || dataSource[i + 1] == null) continue
+            let gap1 = dataSource[i][key] - dataSource[i - 1][key]
+            let gap2 = dataSource[i][key] - dataSource[i + 1][key]
+
+            let gap3 = dataSource[i - 1][key] - dataSource[i][key]
+            let gap4 = dataSource[i + 1][key] - dataSource[i][key]
+
+            let result1 = (gap1 > burrTv && gap2 > burrTv)
+            let result2 = (gap3 > burrTv && gap4 > burrTv)
+
+            if (i == 1) {//第一个点
+                dataSAfterBurr.push(dataSource[0])
+            }
+            if (result1 || result2) {
+                result.push({
+                    type: "burr",
+                    burr: result1 ? Math.min(gap1, gap2) : Math.min(gap3, gap4),
+                    time: dataSource[i].time,
+                    value: dataSource[i][key]
+                })
+            } else {
+                dataSAfterBurr.push(dataSource[i])
+            }
+            if (i == dataSource.length - 2) {//最后一个点
+                dataSAfterBurr.push(dataSource[dataSource.length - 1])
+            }
+        }
+    }
+    return { result: result, dataSAfterBurr: dataSAfterBurr }
+}
+//异常趋势
+let trend = function (dataOne, dataSource, params, key) {
+    let result;
+    if (dataSource.length != 0) {
+        //去完毛刺的新数组
+        let afterBurr = burr(dataOne, dataSource, params, key).dataSAfterBurr;
+        //滑动中值
+        let arrAfterMedian = [];
+        for (let i = 0; i < afterBurr.length; i += parseInt(params.params.win_med)) {
+            let arr = afterBurr.slice(i, i + parseInt(params.params.win_med))
+            let oneMedian = calcMedian(arr, key)
+            arrAfterMedian.push(oneMedian)
+        }
+        //滑动均值
+        let arrAfterAvg = calcMeanValue(arrAfterMedian, params.params.win_avg, key)
+        //错位相减,相当于求导
+        let arrAfterDe = []
+        for (let j = 0; j < arrAfterAvg.length - 1; j++) {
+            let one = {
+                value: arrAfterAvg[j + 1].value - arrAfterAvg[j].value,
+                time: arrAfterAvg[j + 1].time
+            }
+            arrAfterDe.push(one);
+        }
+        //最后判断
+        let finalArray = finalJudge(arrAfterDe, arrAfterMedian, params)
+        result = {
+            calcFinal: finalArray,
+            calcPreprocess: arrAfterAvg//要画预处理+滑动均值完了的曲线
+        };
+    }
+    return result
+}
+let getHour = function (s1, s2) {
+    s1 = new Date(s1.replace(/-/g, '/'))
+    s2 = new Date(s2.replace(/-/g, '/'))
+    let ms = Math.abs(s1.getTime() - s2.getTime())
+    return ms / 1000 / 60 / 60;
+}
+//计算一组数据的中值
+let calcMedian = function (array, key) {
+    let result;
+    if (array != null || array.length > 0) {
+        array.sort((a, b) => { return a[key] - b[key]})
+        if (array.length % 2 == 0) {//偶数
+            let index1 = array.length / 2;
+            result = {
+                value: (array[index1][key] + array[index1 - 1][key]) / 2,
+                time: array[index1].time
+            }
+
+        } else {//奇数
+            let index = (array.length - 1) / 2
+            result = {
+                value: array[index][key],
+                time: array[index].time
+            }
+        }
+    }
+    return result;
+}
+//计算一组数据的均值
+let calcMeanValue = function (array, coef, key) {
+    let result = [];
+    let sum = 0;
+    if (array != null || array.length > 0) {
+        for (let i = 0; i < array.length; i++) {
+            let value;
+            if (i < parseInt(coef)) {
+                sum = sum + array[i].value
+                value = sum / (i + 1)
+            } else {
+                let arr = array.slice(i - parseInt(coef) + 1, i + 1)
+                let ssum = 0;
+                for (let s = 0; s < arr.length; s++) {
+                    ssum = ssum + arr[s].value
+                }
+                value = ssum / parseInt(coef)
+            }
+            let one = {
+                value: value,
+                time: array[i].time
+            }
+            result.push(one)
+        }
+    }
+    return result
+}
+
+let finalJudge = function (array, original, params) {
+    let ups = 1, downs = 1;
+    let tempUp = [], tempDown = [];
+    let point = params.params.win_grad;//渐变点个数
+    let deTv = params.params.thr_der;//导数阈值
+    let greTv = params.params.thr_grad;//渐变阈值
+    let finalArray = [];
+    for (let i = 0; i < array.length; i++)//对最新数组作阈值判断
+    {
+        if (array[i].value > deTv) {
+            ups = ups + 1
+            if (ups == 2) {
+                tempUp.push(original[i])
+            }
+            tempUp.push(original[i + 1])
+            if (tempDown.length >= point) {
+                let bbb = tempDown[tempDown.length - 1].value - tempDown[0].Value
+                if (downs >= point && bbb < -greTv) {
+                    let one = {
+                        startTime: tempDown[0].time,
+                        endTime: tempDown[tempDown.length - 1].time,
+                        startValue: tempDown[0].value,
+                        endValue: tempDown[tempDown.length - 1].value,
+                        value: bbb,
+                        des: "异常下降"
+                    };
+                    finalArray.push(one)
+                }
+            }
+            downs = 1
+            tempDown = []
+        } else if (array[i].value < -deTv) {
+            downs = downs + 1;
+            if (downs == 2) {
+                tempDown.push(original[i])
+            }
+            tempDown.push(original[i + 1])
+            if (tempUp.length >= point) {
+                let aaa = tempUp[tempUp.length - 1].value - tempUp[0].value
+                if (ups >= point && aaa > greTv) {
+                    let one = {
+                        startTime: tempUp[0].time,
+                        endTime: tempUp[tempUp.length - 1].time,
+                        startValue: tempUp[0].value,
+                        endValue: tempUp[tempUp.length - 1].value,
+                        value: aaa,
+                        des: "异常上升"
+                    };
+                    finalArray.push(one)
+                }
+            }
+            ups = 1;
+            tempUp = []
+        }
+    }
+    if (ups >= point) {
+        let ccc = tempUp[tempUp.length - 1].value - tempUp[0].value
+        if (ccc > greTv) {
+            let one = {
+                startTime: tempUp[0].time,
+                endTime: tempUp[tempUp.length - 1].time,
+                startValue: tempUp[0].value,
+                endValue: tempUp[tempUp.length - 1].value,
+                value: ccc,
+                des: "异常上升"
+            };
+            finalArray.push(one)
+        }
+    }
+    if (downs >= point) {
+        let ddd = tempDown[tempDown.length - 1].value - tempDown[0].value
+        if (ddd < -greTv) {
+            let one = {
+                startTime: tempDown[0].time,
+                endTime: tempDown[tempDown.length - 1].time,
+                startValue: tempDown[0].value,
+                endValue: tempDown[tempDown.length - 1].value,
+                value: ddd,
+                des: "异常下降"
+            };
+            finalArray.push(one);
+        }
+    }
+    return finalArray
+}
+async function findThemeItems(models, factor) {
+    try {
+        let factorProto = await models.Factor.findOne({
+            where: { id: factor },
+            attributes: ['id', 'proto']
+        });
+        let protoItems = await models.FactorProtoItem.findAll({
+            where: { proto: factorProto.proto },
+            attributes: ['name', 'fieldName'],
+            include: [{
+                model: models.ItemUnit,
+                where: { default: true },
+                required: true,
+                attributes: ['name']
+            }]
+        });
+        let itemsObj = protoItems.reduce((p, c) => {
+            p[c.fieldName] = {
+                name: c.name,
+                unit: c.itemUnits[0] ? c.itemUnits[0].name : null
+            }
+            return p
+        }, {})
+        return itemsObj
+    } catch (err) {
+        throw err
+    }
+};
+async function findThemeDataFromES(esclient, filter, limit, _source) {
+    try {
+        let rslt = []
+        const client = esclient[THEME_DATA]
+        let params = {
+            index: client.config.index,
+            type: client.config.type,
+            body: filter
+        }
+
+        params.size = limit
+        if (limit == null) {
+            const countRes = await client.count(params)
+            params.size = countRes.count > 10000 ? 10000 : countRes.count
+        }
+
+        params._source = _source || ["sensor", "collect_time", "data"]
+        params.body.sort = { "collect_time": { "order": "desc" } }
+        let res = await client.search(params)
+        rslt = res.hits.hits
+        return rslt
+    } catch (err) {
+        throw err
+    }
+}
+
+
+
+
+
+
+module.exports = {
+    findAbnMethods,
+    findAbnParamList,
+    createAbnParam,
+    updateAbnParam,
+    batchSwitch,
+    deleteAbnParam,
+    getAbnTaskResult
+};
\ No newline at end of file
diff --git a/api/app/lib/controllers/monitor/index.js b/api/app/lib/controllers/monitor/index.js
new file mode 100644
index 0000000..fff7966
--- /dev/null
+++ b/api/app/lib/controllers/monitor/index.js
@@ -0,0 +1,192 @@
+
+const moment = require('moment');
+
+async function getStructures (ctx) {
+   try {
+      const { models } = ctx.fs.dc;
+      const { clickHouse } = ctx.app.fs
+      const { pomsProjectId } = ctx.query
+      let bindRes=[]
+      //选择全局就是查询所有项目下的结构物,有选择项目就只查询对应项目的结构物
+      if(pomsProjectId){
+       bindRes = await models.ProjectCorrelation.findAll({where:{id:{ $in: pomsProjectId.split(',') }}})
+      }else{
+      bindRes = await models.ProjectCorrelation.findAll()
+      }
+      let anxinProjectIds = new Set()
+      for (let b of bindRes) {
+         if (b.anxinProjectId.length) {
+            for (let aid of b.anxinProjectId) {
+               anxinProjectIds.add(aid)
+            }
+         }
+      }
+      let undelStrucRes=[]
+      // if (bindRes) {
+      //    undelStrucRes = anxinProjectIds.size ?
+      //       await clickHouse.anxinyun.query(
+      //          `  
+      //          SELECT 
+      //             t_structure.id AS strucId, 
+      //             t_structure.name AS strucName
+                 
+      //          FROM 
+      //             t_project 
+      //          LEFT JOIN
+      //             t_project_structure
+      //                ON t_project_structure.project = t_project.id
+      //          LEFT JOIN
+      //             t_project_structuregroup
+      //                ON t_project_structuregroup.project = t_project.id
+      //          LEFT JOIN 
+      //             t_structuregroup_structure
+      //                ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup
+      //          LEFT JOIN
+      //             t_project_construction
+      //                ON t_project_construction.project = t_project.id
+      //          LEFT JOIN
+      //             t_structure_site
+      //                ON t_structure_site.siteid = t_project_construction.construction
+      //          RIGHT JOIN
+      //             t_structure
+      //                ON t_structure.id = t_project_structure.structure
+      //                OR t_structure.id = t_structuregroup_structure.structure
+      //                OR t_structure.id = t_structure_site.structid
+               
+      //          WHERE 
+      //             project_state != -1 
+      //                AND
+      //             t_project.id IN (${[...anxinProjectIds].join(',')}, -1)
+      //          ORDER BY strucId
+      //       `
+      //       ).toPromise() :
+      //       []
+      // }
+      undelStrucRes.push({strucId:4036,strucName:'象山港大桥'})
+      undelStrucRes.push({strucId:1,strucName:'象山港大'})
+      ctx.status = 200;
+      ctx.body = undelStrucRes
+   } catch (error) {
+      ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+      ctx.status = 400;
+      ctx.body = {
+         message: typeof error == 'string' ? error : undefined
+      }
+   }
+}
+
+async function getFactors (ctx) {
+   try {
+      const { models } = ctx.fs.dc;
+      const { clickHouse } = ctx.app.fs
+      const { structId,cacl } = ctx.query
+      let list=[]
+      if(cacl){
+         const factorList=await clickHouse.alarmLocal.query(`select distinct SafetyFactorTypeId, SafetyFactorTypeName from sensors where PlatformStructureId =${structId}`).toPromise()
+         let fList=factorList.map(m=>m.SafetyFactorTypeId)
+         list=await clickHouse.alarmLocal.query(`select distinct FactorID, Name,Items,ItemNames from factors where FactorID in (${[...fList,-1].join(',')})`).toPromise()
+      }else{
+         list=await clickHouse.alarmLocal.query(`select distinct SafetyFactorTypeId, SafetyFactorTypeName from sensors where PlatformStructureId =${structId}`).toPromise()
+      }
+      ctx.body=list
+      ctx.status=200
+   }catch(error){
+      ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+      ctx.status = 400;
+      ctx.body = {
+         message: typeof error == 'string' ? error : undefined
+      }
+   }
+}
+//查询设备
+async function getSensors (ctx) {
+   try {
+      const { models } = ctx.fs.dc;
+      const { clickHouse } = ctx.app.fs
+      const { structId,SafetyFactorTypeId } = ctx.query
+      const list=await clickHouse.alarmLocal.query(`
+      select distinct SensorId,SensorLocationDescription,Project
+      from sensors where PlatformStructureId =${structId} 
+      and  SafetyFactorTypeId=${SafetyFactorTypeId}`).toPromise()
+      ctx.body=list
+      ctx.status=200
+   }catch(error){
+      ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+      ctx.status = 400;
+      ctx.body = {
+         message: typeof error == 'string' ? error : undefined
+      }
+   }
+}
+//根据设备id和监测因素id查询监测数据
+async function getMonitorData (ctx) {
+   try{
+      const { clickHouse } = ctx.app.fs
+      const { factorId,sensorId,startTime, endTime,limit,page } = ctx.query
+      const offset=page*limit
+      const factorsList=await clickHouse.alarmLocal.query(`SELECT FactorID,Items,ItemNames,ItemUnits FROM factors 
+      WHERE FactorID=${factorId}
+      `).toPromise()||[]
+      const monitorData=await clickHouse.alarmLocal.query(`SELECT SensorId,CollectTime,Values FROM themes 
+      WHERE SensorId in ('${sensorId}')
+      AND CollectTime >= toDate('${startTime}')
+      AND CollectTime <= toDate('${endTime}')
+      LIMIT ${limit} OFFSET ${offset}
+       `).toPromise()||[]
+       console.log('vmonitorDatamonitorDatamonitorData',monitorData)
+       const id=sensorId&&sensorId.replace('-',':')
+       const sensor=await clickHouse.alarmLocal.query(`SELECT distinct SensorId,SensorLocationDescription FROM sensors WHERE ID='${id}'`).toPromise()
+       //监测项
+       let items={}
+      if(factorsList&&factorsList.length>0){
+         //因素解释
+         let factors=[]
+         //因素名词
+         let factorNames=[]
+         factorsList.map(item=>{
+            factors=item.ItemNames.split(',')
+            factorNames=item.Items.split(',')
+            factors.map(child=>{
+               factorNames.map(p=>{
+                  items[p]={name:child,unit:item.ItemUnits}
+               })
+            })
+         })
+      }
+      //设备数据+数据
+      let sensors=[]
+      if(monitorData&&monitorData.length>0){
+          monitorData.map(data => {
+            const values = {};
+            Object.keys(items).forEach(key => {
+              const index = Object.keys(items).indexOf(key);
+              values[key] = data.Values[index];
+            });
+            sensors.push({ values, time: moment(data.CollectTime).format('YYYY-MM-DD HH:mm:ss') });
+          });
+        
+   }
+   if(sensor&&sensor.length){
+      ctx.body={items,sensors:[{data:sensors,id:sensor[0].SensorId,name:sensor[0].SensorLocationDescription}]}
+      ctx.status=200
+   }else{
+      ctx.body={items,sensors:[]}
+      ctx.status=200
+   }
+   }catch(error){
+      ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+      ctx.status = 400;
+      ctx.body = {
+         message: typeof error == 'string' ? error : undefined
+      }
+   }
+}
+
+
+
+
+
+
+module.exports = {
+   getStructures,getFactors,getSensors,getMonitorData
+}
\ No newline at end of file
diff --git a/api/app/lib/index.js b/api/app/lib/index.js
index b8c4758..c5401bf 100644
--- a/api/app/lib/index.js
+++ b/api/app/lib/index.js
@@ -69,7 +69,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
       AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App, AlarmAppearRecord, AlarmConfirmLog, EmailSendLog, LatestDynamicList, AlarmPushConfig,
       MaintenanceRecord, MaintenanceRecordExecuteUser, MaintenancePlanExecuteUser, MaintenancePlan, EquipmentMaintenanceRecord, EquipmentMaintenanceRecordProject,
       EquipmentMaintenanceRecordExecuteUser, ServerMaintenanceRecordRepairman, ServerMaintenanceRecord,
-      AlarmDataContinuityType, AlarmDataContinuity,
+      AlarmDataContinuityType, AlarmDataContinuity,AbnTypes,AbnReportParams
    } = dc.models;
 
    AppInspection.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' });
@@ -135,5 +135,6 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
    ServerMaintenanceRecordRepairman.belongsTo(ServerMaintenanceRecord, { foreignKey: 'serverMaintenanceRecordId', targetKey: 'id' })
    ServerMaintenanceRecord.hasMany(ServerMaintenanceRecordRepairman, { foreignKey: 'serverMaintenanceRecordId', targetKey: 'id' })
 
-
+   AbnReportParams.belongsTo(AbnTypes, { foreignKey: 'abnTypeId', targetKey: 'id' })
+   AbnTypes.hasMany(AbnReportParams, { foreignKey: 'abnTypeId', targetKey: 'id' })
 };
diff --git a/api/app/lib/models/abn_report_params.js b/api/app/lib/models/abn_report_params.js
new file mode 100644
index 0000000..04647f8
--- /dev/null
+++ b/api/app/lib/models/abn_report_params.js
@@ -0,0 +1,101 @@
+/* eslint-disable*/
+
+'use strict';
+
+module.exports = dc => {
+  const DataTypes = dc.ORM;
+  const sequelize = dc.orm;
+  const AbnReportParams = sequelize.define("abnReportParams", {
+    id: {
+      type: DataTypes.INTEGER,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: true,
+      field: "id",
+      autoIncrement: true
+    },
+    sensorId: {
+      type: DataTypes.STRING,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "sensor_id",
+      autoIncrement: false
+    },
+    sensorLocationDescription: {
+      type: DataTypes.STRING,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "sensor_location_description",
+      autoIncrement: false
+    },
+    factor: {
+      type: DataTypes.STRING,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "factor",
+      autoIncrement: false
+    },
+    factorId: {
+      type: DataTypes.STRING,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "factor_id",
+      autoIncrement: false
+    },
+    abnTypeId: {
+      type: DataTypes.INTEGER,
+      allowNull: true,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "abn_type",
+      autoIncrement: false,
+      references: {
+        key: "id",
+        model: "abnType"
+      }
+    },
+    params: {
+      type: DataTypes.JSONB,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "params",
+      autoIncrement: false
+    },
+    enabled: {
+      type: DataTypes.BOOLEAN,
+      allowNull: false,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "enabled",
+      autoIncrement: false
+    },
+    itemIndex: {
+      type: DataTypes.INTEGER,
+      allowNull: true,
+      defaultValue: null,
+      comment: null,
+      primaryKey: false,
+      field: "item_index",
+      autoIncrement: false
+    }
+  }, {
+    tableName: "abn_report_params",
+    comment: "",
+    indexes: []
+  });
+  dc.models.AbnReportParams = AbnReportParams;
+  return AbnReportParams;
+};
\ No newline at end of file
diff --git a/api/app/lib/models/abn_type.js b/api/app/lib/models/abn_type.js
new file mode 100644
index 0000000..e67070d
--- /dev/null
+++ b/api/app/lib/models/abn_type.js
@@ -0,0 +1,31 @@
+'use strict';
+
+module.exports = (dc) => {
+    const AbnTypes = dc.orm.define('abnTypes',
+        {
+            id: {
+                field: 'id',
+                type: dc.ORM.INTEGER,
+                primaryKey: true,
+                unique: true,
+                allowNull: false,
+                autoIncrement: true
+            },
+            name: {
+                field: 'name',
+                type: dc.ORM.STRING,
+                allowNull: false
+            },
+            des: {
+                field: 'description',
+                type: dc.ORM.STRING,
+                allowNull: false
+            }
+        }, {
+            tableName: 'abn_type'
+        });
+
+    dc.models.AbnTypes = AbnTypes;
+
+    return AbnTypes;
+};
\ No newline at end of file
diff --git a/api/app/lib/routes/dataCacl/index.js b/api/app/lib/routes/dataCacl/index.js
new file mode 100644
index 0000000..f93ba74
--- /dev/null
+++ b/api/app/lib/routes/dataCacl/index.js
@@ -0,0 +1,28 @@
+
+'use strict';
+
+const dataCacl = require('../../controllers/dataCacl');
+
+module.exports = function (app, router, opts) {
+   app.fs.api.logAttr['GET/abnormal/methods'] = { content: '获取异常识别算法', visible: true }
+   router.get('/abnormal/methods', dataCacl.findAbnMethods)
+
+   app.fs.api.logAttr['GET/struct/abnormal/params'] = { content: '获取结构物下异常识别参数配置列表', visible: false }
+   router.get('/struct/abnormal/params', dataCacl.findAbnParamList)
+
+   app.fs.api.logAttr['POST/struct/abnormal/params'] = { content: '新增异常识别参数配置', visible: true }
+   router.post('/struct/abnormal/params', dataCacl.createAbnParam)
+
+   app.fs.api.logAttr['PUT/batch/switch/:ids'] = { content: '异常参数批量配置', visible: true }
+   router.put('/batch/switch/:ids', dataCacl.batchSwitch)
+
+   app.fs.api.logAttr['DELETE/delete/abnormal/params/:ids'] = { content: '删除异常识别参数配置', visible: true }
+   router.del('delete/abnormal/params/:ids', dataCacl.deleteAbnParam)
+
+   app.fs.api.logAttr['PUT/edit/abnormal/params/:ids'] = { content: '修改异常识别参数配置', visible: true }
+   router.put('/edit/abnormal/params/:ids', dataCacl.updateAbnParam)
+
+   app.fs.api.logAttr['POST/struct/:id/abnTask/result/:start/:end'] = { content: '获取异常数据识别任务结果', visible: true }
+   router.post('/struct/:id/abnTask/result/:start/:end', dataCacl.getAbnTaskResult)
+
+}
\ No newline at end of file
diff --git a/api/app/lib/routes/monitor/index.js b/api/app/lib/routes/monitor/index.js
new file mode 100644
index 0000000..84ae094
--- /dev/null
+++ b/api/app/lib/routes/monitor/index.js
@@ -0,0 +1,20 @@
+
+'use strict';
+
+const monitor = require('../../controllers/monitor');
+
+module.exports = function (app, router, opts) {
+
+   app.fs.api.logAttr['GET/project/allStructures'] = { content: '获取对应项目的结构物', visible: true };
+   router.get('/project/allStructures', monitor.getStructures);
+
+   
+   app.fs.api.logAttr['GET/structure/factors'] = { content: '获取对应的监测因素', visible: true };
+   router.get('structure/factors', monitor.getFactors);
+
+   app.fs.api.logAttr['GET/structure/factors/sensors'] = { content: '获取对应的点位', visible: true };
+   router.get('structure/factors/sensors', monitor.getSensors);
+
+   app.fs.api.logAttr['GET/structure/factors/sensors/data'] = { content: '获取对应点位的设备', visible: true };
+   router.get('structure/factors/sensors/data', monitor.getMonitorData);
+}
\ No newline at end of file
diff --git a/api/config.js b/api/config.js
index 372508b..af91bcd 100644
--- a/api/config.js
+++ b/api/config.js
@@ -49,7 +49,7 @@ args.option('clickHousePepEmis', 'clickHouse 项企数据库名称');
 args.option('clickHouseProjectManage', 'clickHouse 项目管理数据库名称');
 args.option('clickHouseVcmp', 'clickHouse 视频平台数据库名称');
 args.option('clickHouseDataAlarm', 'clickHouse 视频平台数据告警库名称');
-// args.option('clickHouseDataAlarmLocal', 'clickHouse 本地化告警相关数据');
+args.option('clickHouseDataAlarmLocal', 'clickHouse 本地化告警相关数据');
 args.option('clickHouseIot', 'clickHouse IOT平台设备信息库名称');
 args.option('clickHouseCamworkflow', 'clickHouse 工作流数据库名称');
 
@@ -125,7 +125,7 @@ const CLICKHOUST_VCMP = process.env.CLICKHOUST_VCMP || flags.clickHouseVcmp
 const CLICKHOUST_DATA_ALARM = process.env.CLICKHOUST_DATA_ALARM || flags.clickHouseDataAlarm
 const CLICKHOUST_IOT = process.env.CLICKHOUST_IOT || flags.clickHouseIot
 const CLICKHOUST_CAM_WORKFLOW = process.env.CLICKHOUST_CAM_WORKFLOW || flags.clickHouseCamworkflow
-// const CLICKHOUST_DATA_ALARM_LOCAL=process.env.CLICKHOUST_DATA_ALARM_LOCAL|| flags.clickHouseDataAlarmLocal
+const CLICKHOUST_DATA_ALARM_LOCAL=process.env.CLICKHOUST_DATA_ALARM_LOCAL|| flags.clickHouseDataAlarmLocal
 
 const CONFIRM_ALARM_ANXIN_USER_ID = process.env.CONFIRM_ALARM_ANXIN_USER_ID || flags.confirmAlarmAnxinUserId
 
@@ -171,7 +171,7 @@ const requireParams = {
    CONFIRM_ALARM_ANXIN_USER_ID,
    VCMP_APP_ID, VCMP_APP_SECRET,
    API_CRAW_URL,
-   // CLICKHOUST_DATA_ALARM_LOCAL,
+   CLICKHOUST_DATA_ALARM_LOCAL,
    TYPES
 }
 
@@ -341,10 +341,10 @@ const product = {
                      name: 'camWorkflow',
                      db: CLICKHOUST_CAM_WORKFLOW
                   },
-                  // {
-                  //    name: 'alarmLocal',
-                  //    db: CLICKHOUST_DATA_ALARM_LOCAL
-                  // }
+                  {
+                     name: 'alarmLocal',
+                     db: CLICKHOUST_DATA_ALARM_LOCAL
+                  }
                ]
             },
             es: {
diff --git a/script/3.7/schema/1.create_abn_type.sql b/script/3.7/schema/1.create_abn_type.sql
new file mode 100644
index 0000000..6e274a6
--- /dev/null
+++ b/script/3.7/schema/1.create_abn_type.sql
@@ -0,0 +1,12 @@
+create  table abn_type
+(
+    id          serial
+        primary key,
+    name        varchar(50) not null,
+    description varchar(50) not null
+);
+INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'interrupt'::varchar(50), '数据中断'::varchar(50));
+INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'burr'::varchar(50), '毛刺'::varchar(50));
+INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'trend'::varchar(50), '趋势'::varchar(50))
+
+
diff --git a/script/3.7/schema/2.create_abn_report_params.sql b/script/3.7/schema/2.create_abn_report_params.sql
new file mode 100644
index 0000000..f07268d
--- /dev/null
+++ b/script/3.7/schema/2.create_abn_report_params.sql
@@ -0,0 +1,15 @@
+create table abn_report_params
+(
+    id            serial
+            primary key,
+    sensor_id        varchar(300)                                                        not null,
+    sensor_location_description  varchar(300)                                                        not null,
+    factor varchar(30)                                                     not null,
+    factor_id varchar(30)                                                     not null,
+    abn_type          integer
+        constraint t_abn_report_params_t_abn_type_id_fk
+            references abn_type,
+    params            jsonb                                                           not null,
+    enabled           boolean default true                                            not null,
+    item_index integer not null
+);
diff --git a/web/client/src/sections/data/actions/index.js b/web/client/src/sections/data/actions/index.js
index 66a9a99..8b92c7b 100644
--- a/web/client/src/sections/data/actions/index.js
+++ b/web/client/src/sections/data/actions/index.js
@@ -1,7 +1,7 @@
 'use strict';
 
 import * as dataQuery from './dataQuery'
-
+import * as monitor from './monitor'
 export default {
-   ...dataQuery
+   ...dataQuery,...monitor
 }
\ No newline at end of file
diff --git a/web/client/src/sections/data/actions/monitor.js b/web/client/src/sections/data/actions/monitor.js
new file mode 100644
index 0000000..4035ec2
--- /dev/null
+++ b/web/client/src/sections/data/actions/monitor.js
@@ -0,0 +1,54 @@
+/*
+ * @Author: zhaobing
+ * @Date: 2023-11-02 11:31:56
+ */
+import { ApiTable, basicAction } from '$utils'
+
+export function getProjectAllStructures (query) {//获取对应项目下所有结构物
+    return (dispatch) => basicAction({
+       type: "get",
+       dispatch: dispatch,
+       actionType: "GET_PROJECT_ALL_STRUCTURES",
+       query: query,
+       url: `${ApiTable.getProjectAllStructures}`,
+       msg: { option: "获取对应项目下的所有结构物" },
+       reducer: { name: "ProjectAllStructures", params: { noClear: true } },
+    });
+ }
+
+
+ export function getFactors (query) {//获取对应结构物下的检测因素
+    return (dispatch) => basicAction({
+       type: "get",
+       dispatch: dispatch,
+       actionType: "GET_FACTORS",
+       query: query,
+       url: `${ApiTable.getFactors}`,
+       msg: { option: "获取对应结构物下的检测因素" },
+       reducer: { name: "Factors", params: { noClear: true } },
+    });
+ }
+
+ export function getSensors (query) {//获取对应检测因素下的设备
+    return (dispatch) => basicAction({
+       type: "get",
+       dispatch: dispatch,
+       actionType: "GET_SENSORS",
+       query: query,
+       url: `${ApiTable.getSensors}`,
+       msg: { option: "获取对应检测因素下的设备" },
+       reducer: { name: "Sensors", params: { noClear: true } },
+    });
+ }
+
+ export function getMonitorData (query) {//获取对应检测因素下的设备的数据(最终的数据)
+   return (dispatch) => basicAction({
+      type: "get",
+      dispatch: dispatch,
+      actionType: "GET_MONITOR_DATA",
+      query: query,
+      url: `${ApiTable.getMonitorData}`,
+      msg: { option: "获取对应检测因素下的设备的数据(最终的数据)" },
+      reducer: { name: "SensorsData", params: { noClear: true } },
+   });
+}
\ No newline at end of file
diff --git a/web/client/src/sections/data/components/lineTimeChartTemplate.jsx b/web/client/src/sections/data/components/lineTimeChartTemplate.jsx
new file mode 100644
index 0000000..e5c52ae
--- /dev/null
+++ b/web/client/src/sections/data/components/lineTimeChartTemplate.jsx
@@ -0,0 +1,359 @@
+
+'use strict';
+import React, { Component } from 'react';
+// 引入 echarts 主模块。
+import * as echarts from 'echarts/lib/echarts';
+// 引入折线图
+import 'echarts/lib/chart/line';
+//引入标签组件
+import ReactEcharts from 'echarts-for-react';
+
+let chartDownload = "";
+class LineTimeChart extends Component {
+    constructor(props) {
+        super(props);
+        this.sliderId = 'sliderId' + Math.floor(Math.random() * 1000000000);
+        this.chartId = 'chartId' + Math.floor(Math.random() * 1000000000);
+        this.legendId = 'legendId' + Math.floor(Math.random() * 1000000000);
+        this.legendListId = 'legendListId' + Math.floor(Math.random() * 1000000000);
+        this.tooltipId = 'tooltipId' + Math.floor(Math.random() * 1000000000);
+        this.state = {
+            option: {},
+        }
+    }
+
+    componentDidMount() {
+        let that = this;
+        this.renderChart();
+        this.reSetLegendStyle(that)
+        window.addEventListener('resize', function () {
+            let a = setInterval(function () { that.reSetLegendStyle(that) }, 100);
+            setTimeout(function () { clearInterval(a) }, 3500)
+        });
+    }
+
+    reSetLegendStyle = (that) => {
+        // Ps.initialize(document.getElementById(that.legendId));
+        // Ps.initialize(document.getElementById(that.tooltipId));
+        // let legendListDom = document.getElementById(that.legendListId)
+        // legendListDom.style.textAlign = 'left';
+        // legendListDom.style.maxWidth = '115px'
+        // document.getElementById(that.legendId).style.left = '10px'
+    }
+
+    download = () => {
+        this.chartDownload.downloadImage();
+    }
+
+    renderChart = () => {
+        const { options } = this.props;
+        const props = this.props;
+        let height = props.height || 300;
+        let showSlider = options && options.sliderStart;
+        let padding = [20, 135, 30, 140]
+        let startTime, endTime, ds, dv, view;
+
+        // let chart = new G2.Chart({
+        //     container: this.chartId,
+        //     forceFit: true,
+        //     height: height,
+        //     padding: padding,
+        // });
+        // chart.source(props.data);
+        // if (showSlider) {
+        //     startTime = options.sliderStart;
+        //     endTime = options.sliderEnd;
+        //     ds = new DataSet({
+        //         state: {
+        //             start: startTime,
+        //             end: endTime,
+        //         }
+        //     });
+        //     dv = ds.createView();
+        //     dv.source(props.data)
+        //         .transform({ // !!! 根据状态量设置数据过滤规则,
+        //             type: 'filter',
+        //             callback: obj => {
+        //                 return obj.time <= ds.state.end && obj.time >= ds.state.start;
+        //             }
+        //         });
+        // }
+        // chart.scale({
+        //     'time': {
+        //         range: [0, 1],
+        //         type: 'time',
+        //         alias: '时间',
+        //         mask: 'YYYY-MM-DD HH:mm:ss',
+        //     },
+        // });
+        // chart.on('tooltip:change', function (ev) {
+        //     let item = ev.items[0];
+        //     item.title = item.title;
+        // });
+        // chart.axis('value', {
+        //     position: 'right'
+        // });
+        // chart.tooltip(true, {
+        //     enterable: true,
+        //     offset: 3,
+        //     crosshairs: {
+        //         type: 'cross',
+        //     },
+        //     inPlot: false,
+        //     containerTpl: `
+        //         
`,
+        // });
+        // chart.legend(true, {
+        //     marker: 'circle',
+        //     useHtml: true,
+        //     position: 'left',
+        //     containerTpl: ``,
+        // })
+        // let guideOption = {
+        //     position: ['min', 'max'],
+        //     content: options.yAlias,
+        //     offsetY: -10
+        // }
+        // if (showSlider) {
+        //     view = chart.view();
+        //     view.source(dv);  // !!! 注意数据源是 ds 创建 DataView 对象
+        //     view.guide().text(guideOption);
+        //     view.line().position('time*value').color('name').shape('line').size(2);
+        // } else {
+        //     chart.guide().text(guideOption);
+        //     chart.line().position('time*value').color('name').shape('line').size(2);
+        // }
+        // chart.render();
+        // if (showSlider) {
+        //     // 创建滑动条
+        //     let slider = new Slider({
+        //         container: this.sliderId,
+        //         padding: [35, 135, 30, 140],
+        //         xAxis: 'time',
+        //         yAxis: 'value',
+        //         start: startTime,
+        //         end: endTime,
+        //         data: props.data,
+        //         scales: {
+        //             'time': {
+        //                 type: 'time',
+        //                 mask: "YYYY-MM-DD HH:mm:ss"
+        //             }
+        //         },
+        //         onChange: ({ startValue, endValue, startText, endText }) => {
+        //             ds.setState('start', startText);
+        //             ds.setState('end', endText);
+        //         } // 更新数据状态量的回调函数
+        //     });
+        //     slider.render();
+        // }
+        // chart.changeData(props.data);
+
+        //组织dataset数据
+        // let data = this.props.data;
+        // let dataset = [], name = [], x = ['product'];
+        // data.map(d => {
+        //     x.push(d.time);
+        //     let value = [];
+        //     if (!name.includes(d.name)) {
+        //         name.push(d.name);
+        //         value.push(d.name);
+        //         value.push(d.value);
+        //         dataset.push(value);
+        //     } else {
+        //         dataset.map(set => {
+        //             if (set[0] == d.name) {
+        //                 set.push(d.value);
+        //             }
+        //         })
+        //     }
+
+        // });
+        // dataset.unshift(x);
+        //组织后端数据组织data
+        let data = this.props.data;
+        let dataArray = [], types = {};
+        let xAxisValue = props.xAxis || 'time';
+        let yAxisValue = props.yAxis || 'value';
+        data.map(dd => {
+            if (!types[dd.name]) {
+                types[dd.name] = Object.assign({
+                    name: dd.name, data: []
+                }, {
+                    type: 'line',
+                    //smooth: true
+                });
+            }
+            types[dd.name].data.push({
+                name: dd.time,
+                value: [dd[xAxisValue], dd[yAxisValue]]
+            })
+
+        });
+        for (let t in types) {
+            dataArray.push(types[t]);
+        }
+        // //根据dataset数据数量给出足够的线空间
+        // let line, lines = [];
+        // line = { type: 'line', smooth: true, seriesLayoutBy: 'row' }
+        // dataset.map(data => {
+        //     lines.push(line);
+        // });
+
+        let eOption = {}
+        //解决只有一条数据时图例和线颜色不同
+        // if (dataset.length == 2) {
+        //     eOption.color = ['#000000']
+        // }
+        //鼠标移入提示
+        eOption.tooltip = {
+            // axisPointer:{
+            //     axis:{}
+            // }
+            trigger: 'axis',
+            //格式化 tooltip
+            formatter: function (params, ticket, callback) {
+                let htmlStr = "";
+                for (let i = 0; i < params.length; i++) {
+                    let param = params[i];
+                    let xName = param.name;//x轴的名称
+                    let seriesName = param.seriesName.split("series");//图例名称
+                    let value = param.value;//y轴值
+                    let color = param.color;//图例颜色
+                    let data = param.value[1] ? param.value[1] : '无数据';
+                    if (value[1] && seriesName.length == 1) {
+                        htmlStr += '' + xName + '    ' + seriesName[0] + ' ' + data + '
';
+                    }
+                }
+                return htmlStr;
+            }
+        }
+        //X轴
+        eOption.xAxis = {
+            //type: 'category',
+            type: 'time',
+            // min: new Date(xxxxx),
+            // //结束时间
+            // max: new Date(xxxxx),
+            // axisLabel: {
+            //     interale: 0,
+            //     rotate: -40,
+            //     formatter: function (value) {//在这里写你需要的时间格式
+            //         var t_date = new Date(value);
+            //         return [t_date.getFullYear(), t_date.getMonth() + 1, t_date.getDate()].join('-') + " "
+            //             + [t_date.getHours(), t_date.getMinutes()].join(':');
+
+            //     }
+            // }
+
+          
+        }
+        //Y轴
+        eOption.yAxis = {
+            type: 'value',
+            min: 'dataMin',
+            max: 'dataMax',
+            name: options.yAlias,
+            axisLabel: {
+                formatter: function (value, index) {
+                    if (value < 0.01) {
+                        if(value<0.001){
+                            return value?value.toFixed(4):0;
+                        }
+                        return value.toFixed(3);
+                    }
+                    return value.toFixed(2);
+                }
+            }
+
+        }
+        //toolbox 设置导出图片
+        eOption.toolbox = {
+            feature: {
+                saveAsImage: {}
+            }
+        }
+        //缩放
+        eOption.dataZoom = [
+            {
+                show: true,
+                realtime: true,
+                start: 0,
+                end: 100
+            },
+            {
+                type: 'inside',
+                realtime: true,
+                start: 0,
+                end: 100
+            }
+        ]
+        //颜色标识
+        eOption.legend = {
+            left: '0',
+            orient: 'vertical',
+            type: 'scroll',
+            icon: 'circle',
+            //文字过长可以使用下面方法截取字符串
+            formatter: function (name) {
+                if (!name) return '';
+                if (name.length > 8) {
+                    if (name.length > 16) {
+                        name = name.slice(0, 8) + `\n` + name.slice(0, 7) + "...";
+                    } else {
+                        name = name.slice(0, 8) + `\n` + name.slice(8);
+                    }
+                    return name;
+                } else {
+                    return name
+                }
+            },
+            tooltip: {
+                show: true
+            }
+        }
+        // //数据源
+        // eOption.dataset = {
+        //     source: dataset
+        // }
+        //props.data;
+        eOption.series = dataArray
+        this.setState({ option: eOption });
+
+        //this.chartDownload = chart;
+    }
+
+    render() {
+        const { data, width, height, options, slider } = this.props;
+        const showSlider = options && options.sliderStart;
+        return (
+            
+                {/* 
+                    
+                
 */}
+                
+                    {/* 
 */}
+                    
+                    {/* 
 */}
+                    {/* {
+                        showSlider ? [
+                            
+                        ] : null
+                    } */}
+                
+            
+        {renderChart()}
+    
)
+
+
+
+
+}
+
+const mapStateToProps = (state) => {
+    const { auth, global, dataContinuityType, dataContinuity,pepProjectId,SensorsData} = state;
+    return {
+       user: auth.user,
+       actions: global.actions,
+       dataContinuityType: dataContinuityType.data || [],
+       dataContinuity: dataContinuity.data || [],
+       pepProjectId:global.pepProjectId,
+       isRequesting:SensorsData.isRequesting
+    };
+}
+export default connect(mapStateToProps)(DataComponent);
+
+
+
+
+
diff --git a/web/client/src/sections/data/containers/dataDetail.jsx b/web/client/src/sections/data/containers/dataDetail.jsx
new file mode 100644
index 0000000..62dc2e7
--- /dev/null
+++ b/web/client/src/sections/data/containers/dataDetail.jsx
@@ -0,0 +1,284 @@
+import React, { useEffect, useState, useRef } from 'react';
+import { connect } from 'react-redux';
+import moment from 'moment';
+import { exportWord } from 'mhtml-to-word'
+import { saveAs } from 'file-saver';
+import { SkeletonScreen, } from "$components";
+import DataTableComponent from './dataTableComponent';
+import DataComponent from './dataComponent.jsx';
+import { IconSearch,IconLineChartStroked,IconStar,IconUser,IconBookOpenStroked} from '@douyinfe/semi-icons';
+import { Divider,Form, Button, Skeleton, Table, Pagination, Space, Row, Col, DatePicker, Cascader, Select, Spin, Alert, message, Menu, Icon,Nav,Tabs, TabPane} from '@douyinfe/semi-ui';
+import '../style.less'
+
+const DataDetail = (props) => {
+   const { dispatch, actions, user, dataContinuityType, dataContinuity,pepProjectId } = props
+   const { data } = actions
+   const [queryPage, setQueryPage] = useState({ limit: 10, page: 0 }); //页码信息
+   const [limits, setLimits] = useState(0)//每页实际条数
+   const [loading, setLoading] = useState(true);
+   const [params, setParams] = useState({})
+   const [checkVis, setCheckVis] = useState(false)
+   const [checkData, setCheckData] = useState({})
+   const [structList,setStructList]=useState([])//结构物筛选列表
+   const [structId,setStructId]=useState(null)//结构物id(根据结构物下拉框赋值)
+   const [factosList,setFactorsList]=useState([])//监测因素列表
+   const [factorId,setFactorId]=useState(null)
+   const [sensorList,setSensorList]=useState([])//设备列表
+   const [sensorId,setSensorId]=useState([])//设备数组id
+   const [project,setProject]=useState(null)//project(eg:{projcet:'nbjj'})
+   const form = useRef();
+   //初始化
+   useEffect(() => {
+      if(factorId&&project){
+         let query={
+            factorId,
+            sensorId:sensorId&&sensorId.length>0?sensorId.map(item=>`${project}-${item}`).join(','):[-11],
+            startTime:form.current.getValue('createTimes')[0],
+            endTime:form.current.getValue('createTimes')[1],
+            limit:queryPage.limit,
+            page:queryPage.page
+         }
+         queryData(query)
+      }
+        
+      // dispatch(sectionData.getContinuityType())
+      // form.current.setValue('createTimes',[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')])
+
+   }, [factorId,project,sensorId])
+
+   //监听变化
+   useEffect(() => {
+      setStructList([])
+      form.current.reset()
+       getData()
+   }, [pepProjectId])
+   //监听结构物变化,查询监测因素
+   useEffect(()=>{
+      setLoading(true);
+    let queryParams = {structId}
+    if(structId){
+      dispatch(data.getFactors({...queryParams})).then(res => {
+         if(res.success){
+            const list=res.payload.data?.map(item=>{
+               return {
+                  value: item.SafetyFactorTypeId,
+                  label: item.SafetyFactorTypeName,
+               }
+            })
+            form.current.setValue('factor',list[0]?.value)
+            setFactorsList(list)
+            setFactorId(list[0]?.value)
+            setLoading(false);
+         }
+      })
+    }
+
+   },[structId])
+   useEffect(()=>{
+      let queryParams = {structId,SafetyFactorTypeId:factorId}
+      if(factorId){
+         dispatch(data.getSensors({...queryParams})).then(res => {
+            if(res.success){
+               const list=res.payload.data?.map(item=>{
+                  return {
+                     value: item.SensorId,
+                     label: item.SensorLocationDescription,
+                  }
+               })
+               form.current.setValue('point',[list[0]?.value])
+               setSensorList(list)
+               setLoading(false)
+               setProject(res.payload.data[0].Project)
+               setSensorId([list[0]?.value])
+            }
+         })
+      }
+   },[factorId])
+   const getData = (queryParams = {pomsProjectId:pepProjectId}) => {
+      setLoading(true);
+      dispatch(data.getProjectAllStructures({...queryParams})).then(res => 
+           {
+            if(res.success){
+               const uniqueIds = new Set();
+               const list=res.payload.data?.filter(item => {
+                  const duplicate = uniqueIds.has(item.strucId);
+                  uniqueIds.add(item.strucId);
+                  return !duplicate
+                })?.map(item => ({
+                  value: item.strucId,
+                  label: item.strucName,
+                  project: item.Project,
+                }))
+                form.current.setValue('struct',list[0]?.value)
+               setStructId(list[0]?.value)
+               setStructList(list)
+               setLoading(false)
+            }
+           }
+               
+      )
+   }
+
+   const downloadWord = (html = '', name = '数据监测 ' + moment().format('YYYY-MM-DD HH:mm:ss')) => {
+      exportWord({
+         mhtml: html,
+         filename: name,
+      })
+      // let blob = new Blob([html], { type: "application/msword;charset=utf-8" });
+      // saveAs(blob, name + '.doc');
+   }
+   //结构物筛选变化回调函数
+   const structChange = (value) => {
+      setStructId(value)
+      form.current.setValue('point','')
+      form.current.setValue('factor','')
+
+      // setStructName(structList.find(item=>item.value===value)?.label)
+   }
+   //结构物筛选发生变化回调函数
+   const factorChange=(value)=>{
+      form.current.setValue('point','')
+      setFactorId(value)
+   }
+   //点位筛选发生改变的回调函数
+   const pointChange=(value)=>{
+      setSensorId(value)
+      // setProject(structList.find(item=>item.value===structId)?.project)
+   }
+   //查询按钮回调函数
+   const queryData=(query)=>{
+      dispatch(data.getMonitorData(query)).then(res=>{
+         if(res.success){
+            setCheckData(res.payload.data)
+         }
+      })
+   }
+   const searchHandler=(values) => {
+      form.current.validate().then(rs=>{
+         let query={
+            factorId,
+            sensorId:sensorId&&sensorId.length>0?sensorId.map(item=>`${project}-${item}`).join(','):[-11],
+            startTime:moment(rs.createTimes[0]).format('YYYY-MM-DD HH:mm:ss'),
+            endTime:moment(rs.createTimes[1]).format('YYYY-MM-DD HH:mm:ss'),
+            limit:queryPage.limit,
+            page:queryPage.page
+         }
+         queryData(query)
+      })
+   }  
+    
+
+   return (
+      <>
+         
+            
+ 
+            
+            
+               
+               
+                
+                
+                     趋势图} itemKey="1">
+                        
+                     
+                     
+                     数据} itemKey="2">
+                     
+
+                     
+                     
+                  
+                
+            
+         
                
                
@@ -73,9 +73,9 @@ const DataQuery = (props) => {
                      />
                      
diff --git a/web/client/src/sections/data/containers/dataTableComponent.jsx b/web/client/src/sections/data/containers/dataTableComponent.jsx
new file mode 100644
index 0000000..25cd0df
--- /dev/null
+++ b/web/client/src/sections/data/containers/dataTableComponent.jsx
@@ -0,0 +1,137 @@
+/*
+ * @Author: zhaobing
+ * @Date: 2023-11-02 09:32:25
+ */
+import React, { useState, useEffect } from 'react';
+import { connect } from 'react-redux';
+import { Table, Button, Alert, Spin } from '@douyinfe/semi-ui'; 
+// import { ApiTable } from '../../../../utils/webapi';
+import FileSaver from 'file-saver';
+import moment from 'moment';
+
+const DataTableComponent = (props) => {
+    const {dataList}=props
+    const [currentPage, setCurrentPage] = useState(1);
+    const [exportingFlag, setExportingFlag] = useState(false);
+    const [isRequesting, setIsRequesting] = useState(false);
+    const [arr,setArr]=useState(dataList.items)
+    const [data,setData]=useState([])
+    const [columns,setColumns]=useState([])
+    //初始化     
+
+    useEffect(()=>{
+
+        // let arr=[{readingNumber:{name: '电表示数', unit: 'kWh'}}]
+        // let dataSource = [{values: {total: 24138.97}, time: "2023-11-03 14:18:27.000"},{values: {total: 24138.97}, time: "2023-11-03 14:18:27.000"}]
+        let columns = []
+        columns.push({
+            title: '设备位置',
+            dataIndex: 'position',
+            key: 'position',
+        });
+        for (let index in arr) {
+            columns.push({
+                title: arr[index].name + '(' + arr[index].unit + ')',
+                dataIndex: index,
+                sorter: (a, b) => b[index] - a[index],
+                key: index,
+            })
+        };
+        columns.push({
+            title: '采集时间',
+            dataIndex: 'acqTime',
+            sorter: (a, b) => b['realTime'] - a['realTime'],
+            key: 'acqTime',
+        });
+        //设备
+        const data1=[]
+        for (let i = 0; i < dataList.length; i++) {
+            for (let k = 0; k < dataList[i].data.length; k++) {
+                let cdataT={}
+                let startT = "";
+                let endT = "";
+                let dataTS = dataList[i].data[k];
+                cdataT.key = `station-${dataList[i].id}-${k}`;
+                cdataT.position = dataList[i].name;
+                cdataT.acqTime = moment(dataTS.time).format('YYYY-MM-DD HH:mm:ss');
+                cdataT.realTime = moment(dataTS.time).valueOf();
+                if (startT == "") {
+                    startT = dataTS.time;
+                    endT = dataTS.time;
+                } else {
+                    if (moment(startT) >= moment(dataTS.time))
+                        startT = dataTS.time;
+                    if (moment(endT) <= moment(dataTS.time))
+                        endT = dataTS.time;
+                }
+                //动态列的值
+                for (let themeItem in arr) {
+                    cdataT[themeItem] = dataTS.values[themeItem];
+                }
+                data1.push(cdataT) 
+            }
+        }
+        setData(data1)
+        setColumns(columns)
+    },[])
+
+
+
+
+
+
+
+    const exporting = () => {
+        setIsRequesting(true);
+    };
+
+    const pageChange = (page, pageSize) => {
+        setCurrentPage(page);
+    };
+
+    const exportCsv = (data, title, filename) => {
+        setExportingFlag(true);
+    };
+
+    const exportVibrationCsv = (data, title, filename) => {
+        setExportingFlag(true);
+    };
+
+    const renderViberationChart = () => {
+    };
+
+    useEffect(() => {
+        setCurrentPage(1);
+    }, [props]);
+    
+
+
+
+    return (
+        
+            
+                
+                {/* 
+                                {
+                                    data.length ?  : null
+                                }
+                 
 */}
+                
+                
+        
+                
+                    
+                
+                
+                    
+                    {
+                        showSlider ? [
+                            
,
+                            
+                        ] : null
+                    }
+                
+            
+        
+                
+                
+            
]}>
+            
+        
+    ;
+}
+
+function mapStateToProps(state) {
+    const { abnItemState_tr, global} = state;
+    return {
+        abnItemCompData: abnItemState_tr.items,
+        actions:global.actions
+    }
+}
+
+export default connect(mapStateToProps)(TrendModal)
diff --git a/web/client/src/sections/install/components/burrModal.jsx b/web/client/src/sections/install/components/burrModal.jsx
new file mode 100644
index 0000000..bab3140
--- /dev/null
+++ b/web/client/src/sections/install/components/burrModal.jsx
@@ -0,0 +1,214 @@
+import React, { useRef,useState,useEffect } from "react";
+import { connect } from "react-redux";
+import moment from 'moment';
+import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row, IconLineChartStroked } from "@douyinfe/semi-ui";
+import {IconInfoCircle} from "@douyinfe/semi-icons"
+import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart';
+import PropTypes from 'prop-types';
+
+const BurrModal = (props) => {
+    const form = useRef()
+    const {dispatch, modalData, closeModal, visible, structId,item,actions,sensorId,project } = props
+    const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss'))
+    const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
+    const{install}=actions
+    useEffect(()=>{
+      // form.current.setValues({stationName: modalData.sensorName,
+      //   factorName: modalData.factorName + "/" + modalData.item,
+      //   burr: modalData.params.thr_burr,
+      //   isEnable: modalData.enabled})
+    },[])
+     //表单类
+ const  btnFormSubmit = () => {
+    let data = modalData;
+    form.current.validate().then(value=>{
+            let paramJson = { "thr_burr": Number(value.burr) };
+            let pushData = {
+              station: data.sensorId,//测点
+              factorId: data.factorId,
+              itemId: data.itemIndex,
+              abnType: 2,//毛刺
+              enabled: value.checked,
+              params: paramJson
+            }
+            dispatch(install.editAbnParams(data.id,pushData)).then(res => {
+              if (res.success) {
+                  const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                  dispatch(install.getAbnParamList({ sensorId:id}))
+                  closeModal()
+              }
+          })
+            // this.props.dispatch(editAbnParams(data.id, pushData)).then(_ => {
+            //   this.props.dispatch(getAbnParamList(this.props.structId)).then(() => {
+            //     this.props.closeModal();
+            //   });
+            // });
+          
+    })
+  }
+const  checkInterger = (rule, value, callback) => {
+    if (!value) {
+      callback();
+    } else {
+      const pattern = /^[1-9]*[1-9][0-9]*$/;
+      if (pattern.test(value)) {
+        callback();
+      } else {
+        callback(new Error('请输入正整数'));
+      }
+    }
+  };
+const timeOnChange = (dates, dateStrings) => {
+    if (dates.length == 0) {
+        setCurrStartTime(null)
+        setcurrEndTime(null)
+      return message.warning('请选择时间');
+    }
+    setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss'))
+    setcurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss'))
+  }
+  //数据对比
+// const dataCompare = () => {
+//     form.current.validate().then(res=>{
+//         if (res.success) {
+//             let paramJson = { "thr_burr": values.burr }
+//             let data = {
+//               station: modalData.stationId,
+//               factorId: modalData.factorId,
+//               itemId: modalData.itemId,
+//               abnType: 'burr',//算法类型
+//               enabled: true,
+//               params: paramJson
+//             };
+//             let start = this.state.currStartTime
+//             let end = this.state.currEndTime
+
+//             // this.props.dispatch(getItemAbnResult_burr(this.props.structId, start, end, data)).then(res => {
+//             // })
+//           }
+//     })
+//   }
+const checkNumber = (rule, val) => {
+  const strRegex = /^-?\d+(\.\d+)?$/
+  if (strRegex.test(val)) {
+      return true
+  }
+  return false
+
+
+
+}
+  // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+  // let stationsData = [];
+  // if (abnItemCompData) {
+  //   originalData = abnItemCompData.stationData;
+  //   calcArray = abnItemCompData.resultArray;
+  //   key = abnItemCompData.itemKey;
+  //   itemName = abnItemCompData.itemName;
+  //   start = originalData && originalData.length > 0 ? originalData[0].time : '';
+  //   end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+  //   for (let i = 0; i < originalData.length; i++) {
+  //     let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+  //     stationsData.push(one);
+  //   }
+  //   for (let j = 0; j < calcArray.length; j++) {
+  //     let one = {
+  //       name: "毛刺点:" + calcArray[j].time + "(突变大小:" + calcArray[j].burr.toFixed(2) + abnItemCompData.unit + ")"
+  //       , value: calcArray[j].value, time: calcArray[j].time
+  //     };
+  //     stationsData.push(one);
+  //   }
+  // }
+  BurrModal.propTypes = {
+        visible: PropTypes.bool.isRequired,
+        closeModal: PropTypes.func.isRequired
+    }
+  return (<>
+    
+           
+           
+          ]}>
+         
+       
+ 
+ >)
+
+}
+   
+
+
+
+
+
+
+function mapStateToProps(state) {
+  const { abnItemState_burr,global } = state;
+  return {
+    abnItemCompData: abnItemState_burr.items,
+    actions:global.actions,
+  }
+}
+
+export default connect(mapStateToProps)(BurrModal)
diff --git a/web/client/src/sections/install/components/interruptModal.jsx b/web/client/src/sections/install/components/interruptModal.jsx
new file mode 100644
index 0000000..775c25b
--- /dev/null
+++ b/web/client/src/sections/install/components/interruptModal.jsx
@@ -0,0 +1,187 @@
+import React, { useRef,useState,useEffect } from "react";
+import { connect } from "react-redux";
+import moment from 'moment';
+import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row } from "@douyinfe/semi-ui";
+import { IconLineChartStroked } from "@douyinfe/semi-icons"
+import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart'
+import PropTypes from 'prop-types';
+
+const InterruptModal = (props) => {
+    const form = useRef()
+    const {modalData,actions,dispatch, closeModal, visible, structId,item,sensorId,project}=props
+    const {install}=actions
+    const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss'))
+    const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
+
+    
+  //表单类
+ const btnFormSubmit = () => {
+    let data = modalData
+    form.current.validate().then(value=>{
+            let paramJson = { "thr_int": Number(value.thr_int) }
+            let pushData = {
+              station: data.sensorId,//测点
+              factorId: data.factorId,
+              itemId: null,
+              abnType: 1,//中断
+              enabled: value.checked,
+              params: paramJson
+            }
+            dispatch(install.editAbnParams(data.id,pushData)).then(res => {
+              if (res.success) {
+                const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                dispatch(install.getAbnParamList({ sensorId:id}))
+                  closeModal()
+              }
+          })
+            // this.props.dispatch(editAbnParams(data.id, pushData)).then(_ => {
+            //   this.props.dispatch(getAbnParamList(this.props.structId)).then(() => {
+            //     this.props.closeModal();
+            //   });
+            // });
+          
+    })
+    // this.props.form.validateFieldsAndScroll((err, values) => {
+      
+    // });
+  }
+
+  
+  const checkInterger = (rule, val) => {
+    // if(val){
+    const strRegex = /^[1-9]*[1-9][0-9]*$/
+    if (strRegex.test(val)) {
+        return true
+    }
+    return false
+    // }
+
+};
+  const timeOnChange = (dates, dateStrings) => {
+    if (dates.length == 0) {
+        setCurrStartTime(null)
+        setcurrEndTime(null)
+      return message.warning('请选择时间');
+    }
+    setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss'))
+    setcurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss'))
+  }
+  //数据对比
+  // const dataCompare = () => {
+  //   form.current.validate().then(_=>{
+  //       let paramJson = { "thr_int": values.interrupt }
+  //       let data = {
+  //         station: modalData.stationId,
+  //         factorId: modalData.factorId,
+  //         itemId: modalData.itemId,
+  //         abnType: 'interrupt',//算法类型
+  //         enabled: true,
+  //         params: paramJson
+  //       };
+  //       let start = this.state.currStartTime;
+  //       let end = this.state.currEndTime;
+  //       this.props.dispatch(getItemAbnResult_int(this.props.structId, start, end, data)).then(res => {
+  //       })
+  //   })
+  // }
+  // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+  // let stationsData = [];
+  // if (abnItemCompData) {
+  //   originalData = abnItemCompData.stationData;
+  //   calcArray = abnItemCompData.resultArray;
+  //   key = abnItemCompData.itemKey;
+  //   itemName = abnItemCompData.itemName;
+  //   start = originalData && originalData.length > 0 ? originalData[0].time : '';
+  //   end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+  //   for (let i = 0; i < originalData.length; i++) {
+  //     let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+  //     stationsData.push(one);
+  //   }
+  //   for (let j = 0; j < calcArray.length; j++) {
+  //     let one = { name: "中断点:" + calcArray[j].time + "(中断时长:" + calcArray[j].hour.toFixed(2) + "h)", value: calcArray[j].value, time: calcArray[j].time };
+  //     stationsData.push(one);
+  //   }
+  // }
+
+// InterruptModal.propTypes = {
+//         visible: PropTypes.bool.isRequired,
+//         closeModal: PropTypes.func.isRequired
+//     }
+    return 
+    
+        
+        
+      
]}>
+      
+    
+  
+
+}
+  
+
+function mapStateToProps(state) {
+  const { global } = state;
+  return {
+    // abnItemCompData: abnItemState_int.items,
+    // abnItemCompData:[],
+    actions:global.actions
+  }
+}
+
+export default connect(mapStateToProps)(InterruptModal)
diff --git a/web/client/src/sections/install/components/theme.jsx b/web/client/src/sections/install/components/theme.jsx
new file mode 100644
index 0000000..0298e64
--- /dev/null
+++ b/web/client/src/sections/install/components/theme.jsx
@@ -0,0 +1,474 @@
+import G2 from 'g2';
+
+const DEFAULT_COLOR = '#ffea00';
+const FONT_FAMILY = '"Microsoft YaHei", "微软雅黑", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", SimSun, "sans-serif"';
+const theme = {
+    defaultColor: DEFAULT_COLOR, // 默认主题色
+    plotCfg: {
+        margin: [20, 80, 60, 80]
+    },
+    facetCfg: {
+        type: 'rect',
+        margin: 10,
+        facetTitle: {
+            titleOffset: 16,
+            colDimTitle: {
+                title: {
+                    fontSize: 14,
+                    textAlign: 'center',
+                    fill: '#999'
+                }
+            },
+            colTitle: {
+                title: {
+                    fontSize: 12,
+                    textAlign: 'center',
+                    fill: '#999'
+                }
+            },
+            rowTitle: {
+                title: {
+                    fontSize: 12,
+                    textAlign: 'center',
+                    rotate: 90,
+                    fill: '#999'
+                }
+            },
+            rowDimTitle: {
+                title: {
+                    fontSize: 12,
+                    textAlign: 'center',
+                    rotate: 90,
+                    fill: '#999'
+                }
+            }
+        }
+    },
+    binWidth: 0.03, // bin 统计的默认值
+    fontFamily: FONT_FAMILY,
+    colors: {
+        'default': ['#ffea00', '#0de3ff', '#fe8501', '#00ff36', '#0097ff', '#fe4c9b', '#9bff00', '#bd67ff', '#f6ba33', '#978cff'],
+        intervalStack: ['#ffea00', '#0de3ff', '#fe8501', '#00ff36', '#0097ff', '#fe4c9b', '#9bff00', '#bd67ff', '#f6ba33', '#978cff']
+    },
+    shapes: {
+        point: ['hollowCircle', 'hollowSquare', 'hollowDiamond', 'hollowBowtie', 'hollowTriangle',
+            'hollowHexagon', 'cross', 'tick', 'plus', 'hyphen', 'line'],
+        line: ['line', 'dash', 'dot'],
+        area: ['area']
+    },
+    hues: ['red', 'yellow', 'green', 'blue', 'orange', 'purple', 'pink', 'brown', 'white', 'gray', 'black'],
+    axis: {
+        top: {
+            position: 'top',
+            titleOffset: 30,
+            title: {
+                fontSize: 12,
+                fill: '#999'
+            },
+            labels: {
+                label: {
+                    fill: '#a2adc2',
+                    fontSize: 12
+                }
+            },
+            tickLine: {
+                lineWidth: 1,
+                stroke: '#ccc',
+                value: 5
+            }
+        },
+        bottom: {
+            position: 'bottom',
+            titleOffset: 45,
+            labelOffset: 20,
+            title: {
+                fontSize: 12,
+                textAlign: 'center',
+                fill: '#999'
+            },
+            labels: {
+                label: {
+                    fill: '#a2adc2',
+                    fontSize: 12
+                }
+            },
+            line: {
+                lineWidth: 1,
+                stroke: '#ccc'
+            },
+            tickLine: {
+                lineWidth: 1,
+                stroke: '#ccc',
+                value: 5
+            }
+        },
+        left: {
+            position: 'left',
+            titleOffset: 60,
+            labelOffset: 13,
+            title: {
+                fontSize: 12,
+                fill: '#999'
+            },
+            labels: {
+                label: {
+                    fill: '#a2adc2'
+                }
+            },
+            line: {
+                lineWidth: 1,
+                stroke: '#ccc'
+            },
+            tickLine: {
+                lineWidth: 1,
+                stroke: '#ccc',
+                value: 5
+            },
+            grid: {
+                line: {
+                    stroke: '#495f7a',
+                    lineWidth: 1,
+                    lineDash: [2, 2]
+                }
+            }
+        },
+        right: {
+            position: 'right',
+            titleOffset: 60,
+            labelOffset: 13,
+            title: {
+                fontSize: 12,
+                fill: '#999'
+            },
+            labels: {
+                label: {
+                    fill: '#a2adc2'
+                }
+            },
+            line: {
+                lineWidth: 1,
+                stroke: '#ccc'
+            },
+            tickLine: {
+                lineWidth: 1,
+                stroke: '#ccc',
+                value: 5
+            }
+        },
+        circle: {
+            labelOffset: 5,
+            line: {
+                lineWidth: 1,
+                stroke: '#ccc'
+            },
+            grid: {
+                line: {
+                    stroke: '#d9d9d9',
+                    lineWidth: 1,
+                    lineDash: [1, 3]
+                }
+            },
+            labels: {
+                label: {
+                    fill: '#a2adc2'
+                }
+            }
+        },
+        gauge: {
+            grid: null,
+            labelOffset: 5,
+            tickLine: {
+                lineWidth: 1,
+                value: -20,
+                stroke: '#ccc'
+            },
+            subTick: 5,
+            labels: {
+                label: {
+                    fill: '#a2adc2'
+                }
+            }
+        },
+        clock: {
+            grid: null,
+            labelOffset: 5,
+            tickLine: {
+                lineWidth: 1,
+                value: -20,
+                stroke: '#C0D0E0'
+            },
+            subTick: 5,
+            labels: {
+                label: {
+                    fill: '#a2adc2'
+                }
+            }
+        },
+        radius: {
+            titleOffset: 45,
+            labels: {
+                label: {
+                    fill: '#a2adc2'
+                }
+            },
+            line: {
+                lineWidth: 1,
+                stroke: '#ccc'
+            },
+            grid: {
+                line: {
+                    stroke: '#d9d9d9',
+                    lineWidth: 1,
+                    lineDash: [2, 2]
+                },
+                type: 'circle'
+            }
+        },
+        helix: {
+            grid: null,
+            labels: {
+                label: null
+            },
+            line: {
+                lineWidth: 1,
+                stroke: '#ccc'
+            },
+            tickLine: {
+                lineWidth: 1,
+                value: 5,
+                stroke: '#ccc'
+            }
+        }
+    },
+    labels: {
+        offset: 14,
+        label: {
+            fill: '#666',
+            fontSize: 12
+        }
+    },
+    treemapLabels: {
+        offset: 10,
+        label: {
+            fill: '#fff',
+            fontSize: 14,
+            textBaseline: 'top',
+            fontStyle: 'bold'
+        }
+    },
+    innerLabels: {
+        label: {
+            fill: '#fff',
+            fontSize: 12
+        }
+    }, // 在theta坐标系下的饼图文本内部的样式
+    thetaLabels: {
+        labelLine: {
+            lineWidth: 1
+        },
+        labelHeight: 14,
+        offset: 30
+    }, // 在theta坐标系下的饼图文本的样式
+    legend: {
+        right: {
+            position: 'right',
+            back: null,
+            title: {
+                fill: '#a2adc2'
+            },
+            spacingX: 10,
+            spacingY: 12,
+            markerAlign: 'center',
+            wordSpaceing: 12,
+            word: {
+                fill: '#a2adc2'
+            },
+            width: 20,
+            height: 156
+        },
+        left: {
+            position: 'left',
+            back: null,
+            title: {
+                fill: '#a2adc2'
+            },
+            spacingX: 10,
+            spacingY: 12,
+            markerAlign: 'center',
+            wordSpaceing: 12,
+            word: {
+                fill: '#a2adc2'
+            },
+            width: 20,
+            height: 156
+        },
+        top: {
+            position: 'top',
+            title: null,
+            back: null,
+            spacingX: 16,
+            spacingY: 10,
+            markerAlign: 'center',
+            wordSpaceing: 12,
+            word: {
+                fill: '#a2adc2'
+            },
+            width: 156,
+            height: 20
+        },
+        bottom: {
+            position: 'bottom',
+            title: null,
+            back: null,
+            spacingX: 16,
+            spacingY: 10,
+            markerAlign: 'center',
+            wordSpaceing: 12,
+            word: {
+                fill: '#a2adc2'
+            },
+            width: 156,
+            height: 20
+        }
+    },
+    tooltip: {
+        crosshairs: false,
+        offset: 15,
+        crossLine: {
+            stroke: '#666'
+        },
+        wordSpaceing: 6,
+        markerCfg: {
+            symbol: 'circle',
+            radius: 3
+        }
+    },
+    activeShape: {
+        point: {
+            radius: 5,
+            fillOpacity: 0.7
+        },
+        hollowPoint: {
+            lineWidth: 2,
+            radius: 4
+        },
+        interval: {
+            fillOpacity: 0.7
+        },
+        hollowInterval: {
+            lineWidth: 2
+        },
+        area: {
+            fillOpacity: 0.85
+        },
+        hollowArea: {
+            lineWidth: 2
+        },
+        line: {
+            lineWidth: 2
+        },
+        polygon: {
+            fillOpacity: 0.75
+        }
+    }, // 图形激活时,鼠标移动到上面
+    shape: {
+        point: {
+            lineWidth: 1,
+            fill: DEFAULT_COLOR,
+            radius: 4
+        },
+        hollowPoint: {
+            fill: '#fff',
+            lineWidth: 1,
+            stroke: DEFAULT_COLOR,
+            radius: 3
+        },
+        interval: {
+            lineWidth: 0,
+            fill: DEFAULT_COLOR,
+            fillOpacity: 0.85
+        },
+        pie: {
+            lineWidth: 1,
+            stroke: '#fff'
+        },
+        hollowInterval: {
+            fill: '#fff',
+            stroke: DEFAULT_COLOR,
+            fillOpacity: 0,
+            lineWidth: 1
+        },
+        area: {
+            lineWidth: 0,
+            fill: DEFAULT_COLOR,
+            fillOpacity: 0.6
+        },
+        polygon: {
+            lineWidth: 0,
+            fill: DEFAULT_COLOR,
+            fillOpacity: 1
+        },
+        hollowPolygon: {
+            fill: '#fff',
+            stroke: DEFAULT_COLOR,
+            fillOpacity: 0,
+            lineWidth: 1
+        },
+        hollowArea: {
+            fill: '#fff',
+            stroke: DEFAULT_COLOR,
+            fillOpacity: 0,
+            lineWidth: 1
+        },
+        line: {
+            stroke: DEFAULT_COLOR,
+            lineWidth: 1,
+            fill: null
+        }
+    },
+    guide: {
+        text: {
+            fill: '#666',
+            fontSize: 12
+        },
+        line: {
+            stroke: DEFAULT_COLOR,
+            lineDash: [0, 2, 2]
+        },
+        rect: {
+            lineWidth: 0,
+            fill: DEFAULT_COLOR,
+            fillOpacity: 0.1
+        },
+        tag: {
+            line: {
+                stroke: DEFAULT_COLOR,
+                lineDash: [0, 2, 2]
+            },
+            text: {
+                fill: '#666',
+                fontSize: 12,
+                textAlign: 'center'
+            },
+            rect: {
+                lineWidth: 0,
+                fill: DEFAULT_COLOR,
+                fillOpacity: 0.1
+            }
+        },
+        html: {
+            align: 'cc'
+        }
+    },
+    tooltipMarker: {
+        fill: '#fff',
+        symbol: 'circle',
+        lineWidth: 2,
+        stroke: DEFAULT_COLOR,
+        radius: 4
+    } // 提示信息在折线图、区域图上形成点的样式
+};
+
+var Theme = G2.Util.mix(true, {}, G2.Theme, theme);
+
+G2.Global.setTheme(Theme);
\ No newline at end of file
diff --git a/web/client/src/sections/install/containers/AbnRecognize.jsx b/web/client/src/sections/install/containers/AbnRecognize.jsx
new file mode 100644
index 0000000..86b647c
--- /dev/null
+++ b/web/client/src/sections/install/containers/AbnRecognize.jsx
@@ -0,0 +1,106 @@
+'use strict';
+
+import React, {  } from 'react';
+import { connect } from 'react-redux';
+import { Row,Col, Card,Icon } from '@douyinfe/semi-ui';
+import { IconSearch,IconLineChartStroked,IconStar,IconUser} from '@douyinfe/semi-icons';
+import Trend from './trend';
+import Burr from './Burr';
+import Interrupt from './Interrupt';
+
+const AbnRecognize = (props) => {
+    // const {abnCompareData}=props
+    const {calcMethod,abnCompareData,factorId,structId,strunctChange,itName,sensorId,project}=props
+    // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+    // let stationsData = [];
+    // if (abnCompareData &&calcMethod == abnCompareData.method) {
+    //     originalData = abnCompareData.stationData;
+    //     calcArray = abnCompareData.resultArray;
+    //     key = abnCompareData.itemKey;
+    //     itemName = abnCompareData.itemName;
+    //     start = originalData && originalData.length > 0 ? originalData[0].time : '';
+    //     end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+    //     for (let i = 0; i < originalData.length; i++) {
+    //         let one = { name: itemName + "(单位:" + abnCompareData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+    //         stationsData.push(one);
+    //     }
+    //     if (calcMethod == 'interrupt') {
+    //         for (let j = 0; j < calcArray.length; j++) {
+    //             let one = { name: "中断点:" + calcArray[j].time + "(中断时长:" + calcArray[j].hour.toFixed(2) + "h)", value: calcArray[j].value, time: calcArray[j].time };
+    //             stationsData.push(one);
+    //         }
+    //     } else if (calcMethod == 'burr') {
+    //         for (let j = 0; j < calcArray.length; j++) {
+    //             let one = {
+    //                 name: "毛刺点:" + calcArray[j].time + "(突变大小:" + calcArray[j].burr.toFixed(2) + abnCompareData.unit + ")",
+    //                 value: calcArray[j].value, time: calcArray[j].time
+    //             };
+    //             stationsData.push(one);
+    //         }
+    //     } else {
+    //         if (calcArray) {
+    //             let preLineData = calcArray.calcPreprocess;
+    //             let abnTrends = calcArray.calcFinal;
+    //             for (let j = 0; j < preLineData.length; j++) {
+    //                 let one = { name: "预处理+滑动均值后数据", value: preLineData[j].value, time: preLineData[j].time };
+    //                 stationsData.push(one);
+    //             }
+    //             for (let t = 0; t < abnTrends.length; t++) {
+    //                 let name = abnTrends[t].startTime + "至" + abnTrends[t].endTime + abnTrends[t].des + ",渐变差值:" + abnTrends[t].value.toFixed(2) + abnCompareData.unit;
+    //                 let start = { name: name, value: abnTrends[t].startValue, time: abnTrends[t].startTime };
+    //                 let end = { name: name, value: abnTrends[t].endValue, time: abnTrends[t].endTime };
+    //                 stationsData.push(start);
+    //                 stationsData.push(end);
+    //             }
+    //         }
+    //     }
+    // }
+
+
+const renderContent=()=> {
+    const SubContent = {
+        'interrupt': ,
+        'burr': ,
+        'trend': ,
+    };
+    return SubContent[calcMethod];
+}
+
+
+
+return (<>
+    
+    {/* 
+                    
+                        数据对比}>
+                            
+                                
+                                    {originalData && originalData[0] == '还没查询' ?
+                                        
 输入参数,点击数据对比展示数据对比图(默认显示选择的第一个传感器)
+                                        :
+                                        originalData && originalData[0] != '还没查询' && originalData.length > 0 ?
+                                            
+                                            :
+                                            
 没有查询到任何有效数据!
}
+                                
+                            
 */}
+                {renderContent()}
+
+>)
+}
+  
+
+
+function mapStateToProps(state) {
+    const { auth, global, OrganizationDeps } = state;
+    return {
+        // loading: OrganizationDeps.isRequesting,
+        // user: auth.user,
+        // actions: global.actions,
+    };
+}
+export default connect(mapStateToProps)(AbnRecognize)
\ No newline at end of file
diff --git a/web/client/src/sections/install/containers/burr.jsx b/web/client/src/sections/install/containers/burr.jsx
new file mode 100644
index 0000000..6ad9459
--- /dev/null
+++ b/web/client/src/sections/install/containers/burr.jsx
@@ -0,0 +1,267 @@
+'use strict';
+
+import React, { useState,useRef } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag,Notification, Form } from '@douyinfe/semi-ui';
+import BurrModal from '../components/burrModal';
+import { useEffect } from 'react';
+
+const Burr = (props) => {
+    const {factorId,abnParam,structId,dispatch,actions,itName,sensorId,project}=props
+    const form = useRef()
+    const [selectedRowKeys, setSelectedRowKeys] = useState([])
+    const [filterFunc, setFilterFunc] = useState({})
+    const [modalVisible, setModalVisible] = useState(false)
+    const [bvBatch, setBvBatch] = useState('')
+    const [modalData, setModalData] = useState(null)
+    const {install}=actions
+    const filterSet = { sensorName: null, factorName: null }
+
+    useEffect(()=>{
+    },[])
+    const onSelectChange = (selectedRowKeys) => {
+        setSelectedRowKeys(selectedRowKeys);
+    }
+    const compareAndEdit = (e, record) => {
+        setModalVisible(true)
+        setModalData(record)
+    }
+    const modalCancel = e => {
+        setModalVisible(false)
+    };
+    const removeItem = e => {
+        dispatch(install.deleteAbnParams(e)).then(res=>{
+            if(res.success){
+                const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                dispatch(install.getAbnParamList({ sensorId:id}))
+            }
+        })
+    };
+    const handleInputChange = e => {
+        if (e != null) {
+            const value = e
+            let func = (ep => (s => (s.sensorName).search(ep) > -1))(value);
+            filterSet.sensorName = func;
+            func = (ep => (s => (s.factorName).search(ep) > -1))(value);
+            filterSet.factorName = func;
+        } else {
+            filterSet.sensorName = null;
+            filterSet.factorName = null;
+        }
+        setFilterFunc(filterSet)
+    }
+    //批量启用or禁用
+    const  onSwitchChange = (e) => {
+        if (selectedRowKeys.length != 0) {
+            let ids = selectedRowKeys.join(',');
+            let data = {
+                enabled: e,
+                use: 'switch'
+            };
+            //批量启用or禁用逻辑
+            dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+                if(res.success){
+                    const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                    dispatch(install.getAbnParamList({ sensorId:id }))
+                }
+            })
+        }
+    }
+    //批量删除
+    const  batchDelete = () => {
+        if (selectedRowKeys.length != 0) {
+            let ids = selectedRowKeys.join(',');
+            dispatch(install.deleteAbnParams(ids)).then(res=>{
+                if(res.success){
+                    const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                    dispatch(install.getAbnParamList({ sensorId:id }))
+                }
+            })
+
+            // this.props.dispatch(deleteAbnParams(ids)).then(_ => {
+            //     this.props.dispatch(getAbnParamList(this.props.structId));
+            // });
+        } else {
+            Notification.warning({
+                content: '您尚未勾选任何参数配置!',
+                duration: 3,
+            })
+        }
+    }
+    //批量保存
+    const batchSave = () => {
+        form.current.validate().then(res=>{
+            let dataSource = abnParam.filter(a => a.abnType == 2 && a.factorId == factorId)
+                if (selectedRowKeys.length != 0) {
+                    let ids = selectedRowKeys.join(',');
+                    let data = {
+                        paramJson: { "thr_burr": bvBatch },
+                        use: 'notSwitch',
+                    };
+                    //批量保存逻辑
+                    dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+                        if(res.success){
+                            const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                            dispatch(install.getAbnParamList({ sensorId:id }))
+                        }
+                    })
+                } else if (dataSource.length != 0) {
+                    Notification.warning({
+                        content: '您尚未勾选任何参数配置!',
+                        duration: 3,
+                    })
+                }
+        })
+    }
+    const batchBurr = (value) => {
+        setBvBatch(value)
+    }
+    const checkNumber = (rule, val) => {
+        const strRegex = /^-?\d+(\.\d+)?$/
+        if (strRegex.test(val)) {
+            return true
+        }
+        return false
+
+
+
+    }
+      let dataSource = abnParam.filter(a => a.abnType == 2 && a.factorId == factorId)
+      const rowSelection = {
+          selectedRowKeys: selectedRowKeys,
+          onChange: onSelectChange,
+      };
+      let filterData = dataSource
+      let flag = false
+      let keyArr = []
+      let tmpds = []
+      let dataPush = []
+      Object.keys(filterFunc).forEach(key => {
+          const filter = filterFunc[key]
+          filterData = dataSource
+          if (filter != null) {
+              flag = true
+              filterData = filterData.filter(filter)
+              if (filterData.length > 0) {
+                  filterData.map(s => {
+                      if (keyArr.indexOf(s.id) == -1) {
+                          keyArr.push(s.id)
+                          dataPush.push(s)
+                      }
+                  })
+              }
+          }
+      })
+      tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id)
+      let columns = [
+          {
+              title: "测点位置",
+              dataIndex: "sensorName",
+              key: "sensorName",
+              width: '20%',
+          },
+          {
+              title: "监测项",
+              dataIndex: "factorName",
+              key: "factorName",
+              width: '20%',
+              render: (text, record) => (
+                  record.factorName + "/" + itName
+              ),
+          },
+          {
+              title: "毛刺阈值",
+              dataIndex: "params",
+              key: "params",
+              width: '20%',
+              render: text => text.thr_burr
+          },
+          {
+              title: "启用状态",
+              dataIndex: "enabled",
+              key: "enabled",
+              width: '20%',
+              render: (text, record) => (
+                  record.enabled ? 已启用 : 已禁用
+              )
+          },
+          {
+              title: "操作",
+              key: "action",
+              width: '20%',
+              render: (text, record) => (
+                  
+                       
+                      
+                       {removeItem(record.id) }}>
+                          
+                      
+                  
+              ),
+          }
+      ]
+
+      return (
+        
+        
+        {modalVisible ?  : ''}
+    
+    )
+
+}
+
+
+
+function mapStateToProps(state) {
+    const { abnParam ,global} = state;
+    // let isRequesting = abnParamState?.isRequesting;
+
+    return {
+        isRequesting: false,//请求状态
+        abnParam: abnParam?.data||[],
+        actions:global.actions
+    }
+}
+export default connect(mapStateToProps)(Burr)
+
diff --git a/web/client/src/sections/install/containers/cacl.jsx b/web/client/src/sections/install/containers/cacl.jsx
new file mode 100644
index 0000000..c035f37
--- /dev/null
+++ b/web/client/src/sections/install/containers/cacl.jsx
@@ -0,0 +1,496 @@
+'use strict'
+
+import React, { useEffect, useState, useRef } from 'react';
+import { connect } from 'react-redux';
+import { Row, Form, Col, Select, Button, Notification } from '@douyinfe/semi-ui';
+import moment from 'moment';
+import AbnRecognize from './AbnRecognize'
+const methodType = {
+    'interrupt': 1,
+    'burr': 2,
+    'trend': 3,
+}
+const methodDes = {
+    'interrupt': "中断",
+    'burr': "毛刺",
+    'trend': "异常趋势",
+}
+
+const Calc = (props) => {
+    const { actions, dispatch, pepProjectId, abnMethods, abnParam } = props
+    const { install, data } = actions
+    const [structId, setStructId] = useState(null)
+    const [methodType, setMethodType] = useState('interrupt')
+    const [loading, setLoading] = useState(false)
+    const [structList, setStructList] = useState([])//结构物筛选列表
+    const [project, setProject] = useState('')//project(eg:{projcet:'nbjj'})
+    const [reCalcFactorId, setReCalcFactorId] = useState(null);
+    const [msg, setMsg] = useState('')
+    const [factosList, setFactorsList] = useState([])//监测因素列表
+    const [factorId, setFactorId] = useState([])
+    const [sensorList, setSensorList] = useState([])//设备列表
+    const [sensorId, setSensorId] = useState([])//设备id'
+    const [itemId, setItemId] = useState(null)//监测项索引
+    const [itemName, setItemName] = useState('')//检测项name
+    const form = useRef()
+    const form2 = useRef()
+    const method = {
+        'interrupt': 1,
+        'burr': 2,
+        'trend': 3,
+    }
+    //初始化
+    useEffect(() => {
+        // getData()
+        dispatch(install.getAbnMethods())
+    }, [])
+    useEffect(() => {
+        if(project){
+        const id = [...sensorId?.map(item=>project+'-'+item), -1].join(',')
+        dispatch(install.getAbnParamList({ sensorId: id }))
+        }
+    }, [sensorId, factorId,project])
+  
+    //监听变化
+    useEffect(() => {
+        setStructList([])
+        form.current.reset()
+        form2.current.reset()
+        form2.current.setValue('method', methodType)
+        getData()
+    }, [pepProjectId])
+    const checkInterger = (rule, val) => {
+        // if(val){
+        const strRegex = /^[1-9]*[1-9][0-9]*$/
+        if (strRegex.test(val)) {
+            return true
+        }
+
+        return false
+        // }
+
+    };
+    const checkNumber = (rule, val) => {
+        const strRegex = /^-?\d+(\.\d+)?$/
+        if (strRegex.test(val)) {
+            return true
+        }
+        return false
+
+
+
+    }
+    const checkPoint = (rule, value) => {
+        const pattern = /^[1-9]*[1-9][0-9]*$/;
+        if (pattern.test(value) && value != 1) {
+            return true
+        }
+        return false
+    }
+    const getJson = (values) => {
+        let paramJson;
+        switch (methodType) {
+            case 'interrupt':
+                return paramJson = { "thr_int": Number(values.iv) };
+            case 'burr':
+                return paramJson = { "thr_burr": Number(values.bv1) }
+            case 'trend':
+                return paramJson = {
+                    "thr_burr": Number(values.bv2),//毛刺阈值
+                    "win_med": Number(values.ws),//滑动中值窗口
+                    "win_avg": Number(values.rc),//滑动均值窗口
+                    "win_grad": Number(values.pn),//渐变点个数
+                    "thr_grad": Number(values.gv),//渐变阈值
+                    "thr_der": Number(values.dv),//导数阈值
+                    "days_Last": Number(values.time),//时间跨度
+                }
+        }
+    }
+    const getStationstoSave = () => {
+        let toSave = [], toSaveName = [], notToSave = [], notToSaveName = []
+        let selectIds = sensorId
+        for (let i = 0; i < selectIds.length; i++) {
+            let seStaName = sensorList.find(a => a.value == selectIds[i]).label
+            let cfg
+            if (methodType == "interrupt") {
+                cfg = abnParam.find(a => a.abnType == 1 && a.factorId == factorId
+                    && a.sensorId == selectIds[i])
+            } else {
+                let type = methodType == "burr" ? 2 : 3
+                cfg = abnParam.find(a => a.abnType == type && a.factorId == factorId
+                    && a.sensorId == selectIds[i])
+            }
+            if (!cfg) {
+                toSave.push(selectIds[i])
+                toSaveName.push(seStaName)
+            } else {
+                notToSave.push(selectIds[i])
+                notToSaveName.push(seStaName)
+            }
+        }
+        return {
+            toSave: toSave, toSaveName: toSaveName,
+            notToSave: notToSave, notToSaveName: notToSaveName
+        };
+    }
+    //保存配置
+    const handleSave = () => {
+        setMsg('')
+        form2.current.validate().then(res => {
+            if (sensorId.length != 0) {
+                let ids = getStationstoSave()
+                if (ids.toSave.length != 0) {
+                    let paramJson = getJson(res)
+                    let data = {
+                        sensorId:ids.toSave,//测点
+                        sensorName: ids.toSaveName,
+                        factorId: factorId,
+                        factorName: factosList.find(a => a.value == factorId).label,
+                        abnType: method[methodType],//算法类型
+                        enabled: true,
+                        params: paramJson,
+                        itemId: itemId,
+
+                    }
+                    let pushData = data.sensorId.map(d => {
+                        return {
+                            sensorId:  project+'-'+d,//测点
+                            sensorName: sensorList.find(a => a.value == d).label,
+                            factorId: factorId,
+                            factorName: factosList.find(a => a.value == factorId).label,
+                            abnType: method[methodType],//算法类型
+                            enabled: true,
+                            params: paramJson,
+                            itemId: methodType == 'interrupt' ? null : itemId
+
+                        }
+                    })
+                    dispatch(install.addAbnParam(pushData)).then(res => {
+                        if (res.success) {
+                            const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                            dispatch(install.getAbnParamList({ sensorId: id }))
+                        }
+                    })
+                    if (ids.notToSave.length != 0) {
+                        let fact = methodType == 'interrupt' ? "因素" : "子项"
+                        Notification.warning({
+                            content: `已存在传感器: ${ids.notToSaveName.join(',')} 在当前监测${fact}下的${methodDes[methodType]}参数配置,本次保存的传感器为:${ids.toSaveName.join(',')}`,
+                            duration: 3,
+                        })
+                    }
+                } else {
+                    let sg = methodType == 'interrupt' ? "因素下的中断"
+                        : methodType == 'burr' ? "子项下的毛刺" : "子项下的异常趋势"
+                    Notification.warning({
+                        content: `已存在选定测点在当前监测 ${sg} 参数配置,本次无可保存的参数配置`,
+                        duration: 3,
+                    })
+                }
+            } else {
+                Notification.warning({
+                    content: `请选择测点`,
+                    duration: 3,
+                })
+                return false
+            }
+
+        })
+    }
+    //数据对比
+    const dataCompare = () => {
+        // this.setState({ msg: '' });
+        // this.props.form.validateFields((err, values) => {
+        //     if (!err) {
+        //         if (this.props.stationsValue.length != 0) {
+        //             let paramJson = this.getJson(values);
+        //             let data = {
+        //                 station: this.props.stationsValue[0],//第一个测点
+        //                 factorId: this.props.factorId,
+        //                 itemId: this.props.itemId,
+        //                 abnType: this.props.methodType,//算法类型
+        //                 enabled: true,
+        //                 params: paramJson
+        //             };
+        //             this.props.dataCompara(data);
+        //         } else {
+        //             message.error('请选择测点!');
+        //             return false;
+        //         }
+        //     }
+        // });
+    }
+
+    //结构物筛选发生变化回调函数
+    const reCalcFactorChange = (value) => {
+        setItemName(factosList?.find(item => item.value === value[0])?.children?.find(p => p.value === value[1])?.label)
+        setItemId(value[1])
+        // form2.current.setValue('point', '')
+        setFactorId(value[0])
+    }
+    //点位筛选发生改变的回调函数
+    const pointChange = (value) => {
+        setSensorId(value)
+        // setProject(structList.find(item => item.value == structId)?.project)
+    }
+    //监听结构物变化,查询监测因素
+    useEffect(() => {
+        setLoading(true);
+        let queryParams = { structId, cacl: 1 }
+        if (structId) {
+            dispatch(data.getFactors({ ...queryParams })).then(res => {
+                if (res.success) {
+                    const list = res.payload.data?.map(item => {
+                        const itemList = item.ItemNames && item.ItemNames.split(',') || []
+                        let child = []
+                        itemList && itemList.length && itemList.forEach((i, index) => {
+                            child.push({ label: i, value: index })
+                        })
+                        return {
+                            label: item.Name,
+                            value: item.FactorID,
+                            children: child
+
+                        }
+
+                    })
+                    form2.current.setValue('factor', [list[0]?.value, list[0]?.children[0]?.value])
+                    setFactorsList(list)
+                    setFactorId(list[0]?.value)
+                    setItemId(list[0]?.children[0]?.value)
+                    setItemName(list[0]?.children[0]?.label)
+                    setLoading(false)
+                }
+            })
+        }
+
+    }, [structId])
+    useEffect(() => {
+        let queryParams = { structId, SafetyFactorTypeId: factorId }
+        if (factorId && structId) {
+            dispatch(data.getSensors({ ...queryParams })).then(res => {
+                if (res.success) {
+                    const list = res.payload.data?.map(item => {
+                        return {
+                            value: item.SensorId,
+                            label: item.SensorLocationDescription,
+                        }
+                    })
+                    form2.current.setValue('point', [list[0]?.value])
+                    setSensorId([list[0]?.value])
+                    setSensorList(list)
+                    setLoading(false);
+                    setProject(res.payload.data[0].Project)
+                }
+            })
+        }
+    }, [factorId])
+
+
+
+
+    //获取结构物函数
+    const getData = (queryParams = { pomsProjectId: pepProjectId }) => {
+        setLoading(true);
+        dispatch(data.getProjectAllStructures({ ...queryParams })).then(res => {
+            if (res.success) {
+                const uniqueIds = new Set();
+                const list = res.payload.data?.filter(item => {
+                    const duplicate = uniqueIds.has(item.strucId);
+                    uniqueIds.add(item.strucId);
+                    return !duplicate
+                })?.map(item => ({
+                    value: item.strucId,
+                    label: item.strucName,
+                    project: item.Project,
+                }))
+                form.current.setValue('struct', list[0]?.value)
+                setStructId(list[0]?.value)
+                setStructList(list)
+                setLoading(false)
+            }
+        }
+
+        )
+    }
+    const changeMethod = (value) => {
+        setMethodType(value)
+    }
+    const clearFactorAndSensor = () => {
+        form2.current.setValue('point', '')
+        form2.current.setValue('factor', '')
+    }
+    const structChange = (value) => {
+        setStructId(value)
+        clearFactorAndSensor()
+    }
+
+
+    const renderParamsConfig = () => {
+        switch (methodType) {
+            case 'interrupt':
+                return (
+                    
+                        
+                        
+                    
+                );
+            case 'burr':
+                return (
+                    
+                        
+                        
+                    
+                );
+            case 'trend':
+                return (
+                    
+                        
+                            
+                                
+                            
+                            
+                                
+                            
+                            
+                                
+                            
+                            
+                                
+                            
+                            
+                                
+                            
+                            
+                                
+                            
+                        
+                    );
+        }
+    }
+
+    return (<>
+        
+
+
+    >)
+}
+
+
+
+function mapStateToProps(state) {
+    const { auth, global, OrganizationDeps, abnMethods, abnParam } = state;
+    return {
+        loading: OrganizationDeps.isRequesting,
+        user: auth.user,
+        actions: global.actions,
+        pepProjectId: global.pepProjectId,
+        abnMethods: abnMethods?.data || [],
+        abnParam: abnParam?.data || []
+    };
+}
+export default connect(mapStateToProps)(Calc);
+
+
diff --git a/web/client/src/sections/install/containers/index.js b/web/client/src/sections/install/containers/index.js
index afbe8a0..4cd2313 100644
--- a/web/client/src/sections/install/containers/index.js
+++ b/web/client/src/sections/install/containers/index.js
@@ -4,5 +4,5 @@ import Roles from './roles';
 import System from './system';
 import Setup from './setup';
 import Set from './set';
-
-export { Roles, System, Setup, Set };
\ No newline at end of file
+import Cacl from './cacl'
+export { Roles, System, Setup, Set,Cacl }
\ No newline at end of file
diff --git a/web/client/src/sections/install/containers/interrupt.jsx b/web/client/src/sections/install/containers/interrupt.jsx
new file mode 100644
index 0000000..7ca3832
--- /dev/null
+++ b/web/client/src/sections/install/containers/interrupt.jsx
@@ -0,0 +1,266 @@
+'use strict';
+
+import React, { useRef, useState, useEffect } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag, Notification, Form } from '@douyinfe/semi-ui';
+import InterruptModal from '../components/InterruptModal';
+import '../style.less'
+
+
+const Interrupt = (props) => {
+    const {dispatch, abnParam,structId,factorId,actions,sensorId,project} = props
+    const form = useRef()
+    const [selectedRowKeys, setSelectedRowKeys] = useState([])
+    const [filterFunc, setFilterFunc] = useState({})
+    const [modalVisible, setModalVisible] = useState(false)
+    const [ivBatch, setIvBatch] = useState('')
+    const [modalData, setModalData] = useState(null)
+    const filterSet = { sensorName: null, factorName: null }
+    const {install}=actions
+    const onSelectChange = (selectedRowKeys) => {
+        setSelectedRowKeys(selectedRowKeys);
+    }
+    const compareAndEdit = (e, record) => {
+        setModalVisible(true)
+        setModalData(record)
+    }
+    const modalCancel = e => {
+        setModalVisible(false)
+    }
+    const removeItem = e => {
+        dispatch(install.deleteAbnParams(e)).then(res=>{
+            if(res.success){
+                const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                dispatch(install.getAbnParamList({ sensorId:id}))
+            }
+        })
+    }
+    const handleInputChange = e => {
+        if (e!= null) {
+            const value = e
+            let func = (ep => (s => (s.sensorName).search(ep) > -1))(value);
+            filterSet.sensorName = func
+            func = (ep => (s => (s.factorName).search(ep) > -1))(value);
+            filterSet.factorName = func
+        } else {
+            filterSet.sensorName = null
+            filterSet.factorName = null
+        }
+        setFilterFunc(filterSet)
+    }
+    //批量启用or禁用
+    const onSwitchChange = (e) => {
+        if (selectedRowKeys.length != 0) {
+            let ids = selectedRowKeys.join(',')
+            let data = {
+                enabled: e,
+                use: 'switch'
+            }
+            dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+                if(res.success){
+                    const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                    dispatch(install.getAbnParamList({ sensorId:id }))
+                }
+            })
+       
+        }
+    }
+    //批量删除
+    const batchDelete = () => {
+        if (selectedRowKeys.length != 0) {
+            let ids = selectedRowKeys.join(',')
+            dispatch(install.deleteAbnParams(ids)).then(res=>{
+                if(res.success){
+                    const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                    dispatch(install.getAbnParamList({ sensorId:id}))
+                }
+            })
+        } else {
+            Notification.warning({
+                content: '您尚未勾选任何参数配置!',
+                duration: 3,
+            })
+        }
+    }
+    //批量保存
+    const batchSave = () => {
+        form.current.validate().then(_ => {
+            let dataSource = abnParam.filter(a => a.abnType == 1 && a.factorId == factorId)
+            if (selectedRowKeys.length != 0) {
+                let ids = selectedRowKeys.join(',');
+                let data = {
+                    paramJson: { "thr_int": ivBatch },
+                    use: 'notSwitch',
+                }
+                
+                dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+                    if(res.success){
+                        const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                        dispatch(install.getAbnParamList({ sensorId:id }))
+                    }
+                })
+            } else if (dataSource.length != 0) {
+                Notification.warning({
+                    content: '您尚未勾选任何参数配置!',
+                    duration: 3,
+                })
+            }
+        })
+    }
+    const batchInterrupt = (value) => {
+        setIvBatch(value)
+    }
+    const checkInterger = (rule, val) => {
+        // if(val){
+        const strRegex = /^[1-9]*[1-9][0-9]*$/
+        if (strRegex.test(val)) {
+            return true
+        }
+        return false
+        // }
+
+    };
+    let dataSource = abnParam.filter(a => a.abnType == 1 && a.factorId == factorId)
+    const rowSelection = {
+        selectedRowKeys: selectedRowKeys,
+        onChange: onSelectChange,
+    };
+    let filterData = dataSource
+    let flag = false
+    let keyArr = []
+    let tmpds = []
+    let dataPush = []
+    Object.keys(filterFunc).forEach(key => {
+        const filter = filterFunc[key]
+        filterData = dataSource
+        if (filter != null) {
+            flag = true
+            filterData = filterData.filter(filter)
+            if (filterData.length > 0) {
+                filterData.map(s => {
+                    if (keyArr.indexOf(s.id) == -1) {
+                        keyArr.push(s.id)
+                        dataPush.push(s)
+                    }
+                })
+            }
+        }
+    })
+    tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id);
+    let columns = [
+        {
+            title: "测点位置",
+            dataIndex: "sensorName",
+            key: "stationName",
+            width: '20%',
+        },
+        {
+            title: "监测因素",
+            dataIndex: "factorName",
+            key: "factorName",
+            width: '20%',
+        },
+        {
+            title: "中断阈值",
+            dataIndex: "params",
+            key: "params",
+            width: '20%',
+            render: text => text.thr_int
+            // render: text => {
+            //     return ()
+            // }
+        },
+        {
+            title: "启用状态",
+            dataIndex: "enabled",
+            key: "enabled",
+            width: '20%',
+            render: (text, record) => (
+                record.enabled ? 已启用 : 已禁用
+            )
+        },
+        {
+            title: "操作",
+            key: "action",
+            width: '20%',
+            render: (text, record) => (
+                
+                         
+                    
+                     { removeItem(record.id) }}>
+                        
+                    
+                
+            ),
+        }
+    ]
+
+    return (
+        
+        
+        {modalVisible ?  : ''}
+    
+    )
+}
+
+
+
+
+
+
+
+
+
+
+
+
+function mapStateToProps(state) {
+    const { abnParam,global } = state;
+    // let isRequesting = abnParamState?.isRequesting;
+
+    return {
+        isRequesting: false,//请求状态
+        abnParam: abnParam?.data || [],
+        actions:global.actions,
+    }
+}
+export default connect(mapStateToProps)(Interrupt)
diff --git a/web/client/src/sections/install/containers/trend.jsx b/web/client/src/sections/install/containers/trend.jsx
new file mode 100644
index 0000000..3fb64a5
--- /dev/null
+++ b/web/client/src/sections/install/containers/trend.jsx
@@ -0,0 +1,425 @@
+'use strict';
+
+import React, { Component, useRef,useState } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag, Notification, Form, Select } from '@douyinfe/semi-ui'
+import {IconPlus} from "@douyinfe/semi-icons"
+import TrendModal from '../components/TrendModal';
+
+
+
+const Trend = (props) => {
+    const { abnParam, factorId,itName,actions,dispatch,sensorId,project } = props
+    const form = useRef()
+    const [selectedRowKeys, setSelectedRowKeys] = useState([])
+    const [filterFunc, setFilterFunc] = useState({})
+    const [modalVisible, setModalVisible] = useState(false)
+    const [showBatchConfig, setShowBatchConfig] = useState(false)
+    const [timeRange, setTimeRange] = useState('')
+    const [bvBatch, setBvBatch] = useState('')
+    const [winSize, setWinSize] = useState('')
+    const [reCoef, setReCoef] = useState('')
+    const [deValue, setDeValue] = useState('')
+    const [graPoint, setGraPoint] = useState('')
+    const [graValue, setGraValue] = useState('')
+    const [modalData, setModalData] = useState(null)
+    const filterSet = { sensorName: null, factorName: null }
+    const {install}=actions
+    const onSelectChange = (selectedRowKeys) => {
+        setSelectedRowKeys(selectedRowKeys)
+    }
+    const compareAndEdit = (e, record) => {
+        setModalVisible(true)
+        setModalData(record)
+    }
+    const modalCancel = e => {
+        setModalVisible(false)
+    };
+    const removeItem = e => {
+        dispatch(install.deleteAbnParams(e)).then(res=>{
+            if(res.success){
+                const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                dispatch(install.getAbnParamList({ sensorId:id}))
+            }
+        })
+    }
+    const handleInputChange = e => {
+        if (e != null) {
+            const value = e
+            let func = (ep => (s => (s.sensorName).search(ep) > -1))(value)
+            filterSet.sensorName = func
+            func = (ep => (s => (s.factorName).search(ep) > -1))(value)
+            filterSet.factorName = func
+        } else {
+            filterSet.sensorName = null
+            filterSet.factorName = null
+        }
+        setFilterFunc(filterSet)
+    }
+    //批量启用or禁用
+    const onSwitchChange = (e) => {
+        if (selectedRowKeys.length != 0) {
+            let ids = selectedRowKeys.join(',');
+            let data = {
+                enabled: e,
+                use: 'switch'
+            };
+            //启用和禁用接口
+            dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+                if(res.success){
+                    const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                    dispatch(install.getAbnParamList({ sensorId:id}))
+                }
+            })
+        }
+    }
+    //批量删除
+    const batchDelete = () => {
+        if (selectedRowKeys.length != 0) {
+            let ids = selectedRowKeys.join(',');
+            //删除接口
+            dispatch(install.deleteAbnParams(ids)).then(res=>{
+                if(res.success){
+                    const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                    dispatch(install.getAbnParamList({ sensorId:id }))
+                }
+            })
+        } else {
+            Notification.warning({
+                content: '您尚未勾选任何参数配置!',
+                duration: 3,
+            })        }
+    }
+    //批量保存
+    const batchSave = () => {
+        form.current.validate().then(value=>{
+            let dataSource = abnParam.filter(a => a.abnType == 3 && a.factorId == factorId)
+                if (selectedRowKeys.length != 0) {
+                    let ids = selectedRowKeys.join(',')
+                    let data = {
+                        paramJson: {
+                            "thr_der": deValue, //导数阈值
+                            "win_avg": reCoef,//滑动均值
+                            "win_med": winSize,//滑动中值
+                            "thr_burr": bvBatch,//毛刺阈值
+                            "thr_grad": graValue,//渐变阈值
+                            "win_grad": graPoint,//渐变点个数
+                            "days_Last": timeRange//分析时长
+                        },
+                        use: 'notSwitch',
+                    };
+
+                    dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+                        if(res.success){
+                            const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+                            dispatch(install.getAbnParamList({ sensorId:id}))
+                        }
+                    })
+                } else if (dataSource.length != 0) {
+                    Notification.warning({
+                        content: '您尚未勾选任何参数配置!',
+                        duration: 3,
+                    })
+                }
+        })
+    }
+    const iconClick = () => {
+        if (!showBatchConfig) {
+            setShowBatchConfig(true);
+        } else {
+            setShowBatchConfig(false);
+        }
+    }
+    const rangeChange = (value) => {
+        setTimeRange(value)
+    }
+    const bvValueBatch = (value) => {
+        setBvBatch(value)
+    }
+    const winSizeBatch = (value) => {
+        setWinSize(value)
+    }
+    const reCoefBatch = (value) => {
+        setReCoef(value)
+    }
+    const deValueBatch = (value) => {
+        setDeValue(value)
+    }
+    const graPointBatch = (value) => {
+        setGraPoint(value)
+    }
+    const graValueBatch = (value) => {
+        setGraValue(value)
+    }
+    const checkInterger = (rule, val) => {
+        // if(val){
+        const strRegex = /^[1-9]*[1-9][0-9]*$/
+        if (strRegex.test(val)) {
+            return true
+        }
+        return false
+        // }
+
+    };
+    const checkPoint = (rule, value) => {
+        const pattern = /^[1-9]*[1-9][0-9]*$/;
+        if (pattern.test(value) && value != 1) {
+            return true
+        }
+        return false
+    }
+    const checkNumber = (rule, val) => {
+        const strRegex = /^-?\d+(\.\d+)?$/
+        if (strRegex.test(val)) {
+            return true
+        }
+        return false
+
+
+
+    }
+
+    let dataSource = abnParam.filter(a => a.abnType == 3 && a.factorId == factorId)
+    const rowSelection = {
+        selectedRowKeys: selectedRowKeys,
+        onChange: onSelectChange,
+    };
+    let filterData = dataSource;
+    let flag = false;
+    let keyArr = [];
+    let tmpds = [];
+    let dataPush = [];
+    Object.keys(filterFunc).forEach(key => {
+        const filter = filterFunc[key];
+        filterData = dataSource;
+        if (filter != null) {
+            flag = true;
+            filterData = filterData.filter(filter)
+            if (filterData.length > 0) {
+                filterData.map(s => {
+                    if (keyArr.indexOf(s.id) == -1) {
+                        keyArr.push(s.id);
+                        dataPush.push(s);
+                    }
+                })
+            }
+        }
+    })
+    tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id);
+    let columns = [
+        {
+            title: "测点位置",
+            dataIndex: "sensorName",
+            key: "sensorName",
+            width: '9%'
+        },
+        {
+            title: "监测项",
+            dataIndex: "factorName",
+            key: "factorName",
+            width: '10%',
+            render: (text, record) => (
+                record.factorName + "/" + itName
+            ),
+        },
+        {
+            title: "分析时段",
+            dataIndex: "params",
+            key: "params1",
+            width: '9%',
+            render: text => text.days_Last + "个月"
+        },
+        {
+            title: "毛刺阈值",
+            dataIndex: "params",
+            key: "params2",
+            width: '9%',
+            render: text => text.thr_burr
+        },
+        {
+            title: "窗口数",
+            dataIndex: "params",
+            key: "params3",
+            width: '9%',
+            render: text => text.win_med
+        },
+        {
+            title: "回归系数",
+            dataIndex: "params",
+            key: "params4",
+            width: '9%',
+            render: text => text.win_avg
+        },
+        {
+            title: "导数阈值",
+            dataIndex: "params",
+            key: "params5",
+            width: '9%',
+            render: text => text.thr_der
+        },
+        {
+            title: "渐变点个数",
+            dataIndex: "params",
+            key: "params6",
+            width: '9%',
+            render: text => text.win_grad
+        },
+        {
+            title: "渐变阈值",
+            dataIndex: "params",
+            key: "params7",
+            width: '9%',
+            render: text => text.thr_grad
+        },
+        {
+            title: "启用状态",
+            dataIndex: "enabled",
+            key: "enabled",
+            width: '9%',
+            render: (text, record) => (
+                record.enabled ? 已启用 : 已禁用
+            )
+        },
+        {
+            title: "操作",
+            key: "action",
+            width: '9%',
+            render: (text, record) => (
+                
+                       
+                    
+                     { removeItem(record.id) }}>
+                        
+                    
+                
+            ),
+        }
+    ]
+    return (
+        
+        
+        {modalVisible ?  : ''}
+    
+    );
+}
+
+function mapStateToProps(state) {
+    const { abnParam,global } = state
+    // let isRequesting = abnParamState?.isRequesting;
+
+    return {
+        isRequesting: false,//请求状态
+        abnParam: abnParam?.data||[],
+        actions:global.actions
+    }
+}
+export default connect(mapStateToProps)(Trend)
+
diff --git a/web/client/src/sections/install/nav-item.jsx b/web/client/src/sections/install/nav-item.jsx
index ea09e55..fa4eddd 100644
--- a/web/client/src/sections/install/nav-item.jsx
+++ b/web/client/src/sections/install/nav-item.jsx
@@ -25,6 +25,15 @@ export function getNavItem (user, dispatch) {
                   items: [{
                      itemKey: 'system', to: '/install/mapping/system', text: '系统映射'
                   }]
+               },
+                {
+                  itemKey: 'data',
+                  text: '数据计算',
+                  icon: ,
+                  to: '/install/data/cacl',
+                  items: [{
+                     itemKey: 'cacl', to: '/install/data/cacl', text: '异常数据识别'
+                  }]
                },
                // {
                //    itemKey: 'order ',
diff --git a/web/client/src/sections/install/routes.js b/web/client/src/sections/install/routes.js
index e983d58..c7eae43 100644
--- a/web/client/src/sections/install/routes.js
+++ b/web/client/src/sections/install/routes.js
@@ -1,5 +1,6 @@
+
 'use strict';
-import { Roles, System, Setup, Set } from './containers';
+import { Roles, System, Setup, Set,Cacl } from './containers/index.js';
 
 export default [
    {
@@ -30,7 +31,17 @@ export default [
                   component: System,
                   breadcrumb: '系统映射',
                }]
-            }, {
+            },{
+               path: '/data',
+               key: 'data',
+               breadcrumb: '数据计算',
+               childRoutes: [{
+                  path: '/cacl',
+                  key: 'cacl',
+                  component: Cacl,
+                  breadcrumb: '异常数据识别',
+               }]
+            },  {
                path: '/order',
                key: 'order',
                breadcrumb: '工单管理',
diff --git a/web/client/src/sections/install/style.less b/web/client/src/sections/install/style.less
index aa91f88..3e338a7 100644
--- a/web/client/src/sections/install/style.less
+++ b/web/client/src/sections/install/style.less
@@ -23,4 +23,5 @@
     .separator{
         display: none;
     }
-}
\ No newline at end of file
+}
+
diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js
index 2597668..8d3f82f 100644
--- a/web/client/src/utils/webapi.js
+++ b/web/client/src/utils/webapi.js
@@ -64,7 +64,7 @@ export const ApiTable = {
    getVcmpAuth: 'vcmp/auth',         // 获取视频平台应用鉴权token
    getAlarmVideoExceptionType: 'alarm/video/exceptionType',  //查询视频设备类型
 
-   // 数据查询
+   // 数据监测
    getDataContinuityType: 'data/continuity/type_list',
    getDataContinuity: 'data/continuity',
    getDataContinuityDetail: 'data/continuity/{continuityId}/detail',
@@ -174,7 +174,21 @@ export const ApiTable = {
    //获取设备信息
    getThingMessages: 'getThingMessages',
    //下发配置(批量单个)
-   distributeConfiguration: 'distributeConfiguration'
+   distributeConfiguration: 'distributeConfiguration',
+   //监测数据相关接口
+   getProjectAllStructures: "project/allStructures",     //获取绑定项目下结构物
+   getFactors:'structure/factors' ,//结构物下的监测因素
+   getSensors:'structure/factors/sensors', //检测因素下的设备数据
+   getMonitorData:'structure/factors/sensors/data',//获取监测数据
+   //异常数据配置相关接口
+   getAbnMethods:'abnormal/methods' ,//获取计算方式
+   getAbnParamList:'struct/abnormal/params',//获取异常参数配置
+   addAbnParamList:'struct/abnormal/params',//新增异常参数配置
+   batchSwitch:'batch/switch/{ids}',//批量启用or禁用
+   deleteAbnParams: 'delete/abnormal/params/{id}',//删除配置
+   editAbnParams: 'edit/abnormal/params/{id}',//修改配置
+   getAbnTaskResult: 'struct/{structId}/abnTask/result/{start}/{end}',//
+
 };
 
 // 项企的接口
diff --git a/web/package.json b/web/package.json
index 57d43b7..ba23dff 100644
--- a/web/package.json
+++ b/web/package.json
@@ -66,6 +66,9 @@
       "file-saver": "^2.0.5",
       "fs-attachment": "^1.0.0",
       "fs-web-server-scaffold": "^1.0.6",
+      "g2": "^2.3.13",
+      "g2-plugin-slider": "^1.2.1",
+      "g2-react": "^1.3.2",
       "koa-better-http-proxy": "^0.2.5",
       "koa-proxy": "^1.0.0-alpha.3",
       "koa-view": "^2.1.4",