Files
smart_storage_app/pages/home-page/policy-management/index.vue

872 lines
23 KiB
Vue
Raw Normal View History

2025-06-30 10:21:25 +08:00
<template>
2026-03-09 15:02:49 +08:00
<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
:placeholder="$t('homePage.policy.selectPlanCurveTem')" v-model="formInfo.temName"
type="select" @click="openSelectTemplate" input-align="right" /></u-form-item>
<u-form-item :label="$t('homePage.policy.IssueDevice')"><u-input
:placeholder="$t('homePage.policy.selectIssueDevice')" 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
:placeholder="$t('homePage.policy.pleaseInputValue')" v-model="formInfo.socUpper" disabled
type="text" input-align="right" /></u-form-item>
<u-form-item :label="$t('homePage.policy.socDownlimit')"><u-input
:placeholder="$t('homePage.policy.pleaseInputValue')" 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">{{
2025-07-01 16:59:10 +08:00
formInfo.status ? formInfo.status : $t('homePage.policy.distributeResult')
}}</view>
2026-03-09 15:02:49 +08:00
</u-form-item>
</u-form>
<view style="display: flex;flex-wrap:wrap;justify-content: space-between;">
<button type="success" size="mini" @click="handleIssue"
style="background-color: #009458;color: #fff;width: 40%;" :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;width: 40%;" :loading="deliveryLoading"
:disabled="isMoreLoading"
v-show="vuex_permissions.includes('strategy:planCurveDis:commandIssuanceData')">
{{ $t('homePage.policy.controlDistribution') }}
</button>
<button type="success" size="mini"
style="background-color: #009458; color: #fff;margin-top: 10px;width: 40%;"
:disabled="isMoreLoading"
v-show="vuex_permissions.includes('strategy:planCurveDis:commandIssuanceData')">
{{ $t('homePage.home.pvSwitch') }}
</button>
<button @click="toDeviceControl" type="success" size="mini"
style="background-color: #009458; color: #fff;margin-top: 10px;width: 40%;"
2026-03-09 15:19:57 +08:00
v-show="vuex_permissions.includes('strategy:business:ems:dispatch')">
2026-03-10 15:37:46 +08:00
{{ $t('homePage.home.emsControl') }}
2026-03-09 15:02:49 +08:00
</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"
2026-03-10 15:37:46 +08:00
:prop="'inputDataBox.' + index + '.newValue'" :key="item.prop">
2026-03-09 15:02:49 +08:00
<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>
2025-06-30 10:21:25 +08:00
</template>
<script>
2026-03-09 15:02:49 +08:00
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: {
toDeviceControl() {
2026-03-10 15:37:46 +08:00
console.log(123)
2026-03-09 15:02:49 +08:00
uni.navigateTo({
url:'/pages/home-page/policy-Config/index'
})
},
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;
},
},
};
2025-06-30 10:21:25 +08:00
</script>
<style lang="scss" scoped>
2026-03-09 15:02:49 +08:00
.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;
}
2025-06-30 10:21:25 +08:00
</style>