Merge branch 'dev' into dev-hub

This commit is contained in:
jackhoo_98 2023-03-20 15:11:18 +08:00
commit fec6852bc4
47 changed files with 435 additions and 215 deletions

View File

@ -3,4 +3,4 @@ import server from '@/utils/request';
// 保存
export const save_api = (data: any) => server.post(`/system/config/scope/_save`, data)
// 获取详情
export const getDetails_api = (data: any) => server.post(`/system/config/scopes`, data)
export const getDetails_api = (data: any) => server.post<any>(`/system/config/scopes`, data)

View File

@ -4,7 +4,13 @@ const color = {
'error': '229, 0, 18',
'success': '36, 178, 118',
'warning': '255, 144, 0',
'default': '102, 102, 102'
'default': '102, 102, 102',
//告警颜色
'level1': '229, 0, 18',
'level2': '255, 148, 87',
'level3': '250, 189, 71',
'level4': '153, 153, 153',
'level5': '196, 196, 196'
}
export const getHexColor = (code: string, pe: number = 0.1) => {
const _color = color[code] || color.default

View File

@ -5,7 +5,14 @@
:class="{ active: active ? 'active' : '' }"
@click="handleClick"
>
<div class="card-content">
<div class="card-type" v-if="slots.type">
<div class="card-type-text"><slot name="type"></slot></div>
</div>
<div
class="card-content"
:class="{'card-content-top-line': !slots.type}"
:style="{ paddingTop: slots.type ? '40px' : '30px' }"
>
<div
class="card-content-bg1"
:style="{
@ -94,11 +101,12 @@ type EmitProps = {
type TableActionsType = Partial<ActionsType>;
const emit = defineEmits<EmitProps>();
const slots = useSlots();
const props = defineProps({
value: {
type: Object as PropType<Record<string, any>>,
default: () => {},
default: () => ({}),
},
showStatus: {
type: Boolean,
@ -118,6 +126,7 @@ const props = defineProps({
},
statusNames: {
type: Object,
default:()=>({'default':'default'})
},
actions: {
type: Array as PropType<TableActionsType[]>,
@ -180,6 +189,7 @@ const handleClick = () => {
.card-warp {
position: relative;
border: 1px solid #e6e6e6;
overflow: hidden;
&:hover {
cursor: pointer;
@ -195,25 +205,29 @@ const handleClick = () => {
border: 1px solid #2f54eb;
}
.card-type {
position: absolute;
top: 0;
left: -15px;
height: 32px;
padding: 0 30px;
color: rgba(0, 0, 0, 0.65);
line-height: 32px;
background-color: rgba(0, 0, 0, 0.06);
transform: skewX(-45deg);
.card-type-text {
display: flex;
align-items: center;
justify-content: center;
transform: skewX(45deg);
}
}
.card-content {
position: relative;
padding: 30px 12px 16px 30px;
overflow: hidden;
&::before {
position: absolute;
top: 0;
left: 30px + 10px;
display: block;
width: 15%;
min-width: 64px;
height: 2px;
background-image: url('/images/rectangle.png');
background-repeat: no-repeat;
background-size: 100% 100%;
content: ' ';
}
.card-item-avatar {
margin-right: 16px;
}
@ -276,6 +290,22 @@ const handleClick = () => {
}
}
.card-content-top-line {
&::before {
position: absolute;
top: 0;
left: 30px + 10px;
display: block;
width: 15%;
min-width: 64px;
height: 2px;
background-image: url('/images/rectangle.png');
background-repeat: no-repeat;
background-size: 100% 100%;
content: ' ';
}
}
.card-content-bg1 {
position: absolute;
right: -5%;

View File

@ -2,32 +2,42 @@ import { defineStore } from 'pinia';
import { systemVersion } from '@/api/comm'
import { useMenuStore } from './menu'
import { getDetails_api } from '@/api/system/basis';
import type { ConfigInfoType } from '@/views/system/Basis/typing';
type SystemStateType = {
isCommunity: boolean;
configInfo: Partial<ConfigInfoType>;
}
export const useSystem = defineStore('system', {
state: () => ({
isCommunity: false,
configInfo: [] as any[]
}),
actions: {
getSystemVersion(): Promise<any[]> {
this.getSystemConfig();
return new Promise(async (res, rej) => {
const resp = await systemVersion()
if (resp.success && resp.result) {
const isCommunity = resp.result.edition === 'community'
this.isCommunity = isCommunity
// 获取菜单
const menu = useMenuStore()
const menuData: any[] = await menu.queryMenuTree(isCommunity)
res(menuData)
state: (): SystemStateType => ({
isCommunity: false,
// configInfo: [] as any[]
configInfo: {}
}),
actions: {
getSystemVersion(): Promise<any[]> {
this.getSystemConfig();
return new Promise(async (res, rej) => {
const resp = await systemVersion()
if (resp.success && resp.result) {
const isCommunity = resp.result.edition === 'community'
this.isCommunity = isCommunity
// 获取菜单
const menu = useMenuStore()
const menuData: any[] = await menu.queryMenuTree(isCommunity)
res(menuData)
}
})
},
async getSystemConfig() {
const params = ['front', 'amap', 'paths'];
const { status, result } = await getDetails_api(params);
if (status === 200) {
params.forEach((key: string) => {
this.configInfo[key] = { ...result.find((item: any) => item.scope === key)?.properties }
})
}
}
})
},
getSystemConfig() {
const params = ['front', 'amap', 'paths'];
getDetails_api(params).then(({ status, result }: any) => {
this.configInfo = status === 200 ? [...result] : [];
})
}
}
})

View File

@ -1,4 +1,41 @@
.ant-form-item-required:before {
position: absolute;
right: -12px;
}
}
// 说明文档样式
.doc {
height: 750px;
padding: 24px;
overflow-y: auto;
color: rgba(#000, 0.8);
font-size: 14px;
background-color: #fafafa;
.url {
padding: 8px 16px;
color: #2f54eb;
background-color: rgba(#a7bdf7, 0.2);
}
h1 {
margin: 16px 0;
color: rgba(#000, 0.85);
font-weight: bold;
font-size: 14px;
&:first-child {
margin-top: 0;
}
}
h2 {
margin: 6px 0;
color: rgba(0, 0, 0, 0.8);
font-size: 14px;
}
.image {
margin: 16px 0;
}
}

View File

@ -109,7 +109,8 @@
style="padding: 0 5px"
@click="i.onClick"
type="link"
:hasPermission="'Northbound/AliCloud:' + i.key"
:danger="i.key === 'delete'"
:hasPermission="i.key === 'view' ? true : 'Northbound/AliCloud:' + i.key"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>

View File

@ -112,7 +112,8 @@
style="padding: 0 5px"
@click="i.onClick"
type="link"
:hasPermission="'Northbound/DuerOS:' + i.key"
:danger="i.key === 'delete'"
:hasPermission="i.key === 'view' ? true : 'Northbound/DuerOS:' + i.key"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>

View File

@ -52,6 +52,7 @@
@click="i.onClick"
type="link"
style="padding: 0px"
:danger="i.key === 'delete'"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>

View File

@ -54,11 +54,11 @@
</div>
</j-col>
</j-row>
<j-row :span="24">
<j-row :span="24" v-if="AmapKey">
<j-col :span="24">
<div class="device-position">
<Guide title="设备分布"></Guide>
<div class="device-map">
<div class="device-map" >
<Amap></Amap>
</div>
</div>
@ -83,6 +83,7 @@ import type { Footer } from '@/views/device/DashBoard/typings';
import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
import { useMenuStore } from '@/store/menu';
import Amap from './components/Amap.vue';
let AmapKey = localStorage.getItem('amap_key');
let productTotal = ref(0);
let productFooter = ref<Footer[]>([
{

View File

@ -232,7 +232,8 @@
@click="i.onClick"
type="link"
style="padding: 0 5px"
:hasPermission="'device/Instance:' + i.key"
:danger="i.key === 'delete'"
:hasPermission="i.key === 'view' ? true : 'device/Instance:' + i.key"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>

View File

@ -11,12 +11,12 @@
</div>
</template>
<j-descriptions-item label="ID">{{
productStore.current.id
}}</j-descriptions-item>
<j-descriptions-item label="产品分类">{{
productStore.current.classifiedName
}}</j-descriptions-item>
<j-descriptions-item label="ID">
<Ellipsis>{{ productStore.current.id }}</Ellipsis>
</j-descriptions-item>
<j-descriptions-item label="产品分类">
<Ellipsis>{{ productStore.current.classifiedName }}</Ellipsis>
</j-descriptions-item>
<j-descriptions-item label="设备类型">{{
productStore.current.deviceType?.text
}}</j-descriptions-item>
@ -29,10 +29,14 @@
}}</j-button>
</j-descriptions-item>
<j-descriptions-item label="创建时间">{{
moment(productStore.current.createTime).format('YYYY-MM-DD HH:mm:ss')
moment(productStore.current.createTime).format(
'YYYY-MM-DD HH:mm:ss',
)
}}</j-descriptions-item>
<j-descriptions-item label="更新时间">{{
moment(productStore.current.modifyTime).format('YYYY-MM-DD HH:mm:ss')
moment(productStore.current.modifyTime).format(
'YYYY-MM-DD HH:mm:ss',
)
}}</j-descriptions-item>
<j-descriptions-item label="说明" :span="3">
@ -41,7 +45,7 @@
</j-descriptions>
</j-card>
<!-- 编辑 -->
<Save ref="saveRef" :isAdd="isAdd" :title="title" @success="refresh"/>
<Save ref="saveRef" :isAdd="isAdd" :title="title" @success="refresh" />
</template>
<script lang="ts" setup>
@ -74,7 +78,7 @@ const changeTables = () => {
/**
* 修改成功刷新
*/
const refresh = () =>{
const refresh = () => {
productStore.refresh(route.params.id as string);
}
};
</script>

View File

@ -317,7 +317,7 @@
:status="slotProps.state.value"
:statusText="slotProps.state.text"
:statusNames="{
enabled: 'success',
enabled: 'processing',
disabled: 'error',
}"
>
@ -1009,6 +1009,7 @@ const submitDevice = async () => {
});
if (resp.status === 200) {
message.success('操作成功!');
productStore.current!.storePolicy = storePolicy
if ((window as any).onTabSaveSuccess) {
if (resp.result) {
(window as any).onTabSaveSuccess(resp);

View File

@ -8,7 +8,14 @@
<template #title>
<div>
<div style="display: flex; align-items: center">
<div>{{ productStore.current.name }}</div>
<a-tooltip>
<template #title>{{
productStore.current.name
}}</template>
<div class="productDetailHead">
{{ productStore.current.name }}
</div>
</a-tooltip>
<div style="margin: -5px 0 0 20px">
<j-popconfirm
title="确认禁用"
@ -237,15 +244,19 @@ const jumpDevice = () => {
termType: 'eq',
value: productStore.current?.id,
};
menuStory.jumpPage('device/Instance',{},{
target: 'device-instance',
q: JSON.stringify({ terms: [{ terms: [{searchParams}] }] }),
});
menuStory.jumpPage(
'device/Instance',
{},
{
target: 'device-instance',
q: JSON.stringify({ terms: [{ terms: [{ searchParams }] }] }),
},
);
};
onMounted(() => {
getProtocol();
if(history.state?.params?.tab){
productStore.tabActiveKey = history.state?.params?.tab
if (history.state?.params?.tab) {
productStore.tabActiveKey = history.state?.params?.tab;
}
});
</script>
@ -254,4 +265,11 @@ onMounted(() => {
.ant-switch-disabled {
cursor: not-allowed;
}
.productDetailHead {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -48,7 +48,7 @@
:status="slotProps.state"
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
:statusNames="{
1: 'success',
1: 'processing',
0: 'error',
}"
>
@ -118,9 +118,13 @@
</CardBox>
</template>
<template #state="slotProps">
<j-badge
<BadgeStatus
:text="slotProps.state === 1 ? '正常' : '禁用'"
:status="statusMap.get(slotProps.state)"
:status="slotProps.state"
:statusNames="{
1: 'processing',
0: 'error',
}"
/>
</template>
<template #action="slotProps">
@ -139,6 +143,7 @@
@click="i.onClick"
type="link"
style="padding: 0px"
:danger="i.key === 'delete'"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>
@ -188,9 +193,6 @@ const menuStory = useMenuStore();
const isAdd = ref<number>(0);
const title = ref<string>('');
const params = ref<Record<string, any>>({});
const statusMap = new Map();
statusMap.set(1, 'success');
statusMap.set(0, 'error');
const columns = [
{
title: 'ID',

View File

@ -41,7 +41,7 @@
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
online: 'success',
online: 'processing',
offline: 'error',
notActive: 'warning',
}"
@ -100,9 +100,14 @@
</CardBox>
</template>
<template #state="slotProps">
<j-badge
<BadgeStatus
:status="slotProps.state?.value"
:text="slotProps.state?.text"
:status="statusMap.get(slotProps.state?.value)"
:statusNames="{
online: 'processing',
offline: 'error',
notActive: 'warning',
}"
/>
</template>
<template #createTime="slotProps">
@ -125,7 +130,8 @@
@click="i.onClick"
type="link"
style="padding: 0 5px"
:hasPermission="'edge/Device:' + i.key"
:danger="i.key === 'delete'"
:hasPermission="i.key === 'view' ? true : 'edge/Device:' + i.key"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>
@ -155,6 +161,7 @@ import { query, _delete, _deploy, _undeploy } from '@/api/device/instance';
import { restPassword } from '@/api/edge/device';
import Save from './Save/index.vue';
import Import from '@/views/device/Instance/Import/index.vue';
import BadgeStatus from '@/components/BadgeStatus/index.vue';
const menuStory = useMenuStore();

View File

@ -19,7 +19,7 @@
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
enabled: 'success',
enabled: 'processing',
disabled: 'error',
}"
>
@ -81,9 +81,13 @@
</CardBox>
</template>
<template #state="slotProps">
<j-badge
<BadgeStatus
:status="slotProps.state?.value"
:text="slotProps.state?.text"
:status="statusMap.get(slotProps.state?.value)"
:statusNames="{
enabled: 'processing',
disabled: 'error',
}"
/>
</template>
<template #sourceId="slotProps">
@ -115,7 +119,8 @@
@click="i.onClick"
type="link"
style="padding: 0 5px"
:hasPermission="'edge/Resource:' + i.key"
:danger="i.key === 'delete'"
:hasPermission="i.key === 'view' ? true : 'edge/Resource:' + i.key"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>
@ -147,13 +152,14 @@ import dayjs from 'dayjs';
import { query, _delete, _start, _stop } from '@/api/edge/resource';
import Save from './Save/index.vue';
import Issue from './Issue/index.vue';
import BadgeStatus from '@/components/BadgeStatus/index.vue';
const menuStory = useMenuStore();
const defaultParams = { sorts: [{ name: 'createTime', order: 'desc' }] };
const statusMap = new Map();
statusMap.set('enabled', 'success');
statusMap.set('enabled', 'processing');
statusMap.set('disabled', 'error');
const options = [

View File

@ -74,12 +74,13 @@ watch(
/**
* 推送
*/
const successCount = ref(0);
const failCount = ref(0);
const flag = ref(false);
const successCount = ref<number>(0);
const failCount = ref<number>(0);
const flag = ref<boolean>(false);
const errMessage = ref<any[]>([]);
const errStr = computed(() => JSON.stringify(errMessage.value));
const publish = () => {
successCount.value = 0;
const activeAPI = `${BASE_API_PATH}/media/gb28181-cascade/${
props.data.id
}/bindings/publish?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`;

View File

@ -239,6 +239,7 @@
placeholder="请选择IP地址"
:options="allList"
@change="setPorts"
showSearch
/>
</j-col>
<j-col :span="10">
@ -766,5 +767,5 @@ const handleSubmit = () => {
</script>
<style lang="less" scoped>
@import './index.less';
// @import './index.less';
</style>

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const Email = () => {
return (

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const Webhook = () => {
return (

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const AliyunSms = () => {
return (

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const AliyunVoice = () => {
return (

View File

@ -1,5 +1,5 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const DingTalkRebot = () => {
const b = '{name}';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const Email = () => {
const a = '{标题}';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
const Webhook = () => {
return (

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -1,4 +1,4 @@
import './index.less';
// import './index.less';
import { Image } from 'ant-design-vue';
import { getImage } from '@/utils/comm';

View File

@ -44,7 +44,7 @@
</j-radio-group>
</j-form-item>
<j-form-item label="说明" name="description">
<j-textarea v-model:value="form.description"></j-textarea>
<j-textarea v-model:value="form.description" showCount :maxlength="200"></j-textarea>
</j-form-item>
<PermissionButton type="primary" @click="handleSave" :hasPermission="['rule-engine/Alarm/Configuration:add','rule-engine/Alarm/Configuration:update']">保存</PermissionButton>
</j-form>

View File

@ -8,7 +8,7 @@
@cancel="closeModal"
@ok="saveCorrelation"
>
<pro-search :columns="columns" @search="handleSearch"/>
<pro-search :columns="columns" @search="handleSearch" />
<div style="height: 500px; overflow-y: auto">
<JProTable
model="CARD"
@ -30,14 +30,14 @@
:params="params"
>
<template #card="slotProps">
<SceneCard
<CardBox
:value="slotProps"
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:active="_selectedRowKeys.includes(slotProps.id)"
@click="handleClick"
:statusNames="{
started: 'success',
started: 'processing',
disable: 'error',
}"
>
@ -59,22 +59,22 @@
:src="typeMap.get(slotProps.triggerType)?.img"
/>
</template>
<template #title>
<template #content>
<Ellipsis style="width: calc(100% - 100px)">
<span style="font-size: 16px; font-weight: 600">
{{ slotProps.name }}
</span>
</Ellipsis>
</template>
<template #subTitle>
<Ellipsis :lineClamp="2">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
<div class="subTitle">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
</div>
</Ellipsis>
</template>
</SceneCard>
</CardBox>
</template>
</JProTable>
</div>
@ -84,7 +84,6 @@
<script lang="ts" setup>
import { query } from '@/api/rule-engine/scene';
import { bindScene } from '@/api/rule-engine/configuration';
import SceneCard from '@/views/rule-engine/Scene/SceneCard.vue';
import { getImage } from '@/utils/comm';
import { message } from 'ant-design-vue';
const columns = [
@ -204,7 +203,7 @@ log();
const handleSearch = (e: any) => {
params.value = e;
};
const emit = defineEmits(['closeSave','saveScene']);
const emit = defineEmits(['closeSave', 'saveScene']);
/**
* 保存选中关联场景
*/

View File

@ -11,21 +11,24 @@
>
<template #headerTitle>
<j-space>
<PermissionButton type="primary" @click="showModal" hasPermission="rule-engine/Alarm/Configuration:add">
<PermissionButton
type="primary"
@click="showModal"
hasPermission="rule-engine/Alarm/Configuration:add"
>
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
</j-space>
</template>
<template #card="slotProps">
<SceneCard
<CardBox
:value="slotProps"
@click="handleClick"
:actions="getActions(slotProps, 'card')"
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
started: 'success',
started: 'processing',
disable: 'error',
}"
>
@ -41,19 +44,19 @@
<template #img>
<img :src="typeMap.get(slotProps.triggerType)?.img" />
</template>
<template #title>
<template #content>
<Ellipsis style="width: calc(100% - 100px)">
<span style="font-size: 16px; font-weight: 600">
{{ slotProps.name }}
</span>
</Ellipsis>
</template>
<template #subTitle>
<Ellipsis :lineClamp="2">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
<div class="subTitle">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
</div>
</Ellipsis>
</template>
<template #actions="item">
@ -64,13 +67,19 @@
...item.tooltip,
}"
@click="item.onClick"
:hasPermission="'rule-engine/Alarm/Configuration:'+item.key"
:hasPermission="'rule-engine/Scene:' + item.key"
>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
<AIcon
type="DeleteOutlined"
v-if="item.key === 'delete'"
/>
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</template>
</PermissionButton>
</template>
</SceneCard>
</CardBox>
</template>
</JProTable>
<Save
@ -86,7 +95,6 @@
import { query } from '@/api/rule-engine/scene';
import { unbindScene } from '@/api/rule-engine/configuration';
import { useRoute } from 'vue-router';
import SceneCard from '@/views/rule-engine/Scene/SceneCard.vue';
import type { ActionsType } from '@/components/Table';
import { getImage } from '@/utils/comm';
import { message } from 'ant-design-vue/es';
@ -166,10 +174,10 @@ const showModal = () => {
const closeSave = () => {
visible.value = false;
};
const saveSuccess = () =>{
const saveSuccess = () => {
visible.value = false;
actionRef.value.reload();
}
};
</script>
<style lang="less" scoped>
</style>

View File

@ -39,7 +39,7 @@
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
enabled: 'success',
enabled: 'processing',
disabled: 'error',
}"
>
@ -122,17 +122,17 @@
>
</template>
<template #state="slotProps">
<j-badge
<BadgeStatus
:text="
slotProps.state?.value === 'enabled'
? '正常'
: '禁用'
"
:status="
slotProps.state?.value === 'enabled'
? 'success'
: 'error'
"
:status="slotProps.state?.value"
:statusNames="{
enabled: 'processing',
disabled: 'error',
}"
/>
</template>
<template #action="slotProps">
@ -151,9 +151,9 @@
type="link"
style="padding: 0px"
:hasPermission="
'rule-engine/Alarm/Configuration:' +
i.key
'rule-engine/Alarm/Configuration:' + i.key
"
:danger="i.key === 'delete'"
>
<template #icon
><AIcon :type="i.icon"
@ -196,6 +196,8 @@ const columns = [
search: {
type: 'string',
},
width: 220,
ellipsis: true,
},
{
title: '类型',
@ -223,6 +225,7 @@ const columns = [
},
],
},
width: 100,
},
{
title: '告警级别',
@ -244,6 +247,8 @@ const columns = [
return [];
},
},
width: 200,
ellipsis: true,
},
{
title: '关联场景联动',
@ -267,6 +272,8 @@ const columns = [
return [];
},
},
width: 220,
ellipsis: true,
},
{
title: '状态',
@ -286,6 +293,7 @@ const columns = [
},
],
},
width: 90,
},
{
title: '说明',
@ -294,6 +302,7 @@ const columns = [
search: {
type: 'string',
},
ellipsis: true,
},
{
title: '操作',
@ -431,8 +440,8 @@ const getActions = (
icon: 'DeleteOutlined',
},
];
return actions.filter((item)=>
item.key != 'tigger' || data.sceneTriggerType == 'manual'
return actions.filter(
(item) => item.key != 'tigger' || data.sceneTriggerType == 'manual',
);
};
const add = () => {

View File

@ -43,6 +43,14 @@
(i) => i.level === slotProps.level,
)?.title || slotProps.level
"
:status="slotProps.level"
:statusNames="{
1: 'level1',
2: 'level2',
3: 'level3',
4: 'level4',
5: 'level5',
}"
>
<template #img>
<img :src="imgMap.get(slotProps.targetType)" alt="" />
@ -80,14 +88,14 @@
</j-col>
<j-col :span="8">
<div class="content-des-title">状态</div>
<j-badge
:status="
slotProps.state.value === 'warning'
? 'error'
: 'default'
"
<BadgeStatus
:status="slotProps.state.value"
:statusName="{
warning: 'warning',
normal: 'default',
}"
>
</j-badge
</BadgeStatus
><span
:style="
slotProps.state.value === 'warning'
@ -184,14 +192,6 @@ titleMap.set('product', '产品');
titleMap.set('device', '设备');
titleMap.set('other', '其他');
titleMap.set('org', '组织');
const colorMap = new Map();
colorMap.set(1, '#E50012');
colorMap.set(2, '#FF9457');
colorMap.set(3, '#FABD47');
colorMap.set(4, '#999999');
colorMap.set(5, '#C4C4C4');
const columns = [
{
title: '名称',

View File

@ -117,6 +117,7 @@
type="link"
style="padding: 0px"
:hasPermission="'rule-engine/Instance:' + i.key"
:danger="i.key === 'delete'"
>
<template #icon
><AIcon :type="i.icon"
@ -247,7 +248,7 @@ const getActions = (
icon: 'EyeOutlined',
onClick: () => {
openRuleEditor(data);
}
},
},
{
key: 'action',

View File

@ -1,11 +1,34 @@
<template>
<j-form-item
:rules="actionRules"
:name="['branches', 0, 'then']"
>
<Action
:thenOptions="data.branches ? data?.branches[0].then : []"
:name="0"
/>
</j-form-item>
</template>
<script>
export default {
name: 'index'
}
<script lang="ts" setup name='SceneSaveManual'>
import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia';
import type { BranchesThen } from '@/views/rule-engine/Scene/typings'
import Action from '../action/index.vue';
const sceneStore = useSceneStore();
const { data } = storeToRefs(sceneStore);
const actionRules = [{
validator(_: any, v?: BranchesThen[]) {
console.log(_, v)
if (!v || (v && !v.length) || (v && v.length && !v[0].actions.length)) {
return Promise.reject('至少配置一个执行动作');
}
return Promise.resolve();
},
}]
</script>
<style scoped>

View File

@ -33,13 +33,13 @@
</div>
</template>
<script lang="ts" setup>
<script lang="ts" setup name='SceneSaveTimer'>
import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia';
import Action from '../action/index.vue';
import AddModel from './AddModal.vue'
import AddButton from '../components/AddButton.vue'
import type { OperationTimer } from '@/views/rule-engine/Scene/typings'
import type { OperationTimer, BranchesThen } from '@/views/rule-engine/Scene/typings'
const sceneStore = useSceneStore();
const { data } = storeToRefs(sceneStore);
@ -55,7 +55,7 @@ const rules = [{
}]
const actionRules = [{
validator(_, v) {
validator(_: any, v?: BranchesThen[]) {
if (!v || (v && !v.length)) {
return Promise.reject('至少配置一个执行动作');
}
@ -76,8 +76,8 @@ const onActionUpdate = (_data: any, type: boolean) => {
}
};
const save = (data: OperationTimer, options: Record<string, any>) => {
data.value.trigger!.timer = data
const save = (_data: OperationTimer, options: Record<string, any>) => {
data.value.trigger!.timer = _data
data.value.options!.trigger = options
}
</script>

View File

@ -14,10 +14,14 @@
<Device v-if='data.triggerType === "device"' />
<Manual v-else-if='data.triggerType === "manual"' />
<Timer v-else-if='data.triggerType === "timer"' />
<j-form-item>
<j-form-item
>
<template #label>
<TitleComponent data='说明' style='font-size: 14px;' />
</template>
<j-textarea
v-model:value="data.description"
placeholder=''
placeholder='请输入说明'
:rows="4"
show-count
:maxLength="200"
@ -27,6 +31,8 @@
<PermissionButton
type='primary'
hasPermission='rule-engine/Scene:update'
:loading='loading'
@click='save'
>
保存
</PermissionButton>
@ -42,14 +48,31 @@ import { keyByLabel } from '../typings'
import Device from './Device/index.vue'
import Manual from './Manual/index.vue'
import Timer from './Timer/index.vue'
import { modify } from '@/api/rule-engine/scene'
import { useMenuStore } from '@/store/menu'
import { message } from 'jetlinks-ui-components'
const sceneStore = useSceneStore()
const menuStore = useMenuStore()
const { data } = storeToRefs(sceneStore)
const { getDetail } = sceneStore
const route = useRoute();
const sceneForm = ref()
const loading = ref(false)
console.log('data',data)
const save = async () => {
const formData = await sceneForm.value.validateFields()
if (formData) {
loading.value = true
const resp = await modify(data.value.id!, data.value).then(res => res)
loading.value = false
if (resp.success) {
menuStore.jumpPage('rule-engine/Scene')
message.success('操作成功')
}
}
}
getDetail(route.query.id as string)

View File

@ -21,13 +21,14 @@
</j-space>
</template>
<template #card="slotProps">
<SceneCard
<CardBox
:value="slotProps"
@click="handleView(slotProps.id, slotProps.triggerType)"
:actions="getActions(slotProps, 'card')"
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
started: 'success',
started: 'processing',
disable: 'error',
}"
>
@ -45,22 +46,19 @@
<template #img>
<img :src="typeMap.get(slotProps.triggerType)?.img" />
</template>
<template #title>
<template #content>
<Ellipsis style="width: calc(100% - 100px)">
<span
style="font-size: 16px; font-weight: 600"
@click.stop="handleView(slotProps.id, slotProps.triggerType)"
>
<span style="font-size: 16px; font-weight: 600">
{{ slotProps.name }}
</span>
</Ellipsis>
</template>
<template #subTitle>
<Ellipsis :lineClamp="2">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
<div class="subTitle">
说明{{
slotProps?.description ||
typeMap.get(slotProps.triggerType)?.tip
}}
</div>
</Ellipsis>
</template>
<template #actions="item">
@ -83,15 +81,19 @@
</template>
</PermissionButton>
</template>
</SceneCard>
</CardBox>
</template>
<template #triggerType="slotProps">
{{ typeMap.get(slotProps.triggerType)?.text }}
</template>
<template #state="slotProps">
<j-badge
<BadgeStatus
:status="slotProps.state?.value"
:text="slotProps.state?.text"
:status="statusMap.get(slotProps.state?.value)"
:statusNames="{
enabled: 'processing',
disabled: 'error',
}"
/>
</template>
<template #action="slotProps">
@ -108,8 +110,9 @@
}"
@click="i.onClick"
type="link"
style="padding: 0px"
:hasPermission="'rule-engine/Scene:' + i.key"
:danger="i.key === 'delete'"
style="padding: 0 5px"
:hasPermission="i.key === 'view' ? true : 'rule-engine/Scene:' + i.key"
>
<template #icon><AIcon :type="i.icon" /></template>
</PermissionButton>
@ -130,6 +133,7 @@ import { message } from 'ant-design-vue';
import type { ActionsType } from '@/components/Table';
import { getImage } from '@/utils/comm';
import SceneCard from './SceneCard.vue';
import BadgeStatus from '@/components/BadgeStatus/index.vue';
const menuStory = useMenuStore();
const visible = ref<boolean>(false);
@ -183,7 +187,7 @@ const columns = [
return {
label: item[1]?.text,
value: item[0],
}
};
}),
},
},
@ -202,6 +206,7 @@ const columns = [
{
dataIndex: 'description',
title: '说明',
ellipsis: true,
search: {
type: 'string',
},
@ -211,7 +216,7 @@ const columns = [
title: '操作',
key: 'action',
fixed: 'right',
width: 250,
width: 200,
scopedSlots: true,
},
];
@ -309,15 +314,15 @@ const getActions = (
popConfirm: {
title: '',
onConfirm: async () => {
const resp = await _execute(data.id)
const resp = await _execute(data.id);
if (resp.status === 200) {
message.success('操作成功!');
sceneRef.value?.reload();
} else {
message.error('操作失败!');
}
}
}
},
},
};
actions.splice(1, 0, _item);
}
@ -374,4 +379,9 @@ const handleView = (id: string, triggerType: string) => {
</script>
<style scoped>
.subTitle {
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
margin-top: 10px;
}
</style>

View File

@ -293,6 +293,7 @@ import { LocalStore } from '@/utils/comm';
import { save_api } from '@/api/system/basis';
import { usePermissionStore } from '@/store/permission';
import { useSystem } from '@/store/system';
import { settingDetail } from '@/api/login';
const action = `${BASE_API_PATH}/file/static`;
const headers = { [TOKEN_KEY]: LocalStore.get(TOKEN_KEY) };
@ -337,18 +338,19 @@ const form = reactive<formType>({
backLoading: false, //
iconLoading: false, //
saveLoading: false,
getDetails: () => {
const configInfo = useSystem().$state.configInfo;
const basis = configInfo.find((item: any) => item.scope === 'front');
const api = configInfo.find((item: any) => item.scope === 'amap');
const basePath = configInfo.find((item: any) => item.scope === 'paths');
getDetails: async () => {
const system = useSystem();
await system.getSystemConfig();
await settingDetail('front');
const configInfo = system.$state.configInfo;
form.formValue = {
...basis.properties,
apiKey: api.properties.apiKey,
'base-path': basePath.properties['base-path'],
logo: basis.properties.logo || '/public/logo.png',
ico: basis.properties.ico || '/public/favicon.ico',
backgroud: basis.properties.backgroud || '/public/images/login.png',
title: configInfo.front?.title,
headerTheme: configInfo.front?.headerTheme,
logo: configInfo.front?.logo || '/public/logo.png',
ico: configInfo.front?.ico || '/public/favicon.ico',
backgroud: configInfo.front?.backgroud || '/public/images/login.png',
apiKey: configInfo.amap?.apiKey,
"base-path": configInfo.paths?.['base-path']
};
},
clickSave: () => {

View File

@ -2,13 +2,13 @@ import type { Rule } from 'ant-design-vue/es/form';
/**基本信息表单 */
export interface formValueType {
title: string; // 系统名称
headerTheme: string; // 主题色
apiKey: string; // 高德 API key
'base-path': string; // 系统后台访问的URL
logo:string,
ico:string,
backgroud:string
title: string | undefined; // 系统名称
headerTheme: string | undefined; // 主题色
apiKey: string | undefined; // 高德 API key
'base-path': string | undefined; // 系统后台访问的URL
logo: string | undefined;
ico: string | undefined;
backgroud: string | undefined;
}
export interface formType {
@ -35,3 +35,20 @@ export interface uploaderType {
beforeIconUpload: (file: UploadProps['beforeUpload']) => void
changeIconUpload: (info: UploadChangeParam) => void
}
export type PathType = {
'base-path': string;
'sso-bind': string;
'sso-redirect': string;
'sso-token-set': string;
}
export type AMapKey = {
apiKey: string;
}
export type ConfigInfoType = {
paths: PathType;
amap: AMapKey;
front: formValueType;
}