Files
smart_storage_app/pages/home-page/policy-management/index.vue
2025-07-01 16:59:10 +08:00

845 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="policy-management">
<u-toast ref="uToast" />
<u-navbar :is-back="true" :background="background" :border-bottom="false" :custom-back="toback">
<view class="slot-wrap">
<stationDropdow disabled style="width: 100%" ref="dropdow" />
</view>
</u-navbar>
<view class="subTab">
<u-subsection :list="policyTypeList" :current="currentType" v-show="inputDataBox.length" :animation="true"
@change="sectionChange" :active-color="'#3D9F7F'">
</u-subsection>
</view>
<template v-if="currentType === 0">
<zero-loading v-if="loading" />
<view v-else class="chart-box">
<Section :title="$t('homePage.policy.planCurve')" />
<charts :id="'pcChart'" :options="pv_option"></charts>
</view>
<view v-if="!loading" class="detail-box">
<u-form :model="formInfo" ref="uForm" :label-width="200">
<u-form-item :label="$t('homePage.policy.planCurveTem')"><u-input v-model="formInfo.temName" type="select"
@click="openSelectTemplate" input-align="right" /></u-form-item>
<u-form-item :label="$t('homePage.policy.IssueDevice')"><u-input v-model="formInfo.srcName" type="select"
@click="openSelectDevice" input-align="right" /></u-form-item>
<u-form-item :label="$t('homePage.policy.operateOrNot')"><u-radio-group active-color="#009458"
v-model="formInfo.operationName" class="group">
<u-radio v-for="(item, index) in radioList" :key="index" :name="item.name" :disabled="item.disabled">
{{ item.name }}
</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item :label="$t('homePage.policy.socUplimit')"><u-input v-model="formInfo.socUpper" disabled
type="text" input-align="right" /></u-form-item>
<u-form-item :label="$t('homePage.policy.socDownlimit')"><u-input v-model="formInfo.socLower" disabled
type="text" input-align="right" /></u-form-item>
<u-form-item :label="$t('homePage.policy.effectiveTime')">
<u-input v-model="formInfo.effectiveTime" type="select" @click="timeShow = true" input-align="right"
:placeholder="$t('homePage.policy.selectEffectiveTime')" />
<u-picker mode="time" v-model="timeShow" input-align="right" @confirm="confirmTime"
:params="timeParams"></u-picker>
</u-form-item>
<u-form-item :label="$t('homePage.policy.distributeResult')" :border-bottom="false">
<view style="color: green; width: 100%; text-align: right">{{
formInfo.status ? formInfo.status : $t('homePage.policy.distributeResult')
}}</view>
</u-form-item>
</u-form>
<view style="display: flex;">
<button type="success" size="mini" @click="handleIssue" style="background-color: #009458;color: #fff"
:loading="isMoreLoading" :disabled="deliveryLoading"
v-show="vuex_permissions.includes('strategy:planCurveDis:commandIssuance')">
{{ $t('homePage.policy.commandDistribution') }}
</button>
<button type="success" size="mini" @click="handleControlDelivery"
style="background-color: #009458; color: #fff" :loading="deliveryLoading" :disabled="isMoreLoading"
v-show="vuex_permissions.includes('strategy:planCurveDis:commandIssuanceData')">
{{ $t('homePage.policy.controlDistribution') }}
</button>
</view>
</view>
</template>
<template v-else>
<zero-loading v-if="loading" />
<view class="new-box">
<Section :title="$t('homePage.policy.genSetting')">
<view slot="right" style="flex: 1">
<view style="text-align: right;width: 100%;">
<u-button type="success" icon="map" style="height: 38rpx;line-height: 38rpx;" size="mini"
@click="save">{{ $t('homePage.policy.save') }}</u-button>
</view>
</view>
</Section>
<view class="input-box">
<u-form :model="formModel" ref="valueForm" labelPosition="left" label-width="350">
<u-form-item v-for="(item, index) in inputDataBox" :label="item.colName"
:prop="'inputDataBox.' + index + '.newValue'">
<view style="width: 100;display: flex;align-items: center;">
<!-- <input class="uni-input" type="decimal" v-model.number="item.newValue" placeholder="请输入值" /> -->
<u-input v-model.number="item.newValue" type="text" input-align="right"
:placeholder="$t('homePage.policy.pleaseInputValue')">
</u-input>
<view style="min-width: 50rpx;margin-left: 5rpx;">
{{ item.unit ? item.unit : '' }}
</view>
</view>
</u-form-item>
</u-form>
</view>
</view>
</template>
<u-modal v-model="warningShow" :show-cancel-button="true" :content="content" @confirm="sureIssue()"
@cancel="closeIssue()"></u-modal>
<u-modal v-model="openShow" :show-cancel-button="true" :content="openContent" @confirm="sureOpen()"
@cancel="closeOpen()"></u-modal>
<u-modal v-model="passwordShow" :show-cancel-button="true" :title="$t('homePage.policy.pleaseInputPassword')"
@confirm="surePassword()" @cancel="closePassword()">
<view class="slot-content" style="padding-left: 10rpx; padding-right: 10rpx">
<u-form :model="form" :label-width="200">
<u-form-item :label="$t('homePage.policy.password')"><u-input v-model="password" type="password"
input-align="right" /></u-form-item>
</u-form>
</view>
</u-modal>
<!-- 计划曲线模板 -->
<u-select @confirm="confirmTem" :default-value="defaultTemplate" v-model="templateSelectShow"
:list="templateList"></u-select>
<!-- 下发设备 -->
<u-select @confirm="confirmDev" :default-value="defaultDevice" v-model="deviceSelectShow"
:list="deviceList"></u-select>
</view>
</template>
<script>
import charts from "@/components/charts/index.vue";
import stationDropdow from "@/components/station-dropdow/index.vue";
import Section from "@/components/section/index.vue";
export default {
components: {
stationDropdow,
charts,
Section,
},
computed: {
currentStation () {
return this.vuex_currentStation;
},
language () {
return this.vuex_language
}
},
watch: {
currentStation: {
handler (val) {
this.loading = true;
this.formInfo = {
temId: undefined,
temName: undefined,
srcName: undefined,
srcId: undefined,
operation: undefined,
status: undefined,
operationName: undefined,
};
this.stationId = val.id;
this.getTemplate();
this.getIssueDevices();
this.getValueLiist()
setTimeout(() => {
this.getIssueStatus();
}, 200);
},
immediate: true,
deep: true,
},
language: {
handler (val) {
if(val === 'zh_CN'){
this.policyTypeList = [{
name: '命令下发'
},
{
name: '通用设置'
}]
}
if(val === 'en_US'){
this.policyTypeList = [{
name: 'Command Distribution'
},
{
name: 'General settings'
}]
}
},
immediate: true,
deep: true,
},
},
data () {
return {
stationId: null,
formInfo: {
temId: undefined,
temName: undefined,
srcName: undefined,
srcId: undefined,
operation: undefined,
status: undefined,
operationName: undefined,
},
policyTypeList: [],
currentType: 0,
radioList: this.$t('homePage.policy.radioList'),
pv_option: {},
templateSelectShow: false,
deviceSelectShow: false,
templateList: [],
deviceList: [],
defaultTemplate: [],
defaultDevice: [],
chartDatas: [],
ratePower: 0,
warningShow: false,
content: null,
issueList: [],
isMoreLoading: false,
loading: false,
moreInterval: null,
background: {
backgroundColor: "#0EA17E",
},
checked: false,
openContent: this.$t('homePage.policy.isOpen'),
openShow: false,
switchLoading: false,
defaultCheck: null,
passwordShow: false,
form: {},
password: null,
defaultPassword: 123456,
formOpen: {},
userId: '',
formModel: {},
inputDataBox: [],
timeShow: false,
timeParams: {
year: false,
month: false,
day: false,
hour: true,
minute: true,
second: false
},
deliveryLoading: false
};
},
beforeDestroy () {
if (this.moreInterval) {
clearInterval(this.moreInterval);
}
},
mounted () {
this.defaultCheck = this.checked;
this.userId = this.vuex_user.userId
},
methods: {
confirmTime (val) {
this.formInfo.effectiveTime = val.hour + ':' + val.minute
this.formInfo.planRefreshH = val.hour
this.formInfo.planRefreshM = val.minute
},
async handleControlDelivery () {
if (!this.formInfo.srcId) {
this.$refs.uToast.show({
title: this.$t('homePage.policy.selectIssueDevice'),
type: "warning",
});
}
if (!this.formInfo.effectiveTime) {
this.$refs.uToast.show({
title: this.$t('homePage.policy.selectEffectiveTime'),
type: "warning",
});
}
if (this.formInfo.srcId && this.formInfo.effectiveTime) {
const params = {
stationId: this.stationId,
srcId: this.formInfo.srcId,
planControlModel: 0, // 计划曲线下发页面固定发0
planRefreshH: this.formInfo.planRefreshH, // 时间选择框的小时
planRefreshM: this.formInfo.planRefreshM // 时间选择框的分钟
}
const res = await this.$u.api.policy.GetPlanCurveIssueData(params)
if (res.data.length > 0) {
this.issueList = res.data
this.handleMoreOrder()
} else {
this.$refs.uToast.show({
title: this.$t('homePage.policy.deliverErrorNewTip'),
type: "warning",
});
this.deliveryLoading = false
}
}
},
inputValue (val, index) {
// let temp = val.replace(/(^[\-0-9][0-9]*(\.[0-9]+)?)$/g, '')
// this.$nextTick(() => {
// this.inputDataBox[index].newValue = temp
// })
},
sectionChange (val) {
this.currentType = val
if (val === 1) {
this.getValueLiist()
}
},
async getValueLiist () {
const res = await this.$u.api.policy.GetSubstitutionValueList({
stationId: this.stationId
})
this.inputDataBox = res.data
},
toback () {
uni.navigateBack({
delta: 1
})
},
closePassword () {
this.password = null;
this.checked = this.defaultCheck;
this.switchLoading = false;
},
sureOpen () {
this.passwordShow = true;
},
closeOpen () {
this.switchLoading = false;
this.checked = this.defaultCheck;
},
surePassword () {
if (+this.password === +this.defaultPassword) {
this.$refs.uToast.show({
title: this.$t('homePage.policy.passwordSuccess'),
type: "success",
});
this.defaultCheck = this.checked;
} else {
this.$refs.uToast.show({
title: this.$t('homePage.policy.passwordError'),
type: "warning",
});
this.checked = this.defaultCheck;
}
this.password = null;
this.switchLoading = false;
},
changeOpen (val) {
this.switchLoading = true;
if (val) {
this.openContent = this.$t('homePage.policy.isOpen');
this.openShow = true;
} else {
this.openContent = this.$t('homePage.policy.isClose');
this.openShow = true;
}
},
confirmTem (val) {
this.formInfo.temName = val[0].label;
this.formInfo.temId = val[0].value;
this.getTemplateDetail();
},
confirmDev (val) {
this.formInfo.srcName = val[0].label;
this.formInfo.srcId = val[0].value;
},
openSelectTemplate () {
this.templateSelectShow = true;
},
openSelectDevice () {
this.deviceSelectShow = true;
},
closeIssue () {
this.warningShow = false;
},
sureIssue () {
this.getIssueDatas();
},
// 下发
async getIssueDatas () {
const params = {
stationId: this.stationId,
temId: this.formInfo.temId,
srcId: this.formInfo.srcId,
};
const res = await this.$u.api.policy.getIssueDatas(params);
this.issueList = res.data;
this.handleMoreOrder();
},
async handleMoreOrder () {
const self = this;
this.isMoreLoading = true;
const params = {
list: this.issueList,
};
try {
const res = await this.$u.api.policy.SetOrderIssued(params);
if (res.data.heartbeatStatus === 0) {
this.isMoreLoading = false;
if (res.data.code === 1) {
self.$refs.uToast.show({
title: res.data.msg,
type: "warning",
});
} else {
self.$refs.uToast.show({
title: res.data.msg,
type: "warning",
});
}
} else {
this.moreInterval = setInterval(() => {
self.getMoreOrderProgressBar();
}, 500);
}
} catch (e) {
this.isMoreLoading = false
this.deliveryLoading = false
//TODO handle the exception
}
},
// 批量下发进度
async getMoreOrderProgressBar () {
const self = this;
const params = {
stationId: this.stationId,
srcId: this.formInfo.srcId,
};
const res = await this.$u.api.policy.GetOrderProgressBar(params);
if (res.data.isEnd === 1) {
clearInterval(self.moreInterval);
self.isMoreLoading = false;
self.deliveryLoading = false
await self.insertIssueStatus();
self.$refs.uToast.show({
title: this.$t('homePage.policy.distributeSuccess'),
type: "success",
});
self.getIssueStatus();
} else if (res.data.isEnd === 2) {
clearInterval(self.moreInterval);
self.isMoreLoading = false;
self.deliveryLoading = false
self.$refs.uToast.show({
title: this.$t('homePage.policy.distributeFail'),
type: "error",
});
self.getIssueStatus();
}
},
async insertIssueStatus () {
const params = {
stationId: this.stationId, // 电站id
planTemId: this.formInfo.temId, // 计划曲线模板id
deviceId: this.formInfo.srcId, // 设备id
issue: 0, // 是否下发 0已下发 1未下发
operation: this.formInfo.operation, // 是否投运 0投运 1未投运
};
this.$u.api.policy.insertIssueStatus(params);
},
// 获取下发设备
async getIssueDevices () {
const res = await this.$u.api.policy.getIssueDevices({
stationId: this.stationId,
});
this.deviceList = res.data;
},
// 获取模板
async getTemplate () {
const res = await this.$u.api.policy.getTemplate({
stationId: this.stationId,
});
this.templateList = res.data;
},
async save () {
const self = this
let flag = false
this.inputDataBox.forEach((item) => {
if (!/(^[\-0-9][0-9]*(\.[0-9]+)?)$/.test(item.newValue)) {
flag = true
}
})
if (flag) {
self.$refs.uToast.show({
title: this.$t('homePage.policy.isNum'),
type: "warning",
});
} else {
try {
await this.$u.api.policy.SaveSubstitutionValue(this.inputDataBox)
this.$refs.uToast.show({
title: this.$t('homePage.policy.saveSuccess'),
type: "success",
});
} catch (e) {
this.$refs.uToast.show({
title: this.$t('homePage.policy.saveError'),
type: "warning",
});
} finally {
this.getValueLiist()
}
}
},
// 获取下发状态
async getIssueStatus () {
this.formInfo.temName = undefined;
this.formInfo.srcName = undefined;
this.formInfo.status = undefined;
try {
const res = await this.$u.api.policy.queryIssueStatus({
stationId: this.stationId,
});
if (res.data) {
this.formInfo.operation = res.data.operation;
this.formInfo.status =
res.data.issue === 1 ? "" : this.$t('homePage.policy.delivered') + res.data.operationDate;
this.formInfo.temId = res.data.planTemId;
this.formInfo.srcId = res.data.deviceId;
this.templateList.find((el) => {
if (+el.value === +this.formInfo.temId) {
this.formInfo.temName = el.label;
}
});
this.deviceList.find((el) => {
if (+el.value === +this.formInfo.srcId) {
this.formInfo.srcName = el.label;
}
});
if (!this.formInfo.operation) {
this.formInfo.operationName = this.$t('homePage.policy.radioList')[0].name;
} else {
this.formInfo.operationName = this.$t('homePage.policy.radioList')[1].name;
}
this.getTemplateDetail();
} else {
this.initChart([]);
this.formInfo.status = "";
}
// this.$forceUpdate()
} catch (error) { } finally {
this.loading = false;
}
},
// 获取模板详情
async getTemplateDetail () {
const params = {
temId: this.formInfo.temId,
};
const res = await this.$u.api.policy.lookPlanningCurveTemplateDetail(
params
);
const chartres = await this.$u.api.policy.lookPlanningCurveTemplateChartData(params);
const data = res.data[0];
const chartData = chartres.data[0];
this.formInfo.temP = data.temP;
this.formInfo.capacity = data.capacity;
this.formInfo.socLower = data.socLower
this.formInfo.socUpper = data.socUpper
this.ratePower = data.ratePower;
this.chartDatas = data.planningCurves;
this.initChart(chartData.planningCurves);
},
initChart (val) {
let dateArr = [];
let valueArr = [];
if (val && val.length > 0) {
val.forEach((el, index) => {
valueArr.push(el.p);
if (index === 0) {
valueArr.push(el.p);
dateArr.push(el.startTime, el.endTime);
} else {
dateArr.push(el.startTime);
}
});
} else {
dateArr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
valueArr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}
this.pv_option = {
animation: true,
animationDuration: 500,
animationEasing: "cubicInOut",
tooltip: {
trigger: "axis",
textStyle: {
textShadowBlur: 10, // 重点
textShadowColor: 'transparent', // 重点
},
axisPointer: {},
confine: true,
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是以外层div的左上角那一点为原点x轴向右y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5; // 自己定个x坐标值以防出屏
y -= 15; // 防止点被覆盖住,可根据情况自行调节
} else {
// 左边放的下
x = pointX - boxWidth - 15;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight + 20 > pointY) {
y = pointY + 15;
} else if (boxHeight > pointY) {
y = 5;
} else {
// 上边放得下
y += pointY - boxHeight;
}
return [x, y];
},
},
xAxis: {
type: "category", //x轴
boundaryGap: false,
data: dateArr,
axisLabel: {
show: true,
color: "#A3A3A3",
},
},
grid: {
top: "10%",
left: "5%",
right: "5%",
bottom: "10%",
containLabel: true,
},
yAxis: {
type: "value",
axisLine: {
show: false,
lineStyle: {
color: "#90e9d8",
},
},
axisLabel: {
show: true,
color: "#A3A3A3",
},
axisTick: {
show: false,
},
nameTextStyle: {
padding: [0, 40, 0, 0], // y轴名字对齐刻度值
},
},
series: [{
name: this.$t('homePage.policy.planCurve') + "(kW)",
data: valueArr,
type: "line",
smooth: false,
step: "start",
lineStyle: {
color: "#00CA9A",
},
axisLabel: {
show: true,
color: "#000",
},
itemStyle: {
normal: {
color: "#00CA9A",
},
},
},],
};
},
// 下发
handleIssue () {
if (!this.formInfo.temId) {
this.$refs.uToast.show({
title: this.$t('homePage.policy.planCurveTem'),
type: "warning",
});
return;
}
if (!this.formInfo.srcId) {
this.$refs.uToast.show({
title: this.$t('homePage.policy.IssueDevice'),
type: "warning",
});
return;
}
// 取出chartdatas里最大和最小数据
let maxData = 0;
const datas = this.chartDatas;
for (var i = 0; i < datas.length; i++) {
const p = Math.abs(datas[i].p);
if (p > maxData) {
maxData = p;
}
}
const power = this.ratePower;
this.content =
power < maxData ?
this.$t('homePage.policy.deliverErrorTip') :
this.$t('homePage.policy.deliverTip');
this.warningShow = true;
},
},
};
</script>
<style lang="scss" scoped>
.policy-management {
height: 100% !important;
background-color: #f8f8f8;
padding: 20rpx;
overflow: auto;
.chart-box {
width: 100%;
height: 540rpx;
padding: 20rpx;
background-color: #ffffff;
margin-bottom: 20rpx;
box-shadow: 0px 4rpx 16rpx rgba(0, 0, 0, 0.1);
border-radius: 8rpx;
}
.new-box {
width: 100%;
height: calc(100vh - 300rpx);
padding: 20rpx;
background-color: #ffffff;
margin-bottom: 20rpx;
box-shadow: 0px 4rpx 16rpx rgba(0, 0, 0, 0.1);
border-radius: 8rpx;
.input-box {
height: calc(100% - 60rpx);
overflow: auto;
padding: 0 10px;
}
}
.detail-box {
width: 100%;
box-shadow: 0px 4rpx 16rpx rgba(0, 0, 0, 0.1);
border-radius: 8rpx;
background-color: #ffffff;
padding: 27rpx;
padding-top: 0;
position: relative;
}
.slot-wrap {
display: flex;
align-items: center;
/* 如果您想让slot内容占满整个导航栏的宽度 */
flex: 1;
/* 如果您想让slot内容与导航栏左右有空隙 */
/* padding: 0 30rpx; */
/deep/ .u-input__input {
color: #fff !important;
}
}
}
.open-box {
padding-left: 27rpx;
padding-right: 27rpx;
background-color: #ffffff;
margin-bottom: 20rpx;
box-shadow: 0px 4rpx 16rpx rgba(0, 0, 0, 0.1);
border-radius: 8rpx;
position: relative;
}
.group {
display: flex;
position: absolute;
right: 27rpx;
}
.subTab {
width: 100%;
text-align: right;
padding-right: 10rpx;
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 10rpx;
}
::v-deep .u-form-right {
display: flex !important;
justify-content: flex-end !important;
text-align: right;
}
::v-deep .uni-input-input {
// text-align: right !important;
}
</style>