Xazn-app/pagesA/scene/detail.vue
2025-05-22 16:37:43 +08:00

853 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>
<page-meta>
<navigation-bar :title="$tt('navBar.sceneDetail')" title-align="center" background-color="#F1F3F9"
front-color="#000000">
</navigation-bar>
</page-meta>
<view class="scene-detail-wrap">
<view class="name-wrap">
<u-cell-group>
<u-cell size="large" :title="$tt('group.name')" :border="true">
<u-input clearable slot="value" border="false" :placeholder="$tt('sceneDetail.inputName')"
fontSize="16" inputAlign="right" v-model="sceneData.sceneName"></u-input>
</u-cell>
<u-cell size="large" :border="false" :title="$tt('sceneDetail.sceneState')">
<view slot="value" style="padding: 14rpx 18rpx;">
<u-switch v-model="sceneData.enable" :activeValue="1" :inactiveValue="2" activeColor="#486ff2"
inactiveColor="#dbdbdb" size="20"></u-switch>
</view>
</u-cell>
</u-cell-group>
</view>
<view class="container-wrap">
<view class="condition-wrap">
<view class="title-wrap">
<view class="left-wrap">
<u-row>
<u-col span="2.5">
<text class="title">{{$tt('sceneDetail.trigger')}}</text>
</u-col>
<u-col span="3.2">
<view class="condition" @click="isCond = true">
<text>{{getCondName(sceneData.cond)}}</text>
<u-icon name="arrow-down" size="10"></u-icon>
</view>
</u-col>
</u-row>
</view>
<view class="right-wrap">
<u-icon name="plus-circle-fill" size="24" color='#486ff2' bold
@click="handleAddTrigger"></u-icon>
</view>
</view>
<view class="timer-wrap">
<u--form labelWidth="75" :labelStyle="{ color: '#868686',fontSize: '28rpx' }">
<u-form-item :label="$tt('scene.delay')">
<view style="width:360rpx;display:flex">
<u-input :placeholder="$tt('scene.iptDelayTime')" clearable @input="onInput"
type="number" v-model="checkDelay" placeholder-style="font-size:10px; width:80px;"
:maxlength="3" customStyle="height:30rpx;">
</u-input>
<u--text :text="$tt('scene.second')" type="tips" margin="0 0 0 8px" size="10"></u--text>
</view>
</u-form-item>
</u--form>
</view>
<view v-if="sceneData.triggers.length === 0" class="empty-cell" @click="handleAddTrigger">
<view class="text">{{$tt('sceneDetail.addConditions')}}</view>
</view>
<view v-else class="swipe-action-wrap">
<u-swipe-action autoClose>
<u-cell-group :border="false">
<!-- 设置key为不同id是为了解决uview的SwipeAction 滑动单元格 关闭事件 删除当前项下一项自动弹出删除选择bug -->
<view class="action-item-wrap" v-for="(item,index) in sceneData.triggers"
:key="getKey(index, item)">
<u-swipe-action-item :options="swipeOptions"
@click="handleDeleteSceneItem('trigger', index)">
<u-cell isLink :border="false"
:customStyle="{ backgroundColor: '#F7F7F7',borderRadius:'10rpx' }"
@click="handleEditSceneItem({type:'trigger', item:item, index:index})">
<view slot="title" class="slot-title">
<u--image :src="getImageUrl(item)" radius="4" width="33"
height="33"></u--image>
<view class="cell-text">
<view class="title">
{{item.productName}}{{item.cronExpression ? 'CRON' : ''}}
</view>
<view v-if="item.deviceCount > 0" class="des">
{{$tt('sceneDetail.deviceNumber')}}{{item.deviceCount}}
</view>
<view class="des">
<template v-if="item.parentName && item.parentName !== item.name">
{{item.parentName}} >>
</template>
<template v-if="item.arrayIndexName">
{{item.arrayIndexName}} >>
</template>
{{item.name}}
{{item.value !== '' ? item.operator ? `${item.operator} ${item.value}` : item.value : ''}}
<!-- 定时触发事件 -->
{{item.cronExpression ? item.cronExpression : ''}}
<!-- 触发条件为上下线设备 -->
{{item.type === 5 ? $tt('sceneDetail.deviceOnline') : item.type === 6 ? $tt('sceneDetail.Equipment') : ''}}
</view>
</view>
</view>
</u-cell>
</u-swipe-action-item>
</view>
</u-cell-group>
</u-swipe-action>
</view>
</view>
<view class="execute-wrap">
<view class="title-wrap">
<view class="left-wrap">
<text class="title">{{$tt('sceneDetail.action')}}</text>
</view>
<view class="right-wrap">
<u-icon name="plus-circle-fill" size="24" color='#486ff2' bold
@click="handleAddAction"></u-icon>
</view>
</view>
<view v-if="sceneData.actions.length === 0" class="empty-cell" @click="handleAddAction">
<view class="text">{{$tt('sceneDetail.addAction')}}</view>
</view>
<view v-else class="swipe-action-wrap">
<u-swipe-action autoClose>
<u-cell-group :border="false">
<view class="action-item-wrap" v-for="(item,index) in sceneData.actions"
:key="getKey(index, item)">
<!-- 设置key为不同id是为了解决uview的SwipeAction 滑动单元格 关闭事件 删除当前项下一项自动弹出删除选择bug -->
<u-swipe-action-item :options="swipeOptions"
@click="handleDeleteSceneItem('action', index)">
<u-cell isLink :border="false"
:customStyle="{ backgroundColor: '#F7F7F7',borderRadius:'10rpx' }"
@click="handleEditSceneItem({type:'action', item:item, index:index})">
<view slot="title" class="slot-title">
<u--image :src="getImageUrl(item)" radius="4" width="33"
height="33"></u--image>
<view class="cell-text">
<!-- 告警执行 -->
<view class="title">
{{item.productName}}{{item.source ? $tt('sceneDetail.alertExecute') : ''}}
</view>
<view v-if="item.deviceCount > 0" class="des">
{{$tt('sceneDetail.deviceNumber')}}{{item.deviceCount}}
</view>
<view class="des">
<template v-if="item.parentName && item.parentName !== item.name">
{{item.parentName}} >>
</template>
<template v-if="item.arrayIndexName">
{{item.arrayIndexName}} >>
</template>
{{item.name}}
{{item.value !== '' ? item.value : ''}}
</view>
</view>
</view>
</u-cell>
</u-swipe-action-item>
</view>
</u-cell-group>
</u-swipe-action>
</view>
</view>
<view class="cell-group-wrap">
<u-cell-group :border="false">
<view class="cell-wrap">
<u-cell size="large" :border="false" :title="$tt('sceneDetail.moreSetting')" isLink
@click="handleMore"></u-cell>
</view>
</u-cell-group>
</view>
<view class="btn-save-wrap">
<u-button type="primary" :customStyle="{ height: '96rpx' }" size="normal" :text="$tt('common.save')"
@click="handleSave"></u-button>
</view>
</view>
<view class="other">
<u-popup :show="isCond" :round="5" mode="bottom" :closeOnClickOverlay="true" @close="isCond = false">
<view class="cond-popup-wrap">
<view class="cell-group-wrap">
<u-cell-group :border="false">
<view class="cell-wrap">
<u-cell :title="$tt('scene.anyCondition')" :name="1" :border="false"
@click="handleConfirmCond"></u-cell>
</view>
<view class="cell-wrap">
<u-cell :title="$tt('scene.allCondition')" :name="2" :border="false"
@click="handleConfirmCond"></u-cell>
</view>
<view class="cell-wrap">
<u-cell :title="$tt('scene.notConditions')" :name="3" :border="false"
@click="handleConfirmCond"></u-cell>
</view>
<view class="cell-wrap">
<u-cell :title="$tt('common.cancel')" name="cancel" :border="false"
@click="handleConfirmCond"></u-cell>
</view>
</u-cell-group>
</view>
</view>
</u-popup>
<u-popup :show="isMore" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="true"
@close="isMore = false">
<view class="more-popup-wrap">
<view class="remark">{{$tt('sceneDetail.moreSetting')}}</view>
<view class="form-wrap">
<u--form :model="more" labelPosition="left" labelWidth="80">
<view class="form-item-wrap">
<u-form-item :label="$tt('sceneDetail.silentTime')" prop="more.silentPeriod">
<u-input v-model="more.silentPeriod" border="none" type="number" inputAlign="right"
:placeholder="$tt('sceneDetail.inputTime')" clearable>
<u--text :text="$tt('sceneDetail.minute')" slot="suffix" margin="0 0 0 6rpx"
type="tips"></u--text>
</u-input>
</u-form-item>
</view>
<view class="form-item-wrap">
<u-form-item :label="$tt('sceneDetail.executionMethod')" prop="more.executeMode">
<template slot="right">
<u-radio-group v-model="more.executeMode">
<u-radio :label="$tt('sceneDetail.serial')" :name="1"
:customStyle="{marginRight: '30rpx'}">
</u-radio>
<u-radio :label="$tt('sceneDetail.parallel')" :name="2">
</u-radio>
</u-radio-group>
</template>
</u-form-item>
</view>
<view class="form-item-wrap">
<u-form-item :label="$tt('sceneDetail.delayed ')" prop="more.executeDelay">
<u-input v-model="more.executeDelay" border="none" type="number" inputAlign="right"
:placeholder="$tt('sceneDetail.inputExtensionTime')" clearable>
<u--text :text="$tt('sceneDetail.seconds')" slot="suffix" margin="0 0 0 6rpx"
type="tips"></u--text>
</u-input>
</u-form-item>
</view>
</u--form>
</view>
<u-button type="primary" :customStyle="{ height: '96rpx' }"
@click="handleConfirmMore">{{$tt('sceneDetail.complete')}}</u-button>
</view>
</u-popup>
<u-popup :show="isTrigger" :round="5" mode="bottom" :closeOnClickOverlay="true" @close="isTrigger = false">
<view class="trigger-popup-wrap">
<view class="title">{{$tt('sceneDetail.addConditions')}}</view>
<view class="cell-group-wrap">
<u-cell-group :border="false">
<view class="cell-wrap">
<u-cell :title="$tt('sceneDetail.deviceTriggered')" icon="/static/common/device_mgt.png"
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
@click="goToDeviceList('trigger')"></u-cell>
</view>
<view class="cell-wrap" v-if="deptId!=null">
<u-cell :title="$tt('sceneDetail.productTriggering')"
icon="/static/common/product_mgt.png" :iconStyle="{ marginRight: '10rpx' }"
:border="false" isLink @click="goToProductList('trigger')"></u-cell>
</view>
<view class="cell-wrap">
<u-cell :title="$tt('sceneDetail.timedTrigger')" icon="/static/common/timing.png"
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
@click="goToTimingList('trigger')"></u-cell>
</view>
</u-cell-group>
</view>
</view>
</u-popup>
<u-popup :show="isAction" :round="5" mode="bottom" :closeOnClickOverlay="true" @close="isAction = false">
<view class="trigger-popup-wrap">
<view class="title">{{$tt('sceneDetail.addAction')}}</view>
<view class="cell-group-wrap">
<u-cell-group :border="false">
<view class="cell-wrap">
<u-cell :title="$tt('sceneDetail.deviceExecution')" icon="/static/common/device_mgt.png"
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
@click="goToDeviceList('action')"></u-cell>
</view>
<view class="cell-wrap" v-if="deptId!=null">
<u-cell :title="$tt('sceneDetail.productExecution')"
icon="/static/common/product_mgt.png" :iconStyle="{ marginRight: '10rpx' }"
:border="false" isLink @click="goToProductList('action')"></u-cell>
</view>
<view class="cell-wrap">
<u-cell :title="$tt('sceneDetail.alertExecute')" icon="/static/common/alarm.png"
:iconStyle="{ marginRight: '10rpx' }" :border="false"
@click="goToWarningList('action')" isLink></u-cell>
</view>
</u-cell-group>
</view>
</view>
</u-popup>
</view>
</view>
</template>
<script>
import {
getScene,
addScene,
updateScene
} from '@/apis/modules/scene.js';
export default {
data() {
return {
sceneId: null,
isCond: false,
isMore: false,
isTrigger: false,
isAction: false,
deptId: null,
// 更多设置
more: {
silentPeriod: 0,
executeMode: 1,
executeDelay: 0
},
swipeOptions: [{
text: this.$tt('common.delete'),
style: {
backgroundColor: '#f56c6c'
}
}],
checkDelay: 0,
sceneData: {
triggers: [],
actions: [],
},
};
},
onLoad(option) {
this.deptId = this.profile.deptId;
this.sceneId = Number(option.sceneId);
if (this.sceneId) {
this.getDetail();
} else {
this.sceneData = uni.getStorageSync('sceneData');
}
},
onShow() {
const callback = uni.getStorageSync('callback');
if (callback) {
this.sceneData = uni.getStorageSync('sceneData');
}
},
onHide() {
// 页面隐藏时关闭弹框
this.isTrigger = false;
this.isAction = false;
// 保存当前页面数据
uni.setStorageSync('sceneData', this.sceneData);
},
methods: {
// 获取一键任务详情
getDetail() {
getScene(this.sceneId).then(res => {
if (res.code === 200) {
this.sceneData = {
...res.data
};
this.checkDelay = this.sceneData.checkDelay;
uni.setStorageSync('sceneData', this.sceneData);
} else {
uni.showToast({
icon: 'none',
title: res.msg
});
}
}).catch(err => {
console.log('场景详情:', err);
})
},
// 获取条件
getCondName(cond) {
if (cond === 1) {
return this.$tt('scene.anyCondition')
} else if (cond === 2) {
return this.$tt('scene.allCondition')
} else if (cond === 3) {
return this.$tt('scene.notConditions')
} else {
return this.$tt('scene.unknown')
}
},
//判断输入延时匹配条件
onInput(event) {
let value = event;
if (value > 600) {
this.checkDelay = '600';
} else if (value < 0) {
this.checkDelay = '0';
}
this.sceneData.checkDelay = this.checkDelay;
},
// 确认选择条件
handleConfirmCond({ name }) {
if (name !== 'cancel') {
this.sceneData.cond = name;
}
this.isCond = false;
},
// 更多设置
handleMore() {
const sceneData = uni.getStorageSync('sceneData');
const { silentPeriod, executeMode, executeDelay } = sceneData;
this.more = { silentPeriod, executeMode, executeDelay };
this.isMore = true;
},
// 确认选择更多
handleConfirmMore() {
const { silentPeriod, executeMode, executeDelay } = this.more;
let sceneData = uni.getStorageSync('sceneData');
sceneData = {
...sceneData,
silentPeriod,
executeMode,
executeDelay
};
uni.setStorageSync('sceneData', sceneData);
this.sceneData = {
...sceneData
};
this.isMore = false;
},
// 添加条件触发器
handleAddTrigger() {
this.isTrigger = true;
},
// 添加执行动作
handleAddAction() {
this.isAction = true;
},
// 设备触发,租户
goToDeviceList(type) {
this.setSourceData(type, 1);
uni.$u.route('/pagesA/scene/product/index', {
type: type
});
},
// 设备触发,终端用户
goToDeviceListTerminal(type) {
this.setSourceData(type, 1);
uni.$u.route('/pagesA/scene/product/device', {
type: type
});
},
// 产品触发
goToProductList(type) {
this.setSourceData(type, 3);
uni.$u.route('/pagesA/scene/product/index', {
type: type
});
},
// 定时
goToTimingList(type) {
this.setSourceData(type, 2);
uni.$u.route('/pagesA/scene/timing/index', {
type: type
});
},
// 告警列表
goToWarningList(type) {
this.setSourceData(type, 4);
uni.$u.route('/pagesA/scene/warning/index');
},
// 获取设备图片
getImageUrl(item) {
if (item.source === 1) {
return '/static/common/device_mgt.png';
} else if (item.source === 2) {
return '/static/common/timing.png';
} else if (item.source === 3) {
return '/static/common/product_mgt.png';
} else if (item.source === 4) {
return '/static/common/alarm.png';
} else {
return '';
}
},
// 删除触发、执行项
handleDeleteSceneItem(type, index) {
const { triggers, actions } = this.sceneData;
if (type === 'trigger') {
const list = triggers.filter((item, i) => i !== index);
this.sceneData = {
...this.sceneData,
triggers: [...list]
};
} else {
const list = actions.filter((item, i) => i !== index);
this.sceneData = {
...this.sceneData,
actions: [...list]
};
}
},
// 编辑触发、执行项, 小程序转换问题,所以用对象传进来
handleEditSceneItem(val) {
const storage = uni.getStorageSync(val.type);
if (storage) {
uni.removeStorageSync(val.type);
}
uni.setStorageSync(val.type, val.item);
uni.removeStorageSync('callback');
if (val.item.type === 5 || val.item.type === 6) {
if (val.item.source === 1) {
uni.$u.route('/pagesA/scene/product/device', {
type: val.type,
editIndex: val.index
});
} else if (val.item.source === 3) {
uni.$u.route('/pagesA/scene/product/index', {
type: val.type,
editIndex: val.index
});
}
} else {
if (val.item.source === 1 || val.item.source === 3) {
uni.$u.route('/pagesA/scene/product/model', {
type: val.type,
editIndex: val.index
});
} else if (val.item.source === 2) {
uni.$u.route('/pagesA/scene/timing/index', {
type: val.type,
editIndex: val.index
});
} else if (val.item.source === 4) {
uni.$u.route('/pagesA/scene/warning/index', {
editIndex: val.index
});
}
}
},
// 保存
handleSave() {
const { sceneId, sceneName, triggers, actions } = this.sceneData;
this.sceneData.checkDelay = this.checkDelay;
if (!sceneName) {
uni.showToast({
icon: 'none',
title: this.$tt('sceneDetail.sceneValidate'),
});
return
};
if (triggers.length === 0) {
uni.showToast({
icon: 'none',
title: this.$tt('sceneDetail.triggerValidate'),
});
return
};
if (actions.length === 0) {
uni.showToast({
icon: 'none',
title: this.$tt('sceneDetail.taskValidate'),
});
return
};
if (sceneId) {
updateScene(this.sceneData).then(res => {
if (res.code === 200) {
uni.showToast({
icon: 'none',
title: this.$tt('common.updateSuccessful'),
});
uni.reLaunch({
url: '/pages/tabBar/scene/index'
});
}
});
} else {
addScene(this.sceneData).then(res => {
if (res.code === 200) {
uni.showToast({
icon: 'none',
title: this.$tt('common.addSuccessful'),
});
uni.reLaunch({
url: '/pages/tabBar/scene/index'
});
}
});
}
},
getKey(index, item) {
return `${index}-${item.id}`
},
// 存储条件、动作来源
setSourceData(type, source) {
let trigger = {
id: '',
name: '',
operator: '',
value: '',
parentId: '', // 父类id
parentName: '', // 父类名称
arrayIndex: '', // 索引
arrayIndexName: '', // 索引名称
productId: null,
productName: '',
deviceCount: 0, // 设备数量
deviceNums: [], // 设备编号
isAdvance: 0, // 自定义cron
cronExpression: '', // cron表达式
source: 1, // 1=设备2=定时3=产品
type: 1, // 1=属性2=功能3=事件
scriptPurpose: 2, // 脚本用途1=数据流2=触发器3=执行动作)
};
let action = {
id: '',
name: '',
value: '',
parentId: '',
parentName: '',
arrayIndex: '',
arrayIndexName: '',
productId: '',
productName: '',
deviceCount: 0,
deviceNums: [],
source: 1,
type: 1,
scriptPurpose: 3,
};
const storage = uni.getStorageSync(type);
if (storage) {
uni.removeStorageSync(type);
}
if (type === 'trigger') {
trigger = {
...trigger,
source
};
uni.setStorageSync('trigger', trigger);
} else {
action = {
...action,
source
};
uni.setStorageSync('action', action);
}
}
}
};
</script>
<style lang="scss">
page {
height: 100%;
background: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.scene-detail-wrap {
height: 100%;
width: 100%;
position: relative;
.name-wrap {
margin: 40rpx 30rpx 20rpx 30rpx;
background: #FFFFFF;
border-radius: 20rpx;
}
.container-wrap {
padding-bottom: 220rpx;
.cell-group-wrap {
margin: 30rpx;
.cell-wrap {
background: #FFFFFF;
border-radius: 20rpx;
padding: 2rpx 0;
}
}
.condition-wrap,
.execute-wrap {
margin: 20rpx 30rpx;
background: #fff;
border-radius: 12rpx;
padding-bottom: 12rpx;
.title-wrap {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 30rpx;
.left-wrap {
flex: 1;
display: flex;
flex-direction: column;
.title {
font-size: 18px;
}
.condition {
display: flex;
flex-direction: row;
justify-content: space-around;
background: #F4F4F4;
border-radius: 8rpx;
font-weight: 400;
font-size: 10px;
color: #333333;
line-height: 21px;
text-align: left;
font-style: normal;
padding: 0 14rpx;
}
}
.right-wrap {}
}
.timer-wrap {
margin: 0 31rpx 30rpx;
::v-deep .u-form-item__body {
padding: 0 !important;
}
}
.empty-cell {
padding: 30rpx;
.text {
height: 96rpx;
border: 2rpx dashed #8686862e;
border-radius: 12rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 28rpx;
}
}
.swipe-action-wrap {
.action-item-wrap {
margin: 0rpx 26rpx 20rpx 26rpx;
.slot-title {
display: flex;
flex-direction: row;
align-items: center;
border-radius: 10rpx;
.cell-text {
display: flex;
flex-direction: column;
justify-content: center;
margin-left: 24rpx;
min-height: 124rpx;
.title {
font-size: 32rpx;
}
.des {
font-size: 28rpx;
color: #868686;
}
}
}
}
}
.btn-wrap {
padding: 0 28rpx;
margin-top: 30rpx;
}
}
.btn-save-wrap {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 999;
padding: 0 30rpx 96rpx 30rpx;
background: #eef3f7f7;
::v-deep .u-button--square {
border-radius: 10rpx;
}
}
}
.other {
.cond-popup-wrap {
padding: 10rpx 0;
.cell-group-wrap {
background: #eef3f7;
.cell-wrap {
text-align: center;
background: #fff;
padding: 5rpx 0;
border-bottom: 1rpx solid #f8f8f8;
&:last-child {
margin-top: 15rpx;
}
}
}
}
.more-popup-wrap {
margin: 30rpx;
.remark {
color: #868686;
font-size: 24rpx;
}
.form-wrap {
margin: 30rpx 0;
.form-item-wrap {
background: #fff;
border-radius: 12rpx;
padding: 4rpx 30rpx;
margin: 20rpx 0;
}
}
}
.trigger-popup-wrap {
padding: 30rpx;
.title {
font-size: 32rpx;
text-align: center;
margin-bottom: 30rpx;
}
.cell-group-wrap {
.cell-wrap {
padding: 6rpx 0;
}
}
}
}
}
</style>