123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- <script setup>
- import { ref, onMounted, computed, onUnmounted } from 'vue';
- import {useMessage, NProgress } from 'naive-ui';
- import { TheChatView } from '@/components';
- import { controlApi } from "@/api/control";
- import BaseTitle from './components/BaseTitle.vue';
- import NumberPanel from './NumberPanel.vue';
- import CirclePanel from './CirclePanel.vue';
- import EchartLeft from './EchartLeft.vue'
- import EchartRight from './EchartRight.vue'
- import DrawerSetting from './DrawerSetting.vue';
- import ParamterCard from './ParamterCard.vue';
- import DrawerWarning from './DrawerWarning.vue';
- import dayjs from 'dayjs';
- let isProcessing = false;
- const lastDataSource = ref({});
- const warningCollectList = ref([]);
- const updateTime = ref(dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss"));
- const message = useMessage();
- const processNumber = ref(0);
- const systemStatus = ref(0);
- const configurationStatus = ref(1);
- const timerPanel = ref(null);
- const timerParams = ref(null);
- // 中间面板数值
- const panelResultData = ref({});
- const paramterDrawerVisible = ref(false);
- const warningDrawerVisible = ref(false);
- const paramterValue = ref({});
- const systemSwitchType = computed(() => configurationStatus.value === 0 && systemStatus.value === 1);
- // 开始投放
- const handleSystemStatus = () => {
- if ( isProcessing ) return;
- isProcessing = true;
- if ( configurationStatus.value == 1 ) {
- return message.warning('当前组态未启用,无法投放');
- }
- const addStatus = systemStatus.value === 0 ? 1 : 0;
- controlApi.putSystemStatus({ addStatus });
- operateNumbers(addStatus === 0 ? "decrement" : "increment", () => {
- systemStatus.value = addStatus;
- isProcessing = false
- message.warning(addStatus === 0 ? '当前投药状态:已停用' : '当前投药状态:投放中');
- })
- }
- // 更新参数设置
- const handleUpdateParams = () => {
- paramterDrawerVisible.value = false;
- initData();
- initPanelData();
- }
- const getNumVal = (type, n1, n2) => {
- if ( type == 0 ) {
- return Math.max(n1, n2);
- }
- if ( type == 1 ) {
- return n1 || n1 == 0 ? Number(n1.toFixed(2)) : '--'
- }
- if ( type == 2 ) {
- return n2 || n2 == 0 ? Number(n2.toFixed(2)) : '--'
- }
- }
- const getCurrentTypeData = () => {
- const {
- type,
- hycXsyOne, hycXsyTwo,
- qycYxyOne, qycYxyTwo, qycAdOne, qycAdTwo,
- htfksd,
- } = lastDataSource.value;
- if ( type == 0 ) {
- return {
- type,
- hycXsy: Math.max(hycXsyOne, hycXsyTwo),
- qycYxy: Math.max(qycYxyOne, qycYxyTwo),
- qycAd: Math.max(qycAdOne, qycAdTwo),
- htfksd,
- }
- }
- if (type == 1) {
- return {
- type,
- hycXsy: hycXsyOne,
- qycYxy: qycYxyOne,
- qycAd: qycAdOne,
- htfksd
- }
- }
- if (type == 2) {
- return {
- type,
- hycXsy: hycXsyTwo,
- qycYxy: qycYxyTwo,
- qycAd: qycAdTwo,
- htfksd
- }
- }
- }
- const operateNumbers = (operationType, callBack) => {
- const totalTime = 3000;
- const steps = 100;
- const intervalTime = totalTime / steps;
- let currentNumber = operationType === 'decrement' ? 100 : 1;
- processNumber.value = currentNumber;
- const intervalId = setInterval(() => {
- operationType === 'increment' ? currentNumber++ : currentNumber--
- processNumber.value = currentNumber
- if ((operationType === 'increment' && currentNumber > 100) ||
- (operationType === 'decrement' && currentNumber < 0)) {
- clearInterval(intervalId);
- callBack && callBack();
- }
- }, intervalTime);
- }
- const isEmpty = (val) => {
- return !(val === null || val === undefined || val === '')
- }
- const handleWarningDraw = () => {
- if ( !warningCollectList.value.length ) {
- return message.warning('当前没有系统告警');
- }
- warningDrawerVisible.value = true;
- }
- const getWarningInfo = async () => {
- const { data } = await controlApi.getWarningParams();
- const { kzmbplbjz, hycxsygkz, xhycbjz, jylpybjz, minAddAmount } = data;
- const {
- hycXsy,
- qycYxy,
- qycAd,
- htfksd,
- // 系统加药量
- medicineAmount,
- } = getCurrentTypeData();
- if ( isEmpty(hycXsy) && isEmpty(htfksd) && isEmpty(kzmbplbjz)) {
- const content = [];
- if (( hycXsy - htfksd ) > kzmbplbjz ) {
- content.push('• 好氧池硝酸盐控制目标偏移过大')
- }
- if ( hycXsy > hycxsygkz ) {
- content.push('• 好氧池硝酸盐超管控值')
- }
- if (content.length) {
- warningCollectList.value.push({
- title: '反硝化异常报警',
- content,
- desc: '请排查现场工况/调整控制参数,非碳源量的问题,建议切换手动控制'
- })
- }
- }
- if (isEmpty(qycYxy) && isEmpty(qycAd) && isEmpty(hycXsy) && isEmpty(xhycbjz)) {
- if ((qycYxy + qycAd - hycXsy) > xhycbjz) {
- warningCollectList.value.push({
- title: '硝化异常报警',
- content: [],
- desc: '请排查进水水质、曝气系统、活性污泥系统等,请切手动运行'
- })
- }
- }
- if (isEmpty(medicineAmount) && isEmpty(minAddAmount) && isEmpty(jylpybjz)) {
- if ( medicineAmount > minAddAmount && medicineAmount > jylpybjz) {
- warningCollectList.value.push({
- title: '加药量偏移报警',
- content: ['• 系统计算加药与现场实际流量计偏移过大'],
- desc: '请排查现场碳源储罐液位、加药泵和流量计等,确保运行正常'
- })
- }
- }
- }
- const initPanelData = async () => {
- const { data } = await controlApi.getLastResult();
- panelResultData.value = data;
- updateTime.value = dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss");
- }
- const initData = async () => {
- const { data } = await controlApi.getBaseData();
- const {
- addType,
- numberBeng, type,
- jsLlOne, jsLlTwo,
- jsCodOne, jsCodTwo,
- jsTnOne, jsTnTwo,
- hycXsyOne, hycXsyTwo,
- qycYxyOne, qycYxyTwo,
- qycAdOne, qycAdTwo,
- // 系统加药量
- medicineAmount,
- // 瞬时加药量
- tytjTransientLL
- } = data;
- const numberBengEunm = {
- 0: '1号加药泵',
- 1: '2号加药泵',
- 2: '3号加药泵'
- }
- const typeEnum = {
- 0: '自动',
- 1: '1号池',
- 2: '2号池',
- 3: '手动'
- }
- paramterValue.value = {
- device: `${numberBengEunm[numberBeng]} / ${typeEnum[type]}`,
- jsll: getNumVal(type, jsLlOne, jsLlTwo) + ' m³/h',
- cod: getNumVal(type, jsCodOne, jsCodTwo) + ' mg/L',
- jszd: getNumVal(type, jsTnOne, jsTnTwo) + ' mg/L',
- hycxsy: getNumVal(type, hycXsyOne, hycXsyTwo) + ' mg/L',
- qycxsy: getNumVal(type, qycYxyOne, qycYxyTwo) + ' mg/L',
- qycan: getNumVal(type, qycAdOne, qycAdTwo) + ' mg/L',
- };
- configurationStatus.value = addType;
- lastDataSource.value = data;
- }
- onMounted(async () => {
- // 初始化
- await initData();
- // 投药量数据
- initPanelData();
- // 获取报警相关数据
- getWarningInfo();
- // 获取是否允许投药开关
- await controlApi.getSystemStatus().then(({ data }) => {
- // 0不允许 1允许
- systemStatus.value = data;
- });
- processNumber.value = systemSwitchType.value ? 100 : 0;
- timerPanel.value = setInterval(initPanelData, 5 * 60 * 1000)
- timerParams.value = setInterval(initData, 40 * 60 * 1000);
- })
- onUnmounted(() => {
- clearInterval(timerPanel);
- clearInterval(timerParams);
- })
- </script>
- <template>
- <section class="flex items-start h-full">
- <TheChatView leftTitle="智适应碳源投加" :isChatSlot="false" :isFooter="false">
- <template #control>
- <div class="control-container">
- <div class="arg-section">
- <div class="left-card space-y-[16px]">
- <BaseTitle title="智能投加计算" type="1"></BaseTitle>
- <ParamterCard :data="paramterValue"></ParamterCard>
- </div>
- <div class="right-card">
- <div class="header">
- <h4 class="title">智能系统参数</h4>
- <ul class="btn-list space-x-[8px]">
- <li class="item" @click="paramterDrawerVisible = true">
- <span>参数设置</span>
- </li>
- <li class="item" @click="handleWarningDraw">
- <span>系统告警</span>
- <span class="waring-circle-icon" v-show="warningCollectList.length != 0"></span>
- </li>
- </ul>
- </div>
- <div class="result-content">
- <div class="number_card space-x-[20px]">
- <NumberPanel direction="left" title="智能控制系数" :value="panelResultData.kzxs"></NumberPanel>
- <CirclePanel :medicineAmount="panelResultData.calculateVal" :tytjTransientLL="panelResultData.realValue"></CirclePanel>
- <NumberPanel direction="right" title="硝酸盐智能设定" unit="mg/L" :value="panelResultData.htfksd"></NumberPanel>
- </div>
- <div class="progress_card space-y-[8px]">
- <span class="time">模型更新时间: {{ updateTime }}</span>
- <div class="progress">
- <NProgress
- processing
- type="line"
- :border-radius="0"
- :percentage="processNumber"
- :show-indicator="false"
- :height="10"
- :color="'red'"
- rail-color="transparent"
- class="custom-progress"
- ></NProgress>
- </div>
- <span class="tips">LibraAI{{ systemSwitchType ? "投药中" : "未启用" }}...</span>
- </div>
- <div class="play_card">
- <div class="play-btn space-x-[6px]" @click="handleSystemStatus">
- <span :class="[systemSwitchType ? 'icon_end' : 'icon_start' ]"></span>
- <span>{{ systemSwitchType ? "暂停" : "开始" }}</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="echart-section space-x-[20px]">
- <EchartLeft></EchartLeft>
- <EchartRight></EchartRight>
- </div>
- </div>
- </template>
- </TheChatView>
- </section>
- <!-- 参数设置 - 抽屉 -->
- <DrawerSetting
- v-model:show="paramterDrawerVisible"
- @on-update="handleUpdateParams"
- ></DrawerSetting>
- <!-- 系统告警 - 抽屉 -->
- <DrawerWarning
- v-model:show="warningDrawerVisible"
- :warning-collect-list="warningCollectList"
- ></DrawerWarning>
- </template>
- <style lang="scss" scoped>
- .control-container {
- height: 100%;
- .arg-section {
- @include flex(x, start, start);
- height: 57%;
- padding: 16px 14px;
- border: 1px solid #fff;
- border-radius: 10px;
- background: url(@/assets/images/control/bg-top.png) left center no-repeat;
- background-size: 878px 100% ;
- overflow: hidden;
- .left-card {
- flex-shrink: 0;
- width: 338px;
- height: 100%;
- display: flex;
- flex-flow: column;
- }
- .right-card {
- flex: 1;
- height: 100%;
- .header {
- height: 40px;
- position: relative;
- text-align: center;
- .title {
- color: #1A2029;
- font-size: 15px;
- font-weight: bold;
- line-height: 24px;
- }
- .btn-list {
- position: absolute;
- top: 0;
- right: 0;
- @include flex(x, center, center);
- color: #1A2029;
- font-size: 12px;
- font-weight: 400;
- .item {
- position: relative;
- @include flex(x, center, center);
- width: 68px;
- height: 28px;
- border-radius: 4px;
- background: #fff;
- cursor: pointer;
- .waring-circle-icon {
- position: absolute;
- top: 2px;
- right: 2px;
- width: 5px;
- height: 5px;
- border-radius: 50%;
- background: #FF4920;
- }
- }
- }
- }
- .result-content {
- @include flex(y, center, around);
- height: calc(100% - 40px);
- // padding: 0px 86px 0 86px;
- background-clip: padding-box;
-
- .number_card {
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .progress_card {
- @include flex(y, center, center);
- padding-top: 6px;
- text-align: center;
- .time {
- width: 180px;
- color: #F6A52C;
- font-size: 10px;
- font-style: normal;
- font-weight: 400;
- line-height: 16px;
- }
- .tips {
- text-align: center;
- font-size: 12px;
- font-weight: 500;
- line-height: 24px;
- background: linear-gradient(90deg, #0059FF 0%, #29C2FA 100%);
- background-clip: text;
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- }
- .progress {
- width: 165px;
- height: 10px;
- background: #cce7ec;
- box-shadow: 0px 5px 5px #ccc;
- }
- }
- .play_card {
- @include flex(x, center, center);
- .play-btn {
- @include flex(x, center, center);
- width: 120px;
- height: 48px;
- background: url("@/assets/images/control/bg-play-btn.png") center center no-repeat;
- background-size: cover;
- color: #2454FF;
- font-size: 14px;
- font-weight: 500;
- line-height: 16px;
- cursor: pointer;
- user-select: none;
- .icon_end, .icon_start {
- display: block;
- width: 28px;
- height: 28px;
- }
- .icon_end {
- background: url("@/assets/images/control/icon-end.svg") center center no-repeat;
- background-size: cover;
- }
- .icon_start {
- background: url("@/assets/images/control/icon-start.svg") center center no-repeat;
- background-size: cover;
- }
- }
- }
- }
- }
- }
- .echart-section {
- @include flex(x, start, start);
- height: 43%;
- padding-top: 16px;
- }
- }
- </style>
- <style lang="scss">
- .progress {
- .custom-progress {
- .n-progress-graph-line-fill {
- background: linear-gradient(to right, rgba(0, 101, 253, 1), rgba(0, 239, 234, 1)) !important;
- }
- }
- }
- // .custom-tab_item {
- // @include flex (x, center, center);
- // height: 35px;
- // border-radius: 4px;
- // background: #F3F5FA;
- // &.n-tabs-tab--active {
- // transition: none !important;
- // border-radius: 4px;
- // transition: none !important;
- // background: url('https://static.fuxicarbon.com/bigModel/pc/tab-border-item-2x.png') -3px 0px no-repeat, linear-gradient(180deg, #F1F3FE 0%, #FFF 100%);
- // background-size: 107% 100%;
- // }
- // }
- .control-container .left-section {
- .n-tabs-nav-scroll-content {
- padding-bottom: 10px;
- }
- }
- </style>
|