From c96f714e6b203d45540e587c99eed7f1a6d73c66 Mon Sep 17 00:00:00 2001 From: deartibers <947466799@qq.com> Date: Fri, 28 Oct 2022 15:41:45 +0800 Subject: [PATCH] =?UTF-8?q?em=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/sections/service/actions/emPush.js | 21 +- .../sections/service/components/pushModal.jsx | 213 ++++++--- .../sections/service/containers/emPush.jsx | 405 +++++++++++++----- web/client/src/sections/service/style.less | 10 +- web/client/src/utils/webapi.js | 1 + 5 files changed, 482 insertions(+), 168 deletions(-) diff --git a/web/client/src/sections/service/actions/emPush.js b/web/client/src/sections/service/actions/emPush.js index 52db5a4..cadbb00 100644 --- a/web/client/src/sections/service/actions/emPush.js +++ b/web/client/src/sections/service/actions/emPush.js @@ -2,10 +2,11 @@ import { ApiTable, basicAction } from '$utils' -export function getPush () { //获取推送配置列表 +export function getPush (query) { //获取推送配置列表 return dispatch => basicAction({ type: 'get', dispatch: dispatch, + query: query, actionType: 'GET_PUSH', url: `${ApiTable.getPush}`, msg: { error: '获取推送配置列表失败' }, @@ -71,4 +72,22 @@ export function getProjectStatus (query) {//获取项目状态列表 msg: { option: "获取项目状态列表" }, reducer: { name: "ProjectStatus", params: { noClear: true } }, }); +} +export function putPushPushId (data) {//更改推送配置状态(禁用或删除) + let pushId = '' + let msg = '' + if (data) { + pushId = data.pushId + msg = data.msg + } + return (dispatch) => + basicAction({ + type: "put", + dispatch: dispatch, + data, + actionType: "PUT_PUSH_PUSHID", + url: `${ApiTable.putPushPushId.replace("{pushId}", pushId)}`, + msg: { option: msg }, //更改推送配置状态(禁用或删除) + reducer: {}, + }); } \ No newline at end of file diff --git a/web/client/src/sections/service/components/pushModal.jsx b/web/client/src/sections/service/components/pushModal.jsx index 6cf391c..de9199b 100644 --- a/web/client/src/sections/service/components/pushModal.jsx +++ b/web/client/src/sections/service/components/pushModal.jsx @@ -19,10 +19,6 @@ function pushModal (props) { } = props; const { service } = actions; const form = useRef();//表单 - const [disablePeople, setDisablePeople] = useState(true); //页码信息 - const [peopleList, setPeopleList] = useState([]); //人员List - const [departmentId, setDepartmentId] = useState(); //部门id - const [peopleId, setPeopleId] = useState(); //人员id const [abnormal, setAbnormal] = useState(false); //异常率推送机制disable const [usersList, setUsersList] = useState([]); //获取全部未删除用户 const [structure, setStructure] = useState(true); //结构物disable @@ -30,27 +26,38 @@ function pushModal (props) { const [projectStructure, setProjectStructure] = useState([]); //获取绑定项目下结构物 const [timeTypeDis, setTimeTypeDis] = useState(true); //通知时效disable const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 + const timeTypePOMS = useRef([]);//表单 + const [interval1, setInterval1] = useState(undefined); // + const [interval2, setInterval2] = useState(undefined); // + const [interval3, setInterval3] = useState(undefined); // + const [deviceProportion, setDeviceProportion] = useState(undefined); // //初始化 useEffect(() => { - // if (editObj.id) { - // let departmentList = [] - // for (let i = 0; i < pepList.length; i++) { - // if (pepList[i].id == editObj.departments[0].id) { - // departmentList = pepList[i].users - // } - // } - // setPeopleList(departmentList) - // setDepartmentId(editObj.departments[0].id) - // setPeopleId(editObj.pepUserId) - // setDisablePeople(false) - // } getOrganizationUsersList()//获取全部未删除用户 getProjectPomsList()//获取已绑定项目 + if (editObj.id) { + getProjectStructureList(editObj.pomsProjectId) + if (editObj.pomsProject?.pepProjectId) { + getProjectStatusList()//获取项目状态列表 + } + else { + setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }]) + timeTypePOMS.current = ['POMS'] + } + if (editObj.tactics == 'immediately') { + setInterval1(editObj.tacticsParams?.interval) + } else if (editObj.tactics == 'continue') { + setInterval2(editObj.tacticsParams?.interval) + } else if (editObj.tactics == 'abnormal_rate') { + setInterval3(editObj.tacticsParams?.interval) + setDeviceProportion(editObj.tacticsParams?.deviceProportion) + } + } }, []); function getOrganizationUsersList () {//获取全部未删除用户 dispatch(service.getOrganizationUsers()).then((res) => { @@ -75,7 +82,7 @@ function pushModal (props) { } setProjectStructure(res.payload?.data) form.current.setValue('strucId', mylist) - form.current.validate(['strucId','timeType']) + form.current.validate(['strucId', 'timeType']) setStructure(false) setTimeTypeDis(false) } @@ -90,7 +97,7 @@ function pushModal (props) { mylist.push(res.payload?.data[i].id) } form.current.setValue('timeType', mylist) - form.current.validate(['strucId','timeType']) + form.current.validate(['strucId', 'timeType']) } }) } @@ -100,7 +107,124 @@ function pushModal (props) { .validate() .then((values) => { if (pushEdit) { - dispatch(service.postPush({ pushId: pushId, pepUserId: values.pepUserId, msg: '编辑推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 + let obj = JSON.parse(JSON.stringify(values)) + if (obj.timeType[0] == 'POMS') { + obj.timeType = [] + } + let regu = /^[0-9]*[1-9][0-9]*$/; + if (obj.tactics == 'immediately') { + if (obj.interval1) { + if (regu.test(obj.interval1)) { + if (obj.interval1 <= 1440) { + obj.tacticsParams = { + interval: obj.interval1 + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else { + Notification.error({ + content: '即时推送时间不能大于1440分钟', + duration: 2, + }) + } + } else { + Notification.error({ + content: '即时推送时间应为正整数', + duration: 2, + }) + } + } else { + Notification.error({ + content: '即时推送时间应为正整数', + duration: 2, + }) + } + } + else if (obj.tactics == 'continue') { + if (obj.interval2) { + if (regu.test(obj.interval2)) { + if (obj.interval2 <= 1440) { + obj.tacticsParams = { + interval: obj.interval2 + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else { + Notification.error({ + content: '持续时长推送时间不能大于1440分钟', + duration: 2, + }) + } + } else { + Notification.error({ + content: '持续时长推送时间应为正整数', + duration: 2, + }) + } + } else { + Notification.error({ + content: '持续时长推送时间应为正整数', + duration: 2, + }) + } + } + else { + if (regu.test(obj.interval3) && regu.test(obj.deviceProportion)) { + if (obj.interval3 <= 720 && obj.deviceProportion <= 100) { + obj.tacticsParams = { + interval: obj.interval3, + deviceProportion: obj.deviceProportion + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else if (obj.interval3 <= 720 && obj.deviceProportion > 100) { + Notification.error({ + content: '异常率推送异常率不能超过100%', + duration: 2, + }) + } else if (obj.interval3 > 720 && obj.deviceProportion <= 100) { + Notification.error({ + content: '异常率推送时间不能超过720小时', + duration: 2, + }) + } else { + Notification.error({ + content: '异常率推送时间不能超过720小时', + duration: 2, + }) + Notification.error({ + content: '异常率推送异常率不能超过100%', + duration: 2, + }) + } + } else if (regu.test(obj.interval3) && !regu.test(obj.deviceProportion)) { + Notification.error({ + content: '异常率推送异常率应为正整数', + duration: 2, + }) + } else if (!regu.test(obj.interval3) && regu.test(obj.deviceProportion)) { + Notification.error({ + content: '异常率推送时间应为正整数', + duration: 2, + }) + } else { + Notification.error({ + content: '异常率推送异常率应为正整数', + duration: 2, + }) + Notification.error({ + content: '异常率推送时间应为正整数', + duration: 2, + }) + } + } + dispatch(service.postPush({ pushId: pushId, ...obj, msg: '编辑推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 if (res.success) { close(); } @@ -108,7 +232,7 @@ function pushModal (props) { } else { let obj = JSON.parse(JSON.stringify(values)) - if (obj.timeType[0] == null) { + if (obj.timeType[0] == 'POMS') { obj.timeType = [] } let regu = /^[0-9]*[1-9][0-9]*$/; @@ -224,7 +348,6 @@ function pushModal (props) { }) } } - console.log('obj', obj); dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 if (res.success) { close(); @@ -274,35 +397,14 @@ function pushModal (props) { getProjectStatusList()//获取项目状态列表 } else { - setProjectStatus([{ construction_status: 'POMS', id: null }]) - form.current.setValue('timeType', [null]) + setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }]) + form.current.setValue('timeType', ['POMS']) form.current.validate() } } } } } - - // for (var key in field) { - // if (key == 'department') { - // if (values.department >= 0) { - // let departmentList = [] - // for (let i = 0; i < pepList.length; i++) { - // if (pepList[i].id == values.department) { - // departmentList = pepList[i].users - // } - // } - // setPeopleList(departmentList) - // setDisablePeople(false) - // form.current.setValue('pepUserId', undefined); - // } - // else { - // setPeopleList([]) - // setDisablePeople(true) - // form.current.setValue('pepUserId', undefined); - // } - // } - // } }} getFormApi={(formApi) => (form.current = formApi)} > @@ -314,7 +416,7 @@ function pushModal (props) { field="name" label='策略名称:' style={{ width: 695 }} - // initValue={editObj?.name || ""} + initValue={editObj?.name || ""} placeholder="请输入策略名称" showClear rules={[{ required: true, message: "请输入策略名称" }]} /> @@ -326,7 +428,7 @@ function pushModal (props) { placeholder="请选择项目" style={{ width: 695 }} rules={[{ required: true, message: "请选择项目" }]} - // initValue={departmentId || ""} + initValue={editObj?.pomsProjectId || ""} filter > { @@ -347,7 +449,7 @@ function pushModal (props) { placeholder="请选择结构物" style={{ width: 695 }} rules={[{ required: true, message: "请选择结构物" }]} - // initValue={departmentId || ""} + initValue={editObj?.strucId || []} disabled={structure} filter multiple @@ -371,6 +473,7 @@ function pushModal (props) { label='推送策略配置:' type='card' direction='horizontal' + initValue={editObj?.tactics || ''} rules={[{ required: true, message: '请选择推送策略' }]}> 分钟内,有告警源新增,则通过【信鸽服务】发送一条通知信息。 @@ -399,7 +502,7 @@ function pushModal (props) { field="interval2" pure style={{ width: 60, height: 20, color: '#1859C1' }} - initValue={editObj?.interval || "10"} + initValue={interval2 || "10"} // rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "请输入正整数" }]} /> 分钟,则通过【信鸽服务】发送一条通知信息。 @@ -417,7 +520,7 @@ function pushModal (props) { field="deviceProportion" pure style={{ width: 60, height: 20, color: '#1859C1' }} - initValue={editObj?.deviceProportion || "40"} + initValue={deviceProportion || "40"} // rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "请输入正整数" }]} /> %,且持续时长超过 @@ -425,7 +528,7 @@ function pushModal (props) { field="interval3" pure style={{ width: 60, height: 20, color: '#1859C1' }} - initValue={editObj?.interval || "2"} + initValue={interval3 || "2"} // rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "请输入正整数" }]} /> 小时,则通过【信鸽服务】发送一条通知信息。 @@ -442,7 +545,7 @@ function pushModal (props) { field="alarmType" style={{ width: 695 }} rules={[{ required: true, message: "监听问题模块" }]} - // initValue={departmentId || ""} + initValue={editObj?.alarmType || []} direction='horizontal' showClear > @@ -464,7 +567,7 @@ function pushModal (props) { placeholder="请选择通知时效" style={{ width: 285 }} rules={[{ required: true, message: "请选择通知时效" }]} - // initValue={departmentId || ""} + initValue={editObj?.timeType.length > 0 ? editObj?.timeType : timeTypePOMS.current} disabled={timeTypeDis} multiple maxTagCount={3} @@ -485,7 +588,7 @@ function pushModal (props) { placeholder="请选择接收人" style={{ width: 285 }} rules={[{ required: true, message: "请选择接收人" }]} - initValue={[user.id]} + initValue={editObj?.receiverPepUserId || [user.id]} filter multiple maxTagCount={3} @@ -511,7 +614,7 @@ function pushModal (props) { pure direction='horizontal' style={{ display: 'flex', justifyContent: 'space-evenly' }} - initValue={false || false} + initValue={editObj?.disable || false} rules={[{ required: true, }]}> 启用 diff --git a/web/client/src/sections/service/containers/emPush.jsx b/web/client/src/sections/service/containers/emPush.jsx index b988ae8..3772b80 100644 --- a/web/client/src/sections/service/containers/emPush.jsx +++ b/web/client/src/sections/service/containers/emPush.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { connect } from 'react-redux'; import { Skeleton, Button, Pagination, Form, Popconfirm, Table, Tooltip } from '@douyinfe/semi-ui'; +import { IconSearch } from '@douyinfe/semi-icons'; import { SkeletonScreen, } from "$components"; import moment from "moment"; import PushModal from '../components/pushModal' @@ -30,6 +31,7 @@ const EmPush = (props) => { const [appArr, setAppArr] = useState([]) //修改时添加应用 const [bindId, setBindId] = useState() //修改时绑定的id const [tableKey, setTableKey] = useState([]) //修改时绑定的id + const [editObj, setEditObj] = useState({});//管理员弹框修改内容 const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 const page = useRef(query.page);//哪一页 const EMPUSH = "empush"; @@ -37,9 +39,9 @@ const EmPush = (props) => { { title: '推送信息', list: [ + { name: "关联项目", value: "projectName" }, { name: "策略名称", value: "name" }, { name: "创建时间", value: "createTime" }, - { name: "关联项目", value: "projectName" }, { name: "接收人", value: "receiverPepUser" }, { name: "推送方式", value: "pushType" }, { name: "监听问题模块", value: "alarmType" }, @@ -77,7 +79,6 @@ const EmPush = (props) => { return {}; } } - const [tableData, setTableData] = useState([]) //表格数据 useEffect(() => { @@ -91,31 +92,28 @@ const EmPush = (props) => { // dispatch(install.getProjectAppList(query)).then((res) => {//获取应用列表 // setAppList(res.payload.data) // }) - getProjectStatusList() localStorage.getItem(EMPUSH) == null ? localStorage.setItem( EMPUSH, - JSON.stringify(['name', 'createTime', 'projectName', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable']) + JSON.stringify(['projectName', 'name', 'createTime', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable']) ) : ""; - attribute(); + getProjectStatusList() }, []) useEffect(() => { getPushList(); }, [query]); function getPushList () { - dispatch(service.getPush(query)).then((res) => {//获取已绑定项目 + let val = form.current.getValues() + // , ...query + dispatch(service.getPush({ ...val })).then((res) => {//获取已绑定项目 if (res.success) { - // let mytableData = JSON.parse(JSON.stringify(res.payload.data.rows)); - // let mytableKey = [] - // for (let index = 0; index < mytableData.length; index++) { - // mytableData[index].key = mytableData[index].id - // mytableKey.push(mytableData[index].id) - // } - // setTableKey(mytableKey) - // setTableData(mytableData) - setTableData(res.payload.data) + let mytableData = JSON.parse(JSON.stringify(res.payload.data)); + for (let index = 0; index < mytableData.length; index++) { + mytableData[index].key = String(mytableData[index].id) + } + setTableData(mytableData) setLimits(res.payload.data.length) mylimits.current = res.payload.data.length } @@ -125,13 +123,14 @@ const EmPush = (props) => { dispatch(service.getProjectStatus()).then((res) => { if (res.success) { setProjectStatus(res.payload?.data) + attribute(res.payload?.data); } }) } - const [columns, setColumns] = useState([//表格属性 + const columns = [//表格属性 { title: "操作", - width: "20%", + width: "12%", dataIndex: "text", key: 'text', render: (_, row) => { @@ -139,55 +138,82 @@ const EmPush = (props) => {
- + {row?.disable ? ( + + ) : ( + { + dispatch(service.putPushPushId({ pushId: row?.id, del: false, disable: true, msg: '更改推送配置状态' })).then(() => { + // setQuery({ limit: 10, page: page.current }) + }) + }} + > + + + )} { - // dispatch(install.deleteProjectBind({ bindId: row?.id, msg: '删除安心云、项目管理项目绑定关系' })).then(() => { - // if (page.current > 0 && mylimits.current < 2) { - // setQuery({ limit: 10, page: page.current - 1 }) - // } else { - // setQuery({ limit: 10, page: page.current }) - // } - // }) + dispatch(service.putPushPushId({ pushId: row?.id, del: true, disable: false, msg: '删除推送配置' })).then(() => { + // if (page.current > 0 && mylimits.current < 2) { + // setQuery({ limit: 10, page: page.current - 1 }) + // } else { + // setQuery({ limit: 10, page: page.current }) + // } + }) }} > @@ -196,30 +222,29 @@ const EmPush = (props) => { ); }, }, - ]) + ] + function expandRowRender (record, index) { + return ( +
+ 结构物: + { + record.structure?.map((item, index) => { + return ( +
+ {item.name} +
+ ) + }) + } +
+ ) + } //获取表格属性设置 - function attribute () { + function attribute (val) { const arr = localStorage.getItem(EMPUSH) ? JSON.parse(localStorage.getItem(EMPUSH)) : []; - const column = [ - { - title: '策略名称', - dataIndex: "name", - key: 'name', - render: (_, row) => { - return row.name - } - }, - { - title: "创建时间", - dataIndex: "createTime", - key: "createTime", - render: (_, r, index) => { - return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss'); - }, - }, { title: '关联项目', dataIndex: "projectName", @@ -250,10 +275,63 @@ const EmPush = (props) => {
} + { + row.pomsProject?.pepProject?.projectName ? ( +
+
+ +
+
+ { + val.map((ite, idx) => { + return ( +
+ {ite.id == row.pomsProject?.pepProject.constructionStatusId ? ite.construction_status : ''} +
+ ) + }) + } +
+
+ ) : ( +
+
+ +
+
+ POMS +
+
+ ) + } ) } }, + { + title: '策略名称', + dataIndex: "name", + key: 'name', + render: (_, row) => { + return row.name + } + }, + { + title: "创建时间", + dataIndex: "createTime", + key: "createTime", + render: (_, r, index) => { + return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss'); + }, + }, { title: '接收人', dataIndex: "receiverPepUser", @@ -280,7 +358,7 @@ const EmPush = (props) => { { row.receiverPepUser.map((item, index) => { return ( -
+
{item.name},
) @@ -314,7 +392,6 @@ const EmPush = (props) => { return (
{ - // alarmTypeObj row.alarmType.map((item, index) => { return (
@@ -355,9 +432,81 @@ const EmPush = (props) => { title: "生效项目节点", dataIndex: "timeType", key: "timeType", - render: (_, r, index) => { - // projectStatus - return r.timeType[0] + render: (_, row, index) => { + return ( +
+ { + row.timeType.length > 0 ? ( + row.timeType.map((item, index) => { + return ( +
1 ? 'none' : 'flex', alignItems: 'center' + }}> +
+ +
+
+ { + val.map((ite, idx) => { + return ( +
+ {ite.id == item ? ite.construction_status : ''} +
+ ) + }) + } +
+
+ ) + }) + ) : ( +
+
+ +
+
+ POMS +
+
+ ) + } + { + row.timeType.length > 2 ? ( + + { + row.timeType.map((item, index) => { + return ( +
+ { + val.map((ite, idx) => { + return ( + + {ite.id == item ? ite.construction_status : ''} + + ) + }) + }, +
+ ) + }) + } +
+ } trigger="click" style={{ lineHeight: 2 }}> +
+ +{row.timeType.length - 2} +
+ + ) : ('') + } +
+ ) }, }, { @@ -372,9 +521,31 @@ const EmPush = (props) => { title: "启用状态", dataIndex: "disable", key: "disable", - render: (_, r, index) => { - // projectStatus - return r.disable + render: (_, row, index) => { + let enableType = '' + if (row.disable) { + enableType = '禁用' + } + else { + if (row.timeType.length > 0) { + for (let i = 0; i < row.timeType.length; i++) { + if (row.timeType[i] == row.pomsProject?.pepProject?.constructionStatusId) { + enableType = '已生效' + break; + } else { + enableType = '未生效' + } + } + } + else { + enableType = '已生效' + } + } + return ( +
+ {enableType} +
+ ) }, }, { @@ -382,7 +553,7 @@ const EmPush = (props) => { dataIndex: "pushCount", key: "pushCount", render: (_, r, index) => { - return r.pushCount + return r.pushCount + '次' }, }, ]; @@ -403,7 +574,7 @@ const EmPush = (props) => {
EM推送
Em push
-
+
console.log(values)} // onValueChange={values=>console.log(values)} @@ -411,47 +582,53 @@ const EmPush = (props) => { layout="horizontal" style={{ position: "relative", width: "100%", flex: 1 }} > + + 项目 + 结构物 + 策略名 + } + field="keyword" + pure + showClear + style={{ width: 260, marginLeft: 12, marginRight: 12 }} + placeholder="请输入或选择关键词" /> - {/* {.map((item) => { - return ( - - {item.name} - - ); - })} */} + 数据中断 + 数据异常 + 策略命中 + 视频异常 + 应用异常 + 设备异常 - {/* {.map((item) => { - return ( - - {item.name} - - ); - })} */} + 已生效 + 未生效 + 禁用 @@ -502,10 +681,13 @@ const EmPush = (props) => { placeholder={SkeletonScreen()} > s)} dataSource={tableData} bordered={false} + hideExpandedColumn={false} empty="暂无数据" + expandedRowRender={expandRowRender} pagination={false} onRow={handleRow} /> @@ -543,6 +725,7 @@ const EmPush = (props) => { { setPushModal(false); }} @@ -558,7 +741,7 @@ const EmPush = (props) => { tableList={tableList} close={() => { setSetup(false); - attribute(); + attribute(projectStatus); }} /> ) : ( diff --git a/web/client/src/sections/service/style.less b/web/client/src/sections/service/style.less index b98d242..7f2491d 100644 --- a/web/client/src/sections/service/style.less +++ b/web/client/src/sections/service/style.less @@ -1,5 +1,13 @@ -.empush{ +.myempush{ .semi-input-wrapper{ margin-bottom: 0px !important; } +} +.emPushTable{ + // .semi-table-row-cell:first-child{ + // display: flex !important; + // } + // .semi-table-row-cell{ + // display: flex !important; + // } } \ No newline at end of file diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index 36b3e65..e420840 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -54,6 +54,7 @@ export const ApiTable = { getOrganizationUsers: "organization/users", //获取全部未删除用户 getProjectStructure: "project/structure", //获取绑定项目下结构物 getProjectStatus: "project/status", //获取项目状态列表 + putPushPushId: "push/{pushId}", //更改推送配置状态(禁用或删除) //控制台 consoleToollink: 'console/toollink', //常用工具