场景联动

This commit is contained in:
2023-04-28 17:37:34 +08:00
parent 5fecd74d59
commit e08ed33cd9
11 changed files with 1542 additions and 187 deletions

44
src/api/device/linkage.js Normal file
View File

@ -0,0 +1,44 @@
import request from "@/utils/request";
// 查询场景联动列表
export function listLinkage(query) {
return request({
url: "/device/linkage/list",
method: "get",
params: query,
});
}
// 查询场景联动详细
export function getLinkage(sceneId) {
return request({
url: "/device/linkage/" + sceneId,
method: "get",
});
}
// 新增场景联动
export function addLinkage(data) {
return request({
url: "/device/linkage",
method: "post",
data: data,
});
}
// 修改场景联动
export function updateLinkage(data) {
return request({
url: "/device/linkage",
method: "put",
data: data,
});
}
// 删除场景联动
export function delLinkage(sceneId) {
return request({
url: "/device/linkage/" + sceneId,
method: "delete",
});
}

View File

@ -8,10 +8,7 @@
width="320"
>
<template #reference>
<el-badge
:hidden="unreadCount === 0"
:value="unreadCount"
>
<el-badge :hidden="unreadCount === 0" :value="unreadCount">
<el-icon class="notification-icon" @click="visible = !visible">
<Bell />
</el-icon>
@ -24,7 +21,7 @@
<div class="notification-content">
<div class="status-tab">
<el-radio-group
v-model="status"
v-model="queryParams.status"
class="ios-style-radio"
size="small"
>
@ -42,7 +39,7 @@
{{ selectMulti ? "取消" : "选择" }}
</el-button>
<el-button
v-if="ids.length && status === 2"
v-if="ids.length && queryParams.status === 2"
link
size="small"
type="danger"
@ -50,18 +47,14 @@
>删除
</el-button>
<el-button
v-if="ids.length && status === 1"
v-if="ids.length && queryParams.status === 1"
link
size="small"
type="danger"
@click="markAsRead"
>标为已读
</el-button>
<el-button
link
size="small"
type="primary"
@click="refreshList"
<el-button link size="small" type="primary" @click="refreshList"
>刷新
</el-button>
</div>
@ -87,7 +80,9 @@
<span class="notification-title">{{
item.msgTitle
}}</span>
<span class="notification-time">{{ dayjs(item.sendTime).format("YYYY-MM-DD HH:mm:ss") }}</span>
<span class="notification-time">{{
dayjs(item.sendTime).format("YYYY-MM-DD HH:mm:ss")
}}</span>
</div>
</div>
</template>
@ -116,8 +111,7 @@
<!-- </template>-->
{{ msgDetail.msgContent }}
</el-dialog>
<el-dialog v-model="showOfflineNotice"
title="下线通知"></el-dialog>
<el-dialog v-model="showOfflineNotice" title="下线通知"></el-dialog>
</div>
</template>
@ -146,9 +140,9 @@ const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
status: 1
status: 1,
},
msgDetail: {}
msgDetail: {},
});
const router = useRouter();
const loadKey = ref(0);
@ -166,36 +160,26 @@ const ids = ref([]); //选中的列表
const refreshList = () => {
ids.value = [];
selectMulti.value = false;
queryParams.value.status = status.value;
queryParams.value.pageNum = 1;
// if (status.value === 2) {
notificationList.value = [];
// }
loadKey.value += 1;
};
watch(status, (value) => {
watch(
() => queryParams.value.status,
(value) => {
ids.value = [];
selectMulti.value = false;
queryParams.value.status = value;
queryParams.value.pageNum = 1;
// if (value === 2) {
notificationList.value = [];
// }
loadKey.value += 1;
});
}
);
const loadMore = async ($state) => {
// console.log($state);
try {
loading.value = true;
const resp = await listMsg(queryParams.value);
// if (queryParams.value.status === 0) {
// notificationStore.pushItems(resp.rows);
// } else if (queryParams.value.status === 2) {
notificationList.value.push(...resp.rows);
// }
// queryParams.value.status === 0 &&
// resp.rows.length &&
// updateMsg(resp.rows.map((el) => el.msgId).join(","), 1);
total.value = resp.total;
if (queryParams.value.status === 1) {
unreadCount.value = resp.total;
@ -232,9 +216,6 @@ const deleteReadMsg = async () => {
const markAsRead = async () => {
await updateMsg(ids.value.join(","), 2);
refreshList();
/* for (const id of ids.value) {
notificationStore.removeItem(id);
}*/
ids.value = [];
selectMulti.value = false;
};
@ -256,55 +237,40 @@ function logout() {
cancelButtonText: "取消",
showCancelButton: false,
showClose: false,
type: "warning"
type: "warning",
})
.then(() => {
userStore.logOut().then(() => {
location.href = "/index";
// router.go("/index")
});
})
.catch(() => {
userStore.logOut().then(() => {
location.href = "/index";
// router.go("/index")
});
});
}
watch(visible, (value) => {
if (value) {
/*当打开通知弹窗时*/
notificationList.value = [];
queryParams.value.pageNum = 1;
queryParams.value.status = 1;
status.value = 1;
loadKey.value++;
}
});
/*获取未读数量*/
const getUnreadCount = async () => {
// loading.value = true;
// notificationList.value = [];
// queryParams.value.pageNum = 1;
// queryParams.value.status = 1;
// status.value = 1;
const resp = await listMsg({
pageSize: 10, pageNum: 1, status: 1
pageSize: 10,
pageNum: 1,
status: 1,
});
// if (queryParams.value.status === 0) {
// notificationStore.pushItems(resp.rows);
// } else if (queryParams.value.status === 2) {
// notificationList.value.push(...resp.rows);
// }
// queryParams.value.status === 0 &&
// resp.rows.length &&
// updateMsg(resp.rows.map((el) => el.msgId).join(","), 1);
unreadCount.value = resp.total;
// queryParams.value.pageNum++;
// loading.value = false;
};
onMounted(() => {
getUnreadCount();
if (userStore.userId) {
@ -337,20 +303,12 @@ onMounted(() => {
console.log(event.data);
const wsData = JSON.parse(event.data);
if (wsData.type === "msg") {
// if (visible.value) {
// refreshList();
// } else {
if (visible.value && queryParams.value.status === 1) {
refreshList();
} else {
getUnreadCount();
}
// }
// const msg = wsData.data;
// updateMsg(msg.msgId, 1);
// notificationStore.pushItem(msg);
} else if (wsData.type === "offline") {
// showOfflineNotice.value = true;
logout();
}
});
@ -396,7 +354,6 @@ onMounted(() => {
margin-top: 16px;
.notification-item {
display: flex;
align-items: center;
justify-content: space-between;
@ -449,7 +406,6 @@ onMounted(() => {
&:last-child {
border-bottom: 1px solid #eeefef;
}
}
}

View File

@ -11,6 +11,7 @@
v-loading="loading"
:data="dataList"
@select="handleSelect"
@row-click="handleRowClick"
@select-all="handleSelectAll"
>
<el-table-column
@ -53,8 +54,8 @@
<template #footer>
<div class="dialog-footer">
<el-button v-if="!readonly" type="primary" @click="confirmSelect"
>确认</el-button
>
>确认
</el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
@ -62,7 +63,7 @@
</template>
<script name="TableSelect" setup>
import { nextTick, reactive, ref, toRefs, watchEffect } from "vue";
import { nextTick, onMounted, reactive, ref, toRefs, watchEffect } from "vue";
import { cloneDeep } from "lodash-es";
import Pagination from "@/components/Pagination/index.vue";
@ -75,6 +76,14 @@ const props = defineProps({
type: Array,
default: [],
},
multiple: {
type: Boolean,
default: true,
},
multiPage: {
type: Boolean,
default: true,
},
readonly: {
type: Boolean,
default: false,
@ -90,7 +99,7 @@ const props = defineProps({
type: Boolean,
},
});
const { list } = toRefs(props);
const { list, multiple } = toRefs(props);
const emit = defineEmits(["update:modelValue", "confirm-select"]);
@ -102,8 +111,8 @@ const selectedProperties = ref([]);
const ids = ref([]);
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
pageNum: props.multiPage ? 1 : null,
pageSize: props.multiPage ? 10 : null,
},
});
@ -135,19 +144,35 @@ const cancel = () => {
selectedProperties.value = [];
emit("update:modelValue", false);
};
const handleSelectChange = (selection) => {};
const handleRowClick = (row, _, event) => {
event.target?.parentNode?.parentNode?.firstElementChild?.firstElementChild?.firstElementChild?.click();
};
/*监听单选事件*/
const handleSelect = (selection, row) => {
const isCheck =
selection.findIndex((el) => el[props.selectKey] === row[props.selectKey]) >
-1;
-1; /* 是否将行切换为选中状态 */
if (isCheck) {
/*如果是单选状态 multiple 为false */
if (!multiple.value) {
// nextTick(() => {
selectedProperties.value = [];
selection.forEach((el) => {
if (el[props.selectKey] !== row[props.selectKey]) {
tableRef.value.toggleRowSelection(el, false);
}
});
// });
}
/*添加选中*/
const isIdNotExist =
selectedProperties.value.findIndex(
(el) => el[props.selectKey] === row[props.selectKey]
) === -1;
if (isIdNotExist) {
console.log(isIdNotExist);
selectedProperties.value.push(row);
}
} else {
@ -205,13 +230,24 @@ const confirmSelect = () => {
defineExpose({
getList,
});
onMounted(() => {
document.documentElement.style.setProperty(
"--show-select-all",
multiple.value ? "visible" : "hidden"
);
document.documentElement.style.setProperty(
"--checkbox-round",
multiple.value ? "inherit" : "50%"
);
});
</script>
<style lang="scss" scoped>
:deep(.el-table__header) {
.el-table-column--selection {
.el-checkbox {
//visibility: hidden;
visibility: var(--show-select-all);
}
}
}

View File

@ -323,3 +323,16 @@ export const noticeStatusDict = [
elTagType: "success",
},
];
export const triggerTypeDict = [
{
label: "设备触发",
value: "1",
elTagType: "danger",
},
{
label: "正常",
value: "0",
elTagType: "success",
},
];

View File

@ -20,7 +20,7 @@ export default {
/**
* 是否显示 tagsView
*/
tagsView: true,
tagsView: false,
/**
* 是否固定头部

View File

@ -11,7 +11,7 @@ const useUserStore = defineStore("user", {
roles: [],
userId: null,
permissions: [],
uniqueId: ""
uniqueId: "",
}),
actions: {
// 登录
@ -77,11 +77,11 @@ const useUserStore = defineStore("user", {
reject(error);
});
});
}
},
},
persist: {
paths: ["uniqueId"]
}
paths: ["uniqueId"],
},
});
export default useUserStore;

File diff suppressed because it is too large Load Diff

View File

@ -666,7 +666,13 @@
<script name="Alert" setup>
import Crontab from "@/components/Crontab/index.vue";
import { addAlertRule, delAlertRule, getAlertRule, listAlertRule, updateAlertRule } from "@/api/product/alertRule";
import {
addAlertRule,
delAlertRule,
getAlertRule,
listAlertRule,
updateAlertRule,
} from "@/api/product/alertRule";
import { getCurrentInstance, reactive, ref, toRefs, watch } from "vue";
import { useDict } from "@/utils/dict";
import { listProduct } from "@/api/product/product";
@ -680,116 +686,116 @@ const { proxy } = getCurrentInstance();
const props = defineProps({
product: {
type: Object,
default: null
default: null,
},
productId: {
type: Number,
default: null
}
default: null,
},
});
const { product, productId } = toRefs(props);
const timerWeeks = [
{
value: 1,
label: "周一"
label: "周一",
},
{
value: 2,
label: "周二"
label: "周二",
},
{
value: 3,
label: "周三"
label: "周三",
},
{
value: 4,
label: "周四"
label: "周四",
},
{
value: 5,
label: "周五"
label: "周五",
},
{
value: 6,
label: "周六"
label: "周六",
},
{
value: 7,
label: "周日"
}
label: "周日",
},
];
// 执行动作源
const actionSource = [
{
value: 1,
label: "设备"
label: "设备",
},
{
value: 3,
label: "告警输出"
}
label: "告警输出",
},
];
// 物模型类别
const modelTypes = [
{
value: "1",
label: "属性"
label: "属性",
},
{
value: "2",
label: "服务"
label: "服务",
},
{
value: "3",
label: "事件"
label: "事件",
},
{
value: "4",
label: "设备上线"
label: "设备上线",
},
{
value: "5",
label: "设备下线"
}
label: "设备下线",
},
];
// 触发器条件
const triggerConditions = [
{
value: "2",
label: "满足所有条件"
label: "满足所有条件",
},
{
value: "1",
label: "满足任一条件"
}
label: "满足任一条件",
},
];
// 告警状态
const alertType = [
{
value: 1,
label: "启动"
label: "启动",
},
{
value: 2,
label: "停止"
}
label: "停止",
},
];
// 周
const timerWeekRepeats = ref([
{
value: "1",
label: "每天"
label: "每天",
},
{
value: "2",
label: "仅此一次"
label: "仅此一次",
},
{
value: "3",
label: "指定"
}
label: "指定",
},
]);
const productOptions = ref([]);
const noticeConfigOptions = ref([]); /*通知配置选项*/
@ -813,12 +819,12 @@ const modelList = ref([]);
const triggerSource = ref([
{
value: "1",
label: "设备触发"
label: "设备触发",
},
{
value: "2",
label: "定时触发"
}
label: "定时触发",
},
]);
const { iot_alert_level, sys_job_status } = useDict(
@ -829,7 +835,7 @@ const data = reactive({
form: {
conditions: "1", // 触发器条件
triggers: [],
actions: []
actions: [],
},
queryParams: {
pageNum: 1,
@ -840,25 +846,25 @@ const data = reactive({
productId: null,
triggers: null,
actions: null,
status: null
status: null,
},
rules: {
noticeConfigId: [
{ required: true, message: "告警通知不能为空", trigger: "change" }
{ required: true, message: "告警通知不能为空", trigger: "change" },
],
alertName: [
{ required: true, message: "告警名称不能为空", trigger: "blur" }
{ required: true, message: "告警名称不能为空", trigger: "blur" },
],
alertLevel: [
{ required: true, message: "告警级别不能为空", trigger: "change" }
{ required: true, message: "告警级别不能为空", trigger: "change" },
],
productId: [{ required: true, message: "产品不能为空", trigger: "change" }],
triggers: [{ required: true, message: "触发器不能为空", trigger: "blur" }],
actions: [{ required: true, message: "执行动作不能为空", trigger: "blur" }]
actions: [{ required: true, message: "执行动作不能为空", trigger: "blur" }],
},
// 产品
productInfo: {},
thingsModel: {}
thingsModel: {},
});
const { queryParams, form, rules, productInfo } = toRefs(data);
@ -866,7 +872,7 @@ const getNoticeConfigOption = (keyword) => {
listNoticeConfig({
configName: keyword,
pageNum: 1,
pageSize: 100
pageSize: 100,
}).then((resp) => {
noticeConfigOptions.value = resp.rows;
});
@ -912,14 +918,14 @@ function reset() {
modelValue: null,
triggerType: "1", //1=设备2=定时3=告警输出
modelType: "1", // 1=属性2=服务
cronExpression: null
cronExpression: null,
// jobId: 0,
// deviceId: 0,
// deviceName: "请选择一个设备",
// id: "",
// name: "",
// isAdvance: 1
}
},
],
actions: [
{
@ -928,14 +934,14 @@ function reset() {
modelValue: null,
modelKey: null,
operator: null,
modelType: "1" // 1=属性2=服务
modelType: "1", // 1=属性2=服务
// id: "",
// name: "",
// deviceId: 0,
// deviceName: "请选择一个设备",
// triggerType: "1", //1=设备2=定时3=告警输出
}
]
},
],
};
proxy.resetForm("alertRef");
}
@ -1021,7 +1027,7 @@ function submitForm() {
function handleDelete(row) {
const _alertIds = row.alertId || ids.value;
proxy.$modal
.confirm("是否确认删除设备告警编号为\"" + _alertIds + "\"的数据项?")
.confirm('是否确认删除设备告警编号为"' + _alertIds + '"的数据项?')
.then(function () {
return delAlertRule(_alertIds);
})
@ -1029,8 +1035,7 @@ function handleDelete(row) {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {
});
.catch(() => {});
}
/** 导出按钮操作 */
@ -1038,7 +1043,7 @@ function handleExport() {
proxy.download(
"device/alert/export",
{
...queryParams.value
...queryParams.value,
},
`alert_${new Date().getTime()}.xlsx`
);
@ -1052,7 +1057,7 @@ const addActionItem = () => {
modelValue: "",
modelKey: "",
operator: "",
modelType: "1" // 1=属性2=服务
modelType: "1", // 1=属性2=服务
// id: "",
// name: "",
// value: ""
@ -1079,20 +1084,20 @@ const setTriggerSource = () => {
triggerSource.value = [
{
value: "1",
label: "设备触发"
}
label: "设备触发",
},
];
} else {
//定时
triggerSource.value = [
{
value: "1",
label: "设备触发"
label: "设备触发",
},
{
value: "2",
label: "定时触发"
}
label: "定时触发",
},
];
}
/**/
@ -1110,7 +1115,7 @@ const addTriggerItem = () => {
modelValue: null,
// conditions: "2",
operator: null,
modelId: null
modelId: null,
// value: "",
// id: "",
// name: "",
@ -1185,7 +1190,7 @@ function getProductOptions(keyword) {
listProduct({
productName: keyword,
pageNum: 1,
pageSize: 20
pageSize: 20,
}).then((resp) => {
productOptions.value = resp.rows;
});
@ -1195,7 +1200,7 @@ function getDeviceOptions(keyword) {
listDevice({
deviceName: keyword,
pageNum: 1,
pageSize: 20
pageSize: 20,
}).then((resp) => {
deviceOptions.value = resp.rows;
});
@ -1203,7 +1208,7 @@ function getDeviceOptions(keyword) {
const getThingsModelOptions = (productId) => {
listModel({
productId
productId,
}).then((resp) => {
modelList.value = resp.rows;
});
@ -1227,7 +1232,7 @@ watch(
getList();
},
{
immediate: true
immediate: true,
}
);

View File

@ -118,7 +118,7 @@
>
<firmware v-if="form.productId" :product-id="form.productId" />
</el-tab-pane>
<el-tab-pane :disabled="!form.productId" label="告警配置" lazy>
<el-tab-pane :disabled="!form.productId" label="告警联动" lazy>
<alert
v-if="form.productId"
:product="form"

View File

@ -198,20 +198,20 @@
<!-- />-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column align="center" label="图片" prop="imgUrl">
<template #default="{ row }">
<el-image
:preview-src-list="[`${baseUrl}/${row.imgUrl}`]"
:src="`${baseUrl}/${row.imgUrl}`"
></el-image>
</template>
</el-table-column>
<!-- <el-table-column align="center" label="图片" prop="imgUrl">-->
<!-- <template #default="{ row }">-->
<!-- <el-image-->
<!-- :preview-src-list="[`${baseUrl}/${row.imgUrl}`]"-->
<!-- :src="`${baseUrl}/${row.imgUrl}`"-->
<!-- ></el-image>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column align="center" label="备注" prop="remark" />
<el-table-column
align="center"
class-name="small-padding fixed-width"
label="操作"
width="190px"
width="240px"
>
<template #default="scope">
<!-- TODO:-->
@ -231,7 +231,17 @@
>详情
<!-- {{ scope.row.status === 2 ? "查看" : "" }}-->
</el-button>
<el-button link type="primary" @click="viewDeviceList(scope.row)"
<el-button
link
type="primary"
@click="viewEditTab(scope.row, 'model')"
>物模型
<!-- {{ scope.row.status === 2 ? "查看" : "" }}-->
</el-button>
<el-button
link
type="primary"
@click="viewEditTab(scope.row, 'device')"
>设备列表
<!-- {{ scope.row.status === 2 ? "查看" : "" }}-->
</el-button>
@ -463,14 +473,14 @@ function handleUpdate(row) {
// });
}
const viewDeviceList = (row) => {
const viewEditTab = (row, tab) => {
const _productId = row.productId;
router.push({
path: "/produc/product/edit",
query: {
productId: _productId,
pageNum: queryParams.value.pageNum,
initTab: "device",
// pageNum: queryParams.value.pageNum,
initTab: tab,
},
});
};

View File

@ -97,16 +97,16 @@
>删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['sys:tenant:export']"
icon="Download"
plain
type="warning"
@click="handleExport"
>导出
</el-button>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- v-hasPermi="['sys:tenant:export']"-->
<!-- icon="Download"-->
<!-- plain-->
<!-- type="warning"-->
<!-- @click="handleExport"-->
<!-- >导出-->
<!-- </el-button>-->
<!-- </el-col>-->
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
@ -384,10 +384,18 @@ const data = reactive({
loginAccount: null,
},
rules: {
expireTime: [
{
required: true,
message: "请选择到期时间",
trigger: "change",
},
],
tenantName: [
{ required: true, message: "请输入租户名称", trigger: "blur" },
],
},
loginUserForm: {},
loginUserRules: {
username: [