custom_components.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. <template>
  2. <el-dialog v-model="isShow" width="1100px" :before-close="handleClose">
  3. <el-form
  4. ref="ruleFormRef"
  5. :model="form"
  6. :rules="rules"
  7. label-position="top"
  8. label-width="200px"
  9. class="custom-comp-form"
  10. >
  11. <el-row
  12. type="flex"
  13. justify="space-between"
  14. align="middle"
  15. :gutter="10"
  16. class="first-row"
  17. >
  18. <el-col :span="15" class="first-row-first-col">
  19. <el-form-item prop="name">
  20. <el-tag size="small">规则名称</el-tag>
  21. <el-input v-model="form.name" />
  22. </el-form-item>
  23. </el-col>
  24. <el-col :span="9" class="first-row-second-col">
  25. <el-form-item prop="category">
  26. <el-tag size="small">报警类别</el-tag>
  27. <el-select v-model="form.category" class="select-mini">
  28. <el-option
  29. key="windturbine"
  30. label="风机"
  31. value="windturbine"
  32. ></el-option>
  33. <el-option
  34. key="inverter"
  35. label="逆变器"
  36. value="inverter"
  37. ></el-option>
  38. <el-option
  39. key="booststation"
  40. label="升压站"
  41. value="booststation"
  42. ></el-option>
  43. </el-select>
  44. </el-form-item>
  45. <el-form-item prop="rank">
  46. <el-tag size="small">报警级别</el-tag>
  47. <el-select v-model="form.rank" class="select-mini">
  48. <el-option key="1" label="低级" value="1" />
  49. <el-option key="2" label="低中级" value="2" />
  50. <el-option key="3" label="中级" value="3" />
  51. <el-option key="4" label="中高级" value="4" />
  52. <el-option key="5" label="高级" value="5" />
  53. </el-select>
  54. </el-form-item>
  55. <el-form-item prop="enabled">
  56. <el-tag size="small">是否启用</el-tag>
  57. <el-switch
  58. v-model="form.enabled"
  59. :active-value="true"
  60. :inactive-value="false"
  61. active-color="#13ce66"
  62. />
  63. </el-form-item>
  64. </el-col>
  65. </el-row>
  66. <el-row :gutter="10" class="second-row">
  67. <el-col :span="6">
  68. <el-form-item prop="stationId">
  69. <el-tag size="small">风场</el-tag>
  70. <el-select
  71. v-model="form.stationId"
  72. style="width: 100%"
  73. @change="stationChange"
  74. >
  75. <el-option
  76. v-for="item in stationList"
  77. :key="item.id"
  78. :value="item.id"
  79. :label="item.name"
  80. />
  81. </el-select>
  82. </el-form-item>
  83. <el-form-item prop="modelId">
  84. <el-tag size="small">风机型号</el-tag>
  85. <el-select
  86. v-model="form.modelId"
  87. style="width: 100%"
  88. @change="modelIdChange"
  89. >
  90. <el-option
  91. v-for="item in state.modelList"
  92. :key="item"
  93. :value="item"
  94. :label="item"
  95. />
  96. </el-select>
  97. </el-form-item>
  98. <el-form-item prop="relatedParts">
  99. <el-tag size="small">所属部件</el-tag>
  100. <el-select v-model="form.relatedParts" style="width: 100%">
  101. <el-option
  102. v-for="i in state.relatePartList"
  103. :key="i.partCode"
  104. :value="i.partCode"
  105. :label="i.name"
  106. />
  107. </el-select>
  108. </el-form-item>
  109. <el-form-item prop="fault">
  110. <el-tag size="small">预警类型</el-tag>
  111. <el-select v-model="form.project" style="width: 100%">
  112. <el-option
  113. v-for="i in faultList"
  114. :key="i.value"
  115. :value="i.value"
  116. :label="i.name"
  117. />
  118. </el-select>
  119. </el-form-item>
  120. </el-col>
  121. <el-col :span="9">
  122. <el-form-item prop="expression">
  123. <el-tag size="small">表达式</el-tag>
  124. <el-input
  125. type="textarea"
  126. rows="14"
  127. v-model="form.expression"
  128. :value="form.expression"
  129. id="expressionInput"
  130. />
  131. </el-form-item>
  132. </el-col>
  133. <el-col :span="9">
  134. <el-tabs type="border-card">
  135. <el-tab-pane label="AI测点">
  136. <el-input v-model="state.AIPointSearch"> </el-input>
  137. <el-table
  138. size="mini"
  139. fit
  140. :show-header="false"
  141. stripe
  142. height="204"
  143. :data="filterAIList"
  144. @row-dblclick="rowDbclick"
  145. >
  146. <el-table-column prop="uniformCode" />
  147. <el-table-column prop="name" />
  148. </el-table>
  149. </el-tab-pane>
  150. <el-tab-pane label="DI测点">
  151. <el-input v-model="state.DIPointSearch"> </el-input>
  152. <el-table
  153. size="mini"
  154. fit
  155. :show-header="false"
  156. stripe
  157. height="204"
  158. :data="filterDIList"
  159. @row-dblclick="rowDbclick"
  160. >
  161. <el-table-column prop="uniformCode" />
  162. <el-table-column prop="name" />
  163. </el-table>
  164. </el-tab-pane>
  165. <el-tab-pane label="函数">
  166. <el-table
  167. size="mini"
  168. fit
  169. :show-header="false"
  170. stripe
  171. height="240"
  172. :data="func"
  173. @row-dblclick="tabFuncRowClickHandle"
  174. >
  175. <el-table-column min-width="60%">
  176. <template #default="scope">
  177. <el-popover trigger="hover" placement="bottom">
  178. <p>描述:{{ scope.row.describe }}</p>
  179. <p>参数:{{ scope.row.param }}</p>
  180. <template #reference>
  181. <span size="medium" transition="fade-in-linear">{{
  182. scope.row.lab
  183. }}</span>
  184. </template>
  185. </el-popover>
  186. </template>
  187. </el-table-column>
  188. <el-table-column min-width="40%">
  189. <template #default="scope">
  190. <el-popover trigger="hover" placement="bottom">
  191. <p>描述:{{ scope.row.describe }}</p>
  192. <p>参数:{{ scope.row.param }}</p>
  193. <template #reference>
  194. <span size="medium" transition="fade-in-linear">{{
  195. scope.row.name
  196. }}</span>
  197. </template>
  198. </el-popover>
  199. </template>
  200. </el-table-column>
  201. </el-table>
  202. </el-tab-pane>
  203. <el-tab-pane label="运算符">
  204. <div class="operator">
  205. <el-button
  206. v-for="item in operator"
  207. :key="item"
  208. size="mini"
  209. class="buttons"
  210. @click="elInputSplit(item)"
  211. >
  212. {{ item }}
  213. </el-button>
  214. </div>
  215. </el-tab-pane>
  216. </el-tabs>
  217. </el-col>
  218. </el-row>
  219. <el-row :gutter="24">
  220. <el-col :span="24">
  221. <el-form-item prop="descriptionShow">
  222. <el-tag size="small">规则描述</el-tag>
  223. <el-input type="textarea" rows="4" v-model="form.descriptionShow" />
  224. </el-form-item>
  225. </el-col>
  226. </el-row>
  227. </el-form>
  228. <template #footer>
  229. <span class="footerButton">
  230. <el-button round size="mini" @click="closeDialog">取 消</el-button>
  231. <el-button round size="mini" @click="submitForm(ruleFormRef)"
  232. >确 定</el-button
  233. >
  234. </span>
  235. </template>
  236. </el-dialog>
  237. </template>
  238. <script setup>
  239. import { ref, onMounted, reactive, computed, watch, nextTick } from "vue";
  240. import { ElMessageBox, ElMessage } from "element-plus";
  241. import {
  242. // fetchAIPointList,
  243. // fetchDIPointList,
  244. // fetchRelatePart,
  245. custombj_postSave,
  246. fetch_electrical_point_ai,
  247. fetch_electrical_point_di,
  248. getWtModel,
  249. fetchRelatePartAndAlarmType,
  250. } from "@/api/zhbj/index.js";
  251. import { useStore } from "vuex";
  252. const store = useStore();
  253. const stationList = computed(() => store.state.stationList);
  254. onMounted(() => {
  255. getfetchRelatePart();
  256. });
  257. watch(
  258. () => props.isVisible,
  259. (val, old) => {
  260. isShow.value = val;
  261. },
  262. {
  263. deep: true,
  264. }
  265. );
  266. watch(
  267. () => props.form?.id,
  268. (val, old) => {
  269. if (val != "") {
  270. nextTick(async () => {
  271. getfetchAIPointList();
  272. getfetchDIPointList();
  273. });
  274. }
  275. },
  276. {
  277. deep: true,
  278. }
  279. );
  280. watch(
  281. () => props.form,
  282. (val, old) => {
  283. nextTick(() => {
  284. form.value = val;
  285. });
  286. },
  287. {
  288. deep: true,
  289. }
  290. );
  291. const isShow = ref(false);
  292. const form = ref({
  293. id: "",
  294. name: "",
  295. descriptionShow: "",
  296. expression: "",
  297. tag: "",
  298. rank: "",
  299. enabled: "1", // 1可用-0禁用
  300. modelId: "",
  301. ednaValue: "",
  302. category: "",
  303. range: 0,
  304. station: "",
  305. windturbine: "",
  306. line: "",
  307. project: "",
  308. electrical: "",
  309. taskstart: "",
  310. relatedParts: "",
  311. userName: "",
  312. });
  313. const emits = defineEmits(["close"]);
  314. const props = defineProps({
  315. isVisible: {
  316. type: Boolean,
  317. defaule: false,
  318. },
  319. form: {
  320. type: Object,
  321. },
  322. });
  323. const toEmits = () => {
  324. emits("close"); // 向父组件传递数据
  325. };
  326. const state = reactive({
  327. relatePartList: [],
  328. modelList: [],
  329. AIPointList: [],
  330. DIPointList: [],
  331. AIPointSearch: "",
  332. DIPointSearch: "",
  333. });
  334. const operator = [
  335. "+",
  336. "-",
  337. "*",
  338. "/",
  339. "(",
  340. ")",
  341. ">",
  342. ">=",
  343. "<",
  344. "<=",
  345. "==",
  346. "!=",
  347. "&&",
  348. "||",
  349. "!",
  350. "%",
  351. "true",
  352. "false",
  353. ".",
  354. ];
  355. const func = [
  356. {
  357. lab: "MR",
  358. name: "移动极差",
  359. param: "测点名,时间(秒)",
  360. describe: "是指两个或多个连续样本值中最大值与最小值之差",
  361. scene: "测点的移动极差超限报警",
  362. },
  363. {
  364. lab: "MAR",
  365. name: "均值极差",
  366. param: "测点名,时间(秒)",
  367. describe: "",
  368. scene: "测点的均值极差计算",
  369. },
  370. {
  371. lab: "RiseExceed",
  372. name: "上升趋势",
  373. param: "测点名,时间(秒),阈值",
  374. describe: "取测点在给定的时间范围内数据上升的量是否超过阈值",
  375. scene: "测点值的上升速度过快等",
  376. },
  377. {
  378. lab: "Sustain",
  379. name: "持续时间",
  380. param: "表达式,时间(秒)",
  381. describe:
  382. "判定状态(表达式成立)持续的时间是否超过给定的时间判断状态持续的时间",
  383. scene: "",
  384. },
  385. {
  386. lab: "LastUpdateTime",
  387. name: "最近数据时间",
  388. param: "测点名",
  389. describe: "",
  390. scene: "判定离线,状态持续时间等",
  391. },
  392. {
  393. lab: "abs",
  394. name: "取绝对值",
  395. param: "double a",
  396. describe: "",
  397. scene: "",
  398. },
  399. {
  400. lab: "acos",
  401. name: "反余弦",
  402. param: "double a",
  403. describe: "",
  404. scene: "",
  405. },
  406. {
  407. lab: "asin",
  408. name: "反正弦",
  409. param: "double a",
  410. describe: "",
  411. scene: "",
  412. },
  413. {
  414. lab: "atan",
  415. name: "反正切",
  416. param: "double a",
  417. describe: "",
  418. scene: "",
  419. },
  420. {
  421. lab: "atan2",
  422. name: "xy坐标转为极坐标",
  423. param: "x,y",
  424. describe: "",
  425. scene: "",
  426. },
  427. {
  428. lab: "ceiling",
  429. name: "向上取整",
  430. param: "double a",
  431. describe: "",
  432. scene: "",
  433. },
  434. {
  435. lab: "cos",
  436. name: "余弦",
  437. param: "double a",
  438. describe: "",
  439. scene: "",
  440. },
  441. {
  442. lab: "cosh",
  443. name: "双曲线余弦",
  444. param: "double a",
  445. describe: "",
  446. scene: "",
  447. },
  448. {
  449. lab: "exp",
  450. name: "欧拉数 e 的 double 次幂的值",
  451. param: "double a",
  452. describe: "",
  453. scene: "",
  454. },
  455. {
  456. lab: "floor",
  457. name: "向下取整",
  458. param: "double a",
  459. describe: "",
  460. scene: "",
  461. },
  462. {
  463. lab: "log",
  464. name: "自然对数",
  465. param: "double a",
  466. describe: "",
  467. scene: "",
  468. },
  469. {
  470. lab: "log10",
  471. name: "底数为 10 的对数",
  472. param: "double a",
  473. describe: "",
  474. scene: "",
  475. },
  476. {
  477. lab: "max",
  478. name: "比较最大值",
  479. param: "double a, double b",
  480. describe: "",
  481. scene: "",
  482. },
  483. {
  484. lab: "min",
  485. name: "比较最小值",
  486. param: "double a, double b",
  487. describe: "",
  488. scene: "",
  489. },
  490. {
  491. lab: "pow",
  492. name: "返回第一个参数的第二个参数次幂的值",
  493. param: "double a, double b",
  494. describe: "",
  495. scene: "",
  496. },
  497. {
  498. lab: "round",
  499. name: "返回最接近参数的 long,或int",
  500. param: "double a",
  501. describe: "",
  502. scene: "",
  503. },
  504. {
  505. lab: "sign",
  506. name: "负数返回-1.0,整数返回1.0,0返回0.0",
  507. param: "float f/double a",
  508. describe: "",
  509. scene: "",
  510. },
  511. {
  512. lab: "sin",
  513. name: "三角正弦值",
  514. param: "double a",
  515. describe: "",
  516. scene: "",
  517. },
  518. {
  519. lab: "sinh",
  520. name: "双曲线正弦",
  521. param: "double x",
  522. describe: "",
  523. scene: "",
  524. },
  525. {
  526. lab: "sqrt",
  527. name: "正平方根",
  528. param: "double a",
  529. describe: "",
  530. scene: "",
  531. },
  532. {
  533. lab: "tan",
  534. name: "正切",
  535. param: "double a",
  536. describe: "",
  537. scene: "",
  538. },
  539. {
  540. lab: "tanh",
  541. name: "双曲线正切",
  542. param: "double x",
  543. describe: "",
  544. scene: "",
  545. },
  546. { lab: "PI", name: "圆周率", param: "", describe: "", scene: "" },
  547. { lab: "E", name: "自然对数", param: "", describe: "", scene: "" },
  548. ];
  549. const faultList = [
  550. {
  551. value: "sensors",
  552. name: "传感器异常",
  553. },
  554. {
  555. value: "parts",
  556. name: "零部件隐患",
  557. },
  558. {
  559. value: "control",
  560. name: "控制参数优化",
  561. },
  562. {
  563. value: "performance",
  564. name: "性能下降",
  565. },
  566. ];
  567. const ruleFormRef = ref(null);
  568. const rules = reactive({
  569. name: [{ required: true, message: "请输入规则名称", trigger: "blur" }],
  570. category: [{ required: true, message: "请选择报警类别", trigger: "change" }],
  571. rank: [{ required: true, message: "请选择报警级别", trigger: "change" }],
  572. stationId: [{ required: true, message: "请选择风场", trigger: "change" }],
  573. expression: [
  574. { required: true, message: "表达式不能为空", trigger: "change" },
  575. ],
  576. });
  577. //stationChange
  578. const stationChange = async () => {
  579. form.value.modelId = "";
  580. getequipmentmodel_list();
  581. };
  582. //modelIdChange
  583. const modelIdChange = async () => {
  584. getfetchAIPointList();
  585. getfetchDIPointList();
  586. };
  587. //机型
  588. const getequipmentmodel_list = async () => {
  589. const { data } = await getWtModel(form.value.stationId);
  590. state.modelList = data;
  591. };
  592. //所属部件
  593. const getfetchRelatePart = async () => {
  594. const res = await fetchRelatePartAndAlarmType();
  595. state.relatePartList = res;
  596. };
  597. // 查询风场AI、DI测点
  598. const getfetchAIPointList = async () => {
  599. // const res = await fetchAIPointList(form.value.station, form.value.modelId);
  600. // state.AIPointList = res.sort(function (a, b) {
  601. // return a.uniformCode - b.uniformCode;
  602. // });
  603. };
  604. const filterAIList = computed(() =>
  605. state.AIPointList?.filter(
  606. (data) =>
  607. !state.AIPointSearch ||
  608. data.uniformCode.includes(state.AIPointSearch) ||
  609. data.name.includes(state.AIPointSearch)
  610. )
  611. );
  612. const filterDIList = computed(() =>
  613. state.DIPointList?.filter(
  614. (data) =>
  615. !state.DIPointSearch ||
  616. data.uniformCode.includes(state.DIPointSearch) ||
  617. data.name.includes(state.DIPointSearch)
  618. )
  619. );
  620. const getfetchDIPointList = async () => {
  621. const res = await fetchDIPointList(form.value.station, form.value.modelId);
  622. state.DIPointList = res.sort(function (a, b) {
  623. return a.uniformCode - b.uniformCode;
  624. });
  625. };
  626. // 函数点击事件
  627. const tabFuncRowClickHandle = (row) => {
  628. let elInput = document.getElementById("expressionInput");
  629. let startPos = elInput.selectionStart; //第0个字符到选中的字符
  630. let endPos = elInput.selectionEnd; //选中字符到末尾字符
  631. if (startPos === undefined || endPos === undefined) return;
  632. let txt = elInput.value;
  633. let func;
  634. if (
  635. row.lab === "MR" ||
  636. row.lab === "MAR" ||
  637. row.lab === "RiseExceed" ||
  638. row.lab === "Sustain" ||
  639. row.lab === "LastUpdateTime"
  640. ) {
  641. func = row.lab + "()";
  642. } else if (row.lab === "PI" || row.lab === "E") {
  643. func = "Math." + row.lab;
  644. } else {
  645. func = "Math." + row.lab + "()";
  646. }
  647. // 将插值添加到选中光标位置
  648. let result = txt.substring(0, startPos) + func + txt.substring(endPos);
  649. elInput.value = result;
  650. // 重新定义光标位置
  651. elInput.focus();
  652. if (row.lab === "PI" || row.lab === "E") {
  653. elInput.selectionStart = startPos + func.length;
  654. elInput.selectionEnd = startPos + func.length;
  655. } else {
  656. elInput.selectionStart = startPos + func.length - 1;
  657. elInput.selectionEnd = startPos + func.length - 1;
  658. }
  659. form.value.expression = result; // 赋值给表单中的的字段
  660. };
  661. //rowDbclick
  662. const rowDbclick = (row) => {
  663. elInputSplit(row.uniformCode);
  664. };
  665. // 表达式字符串拼接
  666. const elInputSplit = async (val) => {
  667. let elInput = document.getElementById("expressionInput");
  668. let startPos = elInput.selectionStart;
  669. let endPos = elInput.selectionEnd;
  670. if (startPos === undefined || endPos === undefined) return;
  671. let txt = elInput.value;
  672. let txtSplit = val;
  673. let result = txt.substring(0, startPos) + txtSplit + txt.substring(endPos);
  674. elInput.value = result;
  675. elInput.focus();
  676. elInput.selectionStart = startPos + txtSplit.length;
  677. elInput.selectionEnd = startPos + txtSplit.length;
  678. };
  679. //保存
  680. const save = async () => {
  681. const res = await custombj_postSave(form.value);
  682. console.warn(res);
  683. ElMessage.success(`保存成功!`);
  684. closeDialog();
  685. };
  686. //提交
  687. const submitForm = async (formEl) => {
  688. if (!formEl) return;
  689. await formEl.validate((valid, fields) => {
  690. if (valid) {
  691. save();
  692. } else {
  693. console.log("error submit!", fields);
  694. }
  695. });
  696. };
  697. //reset
  698. const resetForm = (formEl) => {
  699. formEl.resetFields();
  700. };
  701. //confirm关闭
  702. const handleClose = () => {
  703. ElMessageBox.confirm("确认关闭?")
  704. .then(() => {
  705. closeDialog();
  706. })
  707. .catch(() => {
  708. // catch error
  709. });
  710. };
  711. //关闭触发事件
  712. const closeDialog = () => {
  713. resetForm(ruleFormRef.value);
  714. state.AIPointList = [];
  715. state.DIPointList = [];
  716. toEmits();
  717. };
  718. </script>
  719. <style lang="scss" scoped>
  720. .col-box {
  721. display: flex;
  722. flex-direction: column;
  723. }
  724. .select-mini {
  725. width: 120px;
  726. }
  727. .el-tabs__content {
  728. padding: 0 !important;
  729. }
  730. .el-tabs--border-card {
  731. -webkit-box-shadow: none;
  732. box-shadow: none;
  733. }
  734. .border {
  735. border: solid red 1px;
  736. }
  737. .el-table--mini td {
  738. padding: 3px 0;
  739. }
  740. .el-button-group .el-button--primary {
  741. border: none;
  742. }
  743. .el-form--label-top .el-form-item__label {
  744. padding: 0;
  745. }
  746. </style>