Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
73bb4bbc5f
File diff suppressed because it is too large
Load Diff
|
@ -33,6 +33,13 @@ export const detail = (id: string) => server.get<DeviceInstance>(`/device-instan
|
||||||
*/
|
*/
|
||||||
export const query = (data?: Record<string, any>) => server.post('/device-instance/_query', data)
|
export const query = (data?: Record<string, any>) => server.post('/device-instance/_query', data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不分页查询设备
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryNoPagingPost = (data?: Record<string, any>) => server.post('/device-instance/_query/no-paging?paging=false', data)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除设备
|
* 删除设备
|
||||||
* @param id 设备ID
|
* @param id 设备ID
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import server from '@/utils/request'
|
||||||
|
|
||||||
|
export const restPassword = (id: string) => server.post(`/edge/operations/${id}/auth-user-password-reset/invoke`)
|
||||||
|
|
||||||
|
export const _control = (deviceId: string) => server.get(`/edge/remote/${deviceId}/url`)
|
||||||
|
|
||||||
|
export const _stopControl = (deviceId: string) => server.get(`/edge/remote/${deviceId}/stop`, {})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import server from '@/utils/request'
|
||||||
|
|
||||||
|
export const query = (data: Record<string, any>) => server.post(`/entity/template/_query`, data)
|
||||||
|
|
||||||
|
export const modify = (id: string, data: Record<string, any>) => server.put(`/entity/template/${id}`, data)
|
||||||
|
|
||||||
|
export const _delete = (id: string) => server.remove(`/entity/template/${id}`)
|
||||||
|
|
||||||
|
export const _start = (data: Record<string, any>) => server.post(`/entity/template/start/_batch`, data)
|
||||||
|
|
||||||
|
export const _stop = (data: Record<string, any>) => server.post(`/entity/template/stop/_batch`, data)
|
||||||
|
|
||||||
|
export const queryDeviceList = (data: Record<string, any>) => server.post(`/device-instance/detail/_query`, data)
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
allowClear
|
allowClear
|
||||||
>
|
>
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<j-upload
|
<a-upload
|
||||||
name="file"
|
name="file"
|
||||||
:action="FILE_UPLOAD"
|
:action="FILE_UPLOAD"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
@change="handleFileChange"
|
@change="handleFileChange"
|
||||||
>
|
>
|
||||||
<AIcon type="UploadOutlined" />
|
<AIcon type="UploadOutlined" />
|
||||||
</j-upload>
|
</a-upload>
|
||||||
</template>
|
</template>
|
||||||
</j-input>
|
</j-input>
|
||||||
<j-input
|
<j-input
|
||||||
|
|
|
@ -145,6 +145,9 @@ const extraRouteObj = {
|
||||||
{ code: 'Save', name: '详情' },
|
{ code: 'Save', name: '详情' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
'edge/Device': {
|
||||||
|
children: [{ code: 'Remote', name: '远程控制' }],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<j-modal :maskClosable="false" width="800px" :visible="true" title="导入" @ok="handleSave" @cancel="handleCancel">
|
<j-modal
|
||||||
|
:maskClosable="false"
|
||||||
|
width="800px"
|
||||||
|
:visible="true"
|
||||||
|
title="导入"
|
||||||
|
@ok="handleSave"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<j-form :layout="'vertical'">
|
<j-form :layout="'vertical'">
|
||||||
<j-row>
|
<j-row>
|
||||||
<j-col span="24">
|
<j-col span="24">
|
||||||
<j-form-item label="产品" required>
|
<j-form-item label="产品" required>
|
||||||
<j-select showSearch v-model:value="modelRef.product" placeholder="请选择产品">
|
<j-select
|
||||||
<j-select-option :value="item.id" v-for="item in productList" :key="item.id" :label="item.name">{{ item.name }}</j-select-option>
|
showSearch
|
||||||
|
v-model:value="modelRef.product"
|
||||||
|
placeholder="请选择产品"
|
||||||
|
>
|
||||||
|
<j-select-option
|
||||||
|
:value="item.id"
|
||||||
|
v-for="item in productList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>{{ item.name }}</j-select-option
|
||||||
|
>
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
|
@ -17,7 +34,11 @@
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col span="12">
|
<j-col span="12">
|
||||||
<j-form-item label="文件上传" v-if="modelRef.product">
|
<j-form-item label="文件上传" v-if="modelRef.product">
|
||||||
<NormalUpload :product="modelRef.product" v-model="modelRef.upload" :file="modelRef.file" />
|
<NormalUpload
|
||||||
|
:product="modelRef.product"
|
||||||
|
v-model="modelRef.upload"
|
||||||
|
:file="modelRef.file"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
|
@ -27,16 +48,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { queryNoPagingPost } from '@/api/device/product'
|
import { queryNoPagingPost } from '@/api/device/product';
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'save'])
|
const emit = defineEmits(['close', 'save']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: undefined
|
default: undefined,
|
||||||
}
|
},
|
||||||
})
|
type: String,
|
||||||
const productList = ref<Record<string, any>[]>([])
|
});
|
||||||
|
const productList = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
const modelRef = reactive({
|
const modelRef = reactive({
|
||||||
product: undefined,
|
product: undefined,
|
||||||
|
@ -44,26 +66,40 @@ const modelRef = reactive({
|
||||||
file: {
|
file: {
|
||||||
fileType: 'xlsx',
|
fileType: 'xlsx',
|
||||||
autoDeploy: false,
|
autoDeploy: false,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
() => {
|
() => {
|
||||||
queryNoPagingPost({paging: false}).then(resp => {
|
queryNoPagingPost({
|
||||||
if(resp.status === 200){
|
paging: false,
|
||||||
productList.value = resp.result as Record<string, any>[]
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'state',
|
||||||
|
value: '1',
|
||||||
|
type: 'and'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'accessProvider',
|
||||||
|
value: props?.type
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }]
|
||||||
|
}).then((resp) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
productList.value = resp.result as Record<string, any>[];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
{immediate: true, deep: true}
|
{ immediate: true, deep: true },
|
||||||
)
|
);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('close')
|
emit('close');
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
emit('save')
|
emit('save');
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
|
@ -289,7 +289,6 @@ import { queryTree } from '@/api/device/category';
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
import type { ActionsType } from './typings';
|
import type { ActionsType } from './typings';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { throttle } from 'lodash-es';
|
|
||||||
|
|
||||||
const instanceRef = ref<Record<string, any>>({});
|
const instanceRef = ref<Record<string, any>>({});
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<div class="box">
|
||||||
|
<iframe :src="url" class="box-iframe"></iframe>
|
||||||
|
</div>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { _control, _stopControl } from '@/api/edge/device';
|
||||||
|
|
||||||
|
const url = ref<string>('');
|
||||||
|
const deviceId = ref<string>('');
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => history.state?.params?.id,
|
||||||
|
(newId) => {
|
||||||
|
if (newId) {
|
||||||
|
deviceId.value = newId as string;
|
||||||
|
_control(newId).then((resp: any) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const item = `http://${resp.result?.url}/#/login?token=${resp.result.token}`;
|
||||||
|
url.value = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (deviceId.value) {
|
||||||
|
_stopControl(unref(deviceId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.box {
|
||||||
|
width: 100%;
|
||||||
|
height: 85vh;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.box-iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,265 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
:maskClosable="false"
|
||||||
|
width="650px"
|
||||||
|
:visible="true"
|
||||||
|
:title="!!data?.id ? '编辑' : '新增'"
|
||||||
|
@ok="handleSave"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
:confirmLoading="loading"
|
||||||
|
>
|
||||||
|
<div style="margin-top: 10px">
|
||||||
|
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||||
|
<j-row type="flex">
|
||||||
|
<j-col flex="180px">
|
||||||
|
<j-form-item name="photoUrl">
|
||||||
|
<JProUpload v-model="modelRef.photoUrl" />
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
<j-col flex="auto">
|
||||||
|
<j-form-item
|
||||||
|
name="id"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
|
message: '请输入英文或者数字或者-或者_',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多输入64个字符',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: vailId,
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
ID
|
||||||
|
<j-tooltip
|
||||||
|
title="若不填写,系统将自动生成唯一ID"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
|
</j-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<j-input
|
||||||
|
v-model:value="modelRef.id"
|
||||||
|
placeholder="请输入ID"
|
||||||
|
:disabled="!!data?.id"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="modelRef.name"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
<j-row>
|
||||||
|
<j-col :span="22">
|
||||||
|
<j-form-item
|
||||||
|
name="productId"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择所属产品',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span
|
||||||
|
>所属产品
|
||||||
|
<j-tooltip title="只能选择“正常”状态的产品">
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
|
</j-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<j-select
|
||||||
|
showSearch
|
||||||
|
v-model:value="modelRef.productId"
|
||||||
|
:disabled="!!data?.id"
|
||||||
|
placeholder="请选择所属产品"
|
||||||
|
>
|
||||||
|
<j-select-option
|
||||||
|
:value="item.id"
|
||||||
|
v-for="item in productList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>{{ item.name }}</j-select-option
|
||||||
|
>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="2" style="margin-top: 30px">
|
||||||
|
<PermissionButton
|
||||||
|
type="link"
|
||||||
|
:disabled="data.id"
|
||||||
|
@click="visible = true"
|
||||||
|
hasPermission="device/Product:add"
|
||||||
|
>
|
||||||
|
<AIcon type="PlusOutlined" />
|
||||||
|
</PermissionButton>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
<j-form-item
|
||||||
|
label="说明"
|
||||||
|
name="describe"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
max: 200,
|
||||||
|
message: '最多输入200个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-textarea
|
||||||
|
v-model:value="modelRef.describe"
|
||||||
|
placeholder="请输入说明"
|
||||||
|
showCount
|
||||||
|
:maxlength="200"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
|
<SaveProduct
|
||||||
|
v-model:visible="visible"
|
||||||
|
v-model:productId="modelRef.productId"
|
||||||
|
:channel="'official-edge-gateway'"
|
||||||
|
@close="onClose"
|
||||||
|
:deviceType="'gateway'"
|
||||||
|
@save="onSave"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { queryNoPagingPost } from '@/api/device/product';
|
||||||
|
import { isExists, update } from '@/api/device/instance';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
import SaveProduct from '@/views/media/Device/Save/SaveProduct.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['close', 'save']);
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const productList = ref<Record<string, any>[]>([]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
|
||||||
|
const modelRef = reactive({
|
||||||
|
productId: undefined,
|
||||||
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
describe: '',
|
||||||
|
photoUrl: getImage('/device/instance/device-card.png'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const vailId = async (_: Record<string, any>, value: string) => {
|
||||||
|
if (!props?.data?.id && value) {
|
||||||
|
const resp = await isExists(value);
|
||||||
|
if (resp.status === 200 && resp.result) {
|
||||||
|
return Promise.reject('ID重复');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(newValue) => {
|
||||||
|
queryNoPagingPost({
|
||||||
|
paging: false,
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'state',
|
||||||
|
value: 1,
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'accessProvider',
|
||||||
|
value: 'official-edge-gateway',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).then((resp) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
productList.value = resp.result as Record<string, any>[];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.assign(modelRef, newValue);
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('close');
|
||||||
|
formRef.value.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = (_data: any) => {
|
||||||
|
productList.value.push(_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(async (_data: any) => {
|
||||||
|
loading.value = true;
|
||||||
|
const obj = { ..._data };
|
||||||
|
if (!obj.id) {
|
||||||
|
delete obj.id;
|
||||||
|
}
|
||||||
|
const resp = await update(obj).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
emit('save');
|
||||||
|
formRef.value.resetFields();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.log('error', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,438 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<pro-search
|
||||||
|
:columns="columns"
|
||||||
|
target="edge-device"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
<JProTable
|
||||||
|
ref="edgeDeviceRef"
|
||||||
|
:columns="columns"
|
||||||
|
:request="query"
|
||||||
|
:defaultParams="defaultParams"
|
||||||
|
:params="params"
|
||||||
|
:gridColumn="3"
|
||||||
|
>
|
||||||
|
<template #headerTitle>
|
||||||
|
<j-space>
|
||||||
|
<PermissionButton
|
||||||
|
type="primary"
|
||||||
|
@click="handleAdd"
|
||||||
|
hasPermission="edge/Device:add"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
|
<PermissionButton
|
||||||
|
@click="importVisible = true"
|
||||||
|
hasPermission="edge/Device:import"
|
||||||
|
>
|
||||||
|
<template #icon
|
||||||
|
><AIcon type="ImportOutlined"
|
||||||
|
/></template>
|
||||||
|
导入
|
||||||
|
</PermissionButton>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
<template #card="slotProps">
|
||||||
|
<CardBox
|
||||||
|
:value="slotProps"
|
||||||
|
:actions="getActions(slotProps, 'card')"
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:statusText="slotProps.state?.text"
|
||||||
|
:statusNames="{
|
||||||
|
online: 'success',
|
||||||
|
offline: 'error',
|
||||||
|
notActive: 'warning',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #img>
|
||||||
|
<img
|
||||||
|
:src="getImage('/device/instance/device-card.png')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
|
<span
|
||||||
|
style="font-size: 16px; font-weight: 600"
|
||||||
|
@click.stop="handleView(slotProps.id)"
|
||||||
|
>
|
||||||
|
{{ slotProps.name }}
|
||||||
|
</span>
|
||||||
|
</Ellipsis>
|
||||||
|
<j-row style="margin-top: 20px">
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
设备类型
|
||||||
|
</div>
|
||||||
|
<div>{{ slotProps.deviceType?.text }}</div>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
产品名称
|
||||||
|
</div>
|
||||||
|
<Ellipsis style="width: 100%">
|
||||||
|
{{ slotProps.productName }}
|
||||||
|
</Ellipsis>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
</template>
|
||||||
|
<template #actions="item">
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
|
:hasPermission="'edge/Device:' + item.key"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
v-if="item.key === 'delete'"
|
||||||
|
/>
|
||||||
|
<template v-else>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item?.text }}</span>
|
||||||
|
</template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<j-badge
|
||||||
|
:text="slotProps.state?.text"
|
||||||
|
:status="statusMap.get(slotProps.state?.value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #createTime="slotProps">
|
||||||
|
<span>{{
|
||||||
|
dayjs(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<j-space>
|
||||||
|
<template
|
||||||
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
|
:key="i.key"
|
||||||
|
>
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="i.disabled"
|
||||||
|
:popConfirm="i.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}"
|
||||||
|
@click="i.onClick"
|
||||||
|
type="link"
|
||||||
|
style="padding: 0 5px"
|
||||||
|
:hasPermission="'edge/Device:' + i.key"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon :type="i.icon" /></template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
</JProTable>
|
||||||
|
<Save
|
||||||
|
v-if="visible"
|
||||||
|
:data="current"
|
||||||
|
@close="visible = false"
|
||||||
|
@save="saveBtn"
|
||||||
|
/>
|
||||||
|
<Import @save="onRefresh" @close="importVisible = false" v-if="importVisible" type="official-edge-gateway" />
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { queryNoPagingPost } from '@/api/device/product';
|
||||||
|
import { queryTree } from '@/api/device/category';
|
||||||
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
import { ActionsType } from '@/views/device/Instance/typings';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
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';
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'productId$product-info',
|
||||||
|
value: 'accessProvider is official-edge-gateway',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusMap = new Map();
|
||||||
|
statusMap.set('online', 'success');
|
||||||
|
statusMap.set('offline', 'error');
|
||||||
|
statusMap.set('notActive', 'warning');
|
||||||
|
|
||||||
|
const params = ref<Record<string, any>>({});
|
||||||
|
const edgeDeviceRef = ref<Record<string, any>>({});
|
||||||
|
const importVisible = ref<boolean>(false);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const current = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
defaultTermType: 'eq',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
first: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '产品名称',
|
||||||
|
dataIndex: 'productName',
|
||||||
|
key: 'productName',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryNoPagingPost({ paging: false }).then((resp: any) => {
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注册时间',
|
||||||
|
dataIndex: 'registryTime',
|
||||||
|
key: 'registryTime',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '禁用', value: 'notActive' },
|
||||||
|
{ label: '离线', value: 'offline' },
|
||||||
|
{ label: '在线', value: 'online' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'classifiedId',
|
||||||
|
dataIndex: 'classifiedId',
|
||||||
|
title: '产品分类',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'treeSelect',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryTree({ paging: false }).then((resp: any) => {
|
||||||
|
resolve(resp.result);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'deviceType',
|
||||||
|
title: '设备类型',
|
||||||
|
valueType: 'select',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '直连设备', value: 'device' },
|
||||||
|
{ label: '网关子设备', value: 'childrenDevice' },
|
||||||
|
{ label: '网关设备', value: 'gateway' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'describe',
|
||||||
|
key: 'describe',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 250,
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const getActions = (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
): ActionsType[] => {
|
||||||
|
if (!data) return [];
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
key: 'view',
|
||||||
|
text: '查看',
|
||||||
|
tooltip: {
|
||||||
|
title: '查看',
|
||||||
|
},
|
||||||
|
icon: 'EyeOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
handleView(data.id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
text: '编辑',
|
||||||
|
tooltip: {
|
||||||
|
title: '编辑',
|
||||||
|
},
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
visible.value = true;
|
||||||
|
current.value = data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'setting',
|
||||||
|
text: '远程控制',
|
||||||
|
tooltip: {
|
||||||
|
title: '远程控制',
|
||||||
|
},
|
||||||
|
icon: 'ControlOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
menuStory.jumpPage('edge/Device/Remote', { id: data.id });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'password',
|
||||||
|
text: '重置密码',
|
||||||
|
tooltip: {
|
||||||
|
title: '重置密码',
|
||||||
|
},
|
||||||
|
icon: 'RedoOutlined',
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认重置密码为P@ssw0rd?',
|
||||||
|
onConfirm: async () => {
|
||||||
|
restPassword(data.id).then((resp: any) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
edgeDeviceRef.value?.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'action',
|
||||||
|
text: data.state?.value !== 'notActive' ? '禁用' : '启用',
|
||||||
|
tooltip: {
|
||||||
|
title: data.state?.value !== 'notActive' ? '禁用' : '启用',
|
||||||
|
},
|
||||||
|
icon:
|
||||||
|
data.state.value !== 'notActive'
|
||||||
|
? 'StopOutlined'
|
||||||
|
: 'CheckCircleOutlined',
|
||||||
|
popConfirm: {
|
||||||
|
title: `确认${
|
||||||
|
data.state.value !== 'notActive' ? '禁用' : '启用'
|
||||||
|
}?`,
|
||||||
|
onConfirm: async () => {
|
||||||
|
let response = undefined;
|
||||||
|
if (data.state.value !== 'notActive') {
|
||||||
|
response = await _undeploy(data.id);
|
||||||
|
} else {
|
||||||
|
response = await _deploy(data.id);
|
||||||
|
}
|
||||||
|
if (response && response.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
edgeDeviceRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
text: '删除',
|
||||||
|
disabled: data.state?.value !== 'notActive',
|
||||||
|
tooltip: {
|
||||||
|
title:
|
||||||
|
data.state.value !== 'notActive'
|
||||||
|
? '已启用的设备不能删除'
|
||||||
|
: '删除',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除?',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const resp = await _delete(data.id);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
edgeDeviceRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: 'DeleteOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (type === 'card')
|
||||||
|
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (_params: any) => {
|
||||||
|
params.value = _params;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleView = (id: string) => {
|
||||||
|
menuStory.jumpPage('device/Instance/Detail', { id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
visible.value = true;
|
||||||
|
current.value = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveBtn = () => {
|
||||||
|
visible.value = false;
|
||||||
|
edgeDeviceRef.value?.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
importVisible.value = false
|
||||||
|
edgeDeviceRef.value?.reload();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,132 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
visible
|
||||||
|
title="下发结果"
|
||||||
|
:width="900"
|
||||||
|
@ok="emit('close')"
|
||||||
|
@cancel="emit('close')"
|
||||||
|
>
|
||||||
|
<j-row>
|
||||||
|
<j-col :span="8">
|
||||||
|
<div>成功:{{ count }}</div>
|
||||||
|
<div>
|
||||||
|
失败:{{ countErr }}
|
||||||
|
<j-button @click="_download(errMessage || '', '下发失败原因')" v-if="errMessage.length" type="link"
|
||||||
|
>下载</j-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="8">下发设备数量:{{ list.length || 0 }}</j-col>
|
||||||
|
<j-col :span="8">已下发数量:{{ countErr + count }}</j-col>
|
||||||
|
</j-row>
|
||||||
|
<div v-if="!flag">
|
||||||
|
<j-textarea :rows="20" :value="JSON.stringify(errMessage)" />
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LocalStore } from '@/utils/comm';
|
||||||
|
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { EventSourcePolyfill } from 'event-source-polyfill';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
const count = ref<number>(0);
|
||||||
|
const countErr = ref<number>(0);
|
||||||
|
const flag = ref<boolean>(true);
|
||||||
|
const errMessage = ref<any[]>([]);
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
let dt = 0;
|
||||||
|
let et = 0;
|
||||||
|
const errMessages: any[] = [];
|
||||||
|
const _terms = {
|
||||||
|
deviceId: (props.list || []).map((item: any) => item?.id),
|
||||||
|
params: JSON.stringify({
|
||||||
|
name: props.data.name,
|
||||||
|
targetId: props.data.targetId,
|
||||||
|
targetType: props.data.targetType,
|
||||||
|
category: props.data.category,
|
||||||
|
metadata: props.data?.metadata,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const url = new URLSearchParams();
|
||||||
|
Object.keys(_terms).forEach((key) => {
|
||||||
|
if (Array.isArray(_terms[key]) && _terms[key].length) {
|
||||||
|
_terms[key].map((item: string) => {
|
||||||
|
url.append(key, item);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
url.append(key, _terms[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const source = new EventSourcePolyfill(
|
||||||
|
`${BASE_API_PATH}/edge/operations/entity-template-save/invoke/_batch?:X_Access_Token=${LocalStore.get(
|
||||||
|
TOKEN_KEY,
|
||||||
|
)}&${url}`,
|
||||||
|
);
|
||||||
|
source.onmessage = (e: any) => {
|
||||||
|
const res = JSON.parse(e.data);
|
||||||
|
if (res.successful) {
|
||||||
|
dt += 1;
|
||||||
|
count.value = dt;
|
||||||
|
} else {
|
||||||
|
et += 1;
|
||||||
|
countErr.value = et;
|
||||||
|
flag.value = false;
|
||||||
|
if (errMessages.length <= 5) {
|
||||||
|
errMessages.push({ ...res });
|
||||||
|
errMessage.value = [...errMessages];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
source.onerror = () => {
|
||||||
|
source.close();
|
||||||
|
};
|
||||||
|
source.onopen = () => {};
|
||||||
|
};
|
||||||
|
|
||||||
|
const _download = (record: Record<string, any>, fileName: string, format?: string) => {
|
||||||
|
// 创建隐藏的可下载链接
|
||||||
|
const ghostLink = document.createElement('a');
|
||||||
|
ghostLink.download = `${fileName ? '' : record?.name}${fileName}_${dayjs(new Date()).format(
|
||||||
|
format || 'YYYY_MM_DD',
|
||||||
|
)}.txt`;
|
||||||
|
ghostLink.style.display = 'none';
|
||||||
|
//字符串内容转成Blob地址
|
||||||
|
const blob = new Blob([JSON.stringify(record)]);
|
||||||
|
ghostLink.href = URL.createObjectURL(blob);
|
||||||
|
//触发点击
|
||||||
|
document.body.appendChild(ghostLink);
|
||||||
|
ghostLink.click();
|
||||||
|
//移除
|
||||||
|
document.body.removeChild(ghostLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data.id,
|
||||||
|
(newId) => {
|
||||||
|
if(newId){
|
||||||
|
getData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,181 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
visible
|
||||||
|
title="下发设备"
|
||||||
|
:width="1000"
|
||||||
|
@ok="onSave"
|
||||||
|
@cancel="onCancel"
|
||||||
|
>
|
||||||
|
<div class="alert">
|
||||||
|
<AIcon
|
||||||
|
type="InfoCircleOutlined"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
/>离线设备无法进行设备模板下发
|
||||||
|
</div>
|
||||||
|
<pro-search
|
||||||
|
:columns="columns"
|
||||||
|
target="edge-resource-issue"
|
||||||
|
@search="handleSearch"
|
||||||
|
type="simple"
|
||||||
|
class="search"
|
||||||
|
/>
|
||||||
|
<JProTable
|
||||||
|
ref="edgeResourceIssueRef"
|
||||||
|
:columns="columns"
|
||||||
|
:request="queryDeviceList"
|
||||||
|
:defaultParams="defaultParams"
|
||||||
|
:params="params"
|
||||||
|
model="TABLE"
|
||||||
|
:bodyStyle="{ padding: 0 }"
|
||||||
|
:rowSelection="{
|
||||||
|
selectedRowKeys: _selectedRowKeys,
|
||||||
|
onChange: onSelectChange,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<j-badge
|
||||||
|
:text="slotProps.state?.text"
|
||||||
|
:status="statusMap.get(slotProps.state?.value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #sourceId="slotProps">
|
||||||
|
{{ slotProps.sourceName }}
|
||||||
|
</template>
|
||||||
|
<template #registerTime="slotProps">
|
||||||
|
<span>{{
|
||||||
|
dayjs(slotProps.registerTime).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
|
</JProTable>
|
||||||
|
<Result v-if="visible" :data="props.data" :list="_data" @close="onCancel" />
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { queryDeviceList } from '@/api/edge/resource';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import Result from './Result.vue';
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'productId$product-info',
|
||||||
|
value: 'accessProvider is official-edge-gateway',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
const params = ref({});
|
||||||
|
const edgeResourceIssueRef = ref();
|
||||||
|
const _selectedRowKeys = ref<string[]>([]);
|
||||||
|
const _data = ref<any[]>([]);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
const statusMap = new Map();
|
||||||
|
statusMap.set('online', 'success');
|
||||||
|
statusMap.set('offline', 'error');
|
||||||
|
statusMap.set('notActive', 'warning');
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
ellipsis: true,
|
||||||
|
width: 200,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '产品名称',
|
||||||
|
dataIndex: 'productName',
|
||||||
|
key: 'productName',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
ellipsis: true,
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注册时间',
|
||||||
|
dataIndex: 'registerTime',
|
||||||
|
key: 'registerTime',
|
||||||
|
width: 200,
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '禁用', value: 'notActive' },
|
||||||
|
{ label: '离线', value: 'offline' },
|
||||||
|
{ label: '在线', value: 'online' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onSelectChange = (keys: string[], _options: any[]) => {
|
||||||
|
_selectedRowKeys.value = [...keys];
|
||||||
|
_data.value = _options;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (v: any) => {
|
||||||
|
params.value = v;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
if(_data.value.length){
|
||||||
|
visible.value = true
|
||||||
|
} else {
|
||||||
|
onlyMessage('请选择设备', 'error')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.search {
|
||||||
|
padding: 0 0 0 24px;
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.55);
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<j-modal visible title="编辑" :width="700" @ok="onSave" @cancel="onCancel">
|
||||||
|
<MonacoEditor
|
||||||
|
style="width: 100%; height: 370px"
|
||||||
|
theme="vs"
|
||||||
|
v-model="monacoValue"
|
||||||
|
language="json"
|
||||||
|
/>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||||
|
import { modify } from '@/api/edge/resource';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['close', 'save']);
|
||||||
|
|
||||||
|
const monacoValue = ref<string>('{}');
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
monacoValue.value = props.data?.metadata || '{}';
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSave = async () => {
|
||||||
|
const resp = await modify(props.data.id, { metadata: unref(monacoValue) });
|
||||||
|
if (resp.status === 200) {
|
||||||
|
emit('save');
|
||||||
|
onlyMessage('操作成功', 'success');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,383 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<pro-search
|
||||||
|
:columns="columns"
|
||||||
|
target="edge-resource"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
<JProTable
|
||||||
|
ref="edgeResourceRef"
|
||||||
|
:columns="columns"
|
||||||
|
:request="query"
|
||||||
|
:defaultParams="defaultParams"
|
||||||
|
:params="params"
|
||||||
|
>
|
||||||
|
<template #card="slotProps">
|
||||||
|
<CardBox
|
||||||
|
:value="slotProps"
|
||||||
|
:actions="getActions(slotProps, 'card')"
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:statusText="slotProps.state?.text"
|
||||||
|
:statusNames="{
|
||||||
|
enabled: 'success',
|
||||||
|
disabled: 'error',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #img>
|
||||||
|
<img
|
||||||
|
:src="getImage('/device/instance/device-card.png')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
|
<span
|
||||||
|
style="font-size: 16px; font-weight: 600"
|
||||||
|
@click.stop="handleView(slotProps.id)"
|
||||||
|
>
|
||||||
|
{{ slotProps.name }}
|
||||||
|
</span>
|
||||||
|
</Ellipsis>
|
||||||
|
<j-row style="margin-top: 20px">
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
通讯协议
|
||||||
|
</div>
|
||||||
|
<Ellipsis>{{
|
||||||
|
options.find(
|
||||||
|
(i) => i.value === slotProps.category,
|
||||||
|
)?.label || slotProps.category
|
||||||
|
}}</Ellipsis>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
所属边缘网关
|
||||||
|
</div>
|
||||||
|
<Ellipsis style="width: 100%">
|
||||||
|
{{ slotProps.sourceName }}
|
||||||
|
</Ellipsis>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
</template>
|
||||||
|
<template #actions="item">
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
|
:hasPermission="'edge/Resource:' + item.key"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
v-if="item.key === 'delete'"
|
||||||
|
/>
|
||||||
|
<template v-else>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item?.text }}</span>
|
||||||
|
</template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<j-badge
|
||||||
|
:text="slotProps.state?.text"
|
||||||
|
:status="statusMap.get(slotProps.state?.value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #sourceId="slotProps">
|
||||||
|
{{ slotProps.sourceName }}
|
||||||
|
</template>
|
||||||
|
<template #category="slotProps">
|
||||||
|
{{
|
||||||
|
options.find((i) => i.value === slotProps.category)
|
||||||
|
?.label || slotProps.category
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template #createTime="slotProps">
|
||||||
|
<span>{{
|
||||||
|
dayjs(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<j-space>
|
||||||
|
<template
|
||||||
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
|
:key="i.key"
|
||||||
|
>
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="i.disabled"
|
||||||
|
:popConfirm="i.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}"
|
||||||
|
@click="i.onClick"
|
||||||
|
type="link"
|
||||||
|
style="padding: 0 5px"
|
||||||
|
:hasPermission="'edge/Resource:' + i.key"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon :type="i.icon" /></template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
</JProTable>
|
||||||
|
<Save
|
||||||
|
v-if="visible"
|
||||||
|
:data="current"
|
||||||
|
@close="visible = false"
|
||||||
|
@save="saveBtn"
|
||||||
|
/>
|
||||||
|
<Issue
|
||||||
|
v-if="settingVisible"
|
||||||
|
:data="current"
|
||||||
|
@close="settingVisible = false"
|
||||||
|
/>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { queryNoPagingPost } from '@/api/device/instance';
|
||||||
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
import { ActionsType } from '@/views/device/Instance/typings';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
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';
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
|
|
||||||
|
const defaultParams = { sorts: [{ name: 'createTime', order: 'desc' }] };
|
||||||
|
|
||||||
|
const statusMap = new Map();
|
||||||
|
statusMap.set('enabled', 'success');
|
||||||
|
statusMap.set('disabled', 'error');
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{ label: 'UA接入', value: 'OPC_UA' },
|
||||||
|
{ label: 'Modbus TCP接入', value: 'MODBUS_TCP' },
|
||||||
|
{ label: 'S7-200接入', value: 'snap7' },
|
||||||
|
{ label: 'BACnet接入', value: 'BACNetIp' },
|
||||||
|
{ label: 'MODBUS_RTU接入', value: 'MODBUS_RTU' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const params = ref<Record<string, any>>({});
|
||||||
|
const edgeResourceRef = ref<Record<string, any>>({});
|
||||||
|
const settingVisible = ref<boolean>(false);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const current = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'category',
|
||||||
|
title: '通信协议',
|
||||||
|
valueType: 'select',
|
||||||
|
scopedSlots: true,
|
||||||
|
key: 'category',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: options,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属边缘网关',
|
||||||
|
dataIndex: 'sourceId',
|
||||||
|
key: 'sourceId',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryNoPagingPost({
|
||||||
|
paging: false,
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'productId$product-info',
|
||||||
|
value: 'accessProvider is official-edge-gateway',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sorts: [
|
||||||
|
{
|
||||||
|
name: 'createTime',
|
||||||
|
order: 'desc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).then((resp: any) => {
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
key: 'createTime',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '禁用', value: 'disabled' },
|
||||||
|
{ label: '正常', value: 'enabled' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 250,
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const getActions = (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
): ActionsType[] => {
|
||||||
|
if (!data) return [];
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
text: '编辑',
|
||||||
|
tooltip: {
|
||||||
|
title: '编辑',
|
||||||
|
},
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
visible.value = true;
|
||||||
|
current.value = data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'setting',
|
||||||
|
text: '下发',
|
||||||
|
disabled: data.state?.value === 'disabled',
|
||||||
|
tooltip: {
|
||||||
|
title:
|
||||||
|
data.state.value === 'disabled'
|
||||||
|
? '请先启用,再下发'
|
||||||
|
: '下发',
|
||||||
|
},
|
||||||
|
icon: 'DownSquareOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
settingVisible.value = true;
|
||||||
|
current.value = data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'action',
|
||||||
|
text: data.state?.value !== 'disabled' ? '禁用' : '启用',
|
||||||
|
tooltip: {
|
||||||
|
title: data.state?.value !== 'disabled' ? '禁用' : '启用',
|
||||||
|
},
|
||||||
|
icon:
|
||||||
|
data.state.value !== 'disabled'
|
||||||
|
? 'StopOutlined'
|
||||||
|
: 'CheckCircleOutlined',
|
||||||
|
popConfirm: {
|
||||||
|
title: `确认${
|
||||||
|
data.state.value !== 'disabled' ? '禁用' : '启用'
|
||||||
|
}?`,
|
||||||
|
onConfirm: async () => {
|
||||||
|
let response = undefined;
|
||||||
|
if (data.state.value !== 'disabled') {
|
||||||
|
response = await _stop([data.id]);
|
||||||
|
} else {
|
||||||
|
response = await _start([data.id]);
|
||||||
|
}
|
||||||
|
if (response && response.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
edgeResourceRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
text: '删除',
|
||||||
|
disabled: data.state?.value !== 'disabled',
|
||||||
|
tooltip: {
|
||||||
|
title:
|
||||||
|
data.state.value !== 'disabled'
|
||||||
|
? '请先禁用,再删除。'
|
||||||
|
: '删除',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除?',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const resp = await _delete(data.id);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
edgeResourceRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: 'DeleteOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (type === 'card')
|
||||||
|
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (_params: any) => {
|
||||||
|
params.value = _params;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleView = (id: string) => {
|
||||||
|
menuStory.jumpPage('device/Instance/Detail', { id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveBtn = () => {
|
||||||
|
visible.value = false;
|
||||||
|
edgeResourceRef.value?.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
settingVisible.value = false;
|
||||||
|
edgeResourceRef.value?.reload();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -9,32 +9,52 @@
|
||||||
:confirmLoading="btnLoading"
|
:confirmLoading="btnLoading"
|
||||||
width="660px"
|
width="660px"
|
||||||
>
|
>
|
||||||
<j-form layout="vertical">
|
<j-form ref="formRef" :model="formData" layout="vertical">
|
||||||
<j-form-item label="产品名称" v-bind="validateInfos.name">
|
<j-form-item
|
||||||
|
label="产品名称"
|
||||||
|
name="name"
|
||||||
|
:rules="{ required: true, message: '请输入产品名称' }"
|
||||||
|
>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="formData.name"
|
v-model:value="formData.name"
|
||||||
placeholder="请输入名称"
|
placeholder="请输入名称"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<template v-if="channel === 'gb28181-2016' && formData.accessId">
|
<template v-if="deviceType !== 'gateway'">
|
||||||
<j-form-item
|
<template v-for="(item, index) in extendFormItem" :key="index">
|
||||||
label="接入密码"
|
<j-form-item
|
||||||
v-bind="validateInfos['configuration.access_pwd']"
|
:name="item.name"
|
||||||
>
|
:label="item.label"
|
||||||
<j-input-password
|
:rules="{
|
||||||
v-model:value="formData.configuration.access_pwd"
|
required: item.required,
|
||||||
placeholder="请输入接入密码"
|
message: item.message,
|
||||||
/>
|
trigger: 'change',
|
||||||
</j-form-item>
|
}"
|
||||||
<j-form-item label="流传输模式">
|
>
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="formData.configuration.stream_mode"
|
v-if="item.type === 'enum'"
|
||||||
placeholder="请选择流传输模式"
|
v-model:value="formData[item.name[0]][item.name[1]]"
|
||||||
:options="streamMode"
|
:options="item.options"
|
||||||
/>
|
:placeholder="item.message"
|
||||||
</j-form-item>
|
/>
|
||||||
|
<j-input-password
|
||||||
|
v-else-if="item.type === 'password'"
|
||||||
|
v-model:value="formData[item.name[0]][item.name[1]]"
|
||||||
|
:placeholder="item.message"
|
||||||
|
/>
|
||||||
|
<j-input
|
||||||
|
v-else
|
||||||
|
v-model:value="formData[item.name[0]][item.name[1]]"
|
||||||
|
:placeholder="item.message"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<j-form-item label="接入网关" v-bind="validateInfos.accessId">
|
<j-form-item
|
||||||
|
label="接入网关"
|
||||||
|
name="accessId"
|
||||||
|
:rules="{ required: true, message: '请选择接入网关' }"
|
||||||
|
>
|
||||||
<div class="gateway-box">
|
<div class="gateway-box">
|
||||||
<div v-if="!gatewayList.length">
|
<div v-if="!gatewayList.length">
|
||||||
暂无数据,请先
|
暂无数据,请先
|
||||||
|
@ -119,20 +139,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Form, message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { PropType } from 'vue';
|
|
||||||
import { streamMode } from '@/views/media/Device/const';
|
|
||||||
import DeviceApi from '@/api/media/device';
|
import DeviceApi from '@/api/media/device';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { gatewayType } from '@/views/media/Device/typings';
|
import { gatewayType } from '@/views/media/Device/typings';
|
||||||
import { providerType } from '../const';
|
import { providerType } from '../const';
|
||||||
|
|
||||||
const useForm = Form.useForm;
|
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
(e: 'update:visible', data: boolean): void;
|
(e: 'update:visible', data: boolean): void;
|
||||||
(e: 'update:productId', data: string): void;
|
(e: 'update:productId', data: string): void;
|
||||||
(e: 'close'): void;
|
(e: 'close'): void;
|
||||||
|
(e: 'save', data: Record<string, any>): void;
|
||||||
};
|
};
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
@ -140,6 +157,7 @@ const props = defineProps({
|
||||||
visible: { type: Boolean, default: false },
|
visible: { type: Boolean, default: false },
|
||||||
productId: { type: String, default: '' },
|
productId: { type: String, default: '' },
|
||||||
channel: { type: String, default: '' },
|
channel: { type: String, default: '' },
|
||||||
|
deviceType: { type: String, default: 'device' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const _vis = computed({
|
const _vis = computed({
|
||||||
|
@ -166,20 +184,36 @@ const getGatewayList = async () => {
|
||||||
* @param e
|
* @param e
|
||||||
*/
|
*/
|
||||||
const _selectedRowKeys = ref<string[]>([]);
|
const _selectedRowKeys = ref<string[]>([]);
|
||||||
|
const extendFormItem = ref<any[]>();
|
||||||
const handleClick = async (e: any) => {
|
const handleClick = async (e: any) => {
|
||||||
_selectedRowKeys.value = [e.id];
|
_selectedRowKeys.value = [e.id];
|
||||||
formData.value.accessId = e.id;
|
formData.value.accessId = e.id;
|
||||||
formData.value.accessName = e.name;
|
formData.value.accessName = e.name;
|
||||||
formData.value.accessProvider = e.provider;
|
formData.value.accessProvider = e.provider;
|
||||||
formData.value.messageProtocol = e.provider;
|
formData.value.messageProtocol = e.protocolDetail.id;
|
||||||
formData.value.protocolName = e.protocolDetail.name;
|
formData.value.protocolName = e.protocolDetail.name;
|
||||||
formData.value.transportProtocol = e.transport;
|
formData.value.transportProtocol = e.transport;
|
||||||
|
|
||||||
const { result } = await DeviceApi.getConfiguration(
|
const { result } = await DeviceApi.getConfiguration(
|
||||||
props.channel,
|
e.protocol,
|
||||||
e.transport,
|
e.transport,
|
||||||
);
|
);
|
||||||
console.log('result: ', result);
|
|
||||||
|
extendFormItem.value = result.properties.map((item: any) => ({
|
||||||
|
name: ['configuration', item.property],
|
||||||
|
label: item.name,
|
||||||
|
type: item.type?.type,
|
||||||
|
value: item.type.expands?.defaultValue,
|
||||||
|
options: item.type.elements?.map((e: any) => ({
|
||||||
|
label: e.text,
|
||||||
|
value: e.value,
|
||||||
|
})),
|
||||||
|
required: !!item.type.expands?.required,
|
||||||
|
message:
|
||||||
|
item.type?.type === 'enum'
|
||||||
|
? `请选择${item.name}`
|
||||||
|
: `请输入${item.name}`,
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -187,10 +221,6 @@ watch(
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
getGatewayList();
|
getGatewayList();
|
||||||
|
|
||||||
formRules.value['configuration.access_pwd'][0].required =
|
|
||||||
props.channel === 'gb28181-2016';
|
|
||||||
validate();
|
|
||||||
} else {
|
} else {
|
||||||
emit('close');
|
emit('close');
|
||||||
}
|
}
|
||||||
|
@ -198,6 +228,7 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
|
const formRef = ref();
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
accessId: '',
|
accessId: '',
|
||||||
accessName: '',
|
accessName: '',
|
||||||
|
@ -206,32 +237,20 @@ const formData = ref({
|
||||||
access_pwd: '',
|
access_pwd: '',
|
||||||
stream_mode: 'UDP',
|
stream_mode: 'UDP',
|
||||||
},
|
},
|
||||||
deviceType: 'device',
|
deviceType: props.deviceType,
|
||||||
messageProtocol: '',
|
messageProtocol: '',
|
||||||
name: '',
|
name: '',
|
||||||
protocolName: '',
|
protocolName: '',
|
||||||
transportProtocol: '',
|
transportProtocol: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 验证规则
|
|
||||||
const formRules = ref({
|
|
||||||
name: [{ required: true, message: '请输入产品名称' }],
|
|
||||||
'configuration.access_pwd': [{ required: true, message: '请输入接入密码' }],
|
|
||||||
accessId: [{ required: true, message: '请选择接入网关' }],
|
|
||||||
});
|
|
||||||
|
|
||||||
const { resetFields, validate, validateInfos, clearValidate } = useForm(
|
|
||||||
formData.value,
|
|
||||||
formRules.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交
|
* 提交
|
||||||
*/
|
*/
|
||||||
const btnLoading = ref(false);
|
const btnLoading = ref(false);
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
// console.log('formData.value: ', formData.value);
|
formRef.value
|
||||||
validate()
|
?.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
btnLoading.value = true;
|
btnLoading.value = true;
|
||||||
const res = await DeviceApi.saveProduct(formData.value);
|
const res = await DeviceApi.saveProduct(formData.value);
|
||||||
|
@ -241,20 +260,21 @@ const handleOk = () => {
|
||||||
res.result.id,
|
res.result.id,
|
||||||
);
|
);
|
||||||
if (deployResp.success) {
|
if (deployResp.success) {
|
||||||
|
emit('save', {...res.result})
|
||||||
message.success('操作成功');
|
message.success('操作成功');
|
||||||
handleCancel();
|
handleCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btnLoading.value = false;
|
btnLoading.value = false;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err: any) => {
|
||||||
console.log('err: ', err);
|
console.log('err: ', err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
_vis.value = false;
|
_vis.value = false;
|
||||||
resetFields();
|
formRef.value.resetFields();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -392,53 +392,53 @@ const formRules = ref({
|
||||||
provider: [{ required: true, message: '请选择类型' }],
|
provider: [{ required: true, message: '请选择类型' }],
|
||||||
// 钉钉
|
// 钉钉
|
||||||
'configuration.appKey': [
|
'configuration.appKey': [
|
||||||
{ required: true, message: '请输入AppKey' },
|
{ required: true, message: '请输入AppKey', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
],
|
],
|
||||||
'configuration.appSecret': [
|
'configuration.appSecret': [
|
||||||
{ required: true, message: '请输入AppSecret' },
|
{ required: true, message: '请输入AppSecret', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
],
|
],
|
||||||
// 'configuration.url': [{ required: true, message: '请输入WebHook' }],
|
// 'configuration.url': [{ required: true, message: '请输入WebHook' }],
|
||||||
// 微信
|
// 微信
|
||||||
'configuration.corpId': [
|
'configuration.corpId': [
|
||||||
{ required: true, message: '请输入corpId' },
|
{ required: true, message: '请输入corpId', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
'configuration.corpSecret': [
|
'configuration.corpSecret': [
|
||||||
{ required: true, message: '请输入corpSecret' },
|
{ required: true, message: '请输入corpSecret', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
// 阿里云语音/短信
|
// 阿里云语音/短信
|
||||||
'configuration.regionId': [
|
'configuration.regionId': [
|
||||||
{ required: true, message: '请输入RegionId' },
|
{ required: true, message: '请输入RegionId', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
'configuration.accessKeyId': [
|
'configuration.accessKeyId': [
|
||||||
{ required: true, message: '请输入AccessKeyId' },
|
{ required: true, message: '请输入AccessKeyId', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
'configuration.secret': [
|
'configuration.secret': [
|
||||||
{ required: true, message: '请输入Secret' },
|
{ required: true, message: '请输入Secret', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
// 邮件
|
// 邮件
|
||||||
'configuration.host': [{ required: true, message: '请输入服务器地址' }],
|
'configuration.host': [{ required: true, message: '请输入服务器地址', trigger: 'blur' }],
|
||||||
'configuration.sender': [
|
'configuration.sender': [
|
||||||
{ required: true, message: '请输入发件人' },
|
{ required: true, message: '请输入发件人', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
'configuration.username': [
|
'configuration.username': [
|
||||||
{ required: true, message: '请输入用户名' },
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
'configuration.password': [
|
'configuration.password': [
|
||||||
{ required: true, message: '请输入密码' },
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
// webhook
|
// webhook
|
||||||
'configuration.url': [
|
'configuration.url': [
|
||||||
{ required: true, message: '请输入Webhook' },
|
{ required: true, message: '请输入Webhook', trigger: 'blur' },
|
||||||
// {
|
// {
|
||||||
// pattern:
|
// pattern:
|
||||||
// /^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[j-z]{2,6}\/?/,
|
// /^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[j-z]{2,6}\/?/,
|
||||||
|
@ -458,8 +458,6 @@ const getDetail = async () => {
|
||||||
const res = await configApi.detail(route.params.id as string);
|
const res = await configApi.detail(route.params.id as string);
|
||||||
// formData.value = res.result;
|
// formData.value = res.result;
|
||||||
Object.assign(formData.value, res.result);
|
Object.assign(formData.value, res.result);
|
||||||
// console.log('res.result: ', res.result);
|
|
||||||
// console.log('formData.value: ', formData.value);
|
|
||||||
};
|
};
|
||||||
getDetail();
|
getDetail();
|
||||||
|
|
||||||
|
@ -537,7 +535,6 @@ const btnLoading = ref<boolean>(false);
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
validate()
|
validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
// console.log('formData.value: ', formData.value);
|
|
||||||
btnLoading.value = true;
|
btnLoading.value = true;
|
||||||
let res;
|
let res;
|
||||||
if (!formData.value.id) {
|
if (!formData.value.id) {
|
||||||
|
@ -545,7 +542,6 @@ const handleSubmit = () => {
|
||||||
} else {
|
} else {
|
||||||
res = await configApi.update(formData.value);
|
res = await configApi.update(formData.value);
|
||||||
}
|
}
|
||||||
// console.log('res: ', res);
|
|
||||||
if (res?.success) {
|
if (res?.success) {
|
||||||
message.success('保存成功');
|
message.success('保存成功');
|
||||||
router.back();
|
router.back();
|
||||||
|
|
|
@ -147,6 +147,19 @@
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
<template #type="slotProps">
|
||||||
|
<span> {{ getMethodTxt(slotProps.type) }}</span>
|
||||||
|
</template>
|
||||||
|
<template #provider="slotProps">
|
||||||
|
<span>
|
||||||
|
{{ getProviderTxt(slotProps.type, slotProps.provider) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- <template #description="slotProps">
|
||||||
|
<Ellipsis>
|
||||||
|
{{ slotProps.description }}
|
||||||
|
</Ellipsis>
|
||||||
|
</template> -->
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<j-space :size="16">
|
<j-space :size="16">
|
||||||
<template
|
<template
|
||||||
|
@ -205,6 +218,7 @@ const columns = [
|
||||||
title: '配置名称',
|
title: '配置名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
width: 100,
|
||||||
search: {
|
search: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
@ -214,6 +228,7 @@ const columns = [
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
key: 'type',
|
key: 'type',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
width: 100,
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: NOTICE_METHOD,
|
options: NOTICE_METHOD,
|
||||||
|
@ -227,6 +242,7 @@ const columns = [
|
||||||
dataIndex: 'provider',
|
dataIndex: 'provider',
|
||||||
key: 'provider',
|
key: 'provider',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
width: 200,
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: providerList,
|
options: providerList,
|
||||||
|
@ -239,6 +255,8 @@ const columns = [
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
|
scopedSlots: true,
|
||||||
|
ellipsis: true,
|
||||||
search: {
|
search: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
@ -272,6 +290,14 @@ const getLogo = (type: string, provider: string) => {
|
||||||
const getMethodTxt = (type: string) => {
|
const getMethodTxt = (type: string) => {
|
||||||
return NOTICE_METHOD.find((f) => f.value === type)?.label;
|
return NOTICE_METHOD.find((f) => f.value === type)?.label;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 根据类型展示对应文案
|
||||||
|
* @param type
|
||||||
|
* @param provider
|
||||||
|
*/
|
||||||
|
const getProviderTxt = (type: string, provider: string) => {
|
||||||
|
return MSG_TYPE[type].find((f: any) => f.value === provider)?.label;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增
|
* 新增
|
||||||
|
|
|
@ -50,34 +50,50 @@
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
:name="['templateDetailTable', index, 'value']"
|
:name="['templateDetailTable', index, 'value']"
|
||||||
:rules="{
|
:rules="[
|
||||||
required: record.required,
|
{
|
||||||
message: '该字段为必填字段',
|
required: record.required,
|
||||||
}"
|
message: '该字段为必填字段',
|
||||||
|
},
|
||||||
|
...record.otherRules,
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<ToUser
|
<template
|
||||||
v-if="record.type === 'user'"
|
v-if="
|
||||||
v-model:toUser="record.value"
|
data.type === 'dingTalk' ||
|
||||||
:type="data.type"
|
data.type === 'weixin'
|
||||||
:config-id="formData.configId"
|
"
|
||||||
/>
|
>
|
||||||
<ToOrg
|
<ToUser
|
||||||
v-else-if="record.type === 'org'"
|
v-if="record.type === 'user'"
|
||||||
:type="data.type"
|
v-model:toUser="record.value"
|
||||||
:config-id="formData.configId"
|
:type="data.type"
|
||||||
v-model:toParty="record.value"
|
:config-id="formData.configId"
|
||||||
/>
|
/>
|
||||||
<ToTag
|
<ToOrg
|
||||||
v-else-if="record.type === 'tag'"
|
v-else-if="record.type === 'org'"
|
||||||
:type="data.type"
|
:type="data.type"
|
||||||
:config-id="formData.configId"
|
:config-id="formData.configId"
|
||||||
v-model:toTag="record.value"
|
v-model:toParty="record.value"
|
||||||
/>
|
/>
|
||||||
<ValueItem
|
<ToTag
|
||||||
v-else
|
v-else-if="record.type === 'tag'"
|
||||||
v-model:modelValue="record.value"
|
:type="data.type"
|
||||||
:itemType="record.type"
|
:config-id="formData.configId"
|
||||||
/>
|
v-model:toTag="record.value"
|
||||||
|
/>
|
||||||
|
<ValueItem
|
||||||
|
v-else
|
||||||
|
v-model:modelValue="record.value"
|
||||||
|
:itemType="record.type"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ValueItem
|
||||||
|
v-model:modelValue="record.value"
|
||||||
|
:itemType="record.type"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
@ -100,6 +116,8 @@ import { message } from 'ant-design-vue';
|
||||||
import ToUser from '../Detail/components/ToUser.vue';
|
import ToUser from '../Detail/components/ToUser.vue';
|
||||||
import ToOrg from '../Detail/components/ToOrg.vue';
|
import ToOrg from '../Detail/components/ToOrg.vue';
|
||||||
import ToTag from '../Detail/components/ToTag.vue';
|
import ToTag from '../Detail/components/ToTag.vue';
|
||||||
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
|
import { phoneRegEx } from '@/utils/validate';
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
(e: 'update:visible', data: boolean): void;
|
(e: 'update:visible', data: boolean): void;
|
||||||
|
@ -154,8 +172,28 @@ const getTemplateDetail = async () => {
|
||||||
formData.value.templateDetailTable = result.variableDefinitions.map(
|
formData.value.templateDetailTable = result.variableDefinitions.map(
|
||||||
(m: any) => ({
|
(m: any) => ({
|
||||||
...m,
|
...m,
|
||||||
type: m.expands ? m.expands.businessType : m.type,
|
type: m.expands?.businessType ? m.expands.businessType : m.type,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
|
// 电话字段校验
|
||||||
|
otherRules:
|
||||||
|
m.id === 'calledNumber' || m.id === 'phoneNumber'
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: 'change',
|
||||||
|
validator(_rule: Rule, value: string) {
|
||||||
|
if (!value) return Promise.resolve();
|
||||||
|
if (!phoneRegEx(value))
|
||||||
|
return Promise.reject('请输入有效号码');
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: '',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
AgentID
|
AgentId
|
||||||
<j-tooltip title="应用唯一标识">
|
<j-tooltip title="应用唯一标识">
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
v-model:value="
|
v-model:value="
|
||||||
formData.template.agentId
|
formData.template.agentId
|
||||||
"
|
"
|
||||||
placeholder="请输入AppSecret"
|
placeholder="请输入AgentId"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-row :gutter="10">
|
<j-row :gutter="10">
|
||||||
|
@ -271,7 +271,7 @@
|
||||||
</template>
|
</template>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="formData.template.agentId"
|
v-model:value="formData.template.agentId"
|
||||||
placeholder="请输入agentId"
|
placeholder="请输入AgentId"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-row :gutter="10">
|
<j-row :gutter="10">
|
||||||
|
@ -664,7 +664,6 @@
|
||||||
<j-radio :value="false">自定义</j-radio>
|
<j-radio :value="false">自定义</j-radio>
|
||||||
</j-radio-group>
|
</j-radio-group>
|
||||||
<j-textarea
|
<j-textarea
|
||||||
v-model:value="formData.template.body"
|
|
||||||
placeholder="请求体中的数据来自于发送通知时指定的所有变量"
|
placeholder="请求体中的数据来自于发送通知时指定的所有变量"
|
||||||
v-if="formData.template.contextAsBody"
|
v-if="formData.template.contextAsBody"
|
||||||
disabled
|
disabled
|
||||||
|
@ -896,28 +895,33 @@ watch(
|
||||||
const formRules = ref({
|
const formRules = ref({
|
||||||
type: [{ required: true, message: '请选择通知方式' }],
|
type: [{ required: true, message: '请选择通知方式' }],
|
||||||
name: [
|
name: [
|
||||||
{ required: true, message: '请输入名称' },
|
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
provider: [{ required: true, message: '请选择类型' }],
|
provider: [{ required: true, message: '请选择类型' }],
|
||||||
configId: [{ required: true, message: '请选择绑定配置' }],
|
configId: [{ required: true, message: '请选择绑定配置', trigger: 'blur' }],
|
||||||
// 钉钉
|
// 钉钉
|
||||||
'template.agentId': [{ required: true, message: '请输入agentId' }],
|
'template.agentId': [
|
||||||
'template.messageType': [{ required: true, message: '请选择消息类型' }],
|
{ required: true, message: '请输入AgentId', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
|
],
|
||||||
|
'template.messageType': [
|
||||||
|
{ required: true, message: '请选择消息类型', trigger: 'blur' },
|
||||||
|
],
|
||||||
'template.markdown.title': [
|
'template.markdown.title': [
|
||||||
{ required: true, message: '请输入标题', trigger: 'change' },
|
{ required: true, message: '请输入标题', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
],
|
],
|
||||||
'template.link.title': [
|
'template.link.title': [
|
||||||
{ required: true, message: '请输入标题', trigger: 'change' },
|
{ required: true, message: '请输入标题', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
],
|
],
|
||||||
// 'template.url': [{ required: true, message: '请输入WebHook' }],
|
// 'template.url': [{ required: true, message: '请输入WebHook' }],
|
||||||
// 微信
|
// 微信
|
||||||
// 'template.agentId': [{ required: true, message: '请输入agentId' }],
|
// 'template.agentId': [{ required: true, message: '请输入AgentId' }],
|
||||||
// 邮件
|
// 邮件
|
||||||
'template.subject': [
|
'template.subject': [
|
||||||
{ required: true, message: '请输入标题' },
|
{ required: true, message: '请输入标题', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
],
|
],
|
||||||
'template.sendTo': [
|
'template.sendTo': [
|
||||||
|
@ -944,7 +948,9 @@ const formRules = ref({
|
||||||
],
|
],
|
||||||
// 阿里云语音
|
// 阿里云语音
|
||||||
'template.templateType': [{ required: true, message: '请选择类型' }],
|
'template.templateType': [{ required: true, message: '请选择类型' }],
|
||||||
'template.templateCode': [{ required: true, message: '请输入模板ID' }],
|
'template.templateCode': [
|
||||||
|
{ required: true, message: '请输入模板ID', trigger: 'blur' },
|
||||||
|
],
|
||||||
'template.calledNumber': [
|
'template.calledNumber': [
|
||||||
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
{
|
{
|
||||||
|
@ -978,14 +984,19 @@ const formRules = ref({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// 短信
|
// 短信
|
||||||
'template.code': [{ required: true, message: '请选择模板' }],
|
'template.code': [
|
||||||
'template.signName': [{ required: true, message: '请输入签名' }],
|
{ required: true, message: '请选择模板', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
'template.signName': [
|
||||||
|
{ required: true, message: '请输入签名', trigger: 'blur' },
|
||||||
|
],
|
||||||
// webhook
|
// webhook
|
||||||
description: [{ max: 200, message: '最多可输入200个字符' }],
|
description: [{ max: 200, message: '最多可输入200个字符' }],
|
||||||
'template.message': [
|
'template.message': [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入模板内容',
|
message: '请输入模板内容',
|
||||||
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
{ max: 500, message: '最多可输入500个字符', trigger: 'change' },
|
{ max: 500, message: '最多可输入500个字符', trigger: 'change' },
|
||||||
],
|
],
|
||||||
|
@ -1071,7 +1082,7 @@ const spliceStr = () => {
|
||||||
variableFieldsStr += formData.value.template.body as string;
|
variableFieldsStr += formData.value.template.body as string;
|
||||||
if (formData.value.provider === 'aliyun')
|
if (formData.value.provider === 'aliyun')
|
||||||
variableFieldsStr += formData.value.template.ttsmessage as string;
|
variableFieldsStr += formData.value.template.ttsmessage as string;
|
||||||
// console.log('variableFieldsStr: ', variableFieldsStr);
|
|
||||||
return variableFieldsStr || '';
|
return variableFieldsStr || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1128,7 +1139,6 @@ const handleMessageTypeChange = () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
formData.value.variableDefinitions = [];
|
formData.value.variableDefinitions = [];
|
||||||
// formData.value.template.message = '';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1139,7 +1149,6 @@ const getDetail = async () => {
|
||||||
const res = await templateApi.detail(route.params.id as string);
|
const res = await templateApi.detail(route.params.id as string);
|
||||||
// formData.value = res.result;
|
// formData.value = res.result;
|
||||||
Object.assign(formData.value, res.result);
|
Object.assign(formData.value, res.result);
|
||||||
// console.log('formData.value: ', formData.value);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
getDetail();
|
getDetail();
|
||||||
|
@ -1174,8 +1183,7 @@ const handleTypeChange = () => {
|
||||||
const handleProviderChange = () => {
|
const handleProviderChange = () => {
|
||||||
formData.value.template =
|
formData.value.template =
|
||||||
TEMPLATE_FIELD_MAP[formData.value.type][formData.value.provider];
|
TEMPLATE_FIELD_MAP[formData.value.type][formData.value.provider];
|
||||||
// console.log('formData.value: ', formData.value);
|
|
||||||
// console.log('formData.value.template: ', formData.value.template);
|
|
||||||
getConfigList();
|
getConfigList();
|
||||||
resetPublicFiles();
|
resetPublicFiles();
|
||||||
};
|
};
|
||||||
|
@ -1243,7 +1251,6 @@ const handleSubmit = () => {
|
||||||
delete formData.value.template.link;
|
delete formData.value.template.link;
|
||||||
if (formData.value.template.messageType === 'link')
|
if (formData.value.template.messageType === 'link')
|
||||||
delete formData.value.template.markdown;
|
delete formData.value.template.markdown;
|
||||||
// console.log('formData.value: ', formData.value);
|
|
||||||
// 提交必填验证无法通过, 实际已有值, 问题未知, 暂时解决方法: 延迟验证
|
// 提交必填验证无法通过, 实际已有值, 问题未知, 暂时解决方法: 延迟验证
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
validate()
|
validate()
|
||||||
|
@ -1259,13 +1266,11 @@ const handleSubmit = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
btnLoading.value = true;
|
btnLoading.value = true;
|
||||||
let res;
|
|
||||||
if (!formData.value.id) {
|
const res = formData.value.id
|
||||||
res = await templateApi.save(formData.value);
|
? await templateApi.update(formData.value)
|
||||||
} else {
|
: await templateApi.save(formData.value);
|
||||||
res = await templateApi.update(formData.value);
|
|
||||||
}
|
|
||||||
// console.log('res: ', res);
|
|
||||||
if (res?.success) {
|
if (res?.success) {
|
||||||
message.success('保存成功');
|
message.success('保存成功');
|
||||||
router.back();
|
router.back();
|
||||||
|
@ -1279,14 +1284,4 @@ const handleSubmit = () => {
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
// test
|
|
||||||
// watch(
|
|
||||||
// () => formData.value,
|
|
||||||
// (val) => {
|
|
||||||
// console.log('formData.value: ', val);
|
|
||||||
// },
|
|
||||||
// { deep: true },
|
|
||||||
// );
|
|
||||||
// test
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -110,14 +110,19 @@
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, text, record }">
|
<template #type="slotProps">
|
||||||
<span v-if="column.dataIndex === 'type'">
|
<span> {{ getMethodTxt(slotProps.type) }}</span>
|
||||||
{{ getMethodTxt(record.type) }}
|
</template>
|
||||||
</span>
|
<template #provider="slotProps">
|
||||||
<span v-if="column.dataIndex === 'provider'">
|
<span>
|
||||||
{{ getProviderTxt(record.type, record.provider) }}
|
{{ getProviderTxt(slotProps.type, slotProps.provider) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- <template #description="slotProps">
|
||||||
|
<Ellipsis>
|
||||||
|
{{ slotProps.description }}
|
||||||
|
</Ellipsis>
|
||||||
|
</template> -->
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<j-space :size="16">
|
<j-space :size="16">
|
||||||
<template
|
<template
|
||||||
|
@ -150,12 +155,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import TemplateApi from '@/api/notice/template';
|
import TemplateApi from '@/api/notice/template';
|
||||||
import type { ActionsType } from '@/components/Table/index.vue';
|
import type { ActionsType } from '@/components/Table/index.vue';
|
||||||
// import { getImage, LocalStore } from '@/utils/comm';
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
// import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
|
||||||
|
|
||||||
import { NOTICE_METHOD, MSG_TYPE } from '@/views/notice/const';
|
import { NOTICE_METHOD, MSG_TYPE } from '@/views/notice/const';
|
||||||
|
|
||||||
import Debug from './Debug/index.vue';
|
import Debug from './Debug/index.vue';
|
||||||
import Log from './Log/index.vue';
|
import Log from './Log/index.vue';
|
||||||
import { downloadObject } from '@/utils/utils';
|
import { downloadObject } from '@/utils/utils';
|
||||||
|
@ -210,6 +211,8 @@ const columns = [
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
|
scopedSlots: true,
|
||||||
|
ellipsis: true,
|
||||||
search: {
|
search: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
|
|
@ -82,7 +82,7 @@ export const MSG_TYPE = {
|
||||||
],
|
],
|
||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
label: 'email',
|
label: '邮件',
|
||||||
value: 'embedded',
|
value: 'embedded',
|
||||||
logo: getImage('/notice/email.png'),
|
logo: getImage('/notice/email.png'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-form layout="vertical" :rules="rule" :model="form" ref="formRef">
|
<j-form layout="vertical" :rules="rule" :model="form" ref="formRef">
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item label="名称" name="name">
|
<j-form-item label="名称" name="name">
|
||||||
<a-input
|
<j-input
|
||||||
placeholder="请输入名称"
|
placeholder="请输入名称"
|
||||||
v-model:value="form.name"
|
v-model:value="form.name"
|
||||||
></a-input> </a-form-item
|
></j-input> </j-form-item
|
||||||
></a-col>
|
></j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item label="类型" name="targetType">
|
<j-form-item label="类型" name="targetType">
|
||||||
<a-select
|
<j-select
|
||||||
:options="options"
|
:options="options"
|
||||||
v-model:value="form.targetType"
|
v-model:value="form.targetType"
|
||||||
:disabled="selectDisable"
|
:disabled="selectDisable"
|
||||||
></a-select>
|
></j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
<a-form-item label="级别" name="level">
|
<j-form-item label="级别" name="level">
|
||||||
<a-radio-group v-model:value="form.level">
|
<j-radio-group v-model:value="form.level">
|
||||||
<a-radio-button
|
<j-radio-button
|
||||||
v-for="(item, index) in levelOption"
|
v-for="(item, index) in levelOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
|
@ -40,14 +40,14 @@
|
||||||
alt=""
|
alt=""
|
||||||
/>{{ item.label }}
|
/>{{ item.label }}
|
||||||
</div>
|
</div>
|
||||||
</a-radio-button>
|
</j-radio-button>
|
||||||
</a-radio-group>
|
</j-radio-group>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<a-form-item label="说明" name="description">
|
<j-form-item label="说明" name="description">
|
||||||
<a-textarea v-model:value="form.description"></a-textarea>
|
<j-textarea v-model:value="form.description"></j-textarea>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<PermissionButton type="primary" @click="handleSave" :hasPermission="['rule-engine/Alarm/Configuration:add','rule-engine/Alarm/Configuration:update']">保存</PermissionButton>
|
<PermissionButton type="primary" @click="handleSave" :hasPermission="['rule-engine/Alarm/Configuration:add','rule-engine/Alarm/Configuration:update']">保存</PermissionButton>
|
||||||
</a-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ const handleSave = async () => {
|
||||||
const res = await save(form);
|
const res = await save(form);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
message.success('操作成功');
|
message.success('操作成功,请配置关联的场景联动');
|
||||||
menuStory.jumpPage(
|
menuStory.jumpPage(
|
||||||
'rule-engine/Alarm/Configuration/Save',
|
'rule-engine/Alarm/Configuration/Save',
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
visible
|
visible
|
||||||
title="新增"
|
title="新增"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
@cancel="closeModal"
|
@cancel="closeModal"
|
||||||
@ok="saveCorrelation"
|
@ok="saveCorrelation"
|
||||||
>
|
>
|
||||||
<Search :columns="columns" @search="handleSearch"></Search>
|
<pro-search :columns="columns" @search="handleSearch"/>
|
||||||
<div style="height: 500px; overflow-y: auto">
|
<div style="height: 500px; overflow-y: auto">
|
||||||
<JProTable
|
<JProTable
|
||||||
model="CARD"
|
model="CARD"
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -213,7 +213,7 @@ const saveCorrelation = async () => {
|
||||||
const list = _selectedRowKeys.value.map((item: any) => {
|
const list = _selectedRowKeys.value.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
alarmId: props.id,
|
alarmId: props.id,
|
||||||
releId: item,
|
ruleId: item,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const res = await bindScene([...list]);
|
const res = await bindScene([...list]);
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
ref="actionRef"
|
ref="actionRef"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-space>
|
<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>
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
新增
|
新增
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<SceneCard
|
<SceneCard
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<j-card>
|
<j-card>
|
||||||
<j-tabs :activeKey="activeKey" @change="changeTabs">
|
<j-tabs :activeKey="activeKey" @change="changeTabs">
|
||||||
<j-tab-pane key="1" tab="基础配置">
|
<j-tab-pane key="1" tab="基础配置">
|
||||||
<Base/>
|
<Base />
|
||||||
</j-tab-pane>
|
</j-tab-pane>
|
||||||
<j-tab-pane key="2" tab="关联场景联动">
|
<j-tab-pane key="2" tab="关联场景联动">
|
||||||
<Scene></Scene>
|
<Scene></Scene>
|
||||||
</j-tab-pane>
|
</j-tab-pane>
|
||||||
<j-tab-pane key="3" tab="告警记录">
|
<j-tab-pane key="3" tab="告警记录">
|
||||||
<Log/>
|
<Log v-if="activeKey === '3'" />
|
||||||
</j-tab-pane>
|
</j-tab-pane>
|
||||||
</j-tabs>
|
</j-tabs>
|
||||||
</j-card>
|
</j-card>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -23,13 +23,13 @@ import Log from './Log/indev.vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const changeTabs = (e:any) =>{
|
const changeTabs = (e: any) => {
|
||||||
if(route.query?.id){
|
if (route.query?.id) {
|
||||||
activeKey.value = e;
|
activeKey.value = e;
|
||||||
}else{
|
} else {
|
||||||
message.error('请先保存基础配置');
|
message.error('请先保存基础配置');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
const activeKey = ref('1');
|
const activeKey = ref('1');
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -56,8 +56,8 @@
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</span>
|
</span>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
<a-row>
|
<j-row>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<div class="content-des-title">
|
<div class="content-des-title">
|
||||||
关联场景联动
|
关联场景联动
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,8 +66,8 @@
|
||||||
{{ (slotProps?.scene || []).map((item: any) => item?.name).join(',') || '' }}
|
{{ (slotProps?.scene || []).map((item: any) => item?.name).join(',') || '' }}
|
||||||
</div></Ellipsis
|
</div></Ellipsis
|
||||||
>
|
>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<div class="content-des-title">
|
<div class="content-des-title">
|
||||||
告警级别
|
告警级别
|
||||||
</div>
|
</div>
|
||||||
|
@ -75,8 +75,8 @@
|
||||||
{{ (Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
|
{{ (Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
|
||||||
slotProps.level }}
|
slotProps.level }}
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
<span>{{ map[slotProps.targetType] }}</span>
|
<span>{{ map[slotProps.targetType] }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #level="slotProps">
|
<template #level="slotProps">
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
:title="(Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
|
:title="(Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
|
||||||
slotProps.level"
|
slotProps.level"
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
{{ (Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
|
{{ (Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
|
||||||
slotProps.level }}
|
slotProps.level }}
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<template #sceneId="slotProps">
|
<template #sceneId="slotProps">
|
||||||
<span
|
<span
|
||||||
|
@ -126,7 +126,7 @@
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<template #state="slotProps">
|
<template #state="slotProps">
|
||||||
<a-badge
|
<j-badge
|
||||||
:text="
|
:text="
|
||||||
slotProps.state?.value === 'enabled'
|
slotProps.state?.value === 'enabled'
|
||||||
? '正常'
|
? '正常'
|
||||||
|
@ -140,7 +140,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<j-space :size="16">
|
||||||
<template
|
<template
|
||||||
v-for="i in getActions(slotProps, 'table')"
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
:key="i.key"
|
:key="i.key"
|
||||||
|
@ -168,7 +168,7 @@
|
||||||
/></template>
|
/></template>
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<pro-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="alarm-log-detail"
|
target="alarm-log-detail"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></Search>
|
/>
|
||||||
<JProTable
|
<JProTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
moment(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
|
moment(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
|
||||||
}}</template>
|
}}</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space
|
<j-space
|
||||||
><template
|
><template
|
||||||
v-for="i in getActions(slotProps, 'table')"
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
:key="i.key"
|
:key="i.key"
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<template #icon><AIcon :type="i.icon"/></template>
|
<template #icon><AIcon :type="i.icon"/></template>
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
<Info v-if="visiable" :data="current" @close="close"/>
|
<Info v-if="visiable" :data="current" @close="close"/>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal visible title="详情" okText="确定" cancelText="取消" :width="1000" @ok="closeModal" @cancel="closeModal">
|
<j-modal visible title="详情" okText="确定" cancelText="取消" :width="1000" @ok="closeModal" @cancel="closeModal">
|
||||||
<a-descriptions bordered :column="2">
|
<j-descriptions bordered :column="2">
|
||||||
<a-descriptions-item v-if="props.data.targetType==='device'" label="告警设备" :span="1">{{props.data?.targetName || ''}}</a-descriptions-item>
|
<j-descriptions-item v-if="props.data.targetType==='device'" label="告警设备" :span="1">{{props.data?.targetName || ''}}</j-descriptions-item>
|
||||||
<a-descriptions-item v-if="props.data.targetType==='device'" label="设备ID" :span="1">{{props.data?.targetId || ''}}</a-descriptions-item>
|
<j-descriptions-item v-if="props.data.targetType==='device'" label="设备ID" :span="1">{{props.data?.targetId || ''}}</j-descriptions-item>
|
||||||
<a-descriptions-item label="告警名称" :span="1">{{
|
<j-descriptions-item label="告警名称" :span="1">{{
|
||||||
props.data?.alarmConfigName
|
props.data?.alarmConfigName
|
||||||
}}</a-descriptions-item>
|
}}</j-descriptions-item>
|
||||||
<a-descriptions-item label="告警时间" :span="1">{{
|
<j-descriptions-item label="告警时间" :span="1">{{
|
||||||
moment(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss')
|
moment(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss')
|
||||||
}}</a-descriptions-item>
|
}}</j-descriptions-item>
|
||||||
<a-descriptions-item label="告警级别" :span="1">
|
<j-descriptions-item label="告警级别" :span="1">
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
:title="(Store.get('default-level') || []).find((item: any) => item?.level === data?.level)
|
:title="(Store.get('default-level') || []).find((item: any) => item?.level === data?.level)
|
||||||
?.title || props.data?.level"
|
?.title || props.data?.level"
|
||||||
|
@ -21,10 +21,10 @@
|
||||||
?.title || props.data?.level}}
|
?.title || props.data?.level}}
|
||||||
</span>
|
</span>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</a-descriptions-item>
|
</j-descriptions-item>
|
||||||
<a-descriptions-item label="告警说明" :span="1"
|
<j-descriptions-item label="告警说明" :span="1"
|
||||||
><a-tooltip
|
><j-tooltip
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
:title="data?.description || ''"
|
:title="data?.description || ''"
|
||||||
>
|
>
|
||||||
|
@ -33,14 +33,14 @@
|
||||||
{{ data?.description || '' }}
|
{{ data?.description || '' }}
|
||||||
</span> </Ellipsis
|
</span> </Ellipsis
|
||||||
>
|
>
|
||||||
</a-tooltip></a-descriptions-item
|
</j-tooltip></j-descriptions-item
|
||||||
>
|
>
|
||||||
<a-descriptions-item
|
<j-descriptions-item
|
||||||
label="告警流水"
|
label="告警流水"
|
||||||
:span="2"
|
:span="2"
|
||||||
><div style="max-height: 500px; overflow-y: auto;"><JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" :expand-depth="5"></JsonViewer></div></a-descriptions-item>
|
><div style="max-height: 500px; overflow-y: auto;"><JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" :expand-depth="5"></JsonViewer></div></j-descriptions-item>
|
||||||
</a-descriptions>
|
</j-descriptions>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
title="告警处理"
|
title="告警处理"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
|
@ -9,18 +9,18 @@
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
:confirmLoading="loading"
|
:confirmLoading="loading"
|
||||||
>
|
>
|
||||||
<a-form :rules="rules" layout="vertical" ref="formRef" :model="form">
|
<j-form :rules="rules" layout="vertical" ref="formRef" :model="form">
|
||||||
<a-form-item label="处理结果" name="describe">
|
<j-form-item label="处理结果" name="describe">
|
||||||
<a-textarea
|
<j-textarea
|
||||||
:rows="8"
|
:rows="8"
|
||||||
:maxlength="200"
|
:maxlength="200"
|
||||||
showCount
|
showCount
|
||||||
placeholder="请输入处理结果"
|
placeholder="请输入处理结果"
|
||||||
v-model:value="form.describe"
|
v-model:value="form.describe"
|
||||||
></a-textarea>
|
></j-textarea>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form>
|
</j-form>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -64,6 +64,7 @@ const handleSave = () => {
|
||||||
});
|
});
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
onlyMessage('操作成功!');
|
onlyMessage('操作成功!');
|
||||||
|
emit('closeSolve');
|
||||||
} else {
|
} else {
|
||||||
onlyMessage('操作失败!', 'error');
|
onlyMessage('操作失败!', 'error');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
visible
|
visible
|
||||||
title="处理记录"
|
title="处理记录"
|
||||||
:width="1200"
|
:width="1200"
|
||||||
|
@ -8,11 +8,11 @@
|
||||||
@ok="clsoeModal"
|
@ok="clsoeModal"
|
||||||
@cancel="clsoeModal"
|
@cancel="clsoeModal"
|
||||||
>
|
>
|
||||||
<Search
|
<pro-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="bind-channel"
|
target="bind-channel"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></Search>
|
/>
|
||||||
<JProTable
|
<JProTable
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="alarm-log-card">
|
<div class="alarm-log-card">
|
||||||
<Search
|
<pro-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="alarm-log"
|
target="alarm-log"
|
||||||
v-if="['all', 'detail'].includes(props.type)"
|
v-if="['all', 'detail'].includes(props.type)"
|
||||||
@search="search"
|
@search="search"
|
||||||
></Search>
|
/>
|
||||||
<Search
|
<pro-search
|
||||||
:columns="produtCol"
|
:columns="produtCol"
|
||||||
target="alarm-log"
|
target="alarm-log"
|
||||||
v-if="['product', 'other'].includes(props.type)"
|
v-if="['product', 'other'].includes(props.type)"
|
||||||
@search="search"
|
@search="search"
|
||||||
></Search>
|
/>
|
||||||
<Search
|
<pro-search
|
||||||
:columns="deviceCol"
|
:columns="deviceCol"
|
||||||
target="alarm-log"
|
target="alarm-log"
|
||||||
v-if="props.type === 'device'"
|
v-if="props.type === 'device'"
|
||||||
@search="search"
|
@search="search"
|
||||||
></Search>
|
/>
|
||||||
<Search
|
<pro-search
|
||||||
:columns="orgCol"
|
:columns="orgCol"
|
||||||
target="alarm-log"
|
target="alarm-log"
|
||||||
v-if="props.type === 'org'"
|
v-if="props.type === 'org'"
|
||||||
@search="search"
|
@search="search"
|
||||||
></Search>
|
/>
|
||||||
<JProTable
|
<JProTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="handleSearch"
|
:request="handleSearch"
|
||||||
|
@ -31,6 +31,7 @@
|
||||||
:gridColumns="[1, 1, 2]"
|
:gridColumns="[1, 1, 2]"
|
||||||
:gridColumn="2"
|
:gridColumn="2"
|
||||||
model="CARD"
|
model="CARD"
|
||||||
|
ref="tableRef"
|
||||||
>
|
>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<CardBox
|
<CardBox
|
||||||
|
@ -52,8 +53,8 @@
|
||||||
{{ slotProps.alarmName }}
|
{{ slotProps.alarmName }}
|
||||||
</span>
|
</span>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="8">
|
<j-col :span="8">
|
||||||
<div class="content-des-title">
|
<div class="content-des-title">
|
||||||
{{ titleMap.get(slotProps.targetType) }}
|
{{ titleMap.get(slotProps.targetType) }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,8 +63,8 @@
|
||||||
{{ slotProps?.targetName }}
|
{{ slotProps?.targetName }}
|
||||||
</div></Ellipsis
|
</div></Ellipsis
|
||||||
>
|
>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="8">
|
<j-col :span="8">
|
||||||
<div class="content-des-title">
|
<div class="content-des-title">
|
||||||
最近告警时间
|
最近告警时间
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,17 +77,17 @@
|
||||||
}}
|
}}
|
||||||
</div></Ellipsis
|
</div></Ellipsis
|
||||||
>
|
>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="8">
|
<j-col :span="8">
|
||||||
<div class="content-des-title">状态</div>
|
<div class="content-des-title">状态</div>
|
||||||
<a-badge
|
<j-badge
|
||||||
:status="
|
:status="
|
||||||
slotProps.state.value === 'warning'
|
slotProps.state.value === 'warning'
|
||||||
? 'error'
|
? 'error'
|
||||||
: 'default'
|
: 'default'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
</a-badge
|
</j-badge
|
||||||
><span
|
><span
|
||||||
:style="
|
:style="
|
||||||
slotProps.state.value === 'warning'
|
slotProps.state.value === 'warning'
|
||||||
|
@ -96,8 +97,8 @@
|
||||||
>
|
>
|
||||||
{{ slotProps.state.text }}
|
{{ slotProps.state.text }}
|
||||||
</span>
|
</span>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
|
@ -105,7 +106,6 @@
|
||||||
item.key === 'solve' &&
|
item.key === 'solve' &&
|
||||||
slotProps.state.value === 'normal'
|
slotProps.state.value === 'normal'
|
||||||
"
|
"
|
||||||
:popConfirm="item.popConfirm"
|
|
||||||
:tooltip="{
|
:tooltip="{
|
||||||
...item.tooltip,
|
...item.tooltip,
|
||||||
}"
|
}"
|
||||||
|
@ -152,11 +152,11 @@ import { Store } from 'jetlinks-store';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import type { ActionsType } from '@/components/Table';
|
import type { ActionsType } from '@/components/Table';
|
||||||
import SolveComponent from '../SolveComponent/index.vue';
|
import SolveComponent from '../SolveComponent/index.vue';
|
||||||
import SolveLog from '../SolveLog/index.vue'
|
import SolveLog from '../SolveLog/index.vue';
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
import { usePermissionStore } from '@/store/permission';
|
import { usePermissionStore } from '@/store/permission';
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
|
const tableRef = ref();
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { data } = storeToRefs(alarmStore);
|
const { data } = storeToRefs(alarmStore);
|
||||||
const getDefaulitLevel = () => {
|
const getDefaulitLevel = () => {
|
||||||
|
@ -339,7 +339,7 @@ watchEffect(() => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if(props.type === 'all'){
|
if (props.type === 'all') {
|
||||||
params.value.terms = [];
|
params.value.terms = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -347,24 +347,20 @@ watchEffect(() => {
|
||||||
const search = (data: any) => {
|
const search = (data: any) => {
|
||||||
params.value.terms = [...data?.terms];
|
params.value.terms = [...data?.terms];
|
||||||
if (props.type !== 'all' && !props.id) {
|
if (props.type !== 'all' && !props.id) {
|
||||||
params.value.terms.push(
|
params.value.terms.push({
|
||||||
{
|
termType: 'eq',
|
||||||
termType: 'eq',
|
column: 'targetType',
|
||||||
column: 'targetType',
|
value: props.type,
|
||||||
value: props.type,
|
type: 'and',
|
||||||
type: 'and',
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (props.id) {
|
if (props.id) {
|
||||||
params.value.terms.push (
|
params.value.terms.push({
|
||||||
{
|
termType: 'eq',
|
||||||
termType: 'eq',
|
column: 'alarmConfigId',
|
||||||
column: 'alarmConfigId',
|
value: props.id,
|
||||||
value: props.id,
|
type: 'and',
|
||||||
type: 'and',
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -381,17 +377,19 @@ const getActions = (
|
||||||
title: '告警处理',
|
title: '告警处理',
|
||||||
},
|
},
|
||||||
icon: 'ToolOutlined',
|
icon: 'ToolOutlined',
|
||||||
onClick: () =>{
|
onClick: () => {
|
||||||
data.value.current = currentData;
|
data.value.current = currentData;
|
||||||
data.value.solveVisible = true;
|
data.value.solveVisible = true;
|
||||||
},
|
},
|
||||||
popConfirm:{
|
// popConfirm: {
|
||||||
title: !usePermissionStore().hasPermission('rule-engine/Alarm/Log:action')
|
// title: !usePermissionStore().hasPermission(
|
||||||
? '暂无权限,请联系管理员'
|
// 'rule-engine/Alarm/Log:action',
|
||||||
: data.state?.value === 'normal'
|
// )
|
||||||
? '无告警'
|
// ? '暂无权限,请联系管理员'
|
||||||
: ''
|
// : data.state?.value === 'normal'
|
||||||
}
|
// ? '无告警'
|
||||||
|
// : '',
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'log',
|
key: 'log',
|
||||||
|
@ -400,9 +398,11 @@ const getActions = (
|
||||||
title: '告警日志',
|
title: '告警日志',
|
||||||
},
|
},
|
||||||
icon: 'FileOutlined',
|
icon: 'FileOutlined',
|
||||||
onClick: () =>{
|
onClick: () => {
|
||||||
menuStory.jumpPage(`rule-engine/Alarm/Log/Detail`,{id:currentData.id});
|
menuStory.jumpPage(`rule-engine/Alarm/Log/Detail`, {
|
||||||
}
|
id: currentData.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'detail',
|
key: 'detail',
|
||||||
|
@ -411,10 +411,10 @@ const getActions = (
|
||||||
title: '处理记录',
|
title: '处理记录',
|
||||||
},
|
},
|
||||||
icon: 'FileTextOutlined',
|
icon: 'FileTextOutlined',
|
||||||
onClick:() =>{
|
onClick: () => {
|
||||||
data.value.current = currentData;
|
data.value.current = currentData;
|
||||||
data.value.logVisible = true;
|
data.value.logVisible = true;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return actions;
|
return actions;
|
||||||
|
@ -422,15 +422,16 @@ const getActions = (
|
||||||
/**
|
/**
|
||||||
* 关闭告警日志
|
* 关闭告警日志
|
||||||
*/
|
*/
|
||||||
const closeSolve = () =>{
|
const closeSolve = () => {
|
||||||
data.value.solveVisible = false;
|
data.value.solveVisible = false;
|
||||||
}
|
tableRef.value.reload(params.value);
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 关闭处理记录
|
* 关闭处理记录
|
||||||
*/
|
*/
|
||||||
const closeLog = () =>{
|
const closeLog = () => {
|
||||||
data.value.logVisible = false;
|
data.value.logVisible = false;
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
width="650px"
|
width="650px"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
|
@ -12,30 +12,30 @@
|
||||||
:confirmLoading="loading"
|
:confirmLoading="loading"
|
||||||
>
|
>
|
||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<a-form
|
<j-form
|
||||||
:layout="'vertical'"
|
:layout="'vertical'"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
:model="modelRef"
|
:model="modelRef"
|
||||||
>
|
>
|
||||||
<a-form-item label="名称" name="name">
|
<j-form-item label="名称" name="name">
|
||||||
<a-input
|
<j-input
|
||||||
v-model:value="modelRef.name"
|
v-model:value="modelRef.name"
|
||||||
placeholder="请输入名称"
|
placeholder="请输入名称"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<a-form-item label="说明" name="describe">
|
<j-form-item label="说明" name="describe">
|
||||||
<a-textarea
|
<j-textarea
|
||||||
v-model:value="modelRef.description"
|
v-model:value="modelRef.description"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
showCount
|
showCount
|
||||||
:maxlength="200"
|
:maxlength="200"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div>
|
<div>
|
||||||
<Search
|
<pro-search
|
||||||
:columns="query.columns"
|
:columns="query.columns"
|
||||||
target="device-instance"
|
target="device-instance"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></Search>
|
/>
|
||||||
<JProTable
|
<JProTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="queryList"
|
:request="queryList"
|
||||||
|
@ -53,15 +53,15 @@
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</span>
|
</span>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
<a-row>
|
<j-row>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<Ellipsis>
|
<Ellipsis>
|
||||||
<div>
|
<div>
|
||||||
{{ slotProps.description }}
|
{{ slotProps.description }}
|
||||||
</div>
|
</div>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
<template #state="slotProps">
|
<template #state="slotProps">
|
||||||
<a-badge
|
<j-badge
|
||||||
:text="
|
:text="
|
||||||
slotProps.state?.value === 'started'
|
slotProps.state?.value === 'started'
|
||||||
? '正常'
|
? '正常'
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<j-space :size="16">
|
||||||
<template
|
<template
|
||||||
v-for="i in getActions(slotProps, 'table')"
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
:key="i.key"
|
:key="i.key"
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
/></template>
|
/></template>
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
<!-- 新增、编辑 -->
|
<!-- 新增、编辑 -->
|
||||||
|
|
Loading…
Reference in New Issue