Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
c36d4b95e0
|
@ -2,6 +2,7 @@ import { LocalStore } from '@/utils/comm'
|
||||||
import server from '@/utils/request'
|
import server from '@/utils/request'
|
||||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable'
|
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable'
|
||||||
import { DeviceInstance } from '@/views/device/Instance/typings'
|
import { DeviceInstance } from '@/views/device/Instance/typings'
|
||||||
|
import { UnitType } from '@/views/device/Product/typings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除设备物模型
|
* 删除设备物模型
|
||||||
|
@ -242,3 +243,74 @@ export const unbindBatchDevice = (deviceId: string, data: Record<string, any>) =
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const bindDevice = (deviceId: string, data: Record<string, any>) => server.post(`/device/gateway/${deviceId}/bind`, data)
|
export const bindDevice = (deviceId: string, data: Record<string, any>) => server.post(`/device/gateway/${deviceId}/bind`, data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备接入网关状态
|
||||||
|
* @param id 设备接入网关id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryGatewayState = (id: string) => server.get(`/gateway/device/${id}/detail`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络组件状态
|
||||||
|
* @param id 网络组件id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryNetworkState = (id: string) => server.get(`/network/config/${id}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产品状态
|
||||||
|
* @param id 产品id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryProductState = (id: string) => server.get(`/device/product/${id}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产品配置
|
||||||
|
* @param id 产品id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryProductConfig = (id: string) => server.get(`/device/product/${id}/config-metadata`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备配置
|
||||||
|
* @param id 设备id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryDeviceConfig = (id: string) => server.get(`/device-instance/${id}/config-metadata`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询协议
|
||||||
|
* @param type
|
||||||
|
* @param transport
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const queryProtocolDetail = (type: string, transport: string) => server.get(`/protocol/${type}/transport/${transport}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络组件启用
|
||||||
|
* @param id 网络组件ID
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const startNetwork = (id: string) => server.post(`/network/config/${id}/_start`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用网关
|
||||||
|
* @param id 网关id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const startGateway = (id: string) => server.post(`/gateway/device/${id}/_startup`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关详情
|
||||||
|
* @param id 网关id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getGatewayDetail = (id: string) => server.get(`/gateway/device/${id}`)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 获取单位列表
|
||||||
|
* @returns 单位列表
|
||||||
|
*/
|
||||||
|
export const getUnit = () => server.get<UnitType[]>(`/protocol/units`)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
|
||||||
|
// 获取数据源列表
|
||||||
|
export const getDataSourceList_api = (data: object) => server.post(`/datasource/config/_query/`, data);
|
||||||
|
|
||||||
|
// 获取数据库类型字典
|
||||||
|
export const getDataTypeDict_api = () => server.get(`/datasource/config/types`);
|
||||||
|
|
||||||
|
// 修改数据源状态
|
||||||
|
export const changeStatus_api = (id:string, status:'_disable'|'_enable') => server.put(`/datasource/config/${id}/${status}`);
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<a-select v-model:value="_value" mode="tags" :options="options" :size="size" @change="change"></a-select>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="InputSelect">
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
type valueType = string | number
|
||||||
|
type Emits = {
|
||||||
|
(e: 'update:value', data: valueType | undefined): void;
|
||||||
|
(e: 'change'): void;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
const props = defineProps({
|
||||||
|
value: [String, Number],
|
||||||
|
options: {
|
||||||
|
type: Array as PropType<DefaultOptionType[]> | undefined,
|
||||||
|
},
|
||||||
|
size: String as PropType<SizeType>
|
||||||
|
})
|
||||||
|
const _value = ref<valueType[]>();
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(val) => {
|
||||||
|
_value.value = val ? [val] : undefined
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const change = (value: SelectValue) => {
|
||||||
|
const _val = (value as valueType[])
|
||||||
|
if (_val.length > 1) {
|
||||||
|
emit('update:value', _val.slice(_val.length - 1)?.[0])
|
||||||
|
} else {
|
||||||
|
emit('update:value', value?.[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="less">
|
||||||
|
</style>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<a-popover :visible="visible" placement="left">
|
||||||
|
<template #title>
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div style="width: 150px;">配置元素</div>
|
||||||
|
<close-outlined @click="visible = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div style="max-width: 400px;">
|
||||||
|
<a-form layout="vertical" :model="_value">
|
||||||
|
<value-type-form v-model:value="_value" :name="[]" isSub key="sub"></value-type-form>
|
||||||
|
<a-form-item label="说明" name="description" :rules="[
|
||||||
|
{ max: 200, message: '最多可输入200个字符' },
|
||||||
|
]">
|
||||||
|
<a-textarea v-model:value="_value.description" size="small"></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-button type="dashed" block @click="visible = true">
|
||||||
|
配置元素<edit-outlined class="item-icon" />
|
||||||
|
</a-button>
|
||||||
|
</a-popover>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="ArrayParam">
|
||||||
|
import ValueTypeForm from '@/views/device/components/Metadata/Base/Edit/ValueTypeForm.vue';
|
||||||
|
import { EditOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
type ValueType = Record<any, any>;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<ValueType>,
|
||||||
|
default: () => ({ extends: {} })
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:value', data: ValueType): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const _value = computed({
|
||||||
|
get: () => props.value,
|
||||||
|
set: val => {
|
||||||
|
emit('update:value', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
emit('update:value', { extends: {}, ...props.value })
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.item-icon {
|
||||||
|
color: rgb(136, 136, 136);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,81 @@
|
||||||
|
<template>
|
||||||
|
<div class="boolean-param">
|
||||||
|
<a-row :gutter="4">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label=" " :name="name.concat(['trueText'])" :rules="[
|
||||||
|
{ required: true, message: '请输入trueText' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="value.trueText" placeholder="trueText" size="small" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="-" :name="name.concat(['trueValue'])" :rules="[
|
||||||
|
{ required: true, message: '请输入trueValue' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="value.trueValue" placeholder="trueValue" size="small"/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label=" " :name="name.concat(['falseText'])" :rules="[
|
||||||
|
{ required: true, message: '请输入falseText' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="value.falseText" placeholder="falseText" size="small" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="-" :name="name.concat(['falseValue'])" :rules="[
|
||||||
|
{ required: true, message: '请输入falseValue' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="value.falseValue" placeholder="falseValue" size="small" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="BooleanParam">
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
type ModelValueType = Record<string, string>
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:value', data: ModelValueType): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<ModelValueType>,
|
||||||
|
default: () => ({
|
||||||
|
})
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
emit('update:value',
|
||||||
|
{
|
||||||
|
trueText: '是',
|
||||||
|
trueValue: 'true',
|
||||||
|
falseText: '否',
|
||||||
|
falseValue: 'false',
|
||||||
|
...props.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.boolean-param {
|
||||||
|
:deep(.ant-form-item) {
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.ant-form-item-label {
|
||||||
|
>label {
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,160 @@
|
||||||
|
<template>
|
||||||
|
<div class="enum-param">
|
||||||
|
<div class="list-item" v-for="(item, index) in _value" :key="index">
|
||||||
|
<div class="item-left">
|
||||||
|
<menu-outlined class="item-drag item-icon" />
|
||||||
|
</div>
|
||||||
|
<div class="item-middle item-editable">
|
||||||
|
<a-popover :visible="editIndex === index" placement="top">
|
||||||
|
<template #title>
|
||||||
|
<div class="edit-title" style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div style="width: 150px;">枚举项配置</div>
|
||||||
|
<close-outlined @click="handleClose" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<a-form :model="_value[index]" layout="vertical">
|
||||||
|
<a-form-item label="Value" name="value" :rules="[
|
||||||
|
{ required: true, message: '请输入Value' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="_value[index].value" size="small"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Text" name="text" :rules="[
|
||||||
|
{ required: true, message: '请输入Text' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="_value[index].text" size="small"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<div class="item-edit" @click="handleEdit(index)">
|
||||||
|
{{ item.text || '枚举项配置' }}
|
||||||
|
<edit-outlined class="item-icon" />
|
||||||
|
</div>
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
<div class="item-right">
|
||||||
|
<delete-outlined @click="handleDelete(index)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-button type="dashed" block @click="handleAdd">
|
||||||
|
<template #icon><plus-outlined class="item-icon" /></template>
|
||||||
|
新增枚举型
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="BooleanParam">
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import { MenuOutlined, EditOutlined, DeleteOutlined, PlusOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
type EnumType = {
|
||||||
|
text?: string,
|
||||||
|
value?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:value', data: EnumType[]): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<EnumType[]>,
|
||||||
|
default: () => ([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const _value = ref<EnumType[]>([])
|
||||||
|
watchEffect(() => {
|
||||||
|
_value.value = props.value
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(_value,
|
||||||
|
() => {
|
||||||
|
emit('update:value', _value.value)
|
||||||
|
},
|
||||||
|
{ deep: true })
|
||||||
|
|
||||||
|
const editIndex = ref<number>(-1)
|
||||||
|
const handleEdit = (index: number) => {
|
||||||
|
editIndex.value = index
|
||||||
|
}
|
||||||
|
const handleDelete = (index: number) => {
|
||||||
|
editIndex.value = -1
|
||||||
|
_value.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
const handleClose = () => {
|
||||||
|
editIndex.value = -1
|
||||||
|
}
|
||||||
|
const handleAdd = () => {
|
||||||
|
_value.value.push({})
|
||||||
|
emit('update:value', _value.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.enum-param {
|
||||||
|
.list-item {
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
padding: 3px 6px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
line-height: 26px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
// .item-left {
|
||||||
|
// .item-drag {
|
||||||
|
// cursor: move;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
.item-edit {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-icon {
|
||||||
|
color: rgb(136, 136, 136);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-label) {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
>label {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.ant-form-item-required:not(.ant-form-item-required-mark-optional)::before {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-explain) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-with-help) {
|
||||||
|
.ant-form-item-explain {
|
||||||
|
min-height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&.ant-form-item-with-help {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-select) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,172 @@
|
||||||
|
<template>
|
||||||
|
<div class="json-param">
|
||||||
|
<div class="list-item" v-for="(item, index) in _value" :key="`object_${index}`">
|
||||||
|
<div class="item-left">
|
||||||
|
<menu-outlined class="item-drag item-icon" />
|
||||||
|
</div>
|
||||||
|
<div class="item-middle item-editable">
|
||||||
|
<a-popover :visible="editIndex === index" placement="left">
|
||||||
|
<template #title>
|
||||||
|
<div class="edit-title" style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div style="width: 150px;">配置参数</div>
|
||||||
|
<close-outlined @click="handleClose" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div style="max-width: 400px;">
|
||||||
|
<a-form :model="_value[index]" layout="vertical">
|
||||||
|
<a-form-item label="标识" name="id" :rules="[
|
||||||
|
{ required: true, message: '请输入标识' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_]+$/,
|
||||||
|
message: '请输入英文或者数字或者-或者_',
|
||||||
|
},
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="名称" name="name" :rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<value-type-form v-model:value="_value[index].valueType" :name="['valueType']" isSub
|
||||||
|
key="json_sub"></value-type-form>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="item-edit" @click="handleEdit(index)">
|
||||||
|
{{ item.name || '配置参数' }}
|
||||||
|
<edit-outlined class="item-icon" />
|
||||||
|
</div>
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
<div class="item-right">
|
||||||
|
<delete-outlined @click="handleDelete(index)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-button type="dashed" block @click="handleAdd">
|
||||||
|
<template #icon><plus-outlined class="item-icon" /></template>
|
||||||
|
添加参数
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="JsonParam">
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import { MenuOutlined, EditOutlined, DeleteOutlined, PlusOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||||
|
import ValueTypeForm from '@/views/device/components/Metadata/Base/Edit/ValueTypeForm.vue';
|
||||||
|
|
||||||
|
type JsonType = Record<any, any>;
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:value', data: JsonType[]): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<JsonType[]>,
|
||||||
|
default: () => ([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const _value = ref<JsonType[]>([])
|
||||||
|
watchEffect(() => {
|
||||||
|
_value.value = props.value
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(_value,
|
||||||
|
() => {
|
||||||
|
emit('update:value', _value.value)
|
||||||
|
},
|
||||||
|
{ deep: true })
|
||||||
|
|
||||||
|
const editIndex = ref<number>(-1)
|
||||||
|
const handleEdit = (index: number) => {
|
||||||
|
editIndex.value = index
|
||||||
|
}
|
||||||
|
const handleDelete = (index: number) => {
|
||||||
|
editIndex.value = -1
|
||||||
|
_value.value.slice(index, 1)
|
||||||
|
}
|
||||||
|
const handleClose = () => {
|
||||||
|
editIndex.value = -1
|
||||||
|
}
|
||||||
|
const handleAdd = () => {
|
||||||
|
_value.value.push({
|
||||||
|
valueType: {
|
||||||
|
expands: {}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
emit('update:value', _value.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.json-param {
|
||||||
|
.list-item {
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
padding: 3px 6px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
line-height: 26px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
// .item-left {
|
||||||
|
// .item-drag {
|
||||||
|
// cursor: move;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
.item-edit {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-icon {
|
||||||
|
color: rgb(136, 136, 136);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-label) {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
>label {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.ant-form-item-required:not(.ant-form-item-required-mark-optional)::before {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-explain) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-with-help) {
|
||||||
|
.ant-form-item-explain {
|
||||||
|
min-height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&.ant-form-item-with-help {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-select) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,10 +1,11 @@
|
||||||
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
|
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
|
||||||
import styles from './index.module.less'
|
import styles from './index.module.less'
|
||||||
import { Pagination, Table, Empty, Spin, Alert } from 'ant-design-vue'
|
import { Pagination, Table, Empty, Spin, Alert } from 'ant-design-vue'
|
||||||
import type { TableProps, ColumnProps } from 'ant-design-vue/es/table'
|
import type { TableProps } from 'ant-design-vue/es/table'
|
||||||
import type { TooltipProps } from 'ant-design-vue/es/tooltip'
|
import type { TooltipProps } from 'ant-design-vue/es/tooltip'
|
||||||
import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm'
|
import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm'
|
||||||
import { CSSProperties, PropType } from 'vue';
|
import { CSSProperties, PropType } from 'vue';
|
||||||
|
import type { JColumnsProps } from './types'
|
||||||
|
|
||||||
enum ModelEnum {
|
enum ModelEnum {
|
||||||
TABLE = 'TABLE',
|
TABLE = 'TABLE',
|
||||||
|
@ -40,14 +41,10 @@ export interface ActionsType {
|
||||||
children?: ActionsType[];
|
children?: ActionsType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JColumnProps extends ColumnProps {
|
|
||||||
scopedSlots?: boolean; // 是否为插槽 true: 是 false: 否
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JTableProps extends TableProps {
|
export interface JTableProps extends TableProps {
|
||||||
request?: (params?: Record<string, any>) => Promise<Partial<RequestData>>;
|
request?: (params?: Record<string, any>) => Promise<Partial<RequestData>>;
|
||||||
cardBodyClass?: string;
|
cardBodyClass?: string;
|
||||||
columns: JColumnProps[];
|
columns: JColumnsProps[];
|
||||||
params?: Record<string, any>;
|
params?: Record<string, any>;
|
||||||
model?: keyof typeof ModelEnum | undefined; // 显示table还是card
|
model?: keyof typeof ModelEnum | undefined; // 显示table还是card
|
||||||
// actions?: ActionsType[];
|
// actions?: ActionsType[];
|
||||||
|
@ -156,9 +153,10 @@ const JTable = defineComponent<JTableProps>({
|
||||||
const pageIndex = ref<number>(0)
|
const pageIndex = ref<number>(0)
|
||||||
const pageSize = ref<number>(6)
|
const pageSize = ref<number>(6)
|
||||||
const total = ref<number>(0)
|
const total = ref<number>(0)
|
||||||
const _columns = ref<JColumnProps[]>(props?.columns || [])
|
|
||||||
const loading = ref<boolean>(true)
|
const loading = ref<boolean>(true)
|
||||||
|
|
||||||
|
const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable)))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听宽度,计算显示卡片个数
|
* 监听宽度,计算显示卡片个数
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,5 +3,6 @@ import { ColumnType } from 'ant-design-vue/es/table'
|
||||||
|
|
||||||
export interface JColumnsProps extends ColumnType{
|
export interface JColumnsProps extends ColumnType{
|
||||||
scopedSlots?: boolean;
|
scopedSlots?: boolean;
|
||||||
search: SearchProps
|
search: SearchProps;
|
||||||
|
hideInTable?: boolean;
|
||||||
}
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
<template>
|
||||||
|
<div class="dialog-item" :key="data.key" :class="{'dialog-active' : !data?.upstream}">
|
||||||
|
<div class="dialog-card">
|
||||||
|
<div class="dialog-list" v-for="item in data.list" :key="item.key">
|
||||||
|
<div class="dialog-icon">
|
||||||
|
<AIcon :type="visible.includes(item.key) ? 'DownOutlined' : 'RightOutlined'" />
|
||||||
|
</div>
|
||||||
|
<div class="dialog-box">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<div class="dialog-title">
|
||||||
|
<a-badge :color="statusColor.get(item.error ? 'error' : 'success')" style="margin-right: 5px" />
|
||||||
|
{{operationMap.get(item.operation) || item?.operation}}
|
||||||
|
</div>
|
||||||
|
<div class="dialog-item">{{moment(item.endTime).format('YYYY-MM-DD HH:mm:ss')}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-editor" v-if="visible.includes(item.key)">
|
||||||
|
<a-textarea :bordered="false" :value="item?.detail" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const operationMap = new Map();
|
||||||
|
import moment from 'moment'
|
||||||
|
operationMap.set('connection', '连接');
|
||||||
|
operationMap.set('auth', '权限验证');
|
||||||
|
operationMap.set('decode', '解码');
|
||||||
|
operationMap.set('encode', '编码');
|
||||||
|
operationMap.set('request', '请求');
|
||||||
|
operationMap.set('response', '响应');
|
||||||
|
operationMap.set('downstream', '下行消息');
|
||||||
|
operationMap.set('upstream', '上行消息');
|
||||||
|
|
||||||
|
const statusColor = new Map();
|
||||||
|
statusColor.set('error', '#E50012');
|
||||||
|
statusColor.set('success', '#24B276');
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const visible = ref<string[]>([])
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import 'ant-design-vue/es/style/themes/default.less';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--dialog-primary-color: @primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
|
||||||
|
.dialog-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 60%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.dialog-list {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.dialog-icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
.dialog-title {
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-time {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-editor {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar {
|
||||||
|
width: 5px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-active {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.dialog-card {
|
||||||
|
background-color: @primary-color;
|
||||||
|
|
||||||
|
.dialog-list {
|
||||||
|
.dialog-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-box {
|
||||||
|
.dialog-header {
|
||||||
|
.dialog-title,
|
||||||
|
.dialog-time {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-editor {
|
||||||
|
textarea {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: @primary-color !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<a-table
|
||||||
|
rowKey="id"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
bordered
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, text, record }">
|
||||||
|
<div style="width: 280px">
|
||||||
|
<template v-if="['valueType', 'name'].includes(column.dataIndex)">
|
||||||
|
<span>{{ text }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ValueItem
|
||||||
|
v-model:modelValue="record.value"
|
||||||
|
:itemType="record.type"
|
||||||
|
:options="
|
||||||
|
record.type === 'enum'
|
||||||
|
? (record?.dataType?.elements || []).map(
|
||||||
|
(item) => {
|
||||||
|
return {
|
||||||
|
label: item.text,
|
||||||
|
value: item.value,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: record.type === 'boolean'
|
||||||
|
? [
|
||||||
|
{ label: '是', value: true },
|
||||||
|
{ label: '否', value: false },
|
||||||
|
]
|
||||||
|
: undefined
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { PropType } from "vue-demi";
|
||||||
|
|
||||||
|
|
||||||
|
type Emits = {
|
||||||
|
(e: 'update:modelValue', data: Record<string, any>[]): void;
|
||||||
|
};
|
||||||
|
const _emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const _props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Array as PropType<Record<string, any>[]>,
|
||||||
|
default: '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '参数名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
with: '33%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'valueType',
|
||||||
|
with: '33%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '值',
|
||||||
|
dataIndex: 'value',
|
||||||
|
with: '34%',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// const dataSource = ref<Record<any, any>[]>(_props.modelValue || []);
|
||||||
|
|
||||||
|
const dataSource = computed({
|
||||||
|
get: () => {
|
||||||
|
return _props.modelValue || {
|
||||||
|
messageType: undefined,
|
||||||
|
message: {
|
||||||
|
properties: undefined,
|
||||||
|
functionId: undefined,
|
||||||
|
inputs: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set: (val: any) => {
|
||||||
|
_emit('update:modelValue', val);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,127 @@
|
||||||
|
<template>
|
||||||
|
<div class="function">
|
||||||
|
<a-form
|
||||||
|
:layout="'vertical'"
|
||||||
|
ref="formRef"
|
||||||
|
:model="modelRef"
|
||||||
|
>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item name="messageType" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择',
|
||||||
|
}">
|
||||||
|
<a-select placeholder="请选择" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
||||||
|
<a-select-option value="READ_PROPERTY">读取属性</a-select-option>
|
||||||
|
<a-select-option value="WRITE_PROPERTY">修改属性</a-select-option>
|
||||||
|
<a-select-option value="INVOKE_FUNCTION">调用功能</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6" v-if="['READ_PROPERTY','WRITE_PROPERTY'].includes(modelRef.messageType)">
|
||||||
|
<a-form-item :name="['message', 'properties']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择属性',
|
||||||
|
}">
|
||||||
|
<a-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
||||||
|
<a-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6" v-if="modelRef.messageType === 'WRITE_PROPERTY'">
|
||||||
|
<a-form-item :name="['message', 'value']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入值',
|
||||||
|
}">
|
||||||
|
<a-input />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||||
|
<a-form-item :name="['message', 'functionId']" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择功能',
|
||||||
|
}">
|
||||||
|
<a-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
||||||
|
<a-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-button type="primary" @click="saveBtn">发送</a-button>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION' && modelRef.message.functionId">
|
||||||
|
<a-form-item :name="['message', 'inputs']" label="参数列表" :rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入参数列表',
|
||||||
|
}">
|
||||||
|
<EditTable v-model="modelRef.message.inputs"/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useInstanceStore } from '@/store/instance';
|
||||||
|
import EditTable from './EditTable.vue'
|
||||||
|
|
||||||
|
const instanceStore = useInstanceStore()
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Emits = {
|
||||||
|
(e: 'update:modelValue', data: any): void;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const modelRef = reactive({
|
||||||
|
messageType: undefined,
|
||||||
|
message: {
|
||||||
|
properties: undefined,
|
||||||
|
functionId: undefined,
|
||||||
|
inputs: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const metadata = computed(() => {
|
||||||
|
return JSON.parse(instanceStore.current?.metadata || '{}')
|
||||||
|
})
|
||||||
|
|
||||||
|
const funcChange = (val: string) => {
|
||||||
|
if(val){
|
||||||
|
const arr = metadata.value?.functions.find((item: any) => item.id === val)?.inputs || []
|
||||||
|
const list = arr.map((item: any) => {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
value: undefined,
|
||||||
|
valueType: item?.valueType?.type,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
modelRef.message.inputs = list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveBtn = () => {
|
||||||
|
formRef.value.validate()
|
||||||
|
.then(() => {
|
||||||
|
console.log(toRaw(modelRef))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ saveBtn })
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.function {
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #e7eaec;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
log
|
||||||
|
</template>
|
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-row :gutter="24" style="margin-bottom: 20px;">
|
||||||
|
<a-col :span="12" v-for="item in messageArr" :key="item">
|
||||||
|
<div :style="messageStyleMap.get(item.status)" class="message-status">
|
||||||
|
<a-badge :status="messageStatusMap.get(item.status)" style="margin-right: 5px;" />
|
||||||
|
<span>{{item.text}}</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<div>
|
||||||
|
<TitleComponent data="调试" />
|
||||||
|
<div class="content">
|
||||||
|
<div class="dialog" id="dialog">
|
||||||
|
<template v-for="item in dialogList" :key="item.key">
|
||||||
|
<Dialog :data="item" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div><Function /></div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<div class="right-log">
|
||||||
|
<TitleComponent data="日志" />
|
||||||
|
<div :style="{ marginTop: 10 }">
|
||||||
|
<template v-if="logList.length">
|
||||||
|
<Log v-for="item in logList" :data="item" :key="item.key" />
|
||||||
|
</template>
|
||||||
|
<a-empty v-else />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MessageType } from './util'
|
||||||
|
import { messageStatusMap, messageStyleMap } from './util'
|
||||||
|
import Dialog from './Dialog/index.vue'
|
||||||
|
import Function from './Function/index.vue'
|
||||||
|
import Log from './Log/index.vue'
|
||||||
|
|
||||||
|
const message = reactive<MessageType>({
|
||||||
|
up: {
|
||||||
|
text: '上行消息诊断中',
|
||||||
|
status: 'loading',
|
||||||
|
},
|
||||||
|
down: {
|
||||||
|
text: '下行消息诊断中',
|
||||||
|
status: 'loading',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const dialogList = ref<Record<string, any>>([])
|
||||||
|
const logList = ref<Record<string, any>>([])
|
||||||
|
|
||||||
|
const messageArr = computed(() => {
|
||||||
|
const arr = Object.keys(message) || []
|
||||||
|
return arr.map(i => { return {...message[i], key: i}})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.message-status {
|
||||||
|
padding: 8px 24px;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
max-height: 500px;
|
||||||
|
padding: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #f2f5f7;
|
||||||
|
}
|
||||||
|
.right-log {
|
||||||
|
padding-left: 20px;
|
||||||
|
border-left: 1px solid rgba(0, 0, 0, .09);
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,29 @@
|
||||||
|
export type MessageType = {
|
||||||
|
up: {
|
||||||
|
text: string;
|
||||||
|
status: 'loading' | 'success' | 'error';
|
||||||
|
};
|
||||||
|
down: {
|
||||||
|
text: string;
|
||||||
|
status: 'loading' | 'success' | 'error';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messageStyleMap = new Map();
|
||||||
|
messageStyleMap.set('loading', {
|
||||||
|
background: 'linear-gradient(0deg, rgba(30, 165, 241, 0.03), rgba(30, 165, 241, 0.03)), #FFFFFF',
|
||||||
|
boxShadow: '-2px 0px 0px #1EA5F1',
|
||||||
|
});
|
||||||
|
messageStyleMap.set('error', {
|
||||||
|
background: 'linear-gradient(0deg, rgba(255, 77, 79, 0.03), rgba(255, 77, 79, 0.03)), #FFFFFF',
|
||||||
|
boxShadow: '-2px 0px 0px #FF4D4F',
|
||||||
|
});
|
||||||
|
messageStyleMap.set('success', {
|
||||||
|
background: 'linear-gradient(0deg, rgba(50, 212, 164, 0.03), rgba(50, 212, 164, 0.03)), #FFFFFF',
|
||||||
|
boxShadow: '-2px 0px 0px #32D4A4',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const messageStatusMap = new Map();
|
||||||
|
messageStatusMap.set('loading', 'processing');
|
||||||
|
messageStatusMap.set('error', 'error');
|
||||||
|
messageStatusMap.set('success', 'success');
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { Badge, Descriptions, Modal, Tooltip } from "ant-design-vue"
|
||||||
|
import TitleComponent from '@/components/TitleComponent/index.vue'
|
||||||
|
import styles from './index.module.less'
|
||||||
|
import AIcon from "@/components/AIcon";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
const DiagnosticAdvice = defineComponent({
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['close'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { data } = props
|
||||||
|
return () => <Modal visible title="设备诊断" width={1000} onOk={() => {
|
||||||
|
emit('close')
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
emit('close')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<TitleComponent data="诊断建议" />
|
||||||
|
<div class={styles.advice}>
|
||||||
|
<div class={styles.alert}>
|
||||||
|
<span style={{ marginRight: 10 }}><AIcon type="InfoCircleOutlined" /></span>
|
||||||
|
所有诊断均无异常但设备仍未上线,请检查以下内容
|
||||||
|
</div>
|
||||||
|
<div style={{ marginLeft: 10 }}>
|
||||||
|
{
|
||||||
|
(data?.list || []).map((item: any, index: number) => (
|
||||||
|
<div class={styles.infoItem} key={index} style={{ margin: '10px 0' }}>
|
||||||
|
{item}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 15 }}>
|
||||||
|
<TitleComponent data="连接信息" />
|
||||||
|
<Descriptions column={2}>
|
||||||
|
<Descriptions.Item span={1} label="设备ID">
|
||||||
|
{data?.info?.id || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
{data?.info?.address?.length > 0 && (
|
||||||
|
<Descriptions.Item span={1} label="连接地址">
|
||||||
|
<Tooltip
|
||||||
|
placement="topLeft"
|
||||||
|
title={
|
||||||
|
<div class="serverItem">
|
||||||
|
{(data?.info?.address || []).map((i: any) => (
|
||||||
|
<div key={i.address}>
|
||||||
|
<Badge color={i.health === -1 ? 'red' : 'green'} />
|
||||||
|
{i.address}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class="serverItem">
|
||||||
|
{(data?.info?.address || []).slice(0, 1).map((i: any) => (
|
||||||
|
<div key={i.address}>
|
||||||
|
<Badge color={i.health === -1 ? 'red' : 'green'} />
|
||||||
|
{i.address}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</Descriptions.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(_.flatten(_.map(data?.info?.config, 'properties')) || []).map((item: any, index: number) => (
|
||||||
|
<Descriptions.Item
|
||||||
|
key={index}
|
||||||
|
span={1}
|
||||||
|
label={
|
||||||
|
item?.description ? (
|
||||||
|
<div>
|
||||||
|
<span style={{ marginRight: '10px' }}>{item.name}</span>
|
||||||
|
<Tooltip title={item.description}>
|
||||||
|
<AIcon type="QuestionCircleOutlined" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
item.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{data?.info?.configValue[item?.property] || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
))}
|
||||||
|
</Descriptions>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default DiagnosticAdvice
|
|
@ -0,0 +1,217 @@
|
||||||
|
import AIcon from "@/components/AIcon";
|
||||||
|
import { Button, Descriptions, Modal } from "ant-design-vue"
|
||||||
|
import styles from './index.module.less'
|
||||||
|
|
||||||
|
const ManualInspection = defineComponent({
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => { }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['close', 'save'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
|
||||||
|
const { data } = props
|
||||||
|
|
||||||
|
const dataRender = () => {
|
||||||
|
if (data.type === 'device' || data.type === 'product') {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div class={styles.alert}>
|
||||||
|
<span style={{ marginRight: 10 }}><AIcon type="InfoCircleOutlined" /></span>
|
||||||
|
请检查配置项是否填写正确,若您确定该项无需诊断可
|
||||||
|
<Button type="link" style="padding: 0"
|
||||||
|
onClick={() => {
|
||||||
|
emit('save', data)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
忽略
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 10 }}>
|
||||||
|
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||||
|
{(data?.data?.properties || []).map((item: any) => (
|
||||||
|
<Descriptions.Item
|
||||||
|
key={item.property}
|
||||||
|
label={`${item.name}${item?.description ? `(${item.description})` : ''}`}
|
||||||
|
>
|
||||||
|
{data?.configuration[item.property] || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
))}
|
||||||
|
</Descriptions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{data?.data?.description ? (
|
||||||
|
<div
|
||||||
|
style={{ width: '50%', border: '1px solid #f0f0f0', padding: 10, borderLeft: 'none' }}
|
||||||
|
>
|
||||||
|
<h4>诊断项说明</h4>
|
||||||
|
<p>{data?.data?.description}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (data.type === 'cloud') {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div class={styles.alert}>
|
||||||
|
<span style={{ marginRight: 10 }}><AIcon type="InfoCircleOutlined" /></span>
|
||||||
|
请检查配置项是否填写正确,若您确定该项无需诊断可
|
||||||
|
<Button type="link" style="padding: 0"
|
||||||
|
onClick={() => {
|
||||||
|
emit('save', data)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
忽略
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 10 }}>
|
||||||
|
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||||
|
{data.configuration?.provider === 'OneNet' ? (
|
||||||
|
<>
|
||||||
|
<Descriptions.Item label={'接口地址'}>
|
||||||
|
{data?.configuration?.configuration?.apiAddress || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'apiKey'}>
|
||||||
|
{data?.configuration?.configuration?.apiKey || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'通知Token'}>
|
||||||
|
{data?.configuration?.configuration?.validateToken || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'aesKey'}>
|
||||||
|
{data?.configuration?.configuration?.aesKey || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Descriptions.Item label={'接口地址'}>
|
||||||
|
{data?.configuration?.configuration?.apiAddress || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'appKey'}>
|
||||||
|
{data?.configuration?.configuration?.appKey || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'appSecret'}>
|
||||||
|
{data?.configuration?.configuration?.appSecret || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Descriptions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{data?.configuration?.configuration?.description ? (
|
||||||
|
<div
|
||||||
|
style={{ width: '50%', border: '1px solid #f0f0f0', padding: 10, borderLeft: 'none' }}
|
||||||
|
>
|
||||||
|
<h4>诊断项说明</h4>
|
||||||
|
<p>{data?.configuration?.configuration?.description}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (data.type === 'media') {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div class={styles.alert}>
|
||||||
|
<span style={{ marginRight: 10 }}><AIcon type="InfoCircleOutlined" /></span>
|
||||||
|
请检查配置项是否填写正确,若您确定该项无需诊断可
|
||||||
|
<Button type="link" style="padding: 0"
|
||||||
|
onClick={() => {
|
||||||
|
emit('save', data)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
忽略
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 10 }}>
|
||||||
|
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||||
|
{data?.configuration?.configuration?.shareCluster ? (
|
||||||
|
<>
|
||||||
|
<Descriptions.Item label={'SIP 域'}>
|
||||||
|
{data?.configuration?.configuration?.domain || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'SIP ID'}>
|
||||||
|
{data?.configuration?.configuration?.sipId || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'集群'}>
|
||||||
|
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'SIP 地址'}>
|
||||||
|
{`${data?.configuration?.configuration?.hostPort?.host}:${data?.configuration?.configuration?.hostPort?.port}`}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'公网 Host'}>
|
||||||
|
{`${data?.configuration?.configuration?.hostPort?.publicHost}:${data?.configuration?.configuration?.hostPort?.publicPort}`}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Descriptions.Item label={'SIP 域'}>
|
||||||
|
{data?.configuration?.configuration?.domain || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'SIP ID'}>
|
||||||
|
{data?.configuration?.configuration?.sipId || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'集群'}>
|
||||||
|
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
{data?.configuration?.configuration?.cluster.map((i: any, it: number) => (
|
||||||
|
<div key={it}>
|
||||||
|
<div>节点{it + 1}</div>
|
||||||
|
<Descriptions.Item label={'节点名称'}>
|
||||||
|
{i?.clusterNodeId || ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'SIP 地址'}>
|
||||||
|
{`${i.host}:${i?.port}`}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label={'公网 Host'}>
|
||||||
|
{`${i?.publicHost}:${i?.publicPort}`}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Descriptions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{data?.configuration?.configuration.description ? (
|
||||||
|
<div
|
||||||
|
style={{ width: '50%', border: '1px solid #f0f0f0', padding: 10, borderLeft: 'none' }}
|
||||||
|
>
|
||||||
|
<h4>诊断项说明</h4>
|
||||||
|
<p>{data?.configuration?.description}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => <Modal
|
||||||
|
title="人工检查"
|
||||||
|
visible
|
||||||
|
width={1000}
|
||||||
|
cancelText="去修改"
|
||||||
|
okText="确认无误"
|
||||||
|
onOk={() => {
|
||||||
|
emit('save', data)
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
// TODO 跳转设备和产品
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex' }}>{dataRender()}</div>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ManualInspection
|
|
@ -0,0 +1,90 @@
|
||||||
|
.statusBox {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.statusHeader {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusContent {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 0;
|
||||||
|
border: 1px solid #ececec;
|
||||||
|
border-bottom: none;
|
||||||
|
|
||||||
|
.statusItem {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid #ececec;
|
||||||
|
|
||||||
|
.statusLeft {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.statusImg {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin: 15px 20px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusContext {
|
||||||
|
.statusTitle {
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusDesc {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: #646464;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
.infoItem {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusRight {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
animation: loading 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.55);
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,262 @@
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { VNode } from 'vue';
|
||||||
|
|
||||||
|
export type ListProps = {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
desc?: string;
|
||||||
|
status: 'loading' | 'error' | 'success' | 'warning';
|
||||||
|
text?: string;
|
||||||
|
info?: VNode | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TextColorMap = new Map();
|
||||||
|
TextColorMap.set('loading', 'black');
|
||||||
|
TextColorMap.set('error', 'red');
|
||||||
|
TextColorMap.set('success', 'green');
|
||||||
|
TextColorMap.set('warning', '#FAB247');
|
||||||
|
|
||||||
|
export const StatusMap = new Map();
|
||||||
|
StatusMap.set('error', getImage('/diagnose/status/error.png'));
|
||||||
|
StatusMap.set('success', getImage('/diagnose/status/success.png'));
|
||||||
|
StatusMap.set('warning', getImage('/diagnose/status/warning.png'));
|
||||||
|
StatusMap.set('loading', getImage('/diagnose/status/loading.png'));
|
||||||
|
|
||||||
|
export const networkInitList: ListProps[] = [
|
||||||
|
// {
|
||||||
|
// key: 'access',
|
||||||
|
// name: '设备接入配置',
|
||||||
|
// desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
|
||||||
|
// status: 'loading',
|
||||||
|
// text: '正在诊断中...',
|
||||||
|
// info: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'network',
|
||||||
|
name: '网络组件',
|
||||||
|
desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'gateway',
|
||||||
|
name: '设备接入网关',
|
||||||
|
desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'product',
|
||||||
|
name: '产品状态',
|
||||||
|
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'device',
|
||||||
|
name: '设备状态',
|
||||||
|
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const childInitList: ListProps[] = [
|
||||||
|
// {
|
||||||
|
// key: 'access',
|
||||||
|
// name: '设备接入配置',
|
||||||
|
// desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
|
||||||
|
// status: 'loading',
|
||||||
|
// text: '正在诊断中...',
|
||||||
|
// info: null,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// key: 'network',
|
||||||
|
// name: '网络组件',
|
||||||
|
// desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败',
|
||||||
|
// status: 'loading',
|
||||||
|
// text: '正在诊断中...',
|
||||||
|
// info: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'gateway',
|
||||||
|
name: '设备接入网关',
|
||||||
|
desc: '诊断设备接入网关状态是否正常,网关配置是否正确',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'parent-device',
|
||||||
|
name: '网关父设备',
|
||||||
|
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'product',
|
||||||
|
name: '产品状态',
|
||||||
|
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'device',
|
||||||
|
name: '设备状态',
|
||||||
|
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const cloudInitList: ListProps[] = [
|
||||||
|
// {
|
||||||
|
// key: 'access',
|
||||||
|
// name: '设备接入配置',
|
||||||
|
// desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
|
||||||
|
// status: 'loading',
|
||||||
|
// text: '正在诊断中...',
|
||||||
|
// info: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'gateway',
|
||||||
|
name: '设备接入网关',
|
||||||
|
desc: '诊断设备接入网关状态是否正常,网关配置是否正确',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'product',
|
||||||
|
name: '产品状态',
|
||||||
|
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'device',
|
||||||
|
name: '设备状态',
|
||||||
|
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const channelInitList: ListProps[] = [
|
||||||
|
// {
|
||||||
|
// key: 'access',
|
||||||
|
// name: '设备接入配置',
|
||||||
|
// desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
|
||||||
|
// status: 'loading',
|
||||||
|
// text: '正在诊断中...',
|
||||||
|
// info: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'gateway',
|
||||||
|
name: '设备接入网关',
|
||||||
|
desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'product',
|
||||||
|
name: '产品状态',
|
||||||
|
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'device',
|
||||||
|
name: '设备状态',
|
||||||
|
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const mediaInitList: ListProps[] = [
|
||||||
|
// {
|
||||||
|
// key: 'access',
|
||||||
|
// name: '设备接入配置',
|
||||||
|
// desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
|
||||||
|
// status: 'loading',
|
||||||
|
// text: '正在诊断中...',
|
||||||
|
// info: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'gateway',
|
||||||
|
name: '设备接入网关',
|
||||||
|
desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'product',
|
||||||
|
name: '产品状态',
|
||||||
|
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'device',
|
||||||
|
name: '设备状态',
|
||||||
|
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
||||||
|
status: 'loading',
|
||||||
|
text: '正在诊断中...',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const modifyArrayList = (oldList: ListProps[], item: ListProps, index?: number) => {
|
||||||
|
let newList: ListProps[] = [];
|
||||||
|
if (index !== 0 && !index) {
|
||||||
|
// 添加
|
||||||
|
for (let i = 0; i < oldList.length; i++) {
|
||||||
|
const dt = oldList[i];
|
||||||
|
if (item.key === dt.key) {
|
||||||
|
newList.push(item);
|
||||||
|
} else {
|
||||||
|
newList.push(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 修改
|
||||||
|
oldList.splice(index, 0, item);
|
||||||
|
newList = [...oldList];
|
||||||
|
}
|
||||||
|
return newList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isExit = (arr1: any[], arr2: any[]) => {
|
||||||
|
return arr1.find((item) => arr2.includes(item));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const gatewayList = [
|
||||||
|
'websocket-server',
|
||||||
|
'http-server-gateway',
|
||||||
|
'udp-device-gateway',
|
||||||
|
'coap-server-gateway',
|
||||||
|
'mqtt-client-gateway',
|
||||||
|
'tcp-server-gateway',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const urlMap = new Map();
|
||||||
|
urlMap.set('mqtt-client-gateway', 'topic');
|
||||||
|
urlMap.set('http-server-gateway', 'url');
|
||||||
|
urlMap.set('websocket-server', 'url');
|
||||||
|
urlMap.set('coap-server-gateway', 'url');
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
<template>
|
||||||
|
<a-card>
|
||||||
|
<div class="diagnose">
|
||||||
|
<div class="diagnose-header" :style="{background: headerColorMap.get(topState)}">
|
||||||
|
<div class="diagnose-top">
|
||||||
|
<div class="diagnose-img">
|
||||||
|
<div v-if="topState === 'loading'" style="width: 100%; height: 100%; position: relative">
|
||||||
|
<img :src="headerImgMap.get(topState)" style="height: 100%; position: absolute; z-index: 2" />
|
||||||
|
<img :src="getImage('/diagnose/loading-1.png')" style="height: 100%" />
|
||||||
|
</div>
|
||||||
|
<img v-else :src="headerImgMap.get(topState)" style="height: 100%" />
|
||||||
|
</div>
|
||||||
|
<div class="diagnose-text">
|
||||||
|
<div class="diagnose-title">{{headerTitleMap.get(topState)}}</div>
|
||||||
|
<div class="diagnose-desc">
|
||||||
|
<template v-if="topState !== 'loading'">{{headerDescMap.get(topState)}}</template>
|
||||||
|
<template v-else>已诊断{{count}}个</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="diagnose-progress">
|
||||||
|
<a-progress
|
||||||
|
:percent="percent"
|
||||||
|
:showInfo="false"
|
||||||
|
size="small"
|
||||||
|
:strokeColor="progressMap.get(topState)"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="diagnose-radio">
|
||||||
|
<div class="diagnose-radio-item" :class="item.key === 'message' && topState !== 'success' ? 'disabled' : ''" v-for="item in tabList" :key="item.key" :style="activeKey === item.key ? {...activeStyle} : {}" @click="onTabChange(item.key)">
|
||||||
|
{{item.text}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Message v-if="activeKey === 'message'" />
|
||||||
|
<Status v-else :providerType="providerType" @countChange="countChange" @percentChange="percentChange" @stateChange="stateChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { headerImgMap, headerColorMap, headerTitleMap, headerDescMap, progressMap } from './util'
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import Status from './Status/index'
|
||||||
|
import Message from './Message/index.vue'
|
||||||
|
import { useInstanceStore } from '@/store/instance';
|
||||||
|
|
||||||
|
type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
|
||||||
|
|
||||||
|
const instanceStore = useInstanceStore()
|
||||||
|
|
||||||
|
const tabList = [
|
||||||
|
{ key: 'status', text: '连接状态' },
|
||||||
|
{ key: 'message', text: '消息通信' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const activeStyle = {
|
||||||
|
background: '#FFFFFF',
|
||||||
|
border: '1px solid rgba(0, 0, 0, 0.09)',
|
||||||
|
borderRadius: '2px 2px 0px 0px',
|
||||||
|
color: '#000000BF',
|
||||||
|
};
|
||||||
|
|
||||||
|
const topState = ref<'loading' | 'success' | 'error'>('loading')
|
||||||
|
const count = ref<number>(0)
|
||||||
|
const percent = ref<number>(0)
|
||||||
|
const activeKey = ref<'status' | 'message'>('status')
|
||||||
|
const providerType = ref()
|
||||||
|
|
||||||
|
|
||||||
|
const onTabChange = (key: 'status' | 'message') => {
|
||||||
|
if(topState.value === 'success'){
|
||||||
|
activeKey.value = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const percentChange = (num: number) => {
|
||||||
|
if(num === 0){
|
||||||
|
percent.value = 0
|
||||||
|
} else if( percent.value < 100 && !num) {
|
||||||
|
percent.value += 20
|
||||||
|
} else {
|
||||||
|
percent.value = num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateChange = (_type: 'loading' | 'success' | 'error') => {
|
||||||
|
topState.value = _type
|
||||||
|
}
|
||||||
|
|
||||||
|
const countChange = (num: number) => {
|
||||||
|
count.value = num
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const provider = instanceStore.current?.accessProvider;
|
||||||
|
if (provider === 'fixed-media' || provider === 'gb28181-2016') {
|
||||||
|
providerType.value = 'media'
|
||||||
|
} else if (provider === 'OneNet' || provider === 'Ctwing') {
|
||||||
|
providerType.value = 'cloud'
|
||||||
|
} else if (provider === 'modbus-tcp' || provider === 'opc-ua') {
|
||||||
|
providerType.value = 'channel'
|
||||||
|
} else if (provider === 'child-device') {
|
||||||
|
providerType.value = 'child-device'
|
||||||
|
} else {
|
||||||
|
providerType.value = 'network'
|
||||||
|
}
|
||||||
|
topState.value = 'loading';
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.diagnose {
|
||||||
|
.diagnose-header {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px 25px;
|
||||||
|
|
||||||
|
.diagnose-top {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.diagnose-img {
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagnose-text {
|
||||||
|
.diagnose-title {
|
||||||
|
color: #000c;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagnose-desc {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagnose-progress {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagnose-radio {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.diagnose-radio-item {
|
||||||
|
width: 150px;
|
||||||
|
height: 35px;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #00000073;
|
||||||
|
line-height: 35px;
|
||||||
|
text-align: center;
|
||||||
|
background: #f2f2f2;
|
||||||
|
border-radius: 2px 2px 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagnose-loading {
|
||||||
|
animation: diagnose-loading 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes diagnose-loading {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
|
||||||
|
export const headerImgMap = new Map();
|
||||||
|
headerImgMap.set('loading', getImage('/diagnose/loading-2.png'));
|
||||||
|
headerImgMap.set('error', getImage('/diagnose/error.png'));
|
||||||
|
headerImgMap.set('success', getImage('/diagnose/success.png'));
|
||||||
|
|
||||||
|
export const headerColorMap = new Map();
|
||||||
|
headerColorMap.set('loading', 'linear-gradient(89.95deg, #E6F5FF 0.03%, #E9EAFF 99.95%)');
|
||||||
|
headerColorMap.set(
|
||||||
|
'error',
|
||||||
|
'linear-gradient(89.95deg, rgba(231, 173, 86, 0.1) 0.03%, rgba(247, 111, 93, 0.1) 99.95%)',
|
||||||
|
);
|
||||||
|
headerColorMap.set('success', 'linear-gradient(89.95deg, #E8F8F7 0.03%, #EBEFFA 99.95%)');
|
||||||
|
|
||||||
|
|
||||||
|
export const headerTitleMap = new Map();
|
||||||
|
headerTitleMap.set('loading', '正在诊断中');
|
||||||
|
headerTitleMap.set('error', '发现连接问题');
|
||||||
|
headerTitleMap.set('success', '连接状态正常');
|
||||||
|
|
||||||
|
export const headerDescMap = new Map();
|
||||||
|
headerDescMap.set('loading', '已诊断XX个');
|
||||||
|
headerDescMap.set('error', '请处理连接异常');
|
||||||
|
headerDescMap.set('success', '现在可调试消息通信');
|
||||||
|
|
||||||
|
export const progressMap = new Map();
|
||||||
|
progressMap.set('loading', '#597EF7');
|
||||||
|
progressMap.set('error', '#FAB247');
|
||||||
|
progressMap.set('success', '#32D4A4');
|
|
@ -44,6 +44,7 @@ import Info from './Info/index.vue';
|
||||||
import Running from './Running/index.vue'
|
import Running from './Running/index.vue'
|
||||||
import Metadata from '../../components/Metadata/index.vue';
|
import Metadata from '../../components/Metadata/index.vue';
|
||||||
import ChildDevice from './ChildDevice/index.vue';
|
import ChildDevice from './ChildDevice/index.vue';
|
||||||
|
import Diagnose from './Diagnose/index.vue'
|
||||||
import { _deploy, _disconnect } from '@/api/device/instance'
|
import { _deploy, _disconnect } from '@/api/device/instance'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
@ -52,7 +53,7 @@ const route = useRoute();
|
||||||
const instanceStore = useInstanceStore()
|
const instanceStore = useInstanceStore()
|
||||||
|
|
||||||
const statusMap = new Map();
|
const statusMap = new Map();
|
||||||
statusMap.set('online', 'processing');
|
statusMap.set('online', 'success');
|
||||||
statusMap.set('offline', 'error');
|
statusMap.set('offline', 'error');
|
||||||
statusMap.set('notActive', 'warning');
|
statusMap.set('notActive', 'warning');
|
||||||
|
|
||||||
|
@ -72,7 +73,11 @@ const list = [
|
||||||
{
|
{
|
||||||
key: 'ChildDevice',
|
key: 'ChildDevice',
|
||||||
tab: '子设备'
|
tab: '子设备'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
key: 'Diagnose',
|
||||||
|
tab: '设备诊断'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const tabs = {
|
const tabs = {
|
||||||
|
@ -80,6 +85,7 @@ const tabs = {
|
||||||
Metadata,
|
Metadata,
|
||||||
Running,
|
Running,
|
||||||
ChildDevice,
|
ChildDevice,
|
||||||
|
Diagnose
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search :columns="columns" target="device-instance" />
|
<Search
|
||||||
|
:columns="columns"
|
||||||
|
target="device-instance"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
<JTable
|
<JTable
|
||||||
ref="instanceRef"
|
ref="instanceRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -267,6 +271,13 @@ import Export from './Export/index.vue';
|
||||||
import Process from './Process/index.vue';
|
import Process from './Process/index.vue';
|
||||||
import Save from './Save/index.vue';
|
import Save from './Save/index.vue';
|
||||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||||
|
import {
|
||||||
|
getProviders,
|
||||||
|
queryGatewayList,
|
||||||
|
queryNoPagingPost,
|
||||||
|
queryOrgThree,
|
||||||
|
} from '@/api/device/product';
|
||||||
|
import { queryTree } from '@/api/device/category';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const instanceRef = ref<Record<string, any>>({});
|
const instanceRef = ref<Record<string, any>>({});
|
||||||
|
@ -290,33 +301,172 @@ const columns = [
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
key: 'id',
|
key: 'id',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '设备名称',
|
title: '设备名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '产品名称',
|
title: '产品名称',
|
||||||
dataIndex: 'productName',
|
dataIndex: 'productName',
|
||||||
key: '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: '创建时间',
|
title: '创建时间',
|
||||||
dataIndex: 'createTime',
|
dataIndex: 'createTime',
|
||||||
key: 'createTime',
|
key: 'createTime',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'state',
|
dataIndex: 'state',
|
||||||
key: 'state',
|
key: 'state',
|
||||||
scopedSlots: true,
|
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);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'accessProvider',
|
||||||
|
title: '网关类型',
|
||||||
|
dataIndex: 'accessProvider',
|
||||||
|
valueType: 'select',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
getProviders().then((resp: any) => {
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: `accessProvider is ${item.id}`,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'productId$product-info',
|
||||||
|
dataIndex: 'productId$product-info',
|
||||||
|
title: '接入方式',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryGatewayList({}).then((resp: any) => {
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: `accessId is ${item.id}`,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'deviceType',
|
||||||
|
title: '设备类型',
|
||||||
|
valueType: 'select',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '直连设备', value: 'device' },
|
||||||
|
{ label: '网关子设备', value: 'childrenDevice' },
|
||||||
|
{ label: '网关设备', value: 'gateway' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'id$dim-assets',
|
||||||
|
title: '所属组织',
|
||||||
|
hideInTable: true,
|
||||||
|
search: {
|
||||||
|
type: 'treeSelect',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryOrgThree({}).then((resp: any) => {
|
||||||
|
const formatValue = (list: any[]) => {
|
||||||
|
const _list: any[] = [];
|
||||||
|
list.forEach((item) => {
|
||||||
|
if (item.children) {
|
||||||
|
item.children = formatValue(item.children);
|
||||||
|
}
|
||||||
|
_list.push({
|
||||||
|
...item,
|
||||||
|
id: JSON.stringify({
|
||||||
|
assetType: 'device',
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
type: 'org',
|
||||||
|
id: item.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return _list;
|
||||||
|
};
|
||||||
|
resolve(formatValue(resp.result));
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'describe',
|
dataIndex: 'describe',
|
||||||
key: 'describe',
|
key: 'describe',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
|
@ -543,4 +693,9 @@ const saveBtn = () => {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
instanceRef.value?.reload();
|
instanceRef.value?.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSearch = (_params: any) => {
|
||||||
|
console.log(_params);
|
||||||
|
params.value = _params;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { MetadataItem } from "../Product/typings";
|
||||||
|
|
||||||
export type DeviceInstance = {
|
export type DeviceInstance = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<a-form ref="addFormRef" :model="form.model" layout="vertical">
|
||||||
|
<a-form-item label="标识" name="id" :rules="[
|
||||||
|
{ required: true, message: '请输入标识' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_]+$/,
|
||||||
|
message: '请输入英文或者数字或者-或者_',
|
||||||
|
},
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="form.model.id" size="small"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="名称" name="name" :rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]">
|
||||||
|
<a-input v-model:value="form.model.name" size="small"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<ValueTypeForm :name="['valueType']" v-model:value="form.model.valueType" key="property"></ValueTypeForm>
|
||||||
|
<a-form-item label="读写类型" :name="['expands', 'type']" :rules="[
|
||||||
|
{ required: true, message: '请选择读写类型' },
|
||||||
|
]">
|
||||||
|
<a-select v-model:value="form.model.expands.type" :options="form.expandsType" mode="multiple" size="small"></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="说明" name="description" :rules="[
|
||||||
|
{ max: 200, message: '最多可输入200个字符' },
|
||||||
|
]">
|
||||||
|
<a-textarea v-model:value="form.model.description" size="small"></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="PropertyForm">
|
||||||
|
import ValueTypeForm from './ValueTypeForm.vue'
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
model: {
|
||||||
|
valueType: {
|
||||||
|
expands: {}
|
||||||
|
},
|
||||||
|
expands: {}
|
||||||
|
} as any,
|
||||||
|
expandsType: [
|
||||||
|
{
|
||||||
|
label: '读',
|
||||||
|
value: 'read',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '写',
|
||||||
|
value: 'write',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '上报',
|
||||||
|
value: 'report',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.ant-form-item-label) {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
>label {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.ant-form-item-required:not(.ant-form-item-required-mark-optional)::before {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-explain) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-with-help) {
|
||||||
|
.ant-form-item-explain {
|
||||||
|
min-height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&.ant-form-item-with-help {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-select) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,151 @@
|
||||||
|
<template>
|
||||||
|
<a-form-item label="数据类型" :name="name.concat(['type'])" :rules="[
|
||||||
|
{ required: true, message: '请选择数据类型' },
|
||||||
|
]">
|
||||||
|
<a-select v-model:value="value.type" :options="_dataTypeList" size="small"></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="单位" :name="name.concat(['unit'])" v-if="['int', 'float', 'long', 'double'].includes(value.type)">
|
||||||
|
<InputSelect v-model:value="value.unit" :options="unit.unitOptions" size="small"></InputSelect>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="精度" :name="name.concat(['scale'])" v-if="['float', 'double'].includes(value.type)">
|
||||||
|
<a-input-number v-model:value="value.scale" size="small" :min="0" :max="2147483647" :precision="0"
|
||||||
|
:default-value="2" style="width: 100%"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="布尔值" name="booleanConfig" v-if="['boolean'].includes(value.type)">
|
||||||
|
<BooleanParam
|
||||||
|
:name="name"
|
||||||
|
v-model:value="_value"
|
||||||
|
></BooleanParam>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="枚举项" :name="name.concat(['elements'])" v-if="['enum'].includes(value.type)" :rules="[
|
||||||
|
{ required: true, message: '请配置枚举项' }
|
||||||
|
]">
|
||||||
|
<EnumParam v-model:value="value.elements"></EnumParam>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :name="name.concat(['expands', 'maxLength'])" v-if="['string', 'password'].includes(value.type)">
|
||||||
|
<template #label>
|
||||||
|
<a-space>
|
||||||
|
最大长度
|
||||||
|
<a-tooltip title="字节">
|
||||||
|
<question-circle-outlined style="color: rgb(136, 136, 136); font-size: 12px;" />
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<a-input-number v-model:value="value.expands.maxLength" size="small" :max="2147483647" :min="1" :precision="0"
|
||||||
|
style="width: 100%;"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="元素配置" :name="name.concat(['elementType'])" v-if="['array'].includes(value.type)">
|
||||||
|
<ArrayParam v-model:value="value.elementType" :name="name.concat(['elementType'])"></ArrayParam>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="JSON对象" :name="name.concat(['properties'])" v-if="['object'].includes(value.type)">
|
||||||
|
<JsonParam v-model:value="value.jsonConfig" :name="name.concat(['properties'])"></JsonParam>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="文件类型" :name="name.concat(['fileType'])" v-if="['file'].includes(value.type)" initialValue="url" :rules="[
|
||||||
|
{ required: true, message: '请选择文件类型' },
|
||||||
|
]">
|
||||||
|
<a-select v-model:value="value.fileType" :options="FileTypeList" size="small"></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup mame="BaseForm">
|
||||||
|
import { DataTypeList, FileTypeList } from '@/views/device/data';
|
||||||
|
import { DefaultOptionType } from 'ant-design-vue/es/select';
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import { getUnit } from '@/api/device/instance';
|
||||||
|
import { Store } from 'jetlinks-store';
|
||||||
|
import InputSelect from '@/components/InputSelect/index.vue';
|
||||||
|
import BooleanParam from '@/components/Metadata/BooleanParam/index.vue'
|
||||||
|
import EnumParam from '@/components/Metadata/EnumParam/index.vue'
|
||||||
|
import ArrayParam from '@/components/Metadata/ArrayParam/index.vue'
|
||||||
|
import JsonParam from '@/components/Metadata/JsonParam/index.vue'
|
||||||
|
|
||||||
|
type ValueType = Record<any, any>;
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<ValueType>,
|
||||||
|
default: () => ({
|
||||||
|
extends: {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
isSub: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
default: () => ([]),
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:value', data: ValueType): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
|
||||||
|
const _value = computed({
|
||||||
|
get: () => props.value,
|
||||||
|
set: val => {
|
||||||
|
emit('update:value', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const unit = {
|
||||||
|
unitOptions: [] as DefaultOptionType[],
|
||||||
|
getUnit: () => {
|
||||||
|
getUnit().then(resp => {
|
||||||
|
const _data = resp.result.map(item => ({
|
||||||
|
label: item.description,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
// 缓存单位数据
|
||||||
|
Store.set('units', _data);
|
||||||
|
unit.unitOptions = _data;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
unit.getUnit()
|
||||||
|
|
||||||
|
const _dataTypeList = computed(() => props.isSub ? DataTypeList.filter(item => item.value !== 'array' && item.value !== 'object') : DataTypeList)
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.ant-form-item-label) {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
>label {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.ant-form-item-required:not(.ant-form-item-required-mark-optional)::before {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-explain) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-with-help) {
|
||||||
|
.ant-form-item-explain {
|
||||||
|
min-height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&.ant-form-item-with-help {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-select) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<a-drawer :mask-closable="false" width="25vw" visible :title="`新增${typeMapping[metadataStore.model.type]}`"
|
<a-drawer :mask-closable="false" width="25vw" visible :title="`${title}-${typeMapping[metadataStore.model.type]}`"
|
||||||
@close="close" destroy-on-close :z-index="1000" placement="right">
|
@close="close" destroy-on-close :z-index="1000" placement="right">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</a-button>
|
<a-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</a-button>
|
||||||
</template>
|
</template>
|
||||||
<a-form ref="addFormRef" :model="form.model"></a-form>
|
<PropertyForm v-if="metadataStore.model.type === 'properties'"></PropertyForm>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="Edit">
|
<script lang="ts" setup name="Edit">
|
||||||
|
@ -19,6 +19,7 @@ import { Store } from 'jetlinks-store';
|
||||||
import { SystemConst } from '@/utils/consts';
|
import { SystemConst } from '@/utils/consts';
|
||||||
import { detail } from '@/api/device/instance';
|
import { detail } from '@/api/device/instance';
|
||||||
import { DeviceInstance } from '@/views/device/Instance/typings';
|
import { DeviceInstance } from '@/views/device/Instance/typings';
|
||||||
|
import PropertyForm from './PropertyForm.vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: 'product' | 'device';
|
type: 'product' | 'device';
|
||||||
|
@ -41,6 +42,8 @@ const close = () => {
|
||||||
metadataStore.set('item', {})
|
metadataStore.set('item', {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const title = computed(() => metadataStore.model.action === 'add' ? '新增' : '修改')
|
||||||
|
|
||||||
const addFormRef = ref<FormInstance>()
|
const addFormRef = ref<FormInstance>()
|
||||||
/**
|
/**
|
||||||
* 保存按钮
|
* 保存按钮
|
||||||
|
@ -113,7 +116,7 @@ const save = reactive({
|
||||||
})
|
})
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
model: {}
|
model: {} as Record<string, any>
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
<JTable :loading="loading" :data-source="data" size="small" :columns="columns" row-key="id" model="TABLE">
|
<JTable :loading="loading" :data-source="data" size="small" :columns="columns" row-key="id" model="TABLE">
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-input-search v-model:value="searchValue" placeholder="请输入名称" @search="handleSearch"></a-input-search>
|
<a-input-search v-model:value="searchValue" placeholder="请输入名称" @search="handleSearch"></a-input-search>
|
||||||
|
</template>
|
||||||
|
<template #rightExtraRender>
|
||||||
<PermissionButton type="primary" :uhas-permission="`${permission}:update`" key="add" @click="handleAddClick"
|
<PermissionButton type="primary" :uhas-permission="`${permission}:update`" key="add" @click="handleAddClick"
|
||||||
:udisabled="operateLimits('add', type)" :tooltip="{
|
:disabled="operateLimits('add', type)" :tooltip="{
|
||||||
title: operateLimits('add', type) ? '当前的存储方式不支持新增' : '新增',
|
title: operateLimits('add', type) ? '当前的存储方式不支持新增' : '新增',
|
||||||
}">
|
}">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
@ -31,13 +33,13 @@
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<PermissionButton :has-permission="`${permission}:update`" type="link" key="edit" style="padding: 0"
|
<PermissionButton :uhas-permission="`${permission}:update`" type="link" key="edit" style="padding: 0"
|
||||||
:disabled="operateLimits('updata', type)" @click="handleEditClick(slotProps)" :tooltip="{
|
:udisabled="operateLimits('updata', type)" @click="handleEditClick(slotProps)" :tooltip="{
|
||||||
title: operateLimits('updata', type) ? '当前的存储方式不支持编辑' : '编辑',
|
title: operateLimits('updata', type) ? '当前的存储方式不支持编辑' : '编辑',
|
||||||
}">
|
}">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</PermissionButton>,
|
</PermissionButton>
|
||||||
<PermissionButton :has-permission="`${permission}:delete`" type="link" key="delete" style="padding: 0"
|
<PermissionButton :uhas-permission="`${permission}:delete`" type="link" key="delete" style="padding: 0"
|
||||||
:pop-confirm="{
|
:pop-confirm="{
|
||||||
title: '确认删除?', onConfirm: async () => {
|
title: '确认删除?', onConfirm: async () => {
|
||||||
await removeItem(slotProps);
|
await removeItem(slotProps);
|
||||||
|
@ -58,12 +60,13 @@ import { useInstanceStore } from '@/store/instance'
|
||||||
import { useProductStore } from '@/store/product'
|
import { useProductStore } from '@/store/product'
|
||||||
import { useMetadataStore } from '@/store/metadata'
|
import { useMetadataStore } from '@/store/metadata'
|
||||||
import PermissionButton from '@/components/PermissionButton/index.vue'
|
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||||
import { message } from 'ant-design-vue/es'
|
import { message } from 'ant-design-vue/es'
|
||||||
import { SystemConst } from '@/utils/consts'
|
import { SystemConst } from '@/utils/consts'
|
||||||
import { Store } from 'jetlinks-store'
|
import { Store } from 'jetlinks-store'
|
||||||
import { asyncUpdateMetadata, removeMetadata } from '../metadata'
|
import { asyncUpdateMetadata, removeMetadata } from '../metadata'
|
||||||
import { detail } from '@/api/device/instance'
|
import { detail } from '@/api/device/instance'
|
||||||
|
import Edit from './Edit/index.vue'
|
||||||
// import { detail } from '@/api/device/instance'
|
// import { detail } from '@/api/device/instance'
|
||||||
// import { detail as productDetail } from '@/api/device/product'
|
// import { detail as productDetail } from '@/api/device/product'
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
import { isNoCommunity } from '@/utils/utils';
|
||||||
|
|
||||||
|
export const DataTypeList: { label: string; value: string }[] = [
|
||||||
|
{
|
||||||
|
value: 'int',
|
||||||
|
label: 'int(整数型)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'long',
|
||||||
|
label: 'long(长整数型)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'float',
|
||||||
|
label: 'float(单精度浮点型)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'double',
|
||||||
|
label: 'double(双精度浮点数)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'string',
|
||||||
|
label: 'text(字符串)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'boolean',
|
||||||
|
label: 'boolean(布尔型)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'date',
|
||||||
|
label: 'date(时间型)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'enum',
|
||||||
|
label: 'enum(枚举)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'array',
|
||||||
|
label: 'array(数组)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'object',
|
||||||
|
label: 'object(结构体)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'file',
|
||||||
|
label: 'file(文件)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'password',
|
||||||
|
label: 'password(密码)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'geoPoint',
|
||||||
|
label: 'geoPoint(地理位置)',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PropertySource: { label: string; value: string }[] = isNoCommunity
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
value: 'device',
|
||||||
|
label: '设备',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'manual',
|
||||||
|
label: '手动',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'rule',
|
||||||
|
label: '规则',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
value: 'device',
|
||||||
|
label: '设备',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'manual',
|
||||||
|
label: '手动',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const FileTypeList: { label: string; value: string }[] = [
|
||||||
|
{
|
||||||
|
label: 'URL(链接)',
|
||||||
|
value: 'url',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Base64(Base64编码)',
|
||||||
|
value: 'base64',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'binary',
|
||||||
|
value: 'Binary(二进制)',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const EventLevel: { label: string; value: string }[] = [
|
||||||
|
{
|
||||||
|
label: '普通',
|
||||||
|
value: 'ordinary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '警告',
|
||||||
|
value: 'warn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'urgent',
|
||||||
|
label: '紧急',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DateTypeList = [
|
||||||
|
// {
|
||||||
|
// label: 'String类型的UTC时间戳 (毫秒)',
|
||||||
|
// value: 'string',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
label: 'yyyy-MM-dd',
|
||||||
|
value: 'yyyy-MM-dd',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'yyyy-MM-DD HH:mm:ss',
|
||||||
|
value: 'yyyy-MM-DD HH:mm:ss',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// label: 'yyyy-MM-dd HH:mm:ss EE',
|
||||||
|
// value: 'yyyy-MM-dd HH:mm:ss EE',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// label: 'yyyy-MM-dd HH:mm:ss zzz',
|
||||||
|
// value: 'yyyy-MM-dd HH:mm:ss zzz',
|
||||||
|
// },
|
||||||
|
];
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
const configuration = {
|
export const Configuration = {
|
||||||
parserType: undefined,
|
parserType: undefined,
|
||||||
port: undefined,
|
port: undefined,
|
||||||
host: '0.0.0.0',
|
host: undefined,
|
||||||
publicPort: '',
|
publicPort: '',
|
||||||
publicHost: '',
|
publicHost: '',
|
||||||
remoteHost: '',
|
remoteHost: '',
|
||||||
|
@ -11,7 +11,7 @@ const configuration = {
|
||||||
password: '',
|
password: '',
|
||||||
topicPrefix: '',
|
topicPrefix: '',
|
||||||
maxMessageSize: '',
|
maxMessageSize: '',
|
||||||
certId: '',
|
certId: undefined,
|
||||||
privateKeyAlias: '',
|
privateKeyAlias: '',
|
||||||
clientId: '',
|
clientId: '',
|
||||||
parserConfiguration: {
|
parserConfiguration: {
|
||||||
|
@ -29,24 +29,21 @@ export const FormStates = {
|
||||||
name: '',
|
name: '',
|
||||||
type: 'UDP',
|
type: 'UDP',
|
||||||
shareCluster: true,
|
shareCluster: true,
|
||||||
// configuration,
|
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormStates2 = {
|
export const FormStates2 = {
|
||||||
serverId: undefined,
|
serverId: undefined,
|
||||||
configuration,
|
configuration: Configuration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TCPList = [
|
||||||
|
'TCP_SERVER',
|
||||||
// export const DefaultCluster = {
|
'WEB_SOCKET_SERVER',
|
||||||
|
'HTTP_SERVER',
|
||||||
// }
|
'MQTT_SERVER',
|
||||||
export const DefaultFormStates = {
|
];
|
||||||
...FormStates,
|
export const UDPList = ['UDP', 'COAP_SERVER'];
|
||||||
cluster: [{ ...FormStates2, id: 1 }],
|
|
||||||
};
|
|
||||||
|
|
||||||
const VisibleMost = [
|
const VisibleMost = [
|
||||||
'COAP_SERVER',
|
'COAP_SERVER',
|
||||||
|
@ -63,6 +60,7 @@ export const VisibleData = {
|
||||||
host: VisibleMost,
|
host: VisibleMost,
|
||||||
publicPort: VisibleMost,
|
publicPort: VisibleMost,
|
||||||
publicHost: VisibleMost,
|
publicHost: VisibleMost,
|
||||||
|
serverId: ['MQTT_CLIENT'],
|
||||||
remoteHost: ['MQTT_CLIENT'],
|
remoteHost: ['MQTT_CLIENT'],
|
||||||
remotePort: ['MQTT_CLIENT'],
|
remotePort: ['MQTT_CLIENT'],
|
||||||
secure: ['TCP_SERVER', 'UDP', 'COAP_SERVER'],
|
secure: ['TCP_SERVER', 'UDP', 'COAP_SERVER'],
|
||||||
|
@ -114,3 +112,193 @@ export const Validator = {
|
||||||
),
|
),
|
||||||
regOnlyNumber: new RegExp(/^\d+$/),
|
regOnlyNumber: new RegExp(/^\d+$/),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Rules = {
|
||||||
|
name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择类型',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
shareCluster: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择集群',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
host: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择本地地址',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
port: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择本地端口',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
publicHost: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入公网地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: Validator.regIp || Validator.regDomain,
|
||||||
|
message: '请输入IP或者域名',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
publicPort: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入公网端口',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: Validator.regOnlyNumber,
|
||||||
|
message: '请输入1-65535之间的正整数',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
remoteHost: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入远程地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: Validator.regIp || Validator.regDomain,
|
||||||
|
message: '请输入IP或者域名',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
remotePort: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '输入远程端口',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: Validator.regOnlyNumber,
|
||||||
|
message: '请输入1-65535之间的正整数',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
clientId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入ClientId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
username: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入用户名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入密码',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
topicPrefix: [
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
maxMessageSize: [
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
secure: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
certId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择证书',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
privateKeyAlias: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入私钥别名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parserType: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择粘拆包规则',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
delimited: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入分隔符',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lang: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择脚本语言',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最大可输入64个字符',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
script: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入脚本',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入长度值',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
length: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择长度',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
offset: [
|
||||||
|
{
|
||||||
|
pattern: Validator.regOnlyNumber,
|
||||||
|
message: '请输入0-65535之间的正整数',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
export interface Form2 {
|
export interface ConfigurationType {
|
||||||
id: number;
|
parserType: string | undefined;
|
||||||
serverId: string | undefined;
|
port: string | undefined;
|
||||||
configuration: {
|
host: string | undefined;;
|
||||||
parserType: undefined;
|
|
||||||
port: undefined;
|
|
||||||
host: string;
|
|
||||||
publicPort: string;
|
publicPort: string;
|
||||||
publicHost: string;
|
publicHost: string;
|
||||||
remoteHost: string;
|
remoteHost: string;
|
||||||
|
@ -14,7 +11,7 @@ export interface Form2 {
|
||||||
password: string;
|
password: string;
|
||||||
topicPrefix: string;
|
topicPrefix: string;
|
||||||
maxMessageSize: string;
|
maxMessageSize: string;
|
||||||
certId: string;
|
certId: string | undefined;
|
||||||
privateKeyAlias: string;
|
privateKeyAlias: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
parserConfiguration: {
|
parserConfiguration: {
|
||||||
|
@ -26,6 +23,16 @@ export interface Form2 {
|
||||||
offset: string;
|
offset: string;
|
||||||
little: string | boolean;
|
little: string | boolean;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export interface FormDataType {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
shareCluster: boolean;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
export interface FormData2Type {
|
||||||
|
id?: number | string;
|
||||||
|
serverId?: string | undefined;
|
||||||
|
configuration: ConfigurationType;
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
>
|
>
|
||||||
<a-row :gutter="24">
|
<a-row :gutter="24">
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item label="名称" name="name" :rules=" [
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入名称',
|
message: '请输入名称',
|
||||||
|
@ -20,47 +23,87 @@
|
||||||
max: 64,
|
max: 64,
|
||||||
message: '最多输入64个字符',
|
message: '最多输入64个字符',
|
||||||
},
|
},
|
||||||
]">
|
]"
|
||||||
<a-input placeholder="请输入名称" v-model:value="modelRef.name" />
|
>
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入名称"
|
||||||
|
v-model:value="modelRef.name"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item :name="['accessConfig', 'regionId']" :rules="[{
|
<a-form-item
|
||||||
|
:name="['accessConfig', 'regionId']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择服务地址',
|
message: '请选择服务地址',
|
||||||
}]">
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
服务地址
|
服务地址
|
||||||
<a-tooltip title="阿里云内部给每台机器设置的唯一编号">
|
<a-tooltip
|
||||||
|
title="阿里云内部给每台机器设置的唯一编号"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px;" />
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select placeholder="请选择服务地址" v-model:value="modelRef.accessConfig.regionId" show-search :filter-option="filterOption" @blur="productChange">
|
<a-select
|
||||||
<a-select-option v-for="item in regionsList" :key="item.id" :value="item.id" :label="item.name">{{item.name}}</a-select-option>
|
placeholder="请选择服务地址"
|
||||||
|
v-model:value="
|
||||||
|
modelRef.accessConfig.regionId
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
@blur="productChange"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in regionsList"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>{{ item.name }}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item :name="['accessConfig', 'instanceId']">
|
<a-form-item
|
||||||
|
:name="['accessConfig', 'instanceId']"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
实例ID
|
实例ID
|
||||||
<a-tooltip title="阿里云物联网平台中的实例ID,没有则不填">
|
<a-tooltip
|
||||||
|
title="阿里云物联网平台中的实例ID,没有则不填"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px;" />
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-input placeholder="请输入实例ID" v-model:value="modelRef.accessConfig.instanceId" @blur="productChange" />
|
<a-input
|
||||||
|
placeholder="请输入实例ID"
|
||||||
|
v-model:value="
|
||||||
|
modelRef.accessConfig.instanceId
|
||||||
|
"
|
||||||
|
@blur="productChange"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item :name="['accessConfig', 'accessKeyId']" :rules="[{
|
<a-form-item
|
||||||
|
:name="['accessConfig', 'accessKeyId']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入accessKey',
|
message: '请输入accessKey',
|
||||||
},
|
},
|
||||||
|
@ -68,22 +111,35 @@
|
||||||
max: 64,
|
max: 64,
|
||||||
message: '最多输入64个字符',
|
message: '最多输入64个字符',
|
||||||
},
|
},
|
||||||
]">
|
]"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
accessKey
|
accessKey
|
||||||
<a-tooltip title="用于程序通知方式调用云服务API的用户标识">
|
<a-tooltip
|
||||||
|
title="用于程序通知方式调用云服务API的用户标识"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px;" />
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-input placeholder="请输入accessKey" v-model:value="modelRef.accessConfig.accessKeyId" @blur="productChange" />
|
<a-input
|
||||||
|
placeholder="请输入accessKey"
|
||||||
|
v-model:value="
|
||||||
|
modelRef.accessConfig.accessKeyId
|
||||||
|
"
|
||||||
|
@blur="productChange"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item :name="['accessConfig', 'accessSecret']" :rules="[{
|
<a-form-item
|
||||||
|
:name="['accessConfig', 'accessSecret']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入accessSecret',
|
message: '请输入accessSecret',
|
||||||
},
|
},
|
||||||
|
@ -91,63 +147,176 @@
|
||||||
max: 64,
|
max: 64,
|
||||||
message: '最多输入64个字符',
|
message: '最多输入64个字符',
|
||||||
},
|
},
|
||||||
]">
|
]"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
accessSecret
|
accessSecret
|
||||||
<a-tooltip title="用于程序通知方式调用云服务费API的秘钥标识">
|
<a-tooltip
|
||||||
|
title="用于程序通知方式调用云服务费API的秘钥标识"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px;" />
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-input placeholder="请输入accessSecret" v-model:value="modelRef.accessConfig.accessSecret" @blur="productChange" />
|
<a-input
|
||||||
|
placeholder="请输入accessSecret"
|
||||||
|
v-model:value="
|
||||||
|
modelRef.accessConfig.accessSecret
|
||||||
|
"
|
||||||
|
@blur="productChange"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item name="bridgeProductKey" :rules="{
|
<a-form-item
|
||||||
|
name="bridgeProductKey"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择网桥产品',
|
message: '请选择网桥产品',
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
网桥产品
|
网桥产品
|
||||||
<a-tooltip title="物联网平台对应的阿里云产品">
|
<a-tooltip
|
||||||
|
title="物联网平台对应的阿里云产品"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px;" />
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select placeholder="请选择网桥产品" v-model:value="modelRef.bridgeProductKey" show-search :filter-option="filterOption">
|
<a-select
|
||||||
<a-select-option v-for="item in aliyunProductList" :key="item.productKey" :value="item.productKey" :label="item.productName">{{item.productName}}</a-select-option>
|
placeholder="请选择网桥产品"
|
||||||
|
v-model:value="
|
||||||
|
modelRef.bridgeProductKey
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in aliyunProductList"
|
||||||
|
:key="item.productKey"
|
||||||
|
:value="item.productKey"
|
||||||
|
:label="item.productName"
|
||||||
|
>{{
|
||||||
|
item.productName
|
||||||
|
}}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<p>产品映射</p>
|
<p>产品映射</p>
|
||||||
<a-collapse v-if="modelRef.mappings.length" :activeKey="modelRef.mappings.map((_, _index) => _index)">
|
<a-collapse
|
||||||
<a-collapse-panel v-for="(item, index) in modelRef.mappings" :key="index" :header="item.productKey ? aliyunProductList.find(i => i.productKey === item.productKey)?.productName : `产品映射${index + 1}`">
|
v-if="modelRef.mappings.length"
|
||||||
<template #extra><AIcon type="DeleteOutlined" @click="delItem(index)" /></template>
|
:activeKey="activeKey"
|
||||||
|
@change="onCollChange"
|
||||||
|
>
|
||||||
|
<a-collapse-panel
|
||||||
|
v-for="(
|
||||||
|
item, index
|
||||||
|
) in modelRef.mappings"
|
||||||
|
:key="index"
|
||||||
|
:header="
|
||||||
|
item.productKey
|
||||||
|
? aliyunProductList.find(
|
||||||
|
(i) =>
|
||||||
|
i.productKey ===
|
||||||
|
item.productKey,
|
||||||
|
)?.productName
|
||||||
|
: `产品映射${index + 1}`
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #extra
|
||||||
|
><AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
@click="delItem(index)"
|
||||||
|
/></template>
|
||||||
<a-row :gutter="24">
|
<a-row :gutter="24">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="阿里云产品" :name="['mappings', index, 'productKey']" :rules="{
|
<a-form-item
|
||||||
|
label="阿里云产品"
|
||||||
|
:name="[
|
||||||
|
'mappings',
|
||||||
|
index,
|
||||||
|
'productKey',
|
||||||
|
]"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择阿里云产品',
|
message:
|
||||||
}">
|
'请选择阿里云产品',
|
||||||
<a-select placeholder="请选择阿里云产品" v-model:value="item.productKey" show-search :filter-option="filterOption">
|
}"
|
||||||
<a-select-option v-for="i in getAliyunProductList(item.productKey)" :key="i.productKey" :value="i.productKey" :label="i.productName">{{i.productName}}</a-select-option>
|
>
|
||||||
|
<a-select
|
||||||
|
placeholder="请选择阿里云产品"
|
||||||
|
v-model:value="
|
||||||
|
item.productKey
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="
|
||||||
|
filterOption
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="i in getAliyunProductList(
|
||||||
|
item.productKey,
|
||||||
|
)"
|
||||||
|
:key="i.productKey"
|
||||||
|
:value="
|
||||||
|
i.productKey
|
||||||
|
"
|
||||||
|
:label="
|
||||||
|
i.productName
|
||||||
|
"
|
||||||
|
>{{
|
||||||
|
i.productName
|
||||||
|
}}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="平台产品" :name="['mappings', index, 'productId']" :rules="{
|
<a-form-item
|
||||||
|
label="平台产品"
|
||||||
|
:name="[
|
||||||
|
'mappings',
|
||||||
|
index,
|
||||||
|
'productId',
|
||||||
|
]"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择平台产品',
|
message:
|
||||||
}">
|
'请选择平台产品',
|
||||||
<a-select placeholder="请选择平台产品" v-model:value="item.productId" show-search :filter-option="filterOption">
|
}"
|
||||||
<a-select-option v-for="i in getPlatProduct(item.productId)" :key="i.id" :value="item.id" :label="i.name">{{i.name}}</a-select-option>
|
>
|
||||||
|
<a-select
|
||||||
|
placeholder="请选择平台产品"
|
||||||
|
v-model:value="
|
||||||
|
item.productId
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="
|
||||||
|
filterOption
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="i in getPlatProduct(
|
||||||
|
item.productId,
|
||||||
|
)"
|
||||||
|
:key="i.id"
|
||||||
|
:value="item.id"
|
||||||
|
:label="i.name"
|
||||||
|
>{{
|
||||||
|
i.name
|
||||||
|
}}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -156,17 +325,26 @@
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-button type="dashed" style="width: 100%; margin-top: 10px" @click="addItem">
|
<a-button
|
||||||
|
type="dashed"
|
||||||
|
style="width: 100%; margin-top: 10px"
|
||||||
|
@click="addItem"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="PlusOutlined"
|
type="PlusOutlined"
|
||||||
style="margin-left: 2px;" />添加
|
style="margin-left: 2px"
|
||||||
|
/>添加
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" style="margin-top: 20px">
|
<a-col :span="24" style="margin-top: 20px">
|
||||||
<a-form-item label="说明" name="description" :rules="{
|
<a-form-item
|
||||||
|
label="说明"
|
||||||
|
name="description"
|
||||||
|
:rules="{
|
||||||
max: 200,
|
max: 200,
|
||||||
message: '最多输入200个字符',
|
message: '最多输入200个字符',
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="modelRef.description"
|
v-model:value="modelRef.description"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
|
@ -178,7 +356,12 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div v-if="type === 'edit'">
|
<div v-if="type === 'edit'">
|
||||||
<a-button :loading="loading" type="primary" @click="saveBtn">保存</a-button>
|
<a-button
|
||||||
|
:loading="loading"
|
||||||
|
type="primary"
|
||||||
|
@click="saveBtn"
|
||||||
|
>保存</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="8">
|
<a-col :span="8">
|
||||||
|
@ -190,8 +373,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Doc from './doc.vue'
|
import Doc from './doc.vue';
|
||||||
import {savePatch, detail, getRegionsList, getAliyunProductsList, queryProductList } from '@/api/northbound/alicloud'
|
import {
|
||||||
|
savePatch,
|
||||||
|
detail,
|
||||||
|
getRegionsList,
|
||||||
|
getAliyunProductsList,
|
||||||
|
queryProductList,
|
||||||
|
} from '@/api/northbound/alicloud';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
@ -207,90 +396,108 @@ const modelRef = reactive({
|
||||||
regionId: undefined,
|
regionId: undefined,
|
||||||
instanceId: undefined,
|
instanceId: undefined,
|
||||||
accessKeyId: undefined,
|
accessKeyId: undefined,
|
||||||
accessSecret: undefined
|
accessSecret: undefined,
|
||||||
},
|
},
|
||||||
bridgeProductKey: undefined,
|
bridgeProductKey: undefined,
|
||||||
bridgeProductName: undefined,
|
bridgeProductName: undefined,
|
||||||
mappings: [{
|
mappings: [
|
||||||
|
{
|
||||||
productKey: undefined,
|
productKey: undefined,
|
||||||
productId: undefined,
|
productId: undefined,
|
||||||
}],
|
},
|
||||||
description: undefined
|
],
|
||||||
|
description: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const addItem = () => {
|
const addItem = () => {
|
||||||
|
activeKey.value.push(String(modelRef.mappings.length));
|
||||||
modelRef.mappings.push({
|
modelRef.mappings.push({
|
||||||
productKey: undefined,
|
productKey: undefined,
|
||||||
productId: undefined,
|
productId: undefined,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const delItem = (index: number) => {
|
const delItem = (index: number) => {
|
||||||
modelRef.mappings.splice(index, 1)
|
modelRef.mappings.splice(index, 1);
|
||||||
}
|
};
|
||||||
|
|
||||||
const productList = ref<Record<string, any>[]>([])
|
const productList = ref<Record<string, any>[]>([]);
|
||||||
const regionsList = ref<Record<string, any>[]>([])
|
const regionsList = ref<Record<string, any>[]>([]);
|
||||||
const aliyunProductList = ref<Record<string, any>[]>([])
|
const aliyunProductList = ref<Record<string, any>[]>([]);
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false);
|
||||||
const type = ref<'edit' | 'view'>('view')
|
const type = ref<'edit' | 'view'>('edit');
|
||||||
|
const activeKey = ref<string[]>(['0']);
|
||||||
|
|
||||||
const filterOption = (input: string, option: any) => {
|
const filterOption = (input: string, option: any) => {
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryRegionsList = async () => {
|
const queryRegionsList = async () => {
|
||||||
const resp = await getRegionsList()
|
const resp = await getRegionsList();
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
regionsList.value = resp.result as Record<string, any>[]
|
regionsList.value = resp.result as Record<string, any>[];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
const getProduct = async () => {
|
const getProduct = async () => {
|
||||||
const resp = await queryProductList({
|
const resp = await queryProductList({
|
||||||
paging: false,
|
paging: false,
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
})
|
});
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
productList.value = (resp?.result as Record<string, any>[])
|
productList.value = resp?.result as Record<string, any>[];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getAliyunProduct = async (data: any) => {
|
const getAliyunProduct = async (data: any) => {
|
||||||
if (data.regionId && data.accessKeyId && data.accessSecret) {
|
if (data.regionId && data.accessKeyId && data.accessSecret) {
|
||||||
const resp: any = await getAliyunProductsList(data)
|
const resp: any = await getAliyunProductsList(data);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
aliyunProductList.value = (resp?.result?.data as Record<string, any>[])
|
aliyunProductList.value = resp?.result?.data as Record<
|
||||||
}
|
string,
|
||||||
|
any
|
||||||
|
>[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const productChange = () => {
|
const productChange = () => {
|
||||||
const data = modelRef.accessConfig
|
const data = modelRef.accessConfig;
|
||||||
getAliyunProduct(data)
|
getAliyunProduct(data);
|
||||||
}
|
};
|
||||||
|
|
||||||
const getPlatProduct = (val: string) => {
|
const getPlatProduct = (val: string) => {
|
||||||
const arr = modelRef.mappings.map(item => item?.productId) || []
|
const arr = modelRef.mappings.map((item) => item?.productId) || [];
|
||||||
const checked = _.cloneDeep(arr)
|
const checked = _.cloneDeep(arr);
|
||||||
const _index = checked.findIndex(i => i === val)
|
const _index = checked.findIndex((i) => i === val);
|
||||||
checked.splice(_index, 1)
|
checked.splice(_index, 1);
|
||||||
const list = productList.value.filter((i: any) => !checked.includes(i?.id as any))
|
const list = productList.value.filter(
|
||||||
return list || []
|
(i: any) => !checked.includes(i?.id as any),
|
||||||
}
|
);
|
||||||
|
return list || [];
|
||||||
|
};
|
||||||
|
|
||||||
const getAliyunProductList = (val: string) => {
|
const getAliyunProductList = (val: string) => {
|
||||||
const items = modelRef.mappings.map((item) => item?.productKey) || []
|
const items = modelRef.mappings.map((item) => item?.productKey) || [];
|
||||||
const checked = _.cloneDeep(items)
|
const checked = _.cloneDeep(items);
|
||||||
const _index = checked.findIndex(i => i === val)
|
const _index = checked.findIndex((i) => i === val);
|
||||||
checked.splice(_index, 1)
|
checked.splice(_index, 1);
|
||||||
const list = aliyunProductList.value?.filter((i: any) => !checked.includes(i?.productKey as any))
|
const list = aliyunProductList.value?.filter(
|
||||||
return list || []
|
(i: any) => !checked.includes(i?.productKey as any),
|
||||||
}
|
);
|
||||||
|
return list || [];
|
||||||
|
};
|
||||||
|
|
||||||
const saveBtn = async () => {
|
const onCollChange = (_key: string[]) => {
|
||||||
const data = await formRef.value.validate()
|
activeKey.value = _key;
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveBtn = () => {
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(async (data: any) => {
|
||||||
const product = (aliyunProductList.value || []).find(
|
const product = (aliyunProductList.value || []).find(
|
||||||
(item: any) => item?.bridgeProductKey === data?.bridgeProductKey,
|
(item: any) =>
|
||||||
|
item?.bridgeProductKey === data?.bridgeProductKey,
|
||||||
);
|
);
|
||||||
data.bridgeProductName = product?.productName || '';
|
data.bridgeProductName = product?.productName || '';
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
@ -299,35 +506,43 @@ const saveBtn = async () => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
formRef.value.resetFields();
|
formRef.value.resetFields();
|
||||||
router.push('/iot/northbound/AliCloud/');
|
router.push('/iot/northbound/AliCloud');
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
const _arr = err.errorFields.map((i: any) => i.name);
|
||||||
|
_arr.map((item: string | any[]) => {
|
||||||
|
if (item.length === 3 && !activeKey.value.includes(item[1])) {
|
||||||
|
activeKey.value.push(item[1]);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
watch(
|
watch(
|
||||||
() => route.params?.id,
|
() => route.params?.id,
|
||||||
async (newId) => {
|
async (newId) => {
|
||||||
if (newId) {
|
if (newId) {
|
||||||
queryRegionsList()
|
queryRegionsList();
|
||||||
getProduct()
|
getProduct();
|
||||||
if (newId === ':id' || !newId) return;
|
if (newId === ':id' || !newId) return;
|
||||||
const resp = await detail(newId as string)
|
const resp = await detail(newId as string);
|
||||||
const _data: any = resp.result;
|
const _data: any = resp.result;
|
||||||
if (_data) {
|
if (_data) {
|
||||||
getAliyunProduct(_data?.accessConfig)
|
getAliyunProduct(_data?.accessConfig);
|
||||||
}
|
}
|
||||||
Object.assign(modelRef, _data)
|
Object.assign(modelRef, _data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{immediate: true, deep: true}
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.query.type,
|
() => route.query.type,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
type.value = newVal as 'edit' | 'view'
|
type.value = newVal as 'edit' | 'view';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{immediate: true, deep: true}
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search :columns="columns" target="northbound-dueros" :params="params" />
|
<Search :columns="columns" target="northbound-dueros" @search="handleSearch" />
|
||||||
<JTable
|
<JTable
|
||||||
ref="instanceRef"
|
ref="instanceRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -166,11 +166,17 @@ const columns = [
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '网桥产品',
|
title: '网桥产品',
|
||||||
dataIndex: 'bridgeProductName',
|
dataIndex: 'bridgeProductName',
|
||||||
key: 'bridgeProductName',
|
key: 'bridgeProductName',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
|
@ -182,6 +188,13 @@ const columns = [
|
||||||
dataIndex: 'state',
|
dataIndex: 'state',
|
||||||
key: 'state',
|
key: 'state',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 'enabled' },
|
||||||
|
{ label: '禁用', value: 'disabled' }
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
|
@ -303,4 +316,8 @@ const getActions = (
|
||||||
return actions.filter((i: ActionsType) => i.key !== 'view');
|
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||||
return actions;
|
return actions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSearch = (_params: any) => {
|
||||||
|
params.value = _params
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -127,10 +127,15 @@ const funcChange = (val: string) => {
|
||||||
const saveBtn = () => new Promise((resolve) => {
|
const saveBtn = () => new Promise((resolve) => {
|
||||||
formRef.value.validate()
|
formRef.value.validate()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
const _arr = toRaw(modelRef).value?.message?.inputs || []
|
||||||
|
if(_arr.length && !_arr.every((_a: any) => _a.value)){
|
||||||
|
resolve(false)
|
||||||
|
} else {
|
||||||
resolve(toRaw(modelRef))
|
resolve(toRaw(modelRef))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
resolve(false)
|
resolve(err)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
>
|
>
|
||||||
<a-row :gutter="24">
|
<a-row :gutter="24">
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-form-item label="名称" name="name" :rules=" [
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入名称',
|
message: '请输入名称',
|
||||||
|
@ -20,89 +23,235 @@
|
||||||
max: 64,
|
max: 64,
|
||||||
message: '最多输入64个字符',
|
message: '最多输入64个字符',
|
||||||
},
|
},
|
||||||
]">
|
]"
|
||||||
<a-input placeholder="请输入名称" v-model:value="modelRef.name" />
|
>
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入名称"
|
||||||
|
v-model:value="modelRef.name"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="产品" name="id" :rules="[{
|
<a-form-item
|
||||||
|
label="产品"
|
||||||
|
name="id"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择产品',
|
message: '请选择产品',
|
||||||
}]">
|
},
|
||||||
<a-select :disabled="modelRef.id !== ':id'" placeholder="请选择产品" v-model:value="modelRef.id" show-search :filter-option="filterOption" @change="productChange">
|
]"
|
||||||
<a-select-option v-for="item in productList" :key="item.id" :value="item.id" :label="item.name">{{item.name}}</a-select-option>
|
>
|
||||||
|
<a-select
|
||||||
|
:disabled="type !== 'edit' && modelRef.id && modelRef.id !== ':id'"
|
||||||
|
placeholder="请选择产品"
|
||||||
|
v-model:value="modelRef.id"
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
@change="productChange"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in productList"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>{{ item.name }}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item name="applianceType" :rules="{
|
<a-form-item
|
||||||
|
name="applianceType"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择设备类型',
|
message: '请选择设备类型',
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
设备类型
|
设备类型
|
||||||
<a-tooltip title="DuerOS平台拟定的规范">
|
<a-tooltip
|
||||||
|
title="DuerOS平台拟定的规范"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px;" />
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select placeholder="请选择设备类型" v-model:value="modelRef.applianceType" show-search :filter-option="filterOption" @change="typeChange">
|
<a-select
|
||||||
<a-select-option v-for="item in typeList" :key="item.id" :value="item.id" :label="item.name">{{item.name}}</a-select-option>
|
placeholder="请选择设备类型"
|
||||||
|
v-model:value="modelRef.applianceType"
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
@change="typeChange"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in typeList"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>{{ item.name }}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item name="productName" v-show="false" label="产品名称">
|
<a-form-item
|
||||||
<a-input v-model:value="modelRef.productName" />
|
name="productName"
|
||||||
|
v-show="false"
|
||||||
|
label="产品名称"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modelRef.productName"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<p>动作映射</p>
|
<p>动作映射</p>
|
||||||
<a-collapse v-if="modelRef.actionMappings.length" :activeKey="modelRef.actionMappings.map((_, _index) => _index)">
|
<a-collapse
|
||||||
<a-collapse-panel v-for="(item, index) in modelRef.actionMappings" :key="index" :header="item.action ? getTypesActions(item.action).find(i => i.id === item.action)?.name : `动作映射${index + 1}`">
|
v-if="modelRef.actionMappings.length"
|
||||||
<template #extra><AIcon type="DeleteOutlined" @click="delItem(index)" /></template>
|
:activeKey="actionActiveKey"
|
||||||
|
@change="onActionCollChange"
|
||||||
|
>
|
||||||
|
<a-collapse-panel
|
||||||
|
v-for="(
|
||||||
|
item, index
|
||||||
|
) in modelRef.actionMappings"
|
||||||
|
:key="index"
|
||||||
|
:header="
|
||||||
|
item.action
|
||||||
|
? getTypesActions(
|
||||||
|
item.action,
|
||||||
|
).find(
|
||||||
|
(i) =>
|
||||||
|
i.id === item.action,
|
||||||
|
)?.name
|
||||||
|
: `动作映射${index + 1}`
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #extra
|
||||||
|
><AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
@click="delItem(index)"
|
||||||
|
/></template>
|
||||||
<a-row :gutter="24">
|
<a-row :gutter="24">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item :name="['actionMappings', index, 'action']" :rules="{
|
<a-form-item
|
||||||
|
:name="[
|
||||||
|
'actionMappings',
|
||||||
|
index,
|
||||||
|
'action',
|
||||||
|
]"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择动作',
|
message: '请选择动作',
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
动作
|
动作
|
||||||
<a-tooltip title="DuerOS平台拟定的设备类型具有的相关动作">
|
<a-tooltip
|
||||||
<AIcon type="QuestionCircleOutlined" />
|
title="DuerOS平台拟定的设备类型具有的相关动作"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select placeholder="请选择动作" v-model:value="item.action" show-search :filter-option="filterOption">
|
<a-select
|
||||||
<a-select-option v-for="i in getTypesActions(item.action)" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
placeholder="请选择动作"
|
||||||
|
v-model:value="
|
||||||
|
item.action
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="
|
||||||
|
filterOption
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="i in getTypesActions(
|
||||||
|
item.action,
|
||||||
|
)"
|
||||||
|
:key="i.id"
|
||||||
|
:value="i.id"
|
||||||
|
:label="i.name"
|
||||||
|
>{{
|
||||||
|
i.name
|
||||||
|
}}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item :name="['actionMappings', index, 'actionType']" :rules="{
|
<a-form-item
|
||||||
|
:name="[
|
||||||
|
'actionMappings',
|
||||||
|
index,
|
||||||
|
'actionType',
|
||||||
|
]"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择操作',
|
message: '请选择操作',
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
操作
|
操作
|
||||||
<a-tooltip title="映射物联网平台中所选产品具备的动作">
|
<a-tooltip
|
||||||
<AIcon type="QuestionCircleOutlined" />
|
title="映射物联网平台中所选产品具备的动作"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select placeholder="请选择操作" v-model:value="item.actionType" show-search :filter-option="filterOption">
|
<a-select
|
||||||
<a-select-option value="command">下发指令</a-select-option>
|
placeholder="请选择操作"
|
||||||
<a-select-option value="latestData">获取历史数据</a-select-option>
|
v-model:value="
|
||||||
|
item.actionType
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="
|
||||||
|
filterOption
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
value="command"
|
||||||
|
>下发指令</a-select-option
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
value="latestData"
|
||||||
|
>获取历史数据</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" v-if="item.actionType">
|
<a-col
|
||||||
<a-form-item :name="['actionMappings', index, 'command']">
|
:span="24"
|
||||||
<Command ref="command" :metadata="findProductMetadata" v-model:modelValue="item.command" :actionType="item.actionType" />
|
v-if="item.actionType"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:name="[
|
||||||
|
'actionMappings',
|
||||||
|
index,
|
||||||
|
'command',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<Command
|
||||||
|
ref="command"
|
||||||
|
:metadata="
|
||||||
|
findProductMetadata
|
||||||
|
"
|
||||||
|
v-model:modelValue="
|
||||||
|
item.command
|
||||||
|
"
|
||||||
|
:actionType="
|
||||||
|
item.actionType
|
||||||
|
"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
@ -110,35 +259,118 @@
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-button type="dashed" style="width: 100%; margin-top: 10px" @click="addItem">
|
<a-button
|
||||||
|
type="dashed"
|
||||||
|
style="width: 100%; margin-top: 10px"
|
||||||
|
@click="addItem"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="PlusOutlined"
|
type="PlusOutlined"
|
||||||
style="margin-left: 2px;" />新增动作
|
style="margin-left: 2px"
|
||||||
|
/>新增动作
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<p style="margin-top: 20px">属性映射</p>
|
<p style="margin-top: 20px">属性映射</p>
|
||||||
<a-collapse v-if="modelRef.propertyMappings.length" :activeKey="modelRef.propertyMappings.map((_, _index) => _index)">
|
<a-collapse
|
||||||
<a-collapse-panel v-for="(item, index) in modelRef.propertyMappings" :key="index" :header="item.source ? getDuerOSProperties(item.source).find(i => i.id === item.source)?.name : `属性映射${index + 1}`">
|
v-if="modelRef.propertyMappings.length"
|
||||||
<template #extra><AIcon type="DeleteOutlined" @click="delPropertyItem(index)" /></template>
|
:activeKey="propertyActiveKey"
|
||||||
|
@change="onPropertyCollChange"
|
||||||
|
>
|
||||||
|
<a-collapse-panel
|
||||||
|
v-for="(
|
||||||
|
item, index
|
||||||
|
) in modelRef.propertyMappings"
|
||||||
|
:key="index"
|
||||||
|
:header="
|
||||||
|
item.source
|
||||||
|
? getDuerOSProperties(
|
||||||
|
item.source,
|
||||||
|
).find(
|
||||||
|
(i) =>
|
||||||
|
i.id === item.source,
|
||||||
|
)?.name
|
||||||
|
: `属性映射${index + 1}`
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #extra
|
||||||
|
><AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
@click="delPropertyItem(index)"
|
||||||
|
/></template>
|
||||||
<a-row :gutter="24">
|
<a-row :gutter="24">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="DuerOS属性" :name="['propertyMappings', index, 'source']" :rules="{
|
<a-form-item
|
||||||
|
label="DuerOS属性"
|
||||||
|
:name="[
|
||||||
|
'propertyMappings',
|
||||||
|
index,
|
||||||
|
'source',
|
||||||
|
]"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择DuerOS属性',
|
message:
|
||||||
}">
|
'请选择DuerOS属性',
|
||||||
<a-select placeholder="请选择DuerOS属性" v-model:value="item.source" show-search :filter-option="filterOption">
|
}"
|
||||||
<a-select-option v-for="i in getDuerOSProperties(item.source)" :key="i.id" :value="i.id">{{i.name}}</a-select-option>
|
>
|
||||||
|
<a-select
|
||||||
|
placeholder="请选择DuerOS属性"
|
||||||
|
v-model:value="
|
||||||
|
item.source
|
||||||
|
"
|
||||||
|
show-search
|
||||||
|
:filter-option="
|
||||||
|
filterOption
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="i in getDuerOSProperties(
|
||||||
|
item.source,
|
||||||
|
)"
|
||||||
|
:key="i.id"
|
||||||
|
:value="i.id"
|
||||||
|
>{{
|
||||||
|
i.name
|
||||||
|
}}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="平台属性" :name="['propertyMappings', index, 'target']" :rules="{
|
<a-form-item
|
||||||
|
label="平台属性"
|
||||||
|
:name="[
|
||||||
|
'propertyMappings',
|
||||||
|
index,
|
||||||
|
'target',
|
||||||
|
]"
|
||||||
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择平台属性',
|
message:
|
||||||
}">
|
'请选择平台属性',
|
||||||
<a-select placeholder="请选择平台属性" v-model:value="item.target" mode="tags" show-search :filter-option="filterOption">
|
}"
|
||||||
<a-select-option v-for="i in getProductProperties(item.target)" :key="i.id" :value="item.id">{{i.name}}</a-select-option>
|
>
|
||||||
|
<a-select
|
||||||
|
placeholder="请选择平台属性"
|
||||||
|
v-model:value="
|
||||||
|
item.target
|
||||||
|
"
|
||||||
|
mode="tags"
|
||||||
|
show-search
|
||||||
|
:filter-option="
|
||||||
|
filterOption
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="i in getProductProperties(
|
||||||
|
item.target,
|
||||||
|
)"
|
||||||
|
:key="i.id"
|
||||||
|
:value="item.id"
|
||||||
|
>{{
|
||||||
|
i.name
|
||||||
|
}}</a-select-option
|
||||||
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -147,17 +379,26 @@
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-button type="dashed" style="width: 100%; margin-top: 10px" @click="addPropertyItem">
|
<a-button
|
||||||
|
type="dashed"
|
||||||
|
style="width: 100%; margin-top: 10px"
|
||||||
|
@click="addPropertyItem"
|
||||||
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="PlusOutlined"
|
type="PlusOutlined"
|
||||||
style="margin-left: 2px;" />新增属性
|
style="margin-left: 2px"
|
||||||
|
/>新增属性
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" style="margin-top: 20px">
|
<a-col :span="24" style="margin-top: 20px">
|
||||||
<a-form-item label="说明" name="description" :rules="{
|
<a-form-item
|
||||||
|
label="说明"
|
||||||
|
name="description"
|
||||||
|
:rules="{
|
||||||
max: 200,
|
max: 200,
|
||||||
message: '最多输入200个字符',
|
message: '最多输入200个字符',
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="modelRef.description"
|
v-model:value="modelRef.description"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
|
@ -169,7 +410,12 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div v-if="type === 'edit'">
|
<div v-if="type === 'edit'">
|
||||||
<a-button :loading="loading" type="primary" @click="saveBtn">保存</a-button>
|
<a-button
|
||||||
|
:loading="loading"
|
||||||
|
type="primary"
|
||||||
|
@click="saveBtn"
|
||||||
|
>保存</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="8">
|
<a-col :span="8">
|
||||||
|
@ -181,9 +427,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Doc from './doc.vue'
|
import Doc from './doc.vue';
|
||||||
import Command from './command/index.vue'
|
import Command from './command/index.vue';
|
||||||
import { queryProductList, queryTypes, savePatch, detail } from '@/api/northbound/dueros'
|
import {
|
||||||
|
queryProductList,
|
||||||
|
queryTypes,
|
||||||
|
savePatch,
|
||||||
|
detail,
|
||||||
|
} from '@/api/northbound/dueros';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
@ -197,7 +448,8 @@ const modelRef = reactive({
|
||||||
name: undefined,
|
name: undefined,
|
||||||
applianceType: undefined,
|
applianceType: undefined,
|
||||||
productName: undefined,
|
productName: undefined,
|
||||||
actionMappings: [{
|
actionMappings: [
|
||||||
|
{
|
||||||
actionType: undefined,
|
actionType: undefined,
|
||||||
action: undefined,
|
action: undefined,
|
||||||
command: {
|
command: {
|
||||||
|
@ -205,18 +457,42 @@ const modelRef = reactive({
|
||||||
message: {
|
message: {
|
||||||
properties: undefined,
|
properties: undefined,
|
||||||
functionId: undefined,
|
functionId: undefined,
|
||||||
inputs: []
|
inputs: [],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}],
|
},
|
||||||
propertyMappings: [{
|
],
|
||||||
|
propertyMappings: [
|
||||||
|
{
|
||||||
source: undefined,
|
source: undefined,
|
||||||
target: []
|
target: [],
|
||||||
}],
|
},
|
||||||
description: undefined
|
],
|
||||||
|
description: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const productList = ref<Record<string, any>[]>([]);
|
||||||
|
const typeList = ref<Record<string, any>[]>([]);
|
||||||
|
const command = ref([]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
const type = ref<'edit' | 'view'>('edit');
|
||||||
|
const actionActiveKey = ref<string[]>(['0']);
|
||||||
|
const propertyActiveKey = ref<string[]>(['0']);
|
||||||
|
|
||||||
|
const onPropertyCollChange = (_key: string[]) => {
|
||||||
|
propertyActiveKey.value = _key;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onActionCollChange = (_key: string[]) => {
|
||||||
|
actionActiveKey.value = _key;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
const addItem = () => {
|
const addItem = () => {
|
||||||
|
actionActiveKey.value.push(String(modelRef.actionMappings.length));
|
||||||
modelRef.actionMappings.push({
|
modelRef.actionMappings.push({
|
||||||
actionType: undefined,
|
actionType: undefined,
|
||||||
action: undefined,
|
action: undefined,
|
||||||
|
@ -225,125 +501,133 @@ const addItem = () => {
|
||||||
message: {
|
message: {
|
||||||
properties: undefined,
|
properties: undefined,
|
||||||
functionId: undefined,
|
functionId: undefined,
|
||||||
inputs: []
|
inputs: [],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const productList = ref<Record<string, any>[]>([])
|
|
||||||
const typeList = ref<Record<string, any>[]>([])
|
|
||||||
const command = ref([])
|
|
||||||
const loading = ref<boolean>(false)
|
|
||||||
const type = ref<'edit' | 'view'>('view')
|
|
||||||
|
|
||||||
const filterOption = (input: string, option: any) => {
|
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const delItem = (index: number) => {
|
const delItem = (index: number) => {
|
||||||
modelRef.actionMappings.splice(index, 1)
|
modelRef.actionMappings.splice(index, 1);
|
||||||
}
|
};
|
||||||
|
|
||||||
const addPropertyItem = () => {
|
const addPropertyItem = () => {
|
||||||
|
propertyActiveKey.value.push(String(modelRef.propertyMappings.length));
|
||||||
modelRef.propertyMappings.push({
|
modelRef.propertyMappings.push({
|
||||||
source: undefined,
|
source: undefined,
|
||||||
target: []
|
target: [],
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const delPropertyItem = (index: number) => {
|
const delPropertyItem = (index: number) => {
|
||||||
modelRef.propertyMappings.splice(index, 1)
|
modelRef.propertyMappings.splice(index, 1);
|
||||||
}
|
};
|
||||||
|
|
||||||
const productChange = (value: string) => {
|
const productChange = (value: string) => {
|
||||||
modelRef.propertyMappings = modelRef.propertyMappings.map(item => {
|
modelRef.propertyMappings = modelRef.propertyMappings.map((item) => {
|
||||||
return {source: item.source, target: []}
|
return { source: item.source, target: [] };
|
||||||
})
|
});
|
||||||
const item = productList.value.find(item => item.id === value)
|
const item = productList.value.find((item) => item.id === value);
|
||||||
if (item) {
|
if (item) {
|
||||||
modelRef.productName = item.name
|
modelRef.productName = item.name;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const typeChange = () => {
|
const typeChange = () => {
|
||||||
modelRef.propertyMappings = modelRef.propertyMappings.map(item => {
|
modelRef.propertyMappings = modelRef.propertyMappings.map((item) => {
|
||||||
return {source: undefined, target: item.target}
|
return { source: undefined, target: item.target };
|
||||||
})
|
});
|
||||||
modelRef.actionMappings = modelRef.actionMappings.map(item => {
|
modelRef.actionMappings = modelRef.actionMappings.map((item) => {
|
||||||
return {...item, action: undefined}
|
return { ...item, action: undefined };
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const findApplianceType = computed(() => {
|
const findApplianceType = computed(() => {
|
||||||
if(!modelRef.applianceType) return
|
if (!modelRef.applianceType) return;
|
||||||
return typeList.value.find(item => item.id === modelRef.applianceType)
|
return typeList.value.find((item) => item.id === modelRef.applianceType);
|
||||||
})
|
});
|
||||||
|
|
||||||
const findProductMetadata = computed(() => {
|
const findProductMetadata = computed(() => {
|
||||||
if(!modelRef.id) return
|
if (!modelRef.id) return;
|
||||||
const _product = productList.value?.find((item: any) => item.id === modelRef.id)
|
const _product = productList.value?.find(
|
||||||
return _product?.metadata && JSON.parse(_product.metadata || '{}')
|
(item: any) => item.id === modelRef.id,
|
||||||
})
|
);
|
||||||
|
return _product?.metadata && JSON.parse(_product.metadata || '{}');
|
||||||
|
});
|
||||||
|
|
||||||
// 查询产品列表
|
// 查询产品列表
|
||||||
const getProduct = async (id?: string) => {
|
const getProduct = async (id?: string) => {
|
||||||
const resp = await queryProductList(id)
|
const resp = await queryProductList(id);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
productList.value = (resp?.result as Record<string, any>[])
|
productList.value = resp?.result as Record<string, any>[];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getTypes = async () => {
|
const getTypes = async () => {
|
||||||
const resp = await queryTypes()
|
const resp = await queryTypes();
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
typeList.value = (resp?.result as Record<string, any>[])
|
typeList.value = resp?.result as Record<string, any>[];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getDuerOSProperties = (val: string) => {
|
const getDuerOSProperties = (val: string) => {
|
||||||
const arr = modelRef.propertyMappings.map(item => item?.source) || []
|
const arr = modelRef.propertyMappings.map((item) => item?.source) || [];
|
||||||
const checked = _.cloneDeep(arr)
|
const checked = _.cloneDeep(arr);
|
||||||
const _index = checked.findIndex(i => i === val)
|
const _index = checked.findIndex((i) => i === val);
|
||||||
// 去掉重复的
|
// 去掉重复的
|
||||||
checked.splice(_index, 1)
|
checked.splice(_index, 1);
|
||||||
const targetList = findApplianceType.value?.properties;
|
const targetList = findApplianceType.value?.properties;
|
||||||
const list = targetList?.filter((i: {id: string}) => !checked.includes(i?.id as any))
|
const list = targetList?.filter(
|
||||||
return list || []
|
(i: { id: string }) => !checked.includes(i?.id as any),
|
||||||
}
|
);
|
||||||
|
return list || [];
|
||||||
|
};
|
||||||
|
|
||||||
const getProductProperties = (val: string[]) => {
|
const getProductProperties = (val: string[]) => {
|
||||||
const items = modelRef.propertyMappings.map((item: {target: string[]}) => item?.target.map(j => j)) || []
|
const items =
|
||||||
const checked = _.flatMap(items)
|
modelRef.propertyMappings.map((item: { target: string[] }) =>
|
||||||
const _checked: any[] = []
|
item?.target.map((j) => j),
|
||||||
checked.map(_item => {
|
) || [];
|
||||||
|
const checked = _.flatMap(items);
|
||||||
|
const _checked: any[] = [];
|
||||||
|
checked.map((_item) => {
|
||||||
if (!val.includes(_item)) {
|
if (!val.includes(_item)) {
|
||||||
_checked.push(_item)
|
_checked.push(_item);
|
||||||
}
|
|
||||||
})
|
|
||||||
const sourceList = findProductMetadata.value?.properties
|
|
||||||
const list = sourceList?.filter((i: { id: string }) => !_checked.includes(i.id))
|
|
||||||
return list || []
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
const sourceList = findProductMetadata.value?.properties;
|
||||||
|
const list = sourceList?.filter(
|
||||||
|
(i: { id: string }) => !_checked.includes(i.id),
|
||||||
|
);
|
||||||
|
return list || [];
|
||||||
|
};
|
||||||
|
|
||||||
const getTypesActions = (val: string) => {
|
const getTypesActions = (val: string) => {
|
||||||
const items = modelRef.actionMappings.map((item) => item?.action) || []
|
const items = modelRef.actionMappings.map((item) => item?.action) || [];
|
||||||
const checked = _.cloneDeep(items)
|
const checked = _.cloneDeep(items);
|
||||||
const _index = checked.findIndex(i => i === val)
|
const _index = checked.findIndex((i) => i === val);
|
||||||
checked.splice(_index, 1)
|
checked.splice(_index, 1);
|
||||||
const actionsList = findApplianceType.value?.actions || []
|
const actionsList = findApplianceType.value?.actions || [];
|
||||||
const list = actionsList?.filter((i: { id: string, name: string }) => !checked.includes(i?.id as any))
|
const list = actionsList?.filter(
|
||||||
return list || []
|
(i: { id: string; name: string }) => !checked.includes(i?.id as any),
|
||||||
}
|
);
|
||||||
|
return list || [];
|
||||||
|
};
|
||||||
const saveBtn = async () => {
|
const saveBtn = async () => {
|
||||||
const tasks = []
|
const tasks: any[] = [];
|
||||||
for (let i = 0; i < command.value.length; i++) {
|
for (let i = 0; i < command.value.length; i++) {
|
||||||
const res = await (command.value[i] as any)?.saveBtn()
|
const res = await (command.value[i] as any)?.saveBtn()
|
||||||
tasks.push(res)
|
if(!res || (res?.errorFields && res.errorFields.length)) {
|
||||||
if(!res) break
|
actionActiveKey.value.push(String(i));
|
||||||
|
tasks.push(false);
|
||||||
|
} else {
|
||||||
|
tasks.push(res);
|
||||||
}
|
}
|
||||||
const data = await formRef.value.validate()
|
}
|
||||||
if(tasks.every(item => item) && data){
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(async (data: any) => {
|
||||||
|
if (tasks.every((item) => item) && data) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const resp = await savePatch(toRaw(modelRef));
|
const resp = await savePatch(toRaw(modelRef));
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -353,33 +637,46 @@ const saveBtn = async () => {
|
||||||
router.push('/iot/northbound/DuerOS/');
|
router.push('/iot/northbound/DuerOS/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
const _arr = err.errorFields.map((item: any) => item.name);
|
||||||
|
_arr.map((item: string | any[]) => {
|
||||||
|
if (item.length >= 3) {
|
||||||
|
if(item[0] === 'propertyMappings' && !propertyActiveKey.value.includes(item[1])){
|
||||||
|
propertyActiveKey.value.push(item[1]);
|
||||||
}
|
}
|
||||||
|
if(item[0] === 'actionMappings' && !actionActiveKey.value.includes(item[1])){
|
||||||
|
actionActiveKey.value.push(item[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
watch(
|
watch(
|
||||||
() => route.params?.id,
|
() => route.params?.id,
|
||||||
async (newId) => {
|
async (newId) => {
|
||||||
if (newId) {
|
if (newId) {
|
||||||
getProduct(newId as string)
|
getProduct(newId as string);
|
||||||
getTypes()
|
getTypes();
|
||||||
if (newId === ':id') return;
|
if (newId === ':id') return;
|
||||||
const resp = await detail(newId as string)
|
const resp = await detail(newId as string);
|
||||||
const _data: any = resp.result;
|
const _data: any = resp.result;
|
||||||
if (_data) {
|
if (_data) {
|
||||||
_data.applianceType = _data?.applianceType?.value;
|
_data.applianceType = _data?.applianceType?.value;
|
||||||
}
|
}
|
||||||
Object.assign(modelRef, _data)
|
Object.assign(modelRef, _data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{immediate: true, deep: true}
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.query.type,
|
() => route.query.type,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
type.value = newVal as 'edit' | 'view'
|
type.value = newVal as 'edit' | 'view';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{immediate: true, deep: true}
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
|
@ -1,6 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search :columns="columns" target="northbound-dueros" :params="params" />
|
<Search
|
||||||
|
:columns="columns"
|
||||||
|
target="northbound-dueros"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
<JTable
|
<JTable
|
||||||
ref="instanceRef"
|
ref="instanceRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -22,16 +26,12 @@
|
||||||
:statusText="slotProps.state?.text"
|
:statusText="slotProps.state?.text"
|
||||||
:statusNames="{
|
:statusNames="{
|
||||||
enabled: 'success',
|
enabled: 'success',
|
||||||
disabled: 'error'
|
disabled: 'error',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #img>
|
<template #img>
|
||||||
<slot name="img">
|
<slot name="img">
|
||||||
<img
|
<img :src="getImage('/cloud/dueros.png')" />
|
||||||
:src="
|
|
||||||
getImage('/cloud/dueros.png')
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
@ -43,9 +43,7 @@
|
||||||
</h3>
|
</h3>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<div class="card-item-content-text">
|
<div class="card-item-content-text">产品</div>
|
||||||
产品
|
|
||||||
</div>
|
|
||||||
<div>{{ slotProps?.productName }}</div>
|
<div>{{ slotProps?.productName }}</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
|
@ -145,12 +143,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { query, _undeploy, _deploy, _delete, queryProductList, queryTypes } from '@/api/northbound/dueros';
|
||||||
query,
|
|
||||||
_undeploy,
|
|
||||||
_deploy,
|
|
||||||
_delete
|
|
||||||
} from '@/api/northbound/dueros';
|
|
||||||
import type { ActionsType } from '@/components/Table/index.vue';
|
import type { ActionsType } from '@/components/Table/index.vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
@ -169,17 +162,48 @@ const columns = [
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '产品名称',
|
title: '产品名称',
|
||||||
dataIndex: 'productName',
|
dataIndex: 'productName',
|
||||||
key: 'productName',
|
key: 'productName',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryProductList().then((resp: any) => {
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '设备类型',
|
title: '设备类型',
|
||||||
dataIndex: 'applianceType',
|
dataIndex: 'applianceType',
|
||||||
key: 'applianceType',
|
key: 'applianceType',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
queryTypes().then((resp: any) => {
|
||||||
|
resolve(
|
||||||
|
resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
|
@ -191,6 +215,13 @@ const columns = [
|
||||||
dataIndex: 'state',
|
dataIndex: 'state',
|
||||||
key: 'state',
|
key: 'state',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 'enabled' },
|
||||||
|
{ label: '禁用', value: 'disabled' },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
|
@ -216,8 +247,8 @@ const handleView = (id: string) => {
|
||||||
router.push({
|
router.push({
|
||||||
path: '/iot/northbound/DuerOS/detail/' + id,
|
path: '/iot/northbound/DuerOS/detail/' + id,
|
||||||
query: {
|
query: {
|
||||||
type: 'view'
|
type: 'view',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,8 +280,8 @@ const getActions = (
|
||||||
router.push({
|
router.push({
|
||||||
path: '/iot/northbound/DuerOS/detail/' + data.id,
|
path: '/iot/northbound/DuerOS/detail/' + data.id,
|
||||||
query: {
|
query: {
|
||||||
type: 'edit'
|
type: 'edit',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -313,4 +344,8 @@ const getActions = (
|
||||||
return actions.filter((i: ActionsType) => i.key !== 'view');
|
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||||
return actions;
|
return actions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSearch = (_params: any) => {
|
||||||
|
params.value = _params;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
class="edit-dialog-container"
|
||||||
|
:title="dialog.title"
|
||||||
|
width="1050px"
|
||||||
|
@ok="dialog.handleOk"
|
||||||
|
:confirmLoading="dialog.loading.value"
|
||||||
|
cancelText="取消"
|
||||||
|
okText="确定"
|
||||||
|
v-model:visible="dialog.visible.value"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" :model="form.data" layout="vertical">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
name="name"
|
||||||
|
label="名称"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.name"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
name="typeId"
|
||||||
|
label="类型"
|
||||||
|
:rules="[{ required: true, message: '请选择类型' }]"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.data.typeId"
|
||||||
|
style="width: 120px"
|
||||||
|
:options="form.typeOptions"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24" v-if="form.data.typeId === 'rdb'">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'url']"
|
||||||
|
label="URL"
|
||||||
|
:rules="[{ required: true, message: '请输入URL' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.shareConfig.url"
|
||||||
|
placeholder="请输入r2bdc或者jdbc连接地址,示例:r2dbc:mysql://127.0.0.1:3306/test"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'adminUrl']"
|
||||||
|
label="管理地址"
|
||||||
|
:rules="[{ required: true, message: '请输入管理地址' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.shareConfig.adminUrl"
|
||||||
|
placeholder="请输入管理地址,示例:http://localhost:15672"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'addresses']"
|
||||||
|
label="链接地址"
|
||||||
|
:rules="[{ required: true, message: '请输入链接地址' }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.shareConfig.addresses"
|
||||||
|
placeholder="请输入链接地址,示例:localhost:5672"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'username']"
|
||||||
|
label="用户名"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入用户名' },
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.shareConfig.username"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'password']"
|
||||||
|
label="密码"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入密码' },
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="form.data.shareConfig.password"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24" v-if="form.data.typeId === 'rabbitmq'">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'virtualHost']"
|
||||||
|
label="虚拟域"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入虚拟域' },
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.shareConfig.virtualHost"
|
||||||
|
placeholder="请输入虚拟域"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24" v-if="form.data.typeId === 'rdb'">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
:name="['shareConfig', 'schema']"
|
||||||
|
label="schema"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入schema' },
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.shareConfig.schema"
|
||||||
|
placeholder="请输入schema"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item name="description" label="说明">
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="form.data.description"
|
||||||
|
placeholder="请输入说明"
|
||||||
|
:rows="3"
|
||||||
|
showCount
|
||||||
|
:maxlength="200"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getDataTypeDict_api } from '@/api/system/dataSource';
|
||||||
|
import type { dictItemType, optionItemType, sourceItemType } from '../typing';
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const dialog = {
|
||||||
|
title: '',
|
||||||
|
loading: ref<boolean>(false),
|
||||||
|
visible: ref<boolean>(false),
|
||||||
|
handleOk: () => {},
|
||||||
|
// 打开弹窗
|
||||||
|
changeVisible: (row: sourceItemType) => {
|
||||||
|
if (row.id) dialog.title = '编辑数据源';
|
||||||
|
else dialog.title = '新增数据源';
|
||||||
|
form.data = { ...row };
|
||||||
|
dialog.visible.value = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// 将打开弹窗的操作暴露给父组件
|
||||||
|
defineExpose({
|
||||||
|
openDialog: dialog.changeVisible,
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
data: {
|
||||||
|
shareConfig: {},
|
||||||
|
} as sourceItemType,
|
||||||
|
|
||||||
|
typeOptions: [] as optionItemType[],
|
||||||
|
|
||||||
|
getTypeOption: () => {
|
||||||
|
getDataTypeDict_api().then((resp: any) => {
|
||||||
|
const result = resp.result as dictItemType[];
|
||||||
|
form.typeOptions = result.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
form.getTypeOption();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,304 @@
|
||||||
|
<template>
|
||||||
|
<div class="data-source-container">
|
||||||
|
<Search :columns="query.columns" @search="query.search" />
|
||||||
|
|
||||||
|
<JTable
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="table.columns"
|
||||||
|
:request="getDataSourceList_api"
|
||||||
|
model="TABLE"
|
||||||
|
:params="query.params.value"
|
||||||
|
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
||||||
|
>
|
||||||
|
<template #headerTitle>
|
||||||
|
<PermissionButton
|
||||||
|
type="primary"
|
||||||
|
:uhasPermission="`${permission}:add`"
|
||||||
|
@click="table.openDialog({})"
|
||||||
|
>
|
||||||
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:text="slotProps.state?.text"
|
||||||
|
:statusNames="{
|
||||||
|
enabled: 'success',
|
||||||
|
disabled: 'error',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</BadgeStatus>
|
||||||
|
</template>
|
||||||
|
<template #typeId="slotProps">
|
||||||
|
{{
|
||||||
|
(table.typeOptions.value.length &&
|
||||||
|
table.getTypeLabel(slotProps.typeId)) ||
|
||||||
|
''
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space :size="16">
|
||||||
|
<PermissionButton
|
||||||
|
:uhasPermission="`${permission}:update`"
|
||||||
|
type="link"
|
||||||
|
:tooltip="{
|
||||||
|
title: '编辑',
|
||||||
|
}"
|
||||||
|
@click="table.openDialog(slotProps)"
|
||||||
|
>
|
||||||
|
<AIcon type="EditOutlined" />
|
||||||
|
</PermissionButton>
|
||||||
|
<PermissionButton
|
||||||
|
:uhasPermission="`${permission}:manage`"
|
||||||
|
type="link"
|
||||||
|
:tooltip="{
|
||||||
|
title:
|
||||||
|
slotProps?.typeId === 'rabbitmq'
|
||||||
|
? '暂不支持管理功能'
|
||||||
|
: table.getRowStatus(slotProps)
|
||||||
|
? '管理'
|
||||||
|
: '请先启用数据源',
|
||||||
|
}"
|
||||||
|
@click="
|
||||||
|
() =>
|
||||||
|
router.push(
|
||||||
|
`/system/DataSource/Management?id=${slotProps.id}`,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<AIcon type="icon-ziyuankuguanli" />
|
||||||
|
</PermissionButton>
|
||||||
|
<PermissionButton
|
||||||
|
:uhasPermission="`${permission}:action`"
|
||||||
|
type="link"
|
||||||
|
:popConfirm="{
|
||||||
|
title: `确定要${
|
||||||
|
table.getRowStatus(slotProps) ? '禁用' : '启用'
|
||||||
|
}吗?`,
|
||||||
|
onConfirm: () => table.clickChangeStatus(slotProps),
|
||||||
|
}"
|
||||||
|
:tooltip="{
|
||||||
|
title: table.getRowStatus(slotProps)
|
||||||
|
? '禁用'
|
||||||
|
: '启用',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
:type="
|
||||||
|
table.getRowStatus(slotProps)
|
||||||
|
? 'StopOutlined'
|
||||||
|
: 'PlayCircleOutlined'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<!-- <AIcon type="PlayCircleOutlined" /> -->
|
||||||
|
</PermissionButton>
|
||||||
|
|
||||||
|
<PermissionButton
|
||||||
|
:uhasPermission="`${permission}:delete`"
|
||||||
|
type="link"
|
||||||
|
:tooltip="{
|
||||||
|
title: table.getRowStatus(slotProps)
|
||||||
|
? '请先禁用,再删除'
|
||||||
|
: '删除',
|
||||||
|
}"
|
||||||
|
:popConfirm="{
|
||||||
|
title: `确认删除`,
|
||||||
|
onConfirm: () => table.clickDel(slotProps),
|
||||||
|
}"
|
||||||
|
:disabled="table.getRowStatus(slotProps)"
|
||||||
|
>
|
||||||
|
<AIcon type="DeleteOutlined" />
|
||||||
|
</PermissionButton>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
|
||||||
|
<div class="dialogs">
|
||||||
|
<EditDialog ref="editDialogRef" @confirm="table.refresh" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="DataSource">
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
|
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||||
|
import EditDialog from './components/EditDialog.vue';
|
||||||
|
|
||||||
|
import type { dictItemType, optionItemType, sourceItemType } from './typing';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getDataSourceList_api,
|
||||||
|
getDataTypeDict_api,
|
||||||
|
changeStatus_api,
|
||||||
|
} from '@/api/system/dataSource';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const permission = 'system/Relationship';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'typeId',
|
||||||
|
key: 'typeId',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
if (table.typeOptions.value.length > 0)
|
||||||
|
return resolve(table.typeOptions.value);
|
||||||
|
getDataTypeDict_api().then((resp: any) => {
|
||||||
|
const result = resp.result as dictItemType[];
|
||||||
|
resolve(
|
||||||
|
result.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '正常',
|
||||||
|
value: 'enabled',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已禁用',
|
||||||
|
value: 'disabled',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
params: ref({}),
|
||||||
|
search: (params: object) => {
|
||||||
|
query.params.value = params;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const editDialogRef = ref(); // 新增弹窗实例
|
||||||
|
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||||
|
const table = {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: '250px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'typeId',
|
||||||
|
key: 'typeId',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
scopedSlots: true,
|
||||||
|
width: '120px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
scopedSlots: true,
|
||||||
|
width: '200px',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
typeOptions: ref<optionItemType[]>([]),
|
||||||
|
|
||||||
|
getTypeOption: () => {
|
||||||
|
getDataTypeDict_api().then((resp: any) => {
|
||||||
|
const result = resp.result as dictItemType[];
|
||||||
|
table.typeOptions.value = result.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTypeLabel: (val: string): string => {
|
||||||
|
const options = table.typeOptions.value;
|
||||||
|
if (options.length < 1 || val === '') return '';
|
||||||
|
return options.find((item) => item.value === val)?.label || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowStatus: (row: sourceItemType) => {
|
||||||
|
return row.state?.value === 'enabled';
|
||||||
|
},
|
||||||
|
// 打开编辑弹窗
|
||||||
|
openDialog: (row: sourceItemType | {}) => {
|
||||||
|
editDialogRef.value.openDialog({shareConfig:{},...row});
|
||||||
|
},
|
||||||
|
// 删除
|
||||||
|
clickDel: (row: sourceItemType) => {
|
||||||
|
// delRelation_api(row.id).then((resp: any) => {
|
||||||
|
// if (resp.status === 200) {
|
||||||
|
// tableRef.value?.reload();
|
||||||
|
// message.success('操作成功!');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
clickChangeStatus: (row: sourceItemType) => {
|
||||||
|
const status = row.state.value === 'enabled' ? '_disable' : '_enable';
|
||||||
|
|
||||||
|
changeStatus_api(row.id as string, status).then(() => {
|
||||||
|
message.success('操作成功');
|
||||||
|
table.refresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 刷新列表
|
||||||
|
refresh: () => {
|
||||||
|
tableRef.value.reload();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
table.getTypeOption();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.data-source-container {
|
||||||
|
padding: 24px;
|
||||||
|
:deep(.ant-table-cell) {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,24 @@
|
||||||
|
export type dictItemType = {
|
||||||
|
id: string,
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
export type optionItemType = {
|
||||||
|
label: string,
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
export type sourceItemType = {
|
||||||
|
id?: string,
|
||||||
|
name: string,
|
||||||
|
state: { text: string, value: "enabled" | 'disabled' },
|
||||||
|
typeId: string,
|
||||||
|
shareConfig:{
|
||||||
|
url:string,
|
||||||
|
adminUrl:string,
|
||||||
|
addresses:string,
|
||||||
|
username:string,
|
||||||
|
password:string,
|
||||||
|
virtualHost:string,
|
||||||
|
schema:string
|
||||||
|
}
|
||||||
|
description: string
|
||||||
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { dictType, optionsType } from '../typing.d.ts';
|
import type { dictType, optionsType } from '../typing';
|
||||||
import { updatePermission_api } from '@/api/system/department';
|
import { updatePermission_api } from '@/api/system/department';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,7 @@ import {
|
||||||
} from '@/api/system/department';
|
} from '@/api/system/department';
|
||||||
import { intersection } from 'lodash-es';
|
import { intersection } from 'lodash-es';
|
||||||
|
|
||||||
import { dictType } from '../typing.d.ts';
|
import type { dictType } from '../typing';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
const permission = 'system/Department';
|
const permission = 'system/Department';
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
type dictType = {
|
export type dictType = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
type optionsType = {
|
export type optionsType = {
|
||||||
label: string,
|
label: string,
|
||||||
value: string;
|
value: string;
|
||||||
disabled?:boolean
|
disabled?:boolean
|
||||||
|
|
|
@ -185,16 +185,18 @@
|
||||||
:first-width="3"
|
:first-width="3"
|
||||||
max-height="350px"
|
max-height="350px"
|
||||||
v-model:value="form.data.permissions"
|
v-model:value="form.data.permissions"
|
||||||
|
:key="form.data.id || ''"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<a-button
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:uhasPermission="`${permission}:update`"
|
||||||
@click="form.clickSave"
|
@click="form.clickSave"
|
||||||
v-loading="form.saveLoading"
|
|
||||||
>保存</a-button
|
|
||||||
>
|
>
|
||||||
|
保存
|
||||||
|
</PermissionButton>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 弹窗 -->
|
<!-- 弹窗 -->
|
||||||
|
@ -205,6 +207,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
QuestionCircleFilled,
|
QuestionCircleFilled,
|
||||||
|
@ -222,6 +225,7 @@ import {
|
||||||
addMenuInfo_api,
|
addMenuInfo_api,
|
||||||
} from '@/api/system/menu';
|
} from '@/api/system/menu';
|
||||||
|
|
||||||
|
const permission = 'system/Menu';
|
||||||
// 路由
|
// 路由
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -330,6 +334,7 @@ const dialog = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type formType = {
|
type formType = {
|
||||||
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
code: string;
|
code: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
|
|
@ -8,49 +8,43 @@
|
||||||
noPagination
|
noPagination
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
style="margin-right: 10px"
|
:uhasPermission="`${permission}:update`"
|
||||||
@click="() => dialog.openDialog('新增')"
|
@click="dialog.openDialog('新增')"
|
||||||
><plus-outlined />新增</a-button
|
|
||||||
>
|
>
|
||||||
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip>
|
<PermissionButton
|
||||||
<template #title>编辑</template>
|
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
type="link"
|
||||||
@click="() => dialog.openDialog('编辑', slotProps)"
|
:uhasPermission="`${permission}:update`"
|
||||||
|
:tooltip="{ title: '编辑' }"
|
||||||
|
@click="dialog.openDialog('编辑', slotProps)"
|
||||||
>
|
>
|
||||||
<edit-outlined />
|
<AIcon type="EditOutlined" />
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
<PermissionButton
|
||||||
<a-tooltip>
|
|
||||||
<template #title>查看</template>
|
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
type="link"
|
||||||
@click="() => dialog.openDialog('查看', slotProps)"
|
:uhasPermission="true"
|
||||||
|
:tooltip="{ title: '查看' }"
|
||||||
|
@click="dialog.openDialog('查看', slotProps)"
|
||||||
>
|
>
|
||||||
<search-outlined />
|
<AIcon type="SearchOutlined" />
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
<PermissionButton
|
||||||
|
type="link"
|
||||||
<a-popconfirm
|
:uhasPermission="`${permission}:update`"
|
||||||
title="确认删除"
|
:tooltip="{ title: '删除' }"
|
||||||
ok-text="确定"
|
:popConfirm="{
|
||||||
cancel-text="取消"
|
title: `确认删除`,
|
||||||
@confirm="table.clickDel(slotProps)"
|
onConfirm: () => table.clickDel(slotProps),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<a-tooltip>
|
<AIcon type="DeleteOutlined" />
|
||||||
<template #title>删除</template>
|
</PermissionButton>
|
||||||
<a-button style="padding: 0" type="link">
|
|
||||||
<delete-outlined />
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
@ -66,17 +60,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
EditOutlined,
|
|
||||||
SearchOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import ButtonAddDialog from '../components/ButtonAddDialog.vue';
|
import ButtonAddDialog from '../components/ButtonAddDialog.vue';
|
||||||
|
|
||||||
import { getMenuInfo_api, saveMenuInfo_api } from '@/api/system/menu';
|
import { getMenuInfo_api, saveMenuInfo_api } from '@/api/system/menu';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const permission = 'system/Menu';
|
||||||
// 路由
|
// 路由
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const routeParams = {
|
const routeParams = {
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
max-height="350px"
|
max-height="350px"
|
||||||
v-model:value="form.data.permissions"
|
v-model:value="form.data.permissions"
|
||||||
:disabled="form.mode === '查看'"
|
:disabled="form.mode === '查看'"
|
||||||
|
:key="form.data.id || ''"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="说明" name="describe">
|
<a-form-item label="说明" name="describe">
|
||||||
|
@ -91,7 +92,8 @@ const dialog = reactive({
|
||||||
visible: false,
|
visible: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
handleOk: () => {
|
handleOk: () => {
|
||||||
props.menuInfo.id && formRef.value &&
|
props.menuInfo.id &&
|
||||||
|
formRef.value &&
|
||||||
formRef.value
|
formRef.value
|
||||||
.validate()
|
.validate()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -112,7 +114,7 @@ const dialog = reactive({
|
||||||
saveMenuInfo_api(params)
|
saveMenuInfo_api(params)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
dialog.changeVisible();
|
dialog.changeVisible();
|
||||||
message.success('操作成功')
|
message.success('操作成功');
|
||||||
emits('confirm');
|
emits('confirm');
|
||||||
})
|
})
|
||||||
.finally(() => (dialog.loading = false));
|
.finally(() => (dialog.loading = false));
|
||||||
|
|
|
@ -47,8 +47,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { exportPermission_api } from '@/api/system/permission';
|
import { exportPermission_api } from '@/api/system/permission';
|
||||||
import { Form } from 'ant-design-vue';
|
import { Form } from 'ant-design-vue';
|
||||||
Form.useInjectFormItemContext()
|
Form.useInjectFormItemContext();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
key: string;
|
||||||
value: any[];
|
value: any[];
|
||||||
firstWidth: number;
|
firstWidth: number;
|
||||||
maxHeight: string;
|
maxHeight: string;
|
||||||
|
@ -58,7 +60,6 @@ const emits = defineEmits(['update:value']);
|
||||||
const searchValue = ref<string>('');
|
const searchValue = ref<string>('');
|
||||||
|
|
||||||
const search = reactive({
|
const search = reactive({
|
||||||
value: '',
|
|
||||||
searchTimer: null as null | number,
|
searchTimer: null as null | number,
|
||||||
search: () => {
|
search: () => {
|
||||||
if (search.searchTimer) {
|
if (search.searchTimer) {
|
||||||
|
@ -72,17 +73,34 @@ const search = reactive({
|
||||||
});
|
});
|
||||||
const permission = reactive({
|
const permission = reactive({
|
||||||
list: [] as permissionType[],
|
list: [] as permissionType[],
|
||||||
|
sourceList: [] as any[],
|
||||||
|
|
||||||
|
init: () => {
|
||||||
|
permission.getList();
|
||||||
|
watch(
|
||||||
|
() => props.key,
|
||||||
|
() => {
|
||||||
|
nextTick(() => {
|
||||||
|
permission.list = permission.makeList(
|
||||||
|
props.value,
|
||||||
|
permission.sourceList,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
// 获取权限列表
|
// 获取权限列表
|
||||||
getList: () => {
|
getList: () => {
|
||||||
const params: paramsType = {
|
const params: paramsType = {
|
||||||
paging: false,
|
paging: false,
|
||||||
};
|
};
|
||||||
if (search.value) {
|
if (searchValue.value) {
|
||||||
params.terms = [
|
params.terms = [
|
||||||
{ column: 'name$like', value: `%${search.value}%` },
|
{ column: 'name$like', value: `%${searchValue.value}%` },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
exportPermission_api(params).then((resp) => {
|
exportPermission_api(params).then((resp) => {
|
||||||
|
permission.sourceList = [...(resp.result as any[])];
|
||||||
permission.list = permission.makeList(
|
permission.list = permission.makeList(
|
||||||
props.value,
|
props.value,
|
||||||
resp.result as any[],
|
resp.result as any[],
|
||||||
|
@ -91,7 +109,7 @@ const permission = reactive({
|
||||||
},
|
},
|
||||||
// 全选/取消全选
|
// 全选/取消全选
|
||||||
selectAllOpions: (row: permissionType) => {
|
selectAllOpions: (row: permissionType) => {
|
||||||
row.indeterminate = false
|
row.indeterminate = false;
|
||||||
const newValue = props.value.filter(
|
const newValue = props.value.filter(
|
||||||
(item) => item.permission !== row.id,
|
(item) => item.permission !== row.id,
|
||||||
);
|
);
|
||||||
|
@ -162,8 +180,8 @@ const permission = reactive({
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
permission.init();
|
||||||
|
|
||||||
permission.getList();
|
|
||||||
|
|
||||||
type permissionType = {
|
type permissionType = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -10,13 +10,24 @@
|
||||||
:params="query.params"
|
:params="query.params"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:uhasPermission="`${permission}:add`"
|
||||||
@click="table.toDetails({})"
|
@click="table.toDetails({})"
|
||||||
style="margin-right: 10px"
|
|
||||||
><plus-outlined />新增</a-button
|
|
||||||
>
|
>
|
||||||
<a-button @click="router.push('/system/Menu/Setting')">菜单实例</a-button>
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
|
<a-button
|
||||||
|
style="margin-left: 12px"
|
||||||
|
@click="router.push('/system/Menu/Setting')"
|
||||||
|
>菜单配置</a-button
|
||||||
|
>
|
||||||
|
<!-- <PermissionButton
|
||||||
|
:uhasPermission="true"
|
||||||
|
@click="router.push('/system/Menu/Setting')"
|
||||||
|
>
|
||||||
|
菜单配置
|
||||||
|
</PermissionButton> -->
|
||||||
</template>
|
</template>
|
||||||
<template #createTime="slotProps">
|
<template #createTime="slotProps">
|
||||||
{{ moment(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
{{ moment(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
|
@ -33,30 +44,26 @@
|
||||||
<search-outlined />
|
<search-outlined />
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
|
||||||
<template #title>新增子菜单</template>
|
<PermissionButton
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
type="link"
|
||||||
|
:uhasPermission="`${permission}:add`"
|
||||||
|
:tooltip="{ title: '新增子菜单' }"
|
||||||
@click="table.addChildren(slotProps)"
|
@click="table.addChildren(slotProps)"
|
||||||
>
|
>
|
||||||
<plus-circle-outlined />
|
<AIcon type="PlusCircleOutlined" />
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
<PermissionButton
|
||||||
|
type="link"
|
||||||
<a-popconfirm
|
:uhasPermission="`${permission}:delete`"
|
||||||
title="是否删除该菜单"
|
:tooltip="{ title: '删除' }"
|
||||||
ok-text="确定"
|
:popConfirm="{
|
||||||
cancel-text="取消"
|
title: `是否删除该菜单`,
|
||||||
@confirm="table.clickDel(slotProps)"
|
onConfirm: () => table.clickDel(slotProps),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<a-tooltip>
|
<AIcon type="DeleteOutlined" />
|
||||||
<template #title>删除</template>
|
</PermissionButton>
|
||||||
<a-button style="padding: 0" type="link">
|
|
||||||
<delete-outlined />
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
@ -64,16 +71,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
|
|
||||||
import { getMenuTree_api, delMenuInfo_api } from '@/api/system/menu';
|
import { getMenuTree_api, delMenuInfo_api } from '@/api/system/menu';
|
||||||
import {
|
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||||
SearchOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
PlusCircleOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const permission = 'system/Menu';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// 筛选
|
// 筛选
|
||||||
|
@ -264,5 +270,11 @@ const table = reactive({
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.menu-container {
|
.menu-container {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
|
||||||
|
:deep(.ant-table-cell) {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,12 +11,13 @@
|
||||||
:defaultParams="{ sorts: [{ name: 'id', order: 'asc' }] }"
|
:defaultParams="{ sorts: [{ name: 'id', order: 'asc' }] }"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:uhasPermission="`${permission}:add`"
|
||||||
@click="table.openDialog(undefined)"
|
@click="table.openDialog(undefined)"
|
||||||
style="margin-right: 10px"
|
|
||||||
><plus-outlined />新增</a-button
|
|
||||||
>
|
>
|
||||||
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
<a-dropdown trigger="hover">
|
<a-dropdown trigger="hover">
|
||||||
<a-button>批量操作</a-button>
|
<a-button>批量操作</a-button>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
|
@ -28,19 +29,27 @@
|
||||||
accept=".json"
|
accept=".json"
|
||||||
:showUploadList="false"
|
:showUploadList="false"
|
||||||
:before-upload="table.clickImport"
|
:before-upload="table.clickImport"
|
||||||
|
:disabled="
|
||||||
|
!hasPermission(`${permission}:import`)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<a-button>导入</a-button>
|
<PermissionButton
|
||||||
|
:hasPermission="`${permission}:import`"
|
||||||
|
>
|
||||||
|
导入
|
||||||
|
</PermissionButton>
|
||||||
</a-upload>
|
</a-upload>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item>
|
<a-menu-item>
|
||||||
<a-popconfirm
|
<PermissionButton
|
||||||
title="确认导出?"
|
:uhasPermission="`${permission}:export`"
|
||||||
ok-text="确定"
|
:popConfirm="{
|
||||||
cancel-text="取消"
|
title: `确认导出?`,
|
||||||
@confirm="table.clickExport"
|
onConfirm: () => table.clickExport(),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<a-button>导出</a-button>
|
导出
|
||||||
</a-popconfirm>
|
</PermissionButton>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</template>
|
</template>
|
||||||
|
@ -51,63 +60,53 @@
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip>
|
<PermissionButton
|
||||||
<template #title>编辑</template>
|
:uhasPermission="`${permission}:update`"
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
type="link"
|
||||||
|
:tooltip="{
|
||||||
|
title: '编辑',
|
||||||
|
}"
|
||||||
@click="table.openDialog(slotProps)"
|
@click="table.openDialog(slotProps)"
|
||||||
>
|
>
|
||||||
<edit-outlined />
|
<AIcon type="EditOutlined" />
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
|
||||||
|
|
||||||
<a-popconfirm
|
<PermissionButton
|
||||||
:title="`确定要${
|
:uhasPermission="`${permission}:action`"
|
||||||
|
type="link"
|
||||||
|
:popConfirm="{
|
||||||
|
title: `确定要${
|
||||||
slotProps.status ? '禁用' : '启用'
|
slotProps.status ? '禁用' : '启用'
|
||||||
}吗?`"
|
}吗?`,
|
||||||
ok-text="确定"
|
onConfirm: () => table.changeStatus(slotProps),
|
||||||
cancel-text="取消"
|
}"
|
||||||
@confirm="table.changeStatus(slotProps)"
|
:tooltip="{ title: slotProps.status ? '禁用' : '启用' }"
|
||||||
>
|
>
|
||||||
<a-tooltip>
|
<AIcon
|
||||||
<template #title>{{
|
:type="
|
||||||
slotProps.status ? '禁用' : '启用'
|
slotProps.status
|
||||||
}}</template>
|
? 'StopOutlined'
|
||||||
<a-button style="padding: 0" type="link">
|
: 'PlayCircleOutlined '
|
||||||
<stop-outlined v-if="slotProps.status" />
|
"
|
||||||
<play-circle-outlined v-else />
|
/>
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
|
||||||
</a-popconfirm>
|
|
||||||
|
|
||||||
<a-popconfirm
|
<PermissionButton
|
||||||
title="确认删除"
|
:uhasPermission="`${permission}:delete`"
|
||||||
ok-text="确定"
|
type="link"
|
||||||
cancel-text="取消"
|
:tooltip="{
|
||||||
@confirm="table.clickDel(slotProps)"
|
title: slotProps.status
|
||||||
|
? '请先禁用,再删除'
|
||||||
|
: '删除',
|
||||||
|
}"
|
||||||
|
:popConfirm="{
|
||||||
|
title: `确认删除`,
|
||||||
|
onConfirm: () => table.clickDel(slotProps),
|
||||||
|
}"
|
||||||
:disabled="slotProps.status"
|
:disabled="slotProps.status"
|
||||||
>
|
>
|
||||||
<a-tooltip>
|
<AIcon type="DeleteOutlined" />
|
||||||
<template #title>{{
|
</PermissionButton>
|
||||||
systemPermission('delete')
|
|
||||||
? slotProps.status
|
|
||||||
? '请先禁用,再删除'
|
|
||||||
: '删除'
|
|
||||||
: '暂无权限,请联系管理员'
|
|
||||||
}}</template>
|
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
|
||||||
:disabled="
|
|
||||||
!systemPermission('delete') ||
|
|
||||||
slotProps.status
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<delete-outlined />
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
@ -119,16 +118,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
import EditDialog from './components/EditDialog.vue';
|
import EditDialog from './components/EditDialog.vue';
|
||||||
import StatusLabel from './components/StatusLabel.vue';
|
import StatusLabel from './components/StatusLabel.vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import {
|
|
||||||
EditOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
StopOutlined,
|
|
||||||
PlayCircleOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import {
|
import {
|
||||||
getPermission_api,
|
getPermission_api,
|
||||||
editPermission_api,
|
editPermission_api,
|
||||||
|
@ -138,13 +131,11 @@ import {
|
||||||
import { downloadObject } from '@/utils/utils';
|
import { downloadObject } from '@/utils/utils';
|
||||||
import { usePermissionStore } from '@/store/permission';
|
import { usePermissionStore } from '@/store/permission';
|
||||||
|
|
||||||
|
const permission = 'system/Permission';
|
||||||
|
const hasPermission = usePermissionStore().hasPermission;
|
||||||
|
|
||||||
const editDialogRef = ref(); // 新增弹窗实例
|
const editDialogRef = ref(); // 新增弹窗实例
|
||||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||||
|
|
||||||
// 按钮权限控制
|
|
||||||
const hasPermission = usePermissionStore().hasPermission;
|
|
||||||
const systemPermission = (code: string) =>
|
|
||||||
hasPermission('system/Permission:${code}');
|
|
||||||
// 筛选
|
// 筛选
|
||||||
const query = reactive({
|
const query = reactive({
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -223,12 +214,7 @@ const table = reactive({
|
||||||
tableData: [],
|
tableData: [],
|
||||||
// 打开编辑弹窗
|
// 打开编辑弹窗
|
||||||
openDialog: (row: object | undefined = {}) => {
|
openDialog: (row: object | undefined = {}) => {
|
||||||
let permissionCode = '';
|
|
||||||
if (Object.keys(row).length < 1) permissionCode = 'add';
|
|
||||||
else permissionCode = 'update';
|
|
||||||
if (systemPermission(permissionCode))
|
|
||||||
editDialogRef.value.openDialog(true, row);
|
editDialogRef.value.openDialog(true, row);
|
||||||
else message.warn('暂无权限,请联系管理员');
|
|
||||||
},
|
},
|
||||||
// 导入数据
|
// 导入数据
|
||||||
clickImport: (file: File) => {
|
clickImport: (file: File) => {
|
||||||
|
@ -268,8 +254,6 @@ const table = reactive({
|
||||||
},
|
},
|
||||||
// 修改状态
|
// 修改状态
|
||||||
changeStatus: (row: any) => {
|
changeStatus: (row: any) => {
|
||||||
if (!systemPermission('action'))
|
|
||||||
return message.warn('暂无权限,请联系管理员');
|
|
||||||
const params = {
|
const params = {
|
||||||
...row,
|
...row,
|
||||||
status: row.status ? 0 : 1,
|
status: row.status ? 0 : 1,
|
||||||
|
@ -295,4 +279,18 @@ const table = reactive({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped>
|
||||||
|
.permission-container {
|
||||||
|
padding: 24px;
|
||||||
|
|
||||||
|
.ant-dropdown-trigger {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-cell) {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -7,42 +7,43 @@
|
||||||
:columns="table.columns"
|
:columns="table.columns"
|
||||||
:request="getRelationshipList_api"
|
:request="getRelationshipList_api"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:params="query.params"
|
:params="query.params.value"
|
||||||
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:uhasPermission="`${permission}:add`"
|
||||||
@click="table.openDialog(undefined)"
|
@click="table.openDialog(undefined)"
|
||||||
style="margin-right: 10px"
|
|
||||||
><plus-outlined />新增</a-button
|
|
||||||
>
|
>
|
||||||
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip>
|
<PermissionButton
|
||||||
<template #title>编辑</template>
|
:uhasPermission="`${permission}:update`"
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
type="link"
|
||||||
|
:tooltip="{
|
||||||
|
title: '编辑',
|
||||||
|
}"
|
||||||
@click="table.openDialog(slotProps)"
|
@click="table.openDialog(slotProps)"
|
||||||
>
|
>
|
||||||
<edit-outlined />
|
<AIcon type="EditOutlined" />
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
|
||||||
<a-popconfirm
|
<PermissionButton
|
||||||
title="确认删除"
|
:uhasPermission="`${permission}:delete`"
|
||||||
ok-text="确定"
|
type="link"
|
||||||
cancel-text="取消"
|
:tooltip="{ title: '删除' }"
|
||||||
@confirm="table.clickDel(slotProps)"
|
:popConfirm="{
|
||||||
|
title: `确认删除`,
|
||||||
|
onConfirm: () => table.clickDel(slotProps),
|
||||||
|
}"
|
||||||
|
:disabled="slotProps.status"
|
||||||
>
|
>
|
||||||
<a-tooltip>
|
<AIcon type="DeleteOutlined" />
|
||||||
<template #title>删除</template>
|
</PermissionButton>
|
||||||
<a-button style="padding: 0" type="link">
|
|
||||||
<delete-outlined />
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
@ -52,14 +53,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="Relationship">
|
<script setup lang="ts" name="Relationship">
|
||||||
import { getRelationshipList_api, delRelation_api } from '@/api/system/relationship';
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
|
import {
|
||||||
|
getRelationshipList_api,
|
||||||
|
delRelation_api,
|
||||||
|
} from '@/api/system/relationship';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import EditDialog from './components/EditDialog.vue';
|
import EditDialog from './components/EditDialog.vue';
|
||||||
import {
|
|
||||||
EditOutlined,
|
const permission = 'system/Relationship';
|
||||||
DeleteOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
const query = {
|
const query = {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
|
@ -119,9 +122,9 @@ const query = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
params: {},
|
params: ref({}),
|
||||||
search: (params: object) => {
|
search: (params: object) => {
|
||||||
query.params = params;
|
query.params.value = params;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,7 +161,7 @@ const table = {
|
||||||
],
|
],
|
||||||
// 打开编辑弹窗
|
// 打开编辑弹窗
|
||||||
openDialog: (row: object | undefined = {}) => {
|
openDialog: (row: object | undefined = {}) => {
|
||||||
editDialogRef.value.openDialog(true, row)
|
editDialogRef.value.openDialog(true, row);
|
||||||
},
|
},
|
||||||
// 删除
|
// 删除
|
||||||
clickDel: (row: any) => {
|
clickDel: (row: any) => {
|
||||||
|
@ -179,5 +182,10 @@ const table = {
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.relationship-container {
|
.relationship-container {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
:deep(.ant-table-cell) {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue