Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
c5018e631a
|
|
@ -2,36 +2,36 @@
|
|||
<div class="indicator-box">
|
||||
<template v-if="['int', 'long', 'double', 'float'].includes(type)">
|
||||
<template v-if="value.range">
|
||||
<a-input-number v-model:value="value.value[0]" :max="value.value[1]" size="small"
|
||||
style="width: 100%;"></a-input-number>
|
||||
<j-input-number v-model:value="value.value[0]" :max="value.value[1]" size="small"
|
||||
style="width: 100%;"></j-input-number>
|
||||
~
|
||||
<a-input-number v-model:value="value.value[1]" :min="value.value[0]" size="small"
|
||||
style="width: 100%;"></a-input-number>
|
||||
<j-input-number v-model:value="value.value[1]" :min="value.value[0]" size="small"
|
||||
style="width: 100%;"></j-input-number>
|
||||
</template>
|
||||
<a-input-number v-else v-model:value="value.value" size="small" style="width: 100%;"></a-input-number>
|
||||
<j-input-number v-else v-model:value="value.value" size="small" style="width: 100%;"></j-input-number>
|
||||
</template>
|
||||
<template v-else-if="type === 'date'">
|
||||
<a-range-picker v-if="value.range" show-time v-model:value="value.value" size="small" />
|
||||
<a-date-picker v-else show-time v-model:value="value.value" size="small" />
|
||||
<j-range-picker v-if="value.range" show-time v-model:value="value.value" size="small" />
|
||||
<j-date-picker v-else show-time v-model:value="value.value" size="small" />
|
||||
</template>
|
||||
<template v-else-if="type === 'boolean'">
|
||||
<a-select v-model:value="value.value[0]" :options="list" size="small" placeholder="请选择"></a-select>
|
||||
<j-select v-model:value="value.value[0]" :options="list" size="small" placeholder="请选择"></j-select>
|
||||
</template>
|
||||
<template v-else-if="type === 'string'">
|
||||
<a-input v-model:value="value.value" size="small" placeholder="请输入"></a-input>
|
||||
<j-input v-model:value="value.value" size="small" placeholder="请输入"></j-input>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="value.range">
|
||||
<a-input v-model:value="value.value[0]" :max="value.value[1]" size="small" placeholder="请输入"></a-input>
|
||||
<j-input v-model:value="value.value[0]" :max="value.value[1]" size="small" placeholder="请输入"></j-input>
|
||||
~
|
||||
<a-input v-model:value="value.value[1]" :min="value.value[0]" size="small" placeholder="请输入"></a-input>
|
||||
<j-input v-model:value="value.value[1]" :min="value.value[0]" size="small" placeholder="请输入"></j-input>
|
||||
</template>
|
||||
<a-input-number v-else v-model:value="value.value" size="small" placeholder="请输入"></a-input-number>
|
||||
<j-input-number v-else v-model:value="value.value" size="small" placeholder="请输入"></j-input-number>
|
||||
</template>
|
||||
<div v-if="type !== 'boolean' && type !== 'string'">
|
||||
<a-checkbox style="min-width: 60px; margin-left: 5px;" v-model:checked="value.range" @change="changeChecked">
|
||||
<j-checkbox style="min-width: 60px; margin-left: 5px;" v-model:checked="value.range" @change="changeChecked">
|
||||
范围
|
||||
</a-checkbox>
|
||||
</j-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-popover :visible="visible" placement="left">
|
||||
<j-popover :visible="visible" placement="left">
|
||||
<template #title>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div style="width: 150px;">配置元素</div>
|
||||
|
|
@ -10,19 +10,19 @@
|
|||
<div style="max-width: 400px;">
|
||||
<div class="ant-form-vertical">
|
||||
<value-type-form v-model:value="_value" :name="name" isSub key="sub"></value-type-form>
|
||||
<a-form-item label="说明" :name="name.concat(['description'])" :rules="[
|
||||
<j-form-item label="说明" :name="name.concat(['description'])" :rules="[
|
||||
{ max: 200, message: '最多可输入200个字符' },
|
||||
]">
|
||||
<a-textarea v-model:value="_value.description" size="small"></a-textarea>
|
||||
</a-form-item>
|
||||
<j-textarea v-model:value="_value.description" size="small"></j-textarea>
|
||||
</j-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-button type="dashed" block @click="visible = true">
|
||||
<j-button type="dashed" block @click="visible = true">
|
||||
配置元素
|
||||
<AIcon type="EditOutlined" class="item-icon" />
|
||||
</a-button>
|
||||
</a-popover>
|
||||
</j-button>
|
||||
</j-popover>
|
||||
</template>
|
||||
<script setup lang="ts" name="ArrayParam">
|
||||
import ValueTypeForm from '@/views/device/components/Metadata/Base/Edit/ValueTypeForm.vue';
|
||||
|
|
@ -33,7 +33,7 @@ type ValueType = Record<any, any>;
|
|||
const props = defineProps({
|
||||
value: {
|
||||
type: Object as PropType<ValueType>,
|
||||
default: () => ({ extends: {} })
|
||||
default: () => ({ expands: {} })
|
||||
},
|
||||
name: {
|
||||
type: Array as PropType<(string | number)[]>,
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
<template>
|
||||
<div class="boolean-param">
|
||||
<a-row :gutter="4">
|
||||
<a-col :span="12">
|
||||
<a-form-item label=" " :name="name.concat(['trueText'])" :rules="[
|
||||
<j-row :gutter="4">
|
||||
<j-col :span="12">
|
||||
<j-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="[
|
||||
<j-input v-model:value="value.trueText" placeholder="trueText" size="small" />
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-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="[
|
||||
<j-input v-model:value="value.trueValue" placeholder="trueValue" size="small"/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-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="[
|
||||
<j-input v-model:value="value.falseText" placeholder="falseText" size="small" />
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-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>
|
||||
<j-input v-model:value="value.falseValue" placeholder="falseValue" size="small" />
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="BooleanParam">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-popover placement="left" trigger="click">
|
||||
<j-popover placement="left" trigger="click">
|
||||
<template #title>
|
||||
<div class="edit-title" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div style="width: 150px;">{{ config.name }}</div>
|
||||
|
|
@ -7,18 +7,18 @@
|
|||
</template>
|
||||
<template #content>
|
||||
<div style="max-width: 400px;" class="ant-form-vertical">
|
||||
<a-form-item v-for="item in config.properties" :name="name.concat([item.property])" :label="item.name">
|
||||
<a-select v-model:value="value[item.property]" :options="item.type?.elements?.map((e: { 'text': string, 'value': string }) => ({
|
||||
<j-form-item v-for="item in config.properties" :name="name.concat([item.property])" :label="item.name">
|
||||
<j-select v-model:value="value[item.property]" :options="item.type?.elements?.map((e: { 'text': string, 'value': string }) => ({
|
||||
label: e.text,
|
||||
value: e.value,
|
||||
}))" size="small"></a-select>
|
||||
</a-form-item>
|
||||
}))" size="small"></j-select>
|
||||
</j-form-item>
|
||||
</div>
|
||||
</template>
|
||||
<a-button type="dashed" block>
|
||||
<j-button type="dashed" block>
|
||||
存储配置<AIcon type="EditOutlined" class="item-icon"/>
|
||||
</a-button>
|
||||
</a-popover>
|
||||
</j-button>
|
||||
</j-popover>
|
||||
</template>
|
||||
<script setup lang="ts" name="ConfigParam">
|
||||
import { PropType } from 'vue';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<AIcon type="MenuOutlined" class="item-drag item-icon" />
|
||||
</div>
|
||||
<div class="item-middle item-editable">
|
||||
<a-popover :visible="editIndex === index" placement="top">
|
||||
<j-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>
|
||||
|
|
@ -14,34 +14,34 @@
|
|||
</template>
|
||||
<template #content>
|
||||
<div class="ant-form-vertical">
|
||||
<a-form-item label="Value" :name="name.concat([index, 'value'])" :rules="[
|
||||
<j-form-item label="Value" :name="name.concat([index, '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="name.concat([index, 'text'])" :rules="[
|
||||
<j-input v-model:value="_value[index].value" size="small"></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item label="Text" :name="name.concat([index, 'text'])" :rules="[
|
||||
{ required: true, message: '请输入Text' },
|
||||
]">
|
||||
<a-input v-model:value="_value[index].text" size="small"></a-input>
|
||||
</a-form-item>
|
||||
<j-input v-model:value="_value[index].text" size="small"></j-input>
|
||||
</j-form-item>
|
||||
</div>
|
||||
</template>
|
||||
<div class="item-edit" @click="handleEdit(index)">
|
||||
{{ item.text || '枚举项配置' }}
|
||||
<AIcon type="EditOutlined" class="item-icon" />
|
||||
</div>
|
||||
</a-popover>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<AIcon type="DeleteOutlined" @click="handleDelete(index)" />
|
||||
</div>
|
||||
</div>
|
||||
<a-button type="dashed" block @click="handleAdd">
|
||||
<j-button type="dashed" block @click="handleAdd">
|
||||
<template #icon>
|
||||
<AIcon type="PlusOutlined" class="item-icon" />
|
||||
</template>
|
||||
新增枚举型
|
||||
</a-button>
|
||||
</j-button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="BooleanParam">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<AIcon type="MenuOutlined" class="item-drag item-icon" />
|
||||
</div>
|
||||
<div class="item-middle item-editable">
|
||||
<a-popover :visible="editIndex === index" placement="left">
|
||||
<j-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>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
</template>
|
||||
<template #content>
|
||||
<div style="max-width: 400px;" class="ant-form-vertical">
|
||||
<a-form-item label="标识" :name="name.concat([index, 'id'])" :rules="[
|
||||
<j-form-item label="标识" :name="name.concat([index, 'id'])" :rules="[
|
||||
{ required: true, message: '请输入标识' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
|
|
@ -22,14 +22,14 @@
|
|||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]">
|
||||
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="名称" :name="name.concat([index, 'name'])" :rules="[
|
||||
<j-input v-model:value="_value[index].id" size="small"></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item label="名称" :name="name.concat([index, 'name'])" :rules="[
|
||||
{ required: true, message: '请输入名称' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]">
|
||||
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
||||
</a-form-item>
|
||||
<j-input v-model:value="_value[index].name" size="small"></j-input>
|
||||
</j-form-item>
|
||||
<value-type-form v-model:value="_value[index].valueType" :name="name.concat([index, 'valueType'])" isSub
|
||||
key="json_sub"></value-type-form>
|
||||
</div>
|
||||
|
|
@ -38,18 +38,18 @@
|
|||
{{ item.name || '配置参数' }}
|
||||
<AIcon type="EditOutlined" class="item-icon" />
|
||||
</div>
|
||||
</a-popover>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<AIcon type="DeleteOutlined" @click="handleDelete(index)" />
|
||||
</div>
|
||||
</div>
|
||||
<a-button type="dashed" block @click="handleAdd">
|
||||
<j-button type="dashed" block @click="handleAdd">
|
||||
<template #icon>
|
||||
<AIcon type="PlusOutlined" class="item-icon" />
|
||||
</template>
|
||||
添加参数
|
||||
</a-button>
|
||||
</j-button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="JsonParam">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
{{ `#${index + 1}.` }}
|
||||
</div>
|
||||
<div class="item-middle item-editable">
|
||||
<a-popover :visible="editIndex === index" placement="top" @visible-change="change" trigger="click">
|
||||
<j-popover :visible="editIndex === index" placement="top" @visible-change="change" trigger="click">
|
||||
<template #title>
|
||||
<div class="edit-title" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div style="width: 150px;">配置参数</div>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<template #content>
|
||||
<div>
|
||||
<div class="ant-form-vertical">
|
||||
<a-form-item label="标识" :name="name.concat([index, 'id'])" :rules="[
|
||||
<j-form-item label="标识" :name="name.concat([index, 'id'])" :rules="[
|
||||
{ required: true, message: '请输入标识' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
|
|
@ -24,20 +24,19 @@
|
|||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]">
|
||||
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="名称" :name="name.concat([index, 'name'])" :rules="[
|
||||
<j-input v-model:value="_value[index].id" size="small"></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item label="名称" :name="name.concat([index, 'name'])" :rules="[
|
||||
{ required: true, message: '请输入名称' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]">
|
||||
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="指标值" :name="name.concat([index, 'value'])" :rules="[
|
||||
{ required: true, message: '请输入指标值' },
|
||||
{ validator: () => validateIndicator(_value[index]), message: '请输入指标值' }
|
||||
<j-input v-model:value="_value[index].name" size="small"></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item label="指标值" :name="name.concat([index, 'value'])" :rules="[
|
||||
{ required: true, validator: () => validateIndicator(_value[index]), message: '请输入指标值' }
|
||||
]">
|
||||
<JIndicators v-model:value="_value[index]" :type="type" size="small" :enum="enum" />
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -45,18 +44,18 @@
|
|||
{{ item.name || '配置参数' }}
|
||||
<AIcon type="EditOutlined" class="item-icon" />
|
||||
</div>
|
||||
</a-popover>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<AIcon type="DeleteOutlined" @click="handleDelete(index)" />
|
||||
</div>
|
||||
</div>
|
||||
<a-button type="dashed" block @click="handleAdd">
|
||||
<j-button type="dashed" block @click="handleAdd">
|
||||
<template #icon>
|
||||
<AIcon type="PlusOutlined" class="item-icon" />
|
||||
</template>
|
||||
添加指标
|
||||
</a-button>
|
||||
</j-button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="MetricsParam">
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
<template>
|
||||
<a-form-item :name="name.concat(['script'])">
|
||||
<j-form-item :name="name.concat(['script'])">
|
||||
<f-rule-editor v-model:value="value.script" :id="id"></f-rule-editor>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
<template v-if="showWindow">
|
||||
<a-form-item label="规则配置" :name="name.concat(['isVirtualRule'])">
|
||||
<a-switch v-model:checked="value.isVirtualRule" :checked-value="true" :un-checked-value="false"
|
||||
@change="changeWindow"></a-switch>
|
||||
</a-form-item>
|
||||
<j-form-item label="规则配置" :name="name.concat(['isVirtualRule'])">
|
||||
<j-switch v-model:checked="value.isVirtualRule" :checked-value="true" :un-checked-value="false"
|
||||
@change="changeWindow"></j-switch>
|
||||
</j-form-item>
|
||||
<template v-if="value.isVirtualRule">
|
||||
<a-form-item label="窗口" :name="name.concat(['windowType'])" :rules="[
|
||||
<j-form-item label="窗口" :name="name.concat(['windowType'])" :rules="[
|
||||
{ required: true, message: '请选择窗口' },
|
||||
]">
|
||||
<a-select v-model:value="value.windowType" :options="windowTypeEnum" size="small" allow-clear></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="聚合函数" :name="name.concat(['aggType'])" :rules="[
|
||||
<j-select v-model:value="value.windowType" :options="windowTypeEnum" size="small" allow-clear></j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="聚合函数" :name="name.concat(['aggType'])" :rules="[
|
||||
{ required: true, message: '请选择聚合函数' },
|
||||
]">
|
||||
<a-select v-model:value="value.aggType" :options="aggTypeOptions" size="small" allow-clear></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="spanLabel" :name="name.concat(['window', 'span'])" :rules="[
|
||||
<j-select v-model:value="value.aggType" :options="aggTypeOptions" size="small" allow-clear></j-select>
|
||||
</j-form-item>
|
||||
<j-form-item :label="spanLabel" :name="name.concat(['window', 'span'])" :rules="[
|
||||
{ required: true, message: '请输入窗口长度' },
|
||||
]">
|
||||
<a-input-number v-model:value="value.window.span" size="small" style="width: 100%;"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item :label="everyLabel" :name="name.concat(['window', 'every'])" :rules="[
|
||||
<j-input-number v-model:value="value.window.span" size="small" style="width: 100%;"></j-input-number>
|
||||
</j-form-item>
|
||||
<j-form-item :label="everyLabel" :name="name.concat(['window', 'every'])" :rules="[
|
||||
{ required: true, message: '请输入步长' },
|
||||
]">
|
||||
<a-input-number v-model:value="value.window.every" size="small" style="width: 100%;"></a-input-number>
|
||||
</a-form-item>
|
||||
<j-input-number v-model:value="value.window.every" size="small" style="width: 100%;"></j-input-number>
|
||||
</j-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
v-model:value="myValue"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<form-outlined @click="modalVis = true" />
|
||||
<AIcon type="FormOutlined" @click="modalVis = true" />
|
||||
</template>
|
||||
</j-input>
|
||||
<GeoComponent
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
:showUploadList="false"
|
||||
@change="handleFileChange"
|
||||
>
|
||||
<cloud-upload-outlined />
|
||||
<AIcon type="CloudUploadOutlined" />
|
||||
</j-upload>
|
||||
</template>
|
||||
</j-input>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import Ellipsis from './Ellipsis/index.vue'
|
|||
import JEmpty from './Empty/index.vue'
|
||||
import AMapComponent from './AMapComponent/index.vue'
|
||||
import PathSimplifier from './AMapComponent/PathSimplifier.vue'
|
||||
import ValueItem from './ValueItem/index.vue'
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
|
|
@ -35,5 +36,6 @@ export default {
|
|||
.component('JEmpty', JEmpty)
|
||||
.component('AMapComponent', AMapComponent)
|
||||
.component('PathSimplifier', PathSimplifier)
|
||||
.component('ValueItem', ValueItem)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ declare module '*.bmp';
|
|||
declare module '*.js';
|
||||
declare module '*.ts';
|
||||
declare module 'js-cookie';
|
||||
declare module 'jetlinks-ui-components';
|
||||
declare module 'jetlinks-ui-components';
|
||||
declare module 'vue3-json-viewer';
|
||||
|
|
@ -67,10 +67,10 @@ export default [
|
|||
path: '/form',
|
||||
component: () => import('@/views/demo/Form.vue')
|
||||
},
|
||||
{
|
||||
path: '/system/Api',
|
||||
component: () => import('@/views/system/Platforms/index.vue')
|
||||
},
|
||||
// {
|
||||
// path: '/system/Api',
|
||||
// component: () => import('@/views/system/Platforms/index.vue')
|
||||
// },
|
||||
// end: 测试用, 可删除
|
||||
|
||||
// 初始化
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
在特定场景下,设备无法直接接入阿里云物联网平台时,您可先将设备接入物联网平台,再使用阿里云“云云对接SDK”,快速构建桥接服务,搭建物联网平台与阿里云物联网平台的双向数据通道。
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun2.png')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun2.png')" />
|
||||
</div>
|
||||
<h1>2.配置说明</h1>
|
||||
<div>
|
||||
|
|
@ -26,14 +26,14 @@
|
|||
</div>
|
||||
<div>获取路径:“阿里云物联网平台”--“服务地址”</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun3.png')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun3.png')" />
|
||||
</div>
|
||||
<h2> 2、AccesskeyID/Secret</h2>
|
||||
<div>
|
||||
用于程序通知方式调用云服务费API的用户标识和秘钥获取路径:“阿里云管理控制台”--“用户头像”--“”--“AccessKey管理”--“查看”
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun1.jpg')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun1.jpg')" />
|
||||
</div>
|
||||
<h2> 3. 网桥产品</h2>
|
||||
<div>
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
将阿里云物联网平台中的产品实例与物联网平台的产品实例进行关联。关联后需要进入该产品下的每一个设备的实例信息页,填入对应的阿里云物联网平台设备的DeviceName、DeviceSecret进行一对一绑定。
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun4.png')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun4.png')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<a-card>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="16">
|
||||
<j-card>
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<TitleComponent data="基本信息" />
|
||||
<a-form
|
||||
<j-form
|
||||
:layout="'vertical'"
|
||||
ref="formRef"
|
||||
:model="modelRef"
|
||||
>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
|
|
@ -25,14 +25,14 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入名称"
|
||||
v-model:value="modelRef.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'regionId']"
|
||||
:rules="[
|
||||
{
|
||||
|
|
@ -44,63 +44,62 @@
|
|||
<template #label>
|
||||
<span>
|
||||
服务地址
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="阿里云内部给每台机器设置的唯一编号"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择服务地址"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.regionId
|
||||
"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@blur="productChange"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in regionsList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'instanceId']"
|
||||
>
|
||||
<template #label>
|
||||
<span>
|
||||
实例ID
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="阿里云物联网平台中的实例ID,没有则不填"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入实例ID"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.instanceId
|
||||
"
|
||||
@blur="productChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'accessKeyId']"
|
||||
:rules="[
|
||||
{
|
||||
|
|
@ -116,27 +115,27 @@
|
|||
<template #label>
|
||||
<span>
|
||||
accessKey
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="用于程序通知方式调用云服务API的用户标识"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入accessKey"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.accessKeyId
|
||||
"
|
||||
@blur="productChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'accessSecret']"
|
||||
:rules="[
|
||||
{
|
||||
|
|
@ -152,27 +151,27 @@
|
|||
<template #label>
|
||||
<span>
|
||||
accessSecret
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="用于程序通知方式调用云服务费API的秘钥标识"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入accessSecret"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.accessSecret
|
||||
"
|
||||
@blur="productChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
name="bridgeProductKey"
|
||||
:rules="{
|
||||
required: true,
|
||||
|
|
@ -182,44 +181,43 @@
|
|||
<template #label>
|
||||
<span>
|
||||
网桥产品
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="物联网平台对应的阿里云产品"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择网桥产品"
|
||||
v-model:value="
|
||||
modelRef.bridgeProductKey
|
||||
"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in aliyunProductList"
|
||||
:key="item.productKey"
|
||||
:value="item.productKey"
|
||||
:label="item.productName"
|
||||
>{{
|
||||
item.productName
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<p>产品映射</p>
|
||||
<a-collapse
|
||||
<j-collapse
|
||||
v-if="modelRef.mappings.length"
|
||||
:activeKey="activeKey"
|
||||
@change="onCollChange"
|
||||
>
|
||||
<a-collapse-panel
|
||||
<j-collapse-panel
|
||||
v-for="(
|
||||
item, index
|
||||
) in modelRef.mappings"
|
||||
|
|
@ -239,9 +237,9 @@
|
|||
type="DeleteOutlined"
|
||||
@click="delItem(index)"
|
||||
/></template>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="阿里云产品"
|
||||
:name="[
|
||||
'mappings',
|
||||
|
|
@ -254,19 +252,16 @@
|
|||
'请选择阿里云产品',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择阿里云产品"
|
||||
v-model:value="
|
||||
item.productKey
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getAliyunProductList(
|
||||
item.productKey,
|
||||
item?.productKey || ''
|
||||
)"
|
||||
:key="i.productKey"
|
||||
:value="
|
||||
|
|
@ -277,13 +272,13 @@
|
|||
"
|
||||
>{{
|
||||
i.productName
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="平台产品"
|
||||
:name="[
|
||||
'mappings',
|
||||
|
|
@ -296,36 +291,36 @@
|
|||
'请选择平台产品',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择平台产品"
|
||||
v-model:value="
|
||||
item.productId
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getPlatProduct(
|
||||
item.productId,
|
||||
item.productId || ''
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="item.id"
|
||||
:value="i?.id"
|
||||
:label="i.name"
|
||||
>{{
|
||||
i.name
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-button
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
<j-card v-else>
|
||||
<j-empty />
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
type="dashed"
|
||||
style="width: 100%; margin-top: 10px"
|
||||
@click="addItem"
|
||||
|
|
@ -334,10 +329,10 @@
|
|||
type="PlusOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>添加
|
||||
</a-button>
|
||||
</a-col>
|
||||
<a-col :span="24" style="margin-top: 20px">
|
||||
<a-form-item
|
||||
</j-button>
|
||||
</j-col>
|
||||
<j-col :span="24" style="margin-top: 20px">
|
||||
<j-form-item
|
||||
label="说明"
|
||||
name="description"
|
||||
:rules="{
|
||||
|
|
@ -345,16 +340,16 @@
|
|||
message: '最多输入200个字符',
|
||||
}"
|
||||
>
|
||||
<a-textarea
|
||||
<j-textarea
|
||||
v-model:value="modelRef.description"
|
||||
placeholder="请输入说明"
|
||||
showCount
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
<div v-if="type === 'edit'">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
|
|
@ -365,12 +360,12 @@
|
|||
保存
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<Doc />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-card>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
|
@ -384,7 +379,7 @@ import {
|
|||
queryProductList,
|
||||
} from '@/api/northbound/alicloud';
|
||||
import _ from 'lodash';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
|
@ -430,10 +425,6 @@ const loading = ref<boolean>(false);
|
|||
const type = ref<'edit' | 'view'>('edit');
|
||||
const activeKey = ref<string[]>(['0']);
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const queryRegionsList = async () => {
|
||||
const resp = await getRegionsList();
|
||||
if (resp.status === 200) {
|
||||
|
|
@ -503,8 +494,9 @@ const saveBtn = () => {
|
|||
);
|
||||
data.bridgeProductName = product?.productName || '';
|
||||
loading.value = true;
|
||||
const resp = await savePatch({...toRaw(modelRef), ...data});
|
||||
loading.value = false;
|
||||
const resp = await savePatch({...toRaw(modelRef), ...data}).finally(() => {
|
||||
loading.value = false;
|
||||
})
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
formRef.value.resetFields();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
target="northbound-aliyun"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
<JTable
|
||||
<JProTable
|
||||
ref="instanceRef"
|
||||
:columns="columns"
|
||||
:request="query"
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<j-space>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
|
@ -45,20 +45,20 @@
|
|||
>
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
网桥产品
|
||||
</div>
|
||||
<div>{{ slotProps?.bridgeProductName }}</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
<label>说明</label>
|
||||
</div>
|
||||
<div>{{ slotProps?.description }}</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<PermissionButton
|
||||
|
|
@ -81,13 +81,13 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<a-badge
|
||||
<j-badge
|
||||
:text="slotProps.state?.text"
|
||||
:status="statusMap.get(slotProps.state?.value)"
|
||||
/>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space>
|
||||
<j-space>
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
|
|
@ -104,23 +104,21 @@
|
|||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { query, _undeploy, _deploy, _delete } from '@/api/northbound/alicloud';
|
||||
import type { ActionsType } from '@/components/Table/index.vue';
|
||||
import type { ActionsType } from '@/views/device/Instance/typings'
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
|
||||
const router = useRouter();
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const current = ref<Record<string, any>>({});
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
|
|
@ -149,6 +147,9 @@ const columns = [
|
|||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
key: 'describe',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue-demi";
|
||||
import { PropType } from "vue";
|
||||
|
||||
|
||||
type Emits = {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
<j-form-item name="messageType" label="指令类型" :rules="{
|
||||
required: true,
|
||||
message: '请选择指令类型',
|
||||
}">
|
||||
<j-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
||||
}" class="other">
|
||||
<j-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search>
|
||||
<j-select-option value="READ_PROPERTY">读取属性</j-select-option>
|
||||
<j-select-option value="WRITE_PROPERTY">修改属性</j-select-option>
|
||||
<j-select-option value="INVOKE_FUNCTION">调用功能</j-select-option>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
required: true,
|
||||
message: '请选择属性',
|
||||
}">
|
||||
<j-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
||||
<j-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search @change="onPropertyChange">
|
||||
<j-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
|
|
@ -32,7 +32,27 @@
|
|||
required: true,
|
||||
message: '请输入值',
|
||||
}">
|
||||
<j-input />
|
||||
<ValueItem
|
||||
v-model:modelValue="modelRef.message.value"
|
||||
:itemType="property.type || property.valueType?.type || 'int'"
|
||||
:options="
|
||||
property.valueType?.type === 'enum'
|
||||
? (property?.dataType?.elements || []).map(
|
||||
(item) => {
|
||||
return {
|
||||
label: item?.text,
|
||||
value: item?.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
: property.valueType?.type === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||
|
|
@ -40,7 +60,7 @@
|
|||
required: true,
|
||||
message: '请选择功能',
|
||||
}">
|
||||
<j-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
||||
<j-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search @change="funcChange">
|
||||
<j-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
|
|
@ -62,10 +82,6 @@ import EditTable from './EditTable.vue'
|
|||
|
||||
const formRef = ref();
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
actionType: {
|
||||
type: String,
|
||||
|
|
@ -89,10 +105,12 @@ const props = defineProps({
|
|||
type Emits = {
|
||||
(e: 'update:modelValue', data: any): void;
|
||||
};
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const modelRef = computed({
|
||||
get: () => {
|
||||
onPropertyChange(props.modelValue?.message?.properties)
|
||||
return props.modelValue || {
|
||||
messageType: undefined,
|
||||
message: {
|
||||
|
|
@ -107,6 +125,8 @@ const modelRef = computed({
|
|||
}
|
||||
})
|
||||
|
||||
const property = ref<any>({})
|
||||
|
||||
const funcChange = (val: string) => {
|
||||
if(val){
|
||||
const arr = props.metadata?.functions.find((item: any) => item.id === val)?.inputs || []
|
||||
|
|
@ -122,6 +142,13 @@ const funcChange = (val: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
const onPropertyChange = (val: string) => {
|
||||
if(val){
|
||||
const _item = props.metadata?.properties.find((item: any) => item.id === val)
|
||||
property.value = _item?.[0] || {}
|
||||
}
|
||||
}
|
||||
|
||||
const saveBtn = () => new Promise((resolve) => {
|
||||
formRef.value.validate()
|
||||
.then(() => {
|
||||
|
|
@ -139,4 +166,13 @@ const saveBtn = () => new Promise((resolve) => {
|
|||
|
||||
defineExpose({ saveBtn })
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-form-item){
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.other {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -51,7 +51,6 @@
|
|||
placeholder="请选择产品"
|
||||
v-model:value="modelRef.id"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="productChange"
|
||||
>
|
||||
<j-select-option
|
||||
|
|
@ -89,7 +88,6 @@
|
|||
placeholder="请选择设备类型"
|
||||
v-model:value="modelRef.applianceType"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="typeChange"
|
||||
>
|
||||
<j-select-option
|
||||
|
|
@ -170,13 +168,10 @@
|
|||
item.action
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="i in getTypesActions(
|
||||
item.action,
|
||||
item.action || ''
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
|
|
@ -218,9 +213,6 @@
|
|||
item.actionType
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
value="command"
|
||||
|
|
@ -261,6 +253,9 @@
|
|||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
<j-card v-else>
|
||||
<j-empty />
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
|
|
@ -323,13 +318,10 @@
|
|||
item.source
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="i in getDuerOSProperties(
|
||||
item.source,
|
||||
item.source || '',
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
|
|
@ -361,16 +353,13 @@
|
|||
"
|
||||
mode="tags"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="i in getProductProperties(
|
||||
item.target,
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="item.id"
|
||||
:value="i.id"
|
||||
>{{
|
||||
i.name
|
||||
}}</j-select-option
|
||||
|
|
@ -381,6 +370,9 @@
|
|||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
<j-card v-else>
|
||||
<j-empty />
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
|
|
@ -494,10 +486,6 @@ const onActionCollChange = (_key: string[]) => {
|
|||
actionActiveKey.value = _key;
|
||||
};
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const addItem = () => {
|
||||
actionActiveKey.value.push(String(modelRef.actionMappings.length));
|
||||
modelRef.actionMappings.push({
|
||||
|
|
@ -636,8 +624,9 @@ const saveBtn = async () => {
|
|||
.then(async (data: any) => {
|
||||
if (tasks.every((item) => item) && data) {
|
||||
loading.value = true;
|
||||
const resp = await savePatch(data);
|
||||
loading.value = false;
|
||||
const resp = await savePatch(data).finally(() => {
|
||||
loading.value = false;
|
||||
})
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
formRef.value.resetFields();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
}"
|
||||
@cancelSelect="cancelSelect"
|
||||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
|
|
@ -643,10 +642,6 @@ const onSelectChange = (keys: string[]) => {
|
|||
_selectedRowKeys.value = [...keys];
|
||||
};
|
||||
|
||||
const cancelSelect = () => {
|
||||
_selectedRowKeys.value = [];
|
||||
};
|
||||
|
||||
const handleClick = (dt: any) => {
|
||||
if (_selectedRowKeys.value.includes(dt.id)) {
|
||||
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-form-item label="标识" name="id" :rules="[
|
||||
<j-form-item label="标识" name="id" :rules="[
|
||||
{ required: true, message: '请输入标识' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
|
|
@ -7,14 +7,14 @@
|
|||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]">
|
||||
<a-input v-model:value="value.id" size="small" @change="asyncOtherConfig" :disabled="metadataStore.model.action === 'edit'"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="名称" name="name" :rules="[
|
||||
<j-input v-model:value="value.id" size="small" @change="asyncOtherConfig" :disabled="metadataStore.model.action === 'edit'"></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item label="名称" name="name" :rules="[
|
||||
{ required: true, message: '请输入名称' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]">
|
||||
<a-input v-model:value="value.name" size="small"></a-input>
|
||||
</a-form-item>
|
||||
<j-input v-model:value="value.name" size="small"></j-input>
|
||||
</j-form-item>
|
||||
<template v-if="modelType === 'properties'">
|
||||
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"
|
||||
@change-type="changeValueType"></value-type-form>
|
||||
|
|
@ -22,42 +22,42 @@
|
|||
:valueType="value.valueType"></expands-form>
|
||||
</template>
|
||||
<template v-if="modelType === 'functions'">
|
||||
<a-form-item label="是否异步" name="async" :rules="[
|
||||
<j-form-item label="是否异步" name="async" :rules="[
|
||||
{ required: true, message: '请选择是否异步' },
|
||||
]">
|
||||
<a-radio-group v-model:value="value.async">
|
||||
<a-radio :value="true">是</a-radio>
|
||||
<a-radio :value="false">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="输入参数" name="inputs" :rules="[
|
||||
<j-radio-group v-model:value="value.async">
|
||||
<j-radio :value="true">是</j-radio>
|
||||
<j-radio :value="false">否</j-radio>
|
||||
</j-radio-group>
|
||||
</j-form-item>
|
||||
<j-form-item label="输入参数" name="inputs" :rules="[
|
||||
{ required: true, message: '请输入输入参数' },
|
||||
]">
|
||||
<JsonParam v-model:value="value.inputs" :name="['inputs']"></JsonParam>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
<value-type-form :name="['output']" v-model:value="value.output" key="function" title="输出参数"></value-type-form>
|
||||
</template>
|
||||
<template v-if="modelType === 'events'">
|
||||
<a-form-item label="级别" :name="['expands', 'level']" :rules="[
|
||||
<j-form-item label="级别" :name="['expands', 'level']" :rules="[
|
||||
{ required: true, message: '请选择级别' },
|
||||
]">
|
||||
<a-select v-model:value="value.expands.level" :options="EventLevel" size="small"></a-select>
|
||||
</a-form-item>
|
||||
<j-select v-model:value="value.expands.level" :options="EventLevel" size="small"></j-select>
|
||||
</j-form-item>
|
||||
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="function" title="输出参数"></value-type-form>
|
||||
</template>
|
||||
<template v-if="modelType === 'tags'">
|
||||
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"></value-type-form>
|
||||
<a-form-item label="读写类型" :name="['expands', 'type']" :rules="[
|
||||
<j-form-item label="读写类型" :name="['expands', 'type']" :rules="[
|
||||
{ required: true, message: '请选择读写类型' },
|
||||
]">
|
||||
<a-select v-model:value="value.expands.type" :options="ExpandsTypeList" mode="multiple" size="small"></a-select>
|
||||
</a-form-item>
|
||||
<j-select v-model:value="value.expands.type" :options="ExpandsTypeList" mode="multiple" size="small"></j-select>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<a-form-item label="说明" name="description" :rules="[
|
||||
<j-form-item label="说明" name="description" :rules="[
|
||||
{ max: 200, message: '最多可输入200个字符' },
|
||||
]">
|
||||
<a-textarea v-model:value="value.description" size="small"></a-textarea>
|
||||
</a-form-item>
|
||||
<j-textarea v-model:value="value.description" size="small"></j-textarea>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<script setup lang="ts" name="BaseForm">
|
||||
import { PropType } from 'vue';
|
||||
|
|
|
|||
|
|
@ -1,26 +1,30 @@
|
|||
<template>
|
||||
<a-form-item label="来源" :name="name.concat(['source'])" v-if="type === 'product'" :rules="[
|
||||
<j-form-item label="来源" :name="name.concat(['source'])" v-if="type === 'product'" :rules="[
|
||||
{ required: true, message: '请选择来源' },
|
||||
]">
|
||||
<a-select v-model:value="_value.source" :options="PropertySource" size="small"
|
||||
:disabled="metadataStore.model.action === 'edit'"></a-select>
|
||||
</a-form-item>
|
||||
<j-select v-model:value="_value.source" :options="PropertySource" size="small"
|
||||
:disabled="metadataStore.model.action === 'edit'"></j-select>
|
||||
</j-form-item>
|
||||
<virtual-rule-param v-if="_value.source === 'rule'" v-model:value="_value.virtualRule"
|
||||
:name="name.concat(['virtualRule'])" :id="id" :showWindow="_value.source === 'rule'"></virtual-rule-param>
|
||||
<a-form-item label="读写类型" :name="name.concat(['type'])" :rules="[
|
||||
<j-form-item label="读写类型" :name="name.concat(['type'])" :rules="[
|
||||
{ required: true, message: '请选择读写类型' },
|
||||
]">
|
||||
<a-select v-model:value="_value.type" :options="ExpandsTypeList" mode="multiple" size="small"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="其他配置" v-if="config.length > 0">
|
||||
<a-form-item v-for="(item, index) in config" :key="index">
|
||||
<j-select v-model:value="_value.type" :options="ExpandsTypeList" mode="multiple" size="small"></j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="其他配置" v-if="config.length > 0">
|
||||
<j-form-item v-for="(item, index) in config" :key="index">
|
||||
<config-param v-model:value="_value" :config="item" :name="name"></config-param>
|
||||
</a-form-item>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="type === 'product' && ['int', 'float', 'double', 'long', 'date', 'string', 'boolean'].includes(valueType.type)"
|
||||
label="指标配置" :name="name.concat(['metrics'])">
|
||||
<metrics-param v-model:value="_value.metrics" :type="valueType.type" :enum="valueType" :name="name.concat(['metrics'])"></metrics-param>
|
||||
</a-form-item>
|
||||
</j-form-item>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-if="type === 'product' && ['int', 'float', 'double', 'long', 'date', 'string', 'boolean'].includes(valueType.type)"
|
||||
label="指标配置" :name="name.concat(['metrics'])" :rules="[
|
||||
{ validator: () => validateMetrics(_value.metrics), message: '请输入指标配置' }
|
||||
]">
|
||||
<metrics-param v-model:value="_value.metrics" :type="valueType.type" :enum="valueType"
|
||||
:name="name.concat(['metrics'])"></metrics-param>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<script setup lang="ts" name="ExpandsForm">
|
||||
import { useMetadataStore } from '@/store/metadata';
|
||||
|
|
@ -86,5 +90,15 @@ onMounted(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const validateMetrics = (value: Record<any, any>[]) => {
|
||||
const flag = value.every((item) => {
|
||||
return item.id && item.name && item.value;
|
||||
});
|
||||
if (!flag) {
|
||||
return Promise.reject(new Error('请输入指标配置'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
|
|
@ -1,48 +1,54 @@
|
|||
<template>
|
||||
<a-form-item :label="title" :name="name.concat(['type'])" :rules="[
|
||||
<j-form-item :label="title" :name="name.concat(['type'])" :rules="[
|
||||
metadataStore.model.type !== 'functions' ? { required: true, message: `请选择${title}` } : {},
|
||||
]">
|
||||
<a-select v-model:value="_value.type" :options="metadataStore.model.type === 'events' ? eventDataTypeList : _dataTypeList" size="small" @change="changeType"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="单位" :name="name.concat(['unit'])" v-if="['int', 'float', 'long', 'double'].includes(_value.type)">
|
||||
<j-select v-model:value="_value.type"
|
||||
:options="metadataStore.model.type === 'events' ? eventDataTypeList : _dataTypeList" size="small"
|
||||
@change="changeType"></j-select>
|
||||
</j-form-item>
|
||||
<j-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)">
|
||||
</j-form-item>
|
||||
<j-form-item label="精度" :name="name.concat(['scale'])" v-if="['float', 'double'].includes(_value.type)">
|
||||
<j-input-number v-model:value="_value.scale" size="small" :min="0" :max="2147483647" :precision="0" :default-value="2"
|
||||
style="width: 100%"></j-input-number>
|
||||
</j-form-item>
|
||||
<j-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: '请配置枚举项' }
|
||||
</j-form-item>
|
||||
<j-form-item label="枚举项" :name="name.concat(['elements'])" v-if="['enum'].includes(_value.type)" :rules="[
|
||||
{ required: true, validator: validateEnum, message: '请配置枚举项' }
|
||||
]">
|
||||
<EnumParam v-model:value="_value.elements" :name="name.concat(['elements'])"></EnumParam>
|
||||
</a-form-item>
|
||||
<a-form-item :name="name.concat(['expands', 'maxLength'])" v-if="['string', 'password'].includes(_value.type)">
|
||||
</j-form-item>
|
||||
<j-form-item :name="name.concat(['expands', 'maxLength'])" v-if="['string', 'password'].includes(_value.type)">
|
||||
<template #label>
|
||||
<a-space>
|
||||
<j-space>
|
||||
最大长度
|
||||
<a-tooltip title="字节">
|
||||
<j-tooltip title="字节">
|
||||
<question-circle-outlined style="color: rgb(136, 136, 136); font-size: 12px;" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</j-tooltip>
|
||||
</j-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)">
|
||||
<j-input-number v-model:value="_value.expands.maxLength" size="small" :max="2147483647" :min="1" :precision="0"
|
||||
style="width: 100%;"></j-input-number>
|
||||
</j-form-item>
|
||||
<j-form-item label="元素配置" :name="name.concat(['elementType'])" v-if="['array'].includes(_value.type)" :rules="[
|
||||
{ validator: validateArray }
|
||||
]">
|
||||
<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)" :rules="[]">
|
||||
</j-form-item>
|
||||
<j-form-item label="JSON对象" :name="name.concat(['properties'])" v-if="['object'].includes(_value.type)" :rules="[
|
||||
{ validator: validateJson }
|
||||
]">
|
||||
<JsonParam v-model:value="_value.properties" :name="name.concat(['properties'])"></JsonParam>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件类型" :name="name.concat(['fileType'])" v-if="['file'].includes(_value.type)" initialValue="url"
|
||||
</j-form-item>
|
||||
<j-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>
|
||||
<j-select v-model:value="_value.fileType" :options="FileTypeList" size="small"></j-select>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<script lang="ts" setup mame="BaseForm">
|
||||
import { DataTypeList, FileTypeList } from '@/views/device/data';
|
||||
|
|
@ -56,6 +62,8 @@ import EnumParam from '@/components/Metadata/EnumParam/index.vue'
|
|||
import ArrayParam from '@/components/Metadata/ArrayParam/index.vue'
|
||||
import JsonParam from '@/components/Metadata/JsonParam/index.vue'
|
||||
import { useMetadataStore } from '@/store/metadata';
|
||||
import { Rule } from 'ant-design-vue/es/form';
|
||||
import { Form } from 'ant-design-vue/es';
|
||||
|
||||
type ValueType = Record<any, any>;
|
||||
const props = defineProps({
|
||||
|
|
@ -84,15 +92,8 @@ interface Emits {
|
|||
}
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
// emit('update:value', { extends: {}, ...props.value })
|
||||
|
||||
const metadataStore = useMetadataStore()
|
||||
// const _value = computed({
|
||||
// get: () => props.value,
|
||||
// set: val => {
|
||||
// emit('update:value', val)
|
||||
// }
|
||||
// })
|
||||
|
||||
const _value = ref<ValueType>({})
|
||||
watchEffect(() => {
|
||||
_value.value = props.value || {
|
||||
|
|
@ -142,6 +143,79 @@ const eventDataTypeList = [
|
|||
const changeType = (val: SelectValue) => {
|
||||
emit('changeType', val as string)
|
||||
}
|
||||
|
||||
const validateEnum = async (_rule: Rule, val: Record<any, any>[]) => {
|
||||
if (val.length === 0) return Promise.reject(new Error('请配置枚举项'));
|
||||
const flag = val.every((item) => {
|
||||
return item.value && item.text;
|
||||
});
|
||||
if (!flag) {
|
||||
return Promise.reject(new Error('请配置枚举项'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const validateArray = async (_rule: Rule, val: Record<any, any>) => {
|
||||
if (!val) return Promise.reject(new Error('请输入元素配置'));
|
||||
await validateValueType(_rule, val)
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const validateJson = async (_rule: Rule, val: Record<any, any>[]) => {
|
||||
if (!val || val.length === 0) {
|
||||
return Promise.reject(new Error('请输入配置参数'));
|
||||
}
|
||||
for (let item of val) {
|
||||
if (!item) return Promise.reject(new Error('请输入配置参数'));
|
||||
await validateValueType(_rule, item)
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const validateValueType = async (_rule: Rule, val: Record<any, any>) => {
|
||||
if (!val) return Promise.reject(new Error('请输入元素配置'));
|
||||
if (!val.id) {
|
||||
return Promise.reject(new Error('请输入标识'))
|
||||
}
|
||||
if (!val.name) {
|
||||
return Promise.reject(new Error('请输入名称'))
|
||||
}
|
||||
if (metadataStore.model.type !== 'functions' && !val.valueType?.type) {
|
||||
return Promise.reject(new Error(`请选择${props.title}`))
|
||||
}
|
||||
if (['enum'].includes(val.valueType.type)) {
|
||||
await validateEnum(_rule, val.valueType.elements)
|
||||
}
|
||||
if (['array'].includes(val.valueType.type)) {
|
||||
await validateArray(_rule, val.valueType.elementType)
|
||||
}
|
||||
if (['object'].includes(val.valueType.type)) {
|
||||
await validateJson(_rule, val.valueType.properties)
|
||||
}
|
||||
if (['file'].includes(val.valueType.type) && !val.valueType.fileType) {
|
||||
return Promise.reject(new Error('请选择文件类型'))
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// const rules = ref({
|
||||
// type: [
|
||||
// metadataStore.model.type !== 'functions' ? { required: true, message: `请选择${props.title}` } : {},
|
||||
// ],
|
||||
// elements: [
|
||||
// { required: true, validator: validateEnum, message: '请配置枚举项' }
|
||||
// ],
|
||||
// elementType: [
|
||||
// { validator: validateArray, message: '请输入元素配置' }
|
||||
// ],
|
||||
// properties: [
|
||||
// { validator: validateJson, message: '请输入配置参数' }
|
||||
// ],
|
||||
// fileType: [
|
||||
// { required: true, message: '请选择文件类型' },
|
||||
// ]
|
||||
// })
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-form-item-label) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<a-drawer :mask-closable="false" width="25vw" visible :title="`${title}-${typeMapping[metadataStore.model.type]}`"
|
||||
<j-drawer :mask-closable="false" width="25vw" visible :title="`${title}-${typeMapping[metadataStore.model.type]}`"
|
||||
@close="close" destroy-on-close :z-index="1000" placement="right">
|
||||
<template #extra>
|
||||
<a-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</a-button>
|
||||
<j-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</j-button>
|
||||
</template>
|
||||
<a-form ref="formRef" :model="form.model" layout="vertical">
|
||||
<j-form ref="formRef" :model="form.model" layout="vertical">
|
||||
<BaseForm :model-type="metadataStore.model.type" :type="type" v-model:value="form.model"></BaseForm>
|
||||
</a-form>
|
||||
</a-drawer>
|
||||
</j-form>
|
||||
</j-drawer>
|
||||
</template>
|
||||
<script lang="ts" setup name="Edit">
|
||||
import { useInstanceStore } from '@/store/instance';
|
||||
|
|
@ -22,6 +22,7 @@ import { DeviceInstance } from '@/views/device/Instance/typings';
|
|||
import BaseForm from './BaseForm.vue';
|
||||
import { PropType } from 'vue';
|
||||
import { _deploy } from '@/api/device/product';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
|
|
@ -60,7 +61,7 @@ const form = reactive({
|
|||
model: {} as any,
|
||||
})
|
||||
if (metadataStore.model.action === 'edit') {
|
||||
form.model = metadataStore.model.item
|
||||
form.model = cloneDeep(metadataStore.model.item)
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
|
@ -75,7 +76,9 @@ const save = reactive({
|
|||
const type = metadataStore.model.type
|
||||
const _detail: ProductItem | DeviceInstance = props.type === 'device' ? instanceStore.detail : productStore.current
|
||||
const _metadata = JSON.parse(_detail?.metadata || '{}')
|
||||
const list = _metadata[type] as any[]
|
||||
console.log(_metadata)
|
||||
console.log(type)
|
||||
const list = (_metadata[type] as any[]) || []
|
||||
if (formValue.id) {
|
||||
if (metadataStore.model.action === 'add' && list.some(item => item.id === formValue.id)) {
|
||||
message.error('标识已存在')
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<j-pro-table :loading="loading" :data-source="data" size="small" :columns="columns" row-key="id" model="TABLE">
|
||||
<template #headerTitle>
|
||||
<a-input-search v-model:value="searchValue" placeholder="请输入名称" @search="handleSearch"></a-input-search>
|
||||
<j-input-search v-model:value="searchValue" placeholder="请输入名称" @search="handleSearch"></j-input-search>
|
||||
</template>
|
||||
<template #rightExtraRender>
|
||||
<PermissionButton type="primary" :uhas-permission="`${permission}:update`" key="add" @click="handleAddClick"
|
||||
|
|
@ -135,6 +135,13 @@ const refreshMetadata = () => {
|
|||
watch([route.params.id, type], refreshMetadata, { immediate: true })
|
||||
|
||||
const metadataStore = useMetadataStore()
|
||||
watch(() => metadataStore.model.importMetadata,
|
||||
(val: boolean) => {
|
||||
if (!!val) {
|
||||
refreshMetadata()
|
||||
metadataStore.set('importMetadata', false)
|
||||
}
|
||||
})
|
||||
const handleAddClick = () => {
|
||||
metadataStore.set('edit', true)
|
||||
metadataStore.set('item', undefined)
|
||||
|
|
@ -189,6 +196,4 @@ const removeItem = async (record: MetadataItem) => {
|
|||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
<style scoped lang="less"></style>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<a-drawer :mask-closable="false" title="查看物模型" width="700" v-model:visible="_visible" destroy-on-close @close="close">
|
||||
<j-drawer :mask-closable="false" title="查看物模型" width="700" v-model:visible="_visible" destroy-on-close @close="close">
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleExport">
|
||||
<j-space>
|
||||
<j-button type="primary" @click="handleExport">
|
||||
导出
|
||||
</a-button>
|
||||
</a-space>
|
||||
</j-button>
|
||||
</j-space>
|
||||
</template>
|
||||
<a-spin :spinning="loading">
|
||||
<j-spin :spinning="loading">
|
||||
<div class="cat-content">
|
||||
<p class="cat-tip">
|
||||
物模型是对设备在云端的功能描述,包括设备的属性、服务和事件。物联网平台通过定义一种物的描述语言来描述物模型,称之为
|
||||
|
|
@ -15,15 +15,15 @@
|
|||
组装上报设备的数据。您可以导出完整物模型,用于云端应用开发。
|
||||
</p>
|
||||
</div>
|
||||
<a-tabs @change="handleConvertMetadata" destroy-inactive-tab-pane>
|
||||
<a-tab-pane v-for="item in codecs" :key="item.id" :tab="item.name">
|
||||
<j-tabs @change="handleConvertMetadata" destroy-inactive-tab-pane>
|
||||
<j-tab-pane v-for="item in codecs" :key="item.id" :tab="item.name">
|
||||
<div class="cat-panel">
|
||||
<MonacoEditor v-model="value" theme="vs" style="height: 100%"></MonacoEditor>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</a-drawer>
|
||||
</j-tab-pane>
|
||||
</j-tabs>
|
||||
</j-spin>
|
||||
</j-drawer>
|
||||
</template>
|
||||
<script setup lang="ts" name="Cat">
|
||||
import { message } from 'ant-design-vue/es';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-modal :mask-closable="false" title="导入物模型" destroy-on-close v-model:visible="_visible" @cancel="close"
|
||||
<j-modal :mask-closable="false" title="导入物模型" destroy-on-close v-model:visible="_visible" @cancel="close"
|
||||
@ok="handleImport" :confirm-loading="loading">
|
||||
<div class="import-content">
|
||||
<p class="import-tip">
|
||||
|
|
@ -7,46 +7,46 @@
|
|||
导入的物模型会覆盖原来的属性、功能、事件、标签,请谨慎操作。
|
||||
</p>
|
||||
</div>
|
||||
<a-form layout="vertical" v-model="formModel">
|
||||
<a-form-item v-if="type === 'product'" label="导入方式" v-bind="validateInfos.type">
|
||||
<a-select v-model:value="formModel.type">
|
||||
<a-select-option value="copy">拷贝产品</a-select-option>
|
||||
<a-select-option value="import">导入物模型</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="选择产品" v-bind="validateInfos.copy" v-if="formModel.type === 'copy'">
|
||||
<a-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="物模型类型" v-bind="validateInfos.metadata"
|
||||
<j-form layout="vertical" v-model="formModel">
|
||||
<j-form-item v-if="type === 'product'" label="导入方式" v-bind="validateInfos.type">
|
||||
<j-select v-model:value="formModel.type">
|
||||
<j-select-option value="copy">拷贝产品</j-select-option>
|
||||
<j-select-option value="import">导入物模型</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="选择产品" v-bind="validateInfos.copy" v-if="formModel.type === 'copy'">
|
||||
<j-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label"></j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="物模型类型" v-bind="validateInfos.metadata"
|
||||
v-if="type === 'device' || formModel.type === 'import'">
|
||||
<a-select v-model:value="formModel.metadata">
|
||||
<a-select-option value="jetlinks">Jetlinks物模型</a-select-option>
|
||||
<a-select-option value="alink">阿里云物模型TSL</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="导入类型" v-bind="validateInfos.metadataType"
|
||||
<j-select v-model:value="formModel.metadata">
|
||||
<j-select-option value="jetlinks">Jetlinks物模型</j-select-option>
|
||||
<j-select-option value="alink">阿里云物模型TSL</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="导入类型" v-bind="validateInfos.metadataType"
|
||||
v-if="type === 'device' || formModel.type === 'import'">
|
||||
<a-select v-model:value="formModel.metadataType">
|
||||
<a-select-option value="file">文件上传</a-select-option>
|
||||
<a-select-option value="script">脚本</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件上传" v-bind="validateInfos.upload" v-if="formModel.metadataType === 'file'">
|
||||
<a-input v-model:value="formModel.upload">
|
||||
<j-select v-model:value="formModel.metadataType">
|
||||
<j-select-option value="file">文件上传</j-select-option>
|
||||
<j-select-option value="script">脚本</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="文件上传" v-bind="validateInfos.upload" v-if="formModel.metadataType === 'file'">
|
||||
<j-input v-model:value="formModel.upload">
|
||||
<template #addonAfter>
|
||||
<a-upload v-model:file-list="fileList" :before-upload="beforeUpload" accept=".json"
|
||||
<j-upload v-model:file-list="fileList" :before-upload="beforeUpload" accept=".json"
|
||||
:show-upload-list="false" :action="FILE_UPLOAD" @change="fileChange"
|
||||
:headers="{ 'X-Access-Token': getToken()}">
|
||||
<AIcon type="UploadOutlined" class="upload-button" />
|
||||
</a-upload>
|
||||
</j-upload>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="物模型" v-bind="validateInfos.import" v-if="formModel.metadataType === 'script'">
|
||||
</j-input>
|
||||
</j-form-item>
|
||||
<j-form-item label="物模型" v-bind="validateInfos.import" v-if="formModel.metadataType === 'script'">
|
||||
<MonacoEditor v-model="formModel.import" theme="vs" style="height: 300px"></MonacoEditor>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</template>
|
||||
<script setup lang="ts" name="Import">
|
||||
import { useForm } from 'ant-design-vue/es/form';
|
||||
|
|
@ -54,13 +54,14 @@ import { saveMetadata } from '@/api/device/instance'
|
|||
import { queryNoPagingPost, convertMetadata, modify } from '@/api/device/product'
|
||||
import type { DefaultOptionType } from 'ant-design-vue/es/select';
|
||||
import type { UploadProps, UploadFile, UploadChangeParam } from 'ant-design-vue/es';
|
||||
import type { DeviceMetadata, ProductItem } from '@/views/device/Product/typings'
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { DeviceMetadata } from '@/views/device/Product/typings'
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useInstanceStore } from '@/store/instance'
|
||||
import { useProductStore } from '@/store/product';
|
||||
import { FILE_UPLOAD } from '@/api/comm';
|
||||
import { getToken } from '@/utils/comm';
|
||||
import MonacoEditor from '@/components/MonacoEditor/index.vue'
|
||||
import { useMetadataStore } from '@/store/metadata';
|
||||
|
||||
const route = useRoute()
|
||||
const instanceStore = useInstanceStore()
|
||||
|
|
@ -87,7 +88,6 @@ const _visible = computed({
|
|||
})
|
||||
|
||||
const close = () => {
|
||||
console.log(1)
|
||||
emits('update:visible', false);
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ const operateLimits = (mdata: DeviceMetadata) => {
|
|||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
const metadataStore = useMetadataStore()
|
||||
const handleImport = async () => {
|
||||
validate().then(async (data) => {
|
||||
loading.value = true
|
||||
|
|
@ -224,6 +224,7 @@ const handleImport = async () => {
|
|||
} else {
|
||||
productStore.refresh(id as string)
|
||||
}
|
||||
metadataStore.set('importMetadata', true)
|
||||
// Store.set(SystemConst.GET_METADATA, true)
|
||||
// Store.set(SystemConst.REFRESH_METADATA_TABLE, true)
|
||||
close()
|
||||
|
|
@ -263,13 +264,14 @@ const handleImport = async () => {
|
|||
message.success('导入成功')
|
||||
}
|
||||
}
|
||||
// Store.set(SystemConst.GET_METADATA, true)
|
||||
// Store.set(SystemConst.REFRESH_METADATA_TABLE, true)
|
||||
if (props?.type === 'device') {
|
||||
instanceStore.refresh(id as string)
|
||||
} else {
|
||||
productStore.refresh(id as string)
|
||||
}
|
||||
metadataStore.set('importMetadata', true)
|
||||
// Store.set(SystemConst.GET_METADATA, true)
|
||||
// Store.set(SystemConst.REFRESH_METADATA_TABLE, true)
|
||||
close();
|
||||
} catch (e) {
|
||||
loading.value = false
|
||||
|
|
|
|||
|
|
@ -18,10 +18,25 @@
|
|||
:dataList="deviceStepDetails"
|
||||
/>
|
||||
</j-row>
|
||||
</div>
|
||||
|
||||
<div class="dialog">
|
||||
<ProductChooseDialog
|
||||
v-if="productDialogVisible"
|
||||
v-model:visible="productDialogVisible"
|
||||
@confirm="(id:string)=>jumpPage('device/Product/Detail', { id })"
|
||||
/>
|
||||
<DeviceChooseDialog
|
||||
v-if="deviceDialogVisible"
|
||||
v-model:visible="deviceDialogVisible"
|
||||
@confirm="(id:string)=>jumpPage('device/Instance/Detail', { id })"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="deviceHome">
|
||||
import ProductChooseDialog from '../dialogs/ProductChooseDialog.vue';
|
||||
import DeviceChooseDialog from '../dialogs/DeviceChooseDialog.vue';
|
||||
import BootCard from '../BootCard.vue';
|
||||
import DeviceCountCard from '../DeviceCountCard.vue';
|
||||
import PlatformPicCard from '../PlatformPicCard.vue';
|
||||
|
|
@ -29,6 +44,7 @@ import StepCard from '../StepCard.vue';
|
|||
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import { bootConfig, recommendList } from '../../typing';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
|
||||
// 按钮权限控制
|
||||
const hasPermission = usePermissionStore().hasPermission;
|
||||
|
|
@ -39,6 +55,11 @@ const devicePermission = (action: string) =>
|
|||
const rulePermission = (action: string) =>
|
||||
hasPermission(`rule-engine/Instance:${action}`);
|
||||
|
||||
const { jumpPage } = useMenuStore();
|
||||
|
||||
const productDialogVisible = ref(false);
|
||||
const deviceDialogVisible = ref(false);
|
||||
|
||||
const deviceBootConfig: bootConfig[] = [
|
||||
{
|
||||
english: 'STEP1',
|
||||
|
|
@ -87,7 +108,9 @@ const deviceStepDetails: recommendList[] = [
|
|||
iconUrl: '/images/home/bottom-1.png',
|
||||
linkUrl: 'device/Product/Detail',
|
||||
auth: productPermission('update'),
|
||||
dialogTag: 'accessMethod',
|
||||
onClick: () => {
|
||||
productDialogVisible.value = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '添加测试设备',
|
||||
|
|
@ -105,7 +128,9 @@ const deviceStepDetails: recommendList[] = [
|
|||
'对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-2.png',
|
||||
linkUrl: 'device/Instance/Detail',
|
||||
dialogTag: 'funcTest',
|
||||
onClick: () => {
|
||||
deviceDialogVisible.value = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '批量添加设备',
|
||||
|
|
|
|||
|
|
@ -3,34 +3,34 @@
|
|||
<div class="title">请选择首页视图</div>
|
||||
|
||||
<div class="choose-view">
|
||||
<a-row class="view-content" :gutter="24">
|
||||
<a-col
|
||||
<j-row class="view-content" :gutter="24">
|
||||
<j-col
|
||||
:span="8"
|
||||
class="select-item"
|
||||
:class="{ selected: selectValue === 'device' }"
|
||||
@click="selectValue = 'device'"
|
||||
>
|
||||
<img :src="getImage('/home/device.png')" alt="" />
|
||||
</a-col>
|
||||
<a-col
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="8"
|
||||
class="select-item"
|
||||
:class="{ selected: selectValue === 'ops' }"
|
||||
@click="selectValue = 'ops'"
|
||||
>
|
||||
<img :src="getImage('/home/ops.png')" alt="" />
|
||||
</a-col>
|
||||
<a-col
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="8"
|
||||
class="select-item"
|
||||
:class="{ selected: selectValue === 'comprehensive' }"
|
||||
@click="selectValue = 'comprehensive'"
|
||||
>
|
||||
<img :src="getImage('/home/comprehensive.png')" alt="" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" class="btn" @click="confirm"
|
||||
>确定</a-button
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-button type="primary" class="btn" @click="confirm"
|
||||
>确定</j-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,261 +1,278 @@
|
|||
<template>
|
||||
<Search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
class='search'
|
||||
target="scene-triggrt-device-device"
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type="simple"
|
||||
@search="handleSearch"
|
||||
class="search"
|
||||
target="scene-trigger-device-product"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<a-divider style="margin: 0" />
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
:params='params'
|
||||
:request='productQuery'
|
||||
:gridColumn='2'
|
||||
:gridColumns='[2,2,2]'
|
||||
:bodyStyle='{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0
|
||||
}'
|
||||
ref="actionRef"
|
||||
model="CARD"
|
||||
:columns="columns"
|
||||
:params="params"
|
||||
:request="productQuery"
|
||||
:gridColumn="2"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value='slotProps'
|
||||
:active="rowKey === slotProps.id"
|
||||
:status="slotProps.state"
|
||||
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
|
||||
:statusNames="{ 1: 'success', 0: 'error', }"
|
||||
@click="handleClick"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img width='88' height='88' :src="slotProps.photoUrl || getImage('/device-product.png')" />
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style='width: calc(100% - 100px)'>
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px;font-weight: 600" >
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
:active="rowKey === slotProps.id"
|
||||
:status="String(slotProps.state)"
|
||||
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
|
||||
:statusNames="{ '1': 'success', '0': 'error' }"
|
||||
@click="handleClick(slotProps)"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img
|
||||
:width="88"
|
||||
:height="88"
|
||||
:src="
|
||||
slotProps.photoUrl ||
|
||||
getImage('/device-product.png')
|
||||
"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style="width: calc(100% - 100px)">
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">设备类型</div>
|
||||
<Ellipsis>{{ slotProps.deviceType?.text }}</Ellipsis>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">接入方式</div>
|
||||
<Ellipsis>{{ slotProps?.accessName || '未接入' }}</Ellipsis>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
import { getProviders, queryGatewayList, queryProductList } from '@/api/device/product'
|
||||
import { queryTree } from '@/api/device/category'
|
||||
import { getTreeData_api } from '@/api/system/department'
|
||||
import { isNoCommunity } from '@/utils/utils'
|
||||
import { getImage } from '@/utils/comm'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:rowKey', data: string): void
|
||||
(e: 'update:detail', data: string): void
|
||||
(e: 'change', data: string): void
|
||||
}
|
||||
|
||||
const actionRef = ref()
|
||||
const params = ref({})
|
||||
const props = defineProps({
|
||||
import {
|
||||
getProviders,
|
||||
queryGatewayList,
|
||||
queryProductList,
|
||||
} from '@/api/device/product';
|
||||
import { queryTree } from '@/api/device/category';
|
||||
import { getTreeData_api } from '@/api/system/department';
|
||||
import { isNoCommunity } from '@/utils/utils';
|
||||
import { getImage } from '@/utils/comm';
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:rowKey', data: string): void;
|
||||
(e: 'update:detail', data: string): void;
|
||||
(e: 'change', data: string): void;
|
||||
};
|
||||
|
||||
const actionRef = ref();
|
||||
const params = ref({});
|
||||
const props = defineProps({
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const columns = [
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true
|
||||
}
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '网关类型',
|
||||
dataIndex: 'accessProvider',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () => getProviders().then((resp: any) => {
|
||||
if (isNoCommunity) {
|
||||
return (resp?.result || []).map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}))
|
||||
} else {
|
||||
return (resp?.result || []).filter((item: any) => [
|
||||
'mqtt-server-gateway',
|
||||
'http-server-gateway',
|
||||
'mqtt-client-gateway',
|
||||
'tcp-server-gateway',
|
||||
].includes(item.id))
|
||||
.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '接入方式',
|
||||
dataIndex: 'accessName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () => queryGatewayList().then((resp: any) =>
|
||||
resp.result.map((item: any) => ({
|
||||
label: item.name, value: item.id
|
||||
}))
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '设备类型',
|
||||
dataIndex: 'deviceType',
|
||||
width: 150,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '直连设备', value: 'device' },
|
||||
{ label: '网关子设备', value: 'childrenDevice' },
|
||||
{ label: '网关设备', value: 'gateway' },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
width: '90px',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 0 },
|
||||
{ label: '正常', value: 1 },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
ellipsis: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataIndex: 'classifiedId',
|
||||
title: '分类',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: queryTree({ paging: false }).then(resp => resp.result),
|
||||
componentProps: {
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
dataIndex: 'id$dim-assets',
|
||||
title: '所属组织',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: getTreeData_api({ paging: false }).then((resp: any) => {
|
||||
const formatValue = (list: any[]) => {
|
||||
return list.map((item: any) => {
|
||||
if (item.children) {
|
||||
item.children = formatValue(item.children);
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
value: JSON.stringify({
|
||||
assetType: 'product',
|
||||
targets: [
|
||||
{
|
||||
type: 'org',
|
||||
id: item.id,
|
||||
},
|
||||
],
|
||||
title: '网关类型',
|
||||
dataIndex: 'accessProvider',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
getProviders().then((resp: any) => {
|
||||
if (isNoCommunity) {
|
||||
return (resp?.result || []).map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
} else {
|
||||
return (resp?.result || [])
|
||||
.filter((item: any) =>
|
||||
[
|
||||
'mqtt-server-gateway',
|
||||
'http-server-gateway',
|
||||
'mqtt-client-gateway',
|
||||
'tcp-server-gateway',
|
||||
].includes(item.id),
|
||||
)
|
||||
.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
return formatValue(resp.result)
|
||||
}),
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = p
|
||||
}
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '接入方式',
|
||||
dataIndex: 'accessName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
queryGatewayList().then((resp: any) =>
|
||||
resp.result.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '设备类型',
|
||||
dataIndex: 'deviceType',
|
||||
width: 150,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '直连设备', value: 'device' },
|
||||
{ label: '网关子设备', value: 'childrenDevice' },
|
||||
{ label: '网关设备', value: 'gateway' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
width: '90px',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 0 },
|
||||
{ label: '正常', value: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
ellipsis: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataIndex: 'classifiedId',
|
||||
title: '分类',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: queryTree({ paging: false }).then((resp) => resp.result),
|
||||
componentProps: {
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'id$dim-assets',
|
||||
title: '所属组织',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: getTreeData_api({ paging: false }).then((resp: any) => {
|
||||
const formatValue = (list: any[]) => {
|
||||
return list.map((item: any) => {
|
||||
if (item.children) {
|
||||
item.children = formatValue(item.children);
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
value: JSON.stringify({
|
||||
assetType: 'product',
|
||||
targets: [
|
||||
{
|
||||
type: 'org',
|
||||
id: item.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
});
|
||||
};
|
||||
return formatValue(resp.result);
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = p;
|
||||
};
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
const sorts: any = [];
|
||||
|
||||
|
||||
if (props.rowKey) {
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.rowKey,
|
||||
});
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.rowKey,
|
||||
});
|
||||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts
|
||||
return queryProductList(p)
|
||||
}
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:rowKey', detail.id)
|
||||
emit('update:detail', detail)
|
||||
emit('change', detail)
|
||||
}
|
||||
|
||||
</script>
|
||||
p.sorts = sorts;
|
||||
return queryProductList(p);
|
||||
};
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:rowKey', detail.id);
|
||||
emit('update:detail', detail);
|
||||
emit('change', detail);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.search {
|
||||
.search {
|
||||
margin-bottom: 0;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<div>
|
||||
<template v-if="['valueType', 'name'].includes(column.dataIndex)">
|
||||
<span>{{ text }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<j-input />
|
||||
<!-- TODO -->
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</j-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue";
|
||||
|
||||
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 = computed({
|
||||
get: () => {
|
||||
return _props.modelValue || []
|
||||
},
|
||||
set: (val: any) => {
|
||||
_emit('update:modelValue', val);
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-form-item
|
||||
:name="['message', 'messageType']"
|
||||
label="动作类型"
|
||||
:rules="[{ required: true, message: '请选择动作类型' }]"
|
||||
>
|
||||
<TopCard
|
||||
:typeList="TypeList"
|
||||
v-model:value="modelRef.message.messageType"
|
||||
/>
|
||||
</j-form-item>
|
||||
<template v-if="deviceMessageType === 'INVOKE_FUNCTION'">
|
||||
<j-form-item
|
||||
:name="['message', 'functionId']"
|
||||
label="功能调用"
|
||||
:rules="[{ required: true, message: '请选择功能' }]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
placeholder="请选择功能"
|
||||
v-model:value="modelRef.message.functionId"
|
||||
@change="onFunctionChange"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.functions || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-if="modelRef.message.functionId"
|
||||
:name="['message', 'inputs']"
|
||||
:rules="[{ required: true, message: '请输入功能值' }]"
|
||||
>
|
||||
<EditTable v-model="modelRef.message.inputs" />
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="deviceMessageType === 'READ_PROPERTY'">
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="读取属性"
|
||||
:rules="[{ required: true, message: '请选择读取属性' }]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
placeholder="请选择属性"
|
||||
v-model:value="modelRef.message.properties"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.properties || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="deviceMessageType === 'WRITE_PROPERTY'">
|
||||
<j-row>
|
||||
<j-col :span="11">
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="读取属性"
|
||||
:rules="[
|
||||
{ required: true, message: '请选择读取属性' },
|
||||
]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
placeholder="请选择属性"
|
||||
v-model:value="modelRef.message.properties"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.properties || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="2"></j-col>
|
||||
<j-col :span="11">
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="读取属性"
|
||||
:rules="[
|
||||
{ required: true, message: '请选择读取属性' },
|
||||
]"
|
||||
>
|
||||
<j-select placeholder="请选择">
|
||||
<!-- TODO -->
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getImage } from '@/utils/comm';
|
||||
import TopCard from '../device/TopCard.vue';
|
||||
import { detail } from '@/api/device/instance';
|
||||
import EditTable from './EditTable.vue';
|
||||
|
||||
const TypeList = [
|
||||
{
|
||||
label: '功能调用',
|
||||
value: 'INVOKE_FUNCTION',
|
||||
image: getImage('/scene/invoke-function.png'),
|
||||
tip: '',
|
||||
},
|
||||
{
|
||||
label: '读取属性',
|
||||
value: 'READ_PROPERTY',
|
||||
image: getImage('/scene/read-property.png'),
|
||||
tip: '',
|
||||
},
|
||||
{
|
||||
label: '设置属性',
|
||||
value: 'WRITE_PROPERTY',
|
||||
image: getImage('/scene/write-property.png'),
|
||||
tip: '',
|
||||
},
|
||||
];
|
||||
|
||||
const props = defineProps({
|
||||
values: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const modelRef = reactive({
|
||||
message: {
|
||||
messageType: undefined,
|
||||
functionId: undefined,
|
||||
properties: undefined,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
|
||||
const metadata = ref<{
|
||||
functions: any[];
|
||||
properties: any[];
|
||||
}>({
|
||||
functions: [],
|
||||
properties: [],
|
||||
});
|
||||
|
||||
const deviceMessageType = computed(() => {
|
||||
return modelRef.message.messageType;
|
||||
});
|
||||
|
||||
const onFunctionChange = (val: string) => {
|
||||
const _item = (metadata.value?.functions || []).find((item: any) => {
|
||||
return val === item.id;
|
||||
});
|
||||
const list = (_item?.inputs || []).map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
value: undefined,
|
||||
valueType: item?.valueType?.type,
|
||||
};
|
||||
});
|
||||
modelRef.message.inputs = list;
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
// console.log(props.values)
|
||||
// console.log(metadata.value)
|
||||
// Object.assign()
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [props.values?.productDetail, props.values.deviceDetail],
|
||||
([newVal1, newVal2]) => {
|
||||
if (newVal1) {
|
||||
if (props.values?.selector === 'fixed') {
|
||||
detail(newVal2.id).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
metadata.value = JSON.parse(
|
||||
resp.result?.metadata || '{}',
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
metadata.value = JSON.parse(newVal1?.metadata || '{}');
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
<template>
|
||||
<!-- <j-advanced-search
|
||||
:columns="columns"
|
||||
type="simple"
|
||||
@search="handleSearch"
|
||||
class="search"
|
||||
target="scene-trigger-device-device"
|
||||
/> -->
|
||||
<a-divider style="margin: 0" />
|
||||
<j-pro-table
|
||||
ref="actionRef"
|
||||
model="CARD"
|
||||
:columns="columns"
|
||||
:params="params"
|
||||
:request="deviceQuery"
|
||||
:gridColumn="2"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
@click="handleClick"
|
||||
:active="value[0]?.value === slotProps.id"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
online: 'success',
|
||||
offline: 'error',
|
||||
notActive: 'warning',
|
||||
}"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img
|
||||
:src="getImage('/device/instance/device-card.png')"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style="width: calc(100% - 100px)">
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<j-row style="margin-top: 20px">
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">设备类型</div>
|
||||
<div>{{ slotProps.deviceType?.text }}</div>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">产品名称</div>
|
||||
<Ellipsis style="width: 100%">
|
||||
{{ slotProps.productName }}
|
||||
</Ellipsis>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
import { query } from '@/api/device/instance';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:value', data: any): void;
|
||||
(e: 'change', data: any): void;
|
||||
};
|
||||
|
||||
const actionRef = ref();
|
||||
const params = ref({
|
||||
terms: []
|
||||
});
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Array as PropType<any>,
|
||||
default: []
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
productId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 'notActive' },
|
||||
{ label: '离线', value: 'offline' },
|
||||
{ label: '在线', value: 'online' },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = {
|
||||
...p,
|
||||
terms: [
|
||||
...p.terms,
|
||||
{terms: [{ column: 'productId', value: props?.productId }]}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const deviceQuery = (p: any) => {
|
||||
const sorts: any = [];
|
||||
if (props.value) {
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.value,
|
||||
});
|
||||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts;
|
||||
return query(p);
|
||||
};
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:value', [{ value: detail.id, name: detail.name }]);
|
||||
emit('change', detail);
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
params.value = {
|
||||
...params.value,
|
||||
terms: params.value?.terms ? [
|
||||
...(params.value.terms || []),
|
||||
{terms: [{ column: 'productId', value: props?.productId }]}
|
||||
] : [{terms: [{ column: 'productId', value: props?.productId }]}]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.search {
|
||||
margin-bottom: 0;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in tagList" :key="item.id">
|
||||
<j-row :gutter="24" style="margin-bottom: 12px">
|
||||
<j-col :span="4">
|
||||
<span v-if="index === 0" class="tagName">标签选择</span>
|
||||
<j-select
|
||||
:options="[
|
||||
{ label: '并且', value: 'and' },
|
||||
{ label: '或者', value: 'or' },
|
||||
]"
|
||||
v-else
|
||||
:value="item?.type"
|
||||
style="width: 100%"
|
||||
@select="(key) => onTypeSelect(key, index)"
|
||||
/>
|
||||
</j-col>
|
||||
<j-col :span="16">
|
||||
<j-row :gutter="24">
|
||||
<j-col flex="120px">
|
||||
<j-select
|
||||
style="width: 120px"
|
||||
:value="item.id"
|
||||
placeholder="请选择标签"
|
||||
:options="options"
|
||||
@select="
|
||||
(_, _data) => onTagSelect(_data, index)
|
||||
"
|
||||
/>
|
||||
</j-col>
|
||||
<j-col flex="auto">
|
||||
<ValueItem
|
||||
v-model:modelValue="item.value"
|
||||
:itemType="item.valueType"
|
||||
@change="onValueChange"
|
||||
:options="
|
||||
item.valueType === 'enum'
|
||||
? (item?.dataType?.elements || []).map(
|
||||
(i) => {
|
||||
return {
|
||||
label: i.text,
|
||||
value: i.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
: item.valueType === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
<j-col :span="4">
|
||||
<j-space>
|
||||
<j-button style="padding: 0 8px" @click="addItem">
|
||||
<AIcon type="PlusOutlined" />
|
||||
</j-button>
|
||||
<j-button
|
||||
danger
|
||||
v-if="tagData.length > 1"
|
||||
style="padding: 0 8px"
|
||||
@click="deleteItem(index)"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-button>
|
||||
</j-space>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Array as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
tagData: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:value', 'change']);
|
||||
|
||||
const options = ref<any[]>([]);
|
||||
const tagList = ref<any[]>([]);
|
||||
|
||||
const handleItem = (data: any) => {
|
||||
return {
|
||||
...data,
|
||||
valueType: data.valueType ? data.valueType.type : '-',
|
||||
format: data.valueType ? data.valueType.format : undefined,
|
||||
options: data.valueType ? data.valueType.elements : undefined,
|
||||
value: data.value,
|
||||
};
|
||||
};
|
||||
|
||||
const addItem = () => {
|
||||
tagList.value.push({ type: 'and' });
|
||||
};
|
||||
|
||||
const deleteItem = (_index: number) => {
|
||||
tagList.value.splice(_index, 1);
|
||||
};
|
||||
|
||||
const onTypeSelect = (key: any, _index: number) => {
|
||||
const indexItem = tagList[_index];
|
||||
indexItem.type = key;
|
||||
tagList.value[_index] = indexItem;
|
||||
};
|
||||
|
||||
const onTagSelect = (_data: any, _index: number) => {
|
||||
const newList = [...unref(tagList)];
|
||||
const indexType = newList[_index].type;
|
||||
newList.splice(
|
||||
_index,
|
||||
1,
|
||||
handleItem({ ..._data, value: undefined, type: indexType }),
|
||||
);
|
||||
tagList.value = newList;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.tagData, props.value],
|
||||
([newTag, newVal]) => {
|
||||
options.value = newTag.map((item: any) => {
|
||||
return { label: item.name, value: item.id, ...item };
|
||||
});
|
||||
if (newVal && newVal[0] && newVal[0].name && newTag && newTag.length) {
|
||||
const names: string[] = [];
|
||||
const newTagList = newVal[0].value
|
||||
.filter((valueItem: any) => {
|
||||
return newTag.some(
|
||||
(item: any) => valueItem.column === item.id,
|
||||
);
|
||||
})
|
||||
.map((valueItem: any) => {
|
||||
const oldItem = newTag.find(
|
||||
(item: any) => item.id === valueItem.column,
|
||||
);
|
||||
if (oldItem) {
|
||||
names.push(oldItem.name);
|
||||
return {
|
||||
...handleItem(oldItem),
|
||||
value: valueItem.value,
|
||||
type: valueItem.type,
|
||||
};
|
||||
}
|
||||
return valueItem;
|
||||
});
|
||||
tagList.value = newTagList;
|
||||
} else {
|
||||
tagList.value = [{}];
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
const onValueChange = () => {
|
||||
const newValue = tagList.value
|
||||
.filter((item) => !!item.value)
|
||||
.map((item: any) => {
|
||||
return {
|
||||
column: item.id,
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
};
|
||||
});
|
||||
const arr = newValue
|
||||
.filter((item) => !!item.value)
|
||||
.map((item: any) => {
|
||||
return {
|
||||
column: item.name,
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
};
|
||||
});
|
||||
emits('update:value', [{ value: newValue, name: '标签' }]);
|
||||
emits('change', [{ value: newValue, name: '标签' }], undefined);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tagName::before {
|
||||
position: relative;
|
||||
left: 70px;
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
color: #ff4d4f;
|
||||
font-size: 14px;
|
||||
font-family: SimSun, sans-serif;
|
||||
line-height: 1;
|
||||
content: '*';
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<div class="trigger-way-warp" :class="{ disabled: disabled }">
|
||||
<div
|
||||
v-for="item in typeList"
|
||||
:key="item.value"
|
||||
class="trigger-way-item"
|
||||
:class="{
|
||||
active: _value === item.value,
|
||||
labelBottom: labelBottom,
|
||||
}"
|
||||
@click="onSelect(item.value)"
|
||||
>
|
||||
<div class="'way-item-title">
|
||||
<span class="way-item-label">{{ item.label }}</span>
|
||||
<j-popover v-if="item.tip">
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
class="way-item-icon"
|
||||
/>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class="way-item-image">
|
||||
<img :width="48" :src="item.image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
typeList: {
|
||||
type: Array as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
labelBottom: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:value', 'change']);
|
||||
const _value = ref(props?.value || '');
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
_value.value = newValue || ""
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const onSelect = (_type: string) => {
|
||||
if (!props.disabled) {
|
||||
_value.value = _type;
|
||||
emits('update:value', _type);
|
||||
emits('change', _type)
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.trigger-way-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px 24px;
|
||||
width: 100%;
|
||||
|
||||
.trigger-way-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 237px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #e0e4e8;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
.way-item-title {
|
||||
span {
|
||||
font-size: 16px;
|
||||
}
|
||||
.way-item-label {
|
||||
padding-right: 6px;
|
||||
color: rgba(#000, 0.64);
|
||||
}
|
||||
|
||||
.way-item-icon {
|
||||
color: rgba(#000, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.way-item-image {
|
||||
margin: 0 !important;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.way-item-image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: @primary-color-active;
|
||||
.way-item-image {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.labelBottom {
|
||||
flex-direction: column-reverse;
|
||||
grid-gap: 16px;
|
||||
gap: 0;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
padding: 8px 16px;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.trigger-way-item {
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
color: initial;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,14 +1,354 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<a-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-form-item
|
||||
name="selector"
|
||||
label="选择方式"
|
||||
v-show="!(list.length === 1)"
|
||||
:rules="[{ required: true, message: '请选择方式' }]"
|
||||
>
|
||||
<TopCard
|
||||
:typeList="list"
|
||||
v-model:value="modelRef.selector"
|
||||
@change="onSelectorChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-if="modelRef.selector === 'fixed'"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择设备' }]"
|
||||
>
|
||||
<Device
|
||||
:productId="values.productDetail.id"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
@change="onDeviceChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-else-if="modelRef.selector === 'relation'"
|
||||
label="关系"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择关系' }]"
|
||||
>
|
||||
<j-select
|
||||
placeholder="请选择关系"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
:options="relationList"
|
||||
show-search
|
||||
@change="onRelationChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-else-if="modelRef.selector === 'tag'"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择标签' }]"
|
||||
>
|
||||
<Tag
|
||||
v-model:value="modelRef.selectorValues"
|
||||
:tagData="tagList"
|
||||
@change="onTagChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-else
|
||||
name="selectorValues"
|
||||
label="变量"
|
||||
:rules="[{ required: true, message: '请选择' }]"
|
||||
>
|
||||
<j-tree-select
|
||||
style="width: 100%; height: 100%"
|
||||
:tree-data="builtInList"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
placeholder="请选择参数"
|
||||
@select="onVariableChange"
|
||||
>
|
||||
<template #title="{ name, description }">
|
||||
<a-space>
|
||||
{{ name }}
|
||||
<span style="color: grey; margin-left: 5px">{{
|
||||
description
|
||||
}}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
</j-tree-select>
|
||||
</j-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name="Device">
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import TopCard from './TopCard.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { queryBuiltInParams } from '@/api/rule-engine/scene';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import NoticeApi from '@/api/notice/config';
|
||||
import Device from './Device.vue';
|
||||
import Tag from './Tag.vue';
|
||||
|
||||
const props = defineProps({
|
||||
values: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['save', 'cancel']);
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data } = storeToRefs(sceneStore);
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const modelRef = reactive({
|
||||
selector: 'fixed',
|
||||
selectorValues: undefined,
|
||||
deviceId: '',
|
||||
source: '',
|
||||
relationName: '',
|
||||
upperKey: '',
|
||||
});
|
||||
|
||||
const list = ref<any[]>([]);
|
||||
const builtInList = ref<any[]>([]);
|
||||
const tagList = ref<any[]>([]);
|
||||
const relationList = ref<any[]>([]);
|
||||
|
||||
const TypeList = [
|
||||
{
|
||||
label: '自定义',
|
||||
value: 'fixed',
|
||||
image: getImage('/scene/device-custom.png'),
|
||||
tip: '自定义选择当前产品下的任意设备',
|
||||
},
|
||||
{
|
||||
label: '按关系',
|
||||
value: 'relation',
|
||||
image: getImage('/scene/device-relation.png'),
|
||||
tip: '选择与触发设备具有相同关系的设备',
|
||||
},
|
||||
{
|
||||
label: '按标签',
|
||||
value: 'tag',
|
||||
image: getImage('/scene/device-tag.png'),
|
||||
tip: '按标签选择产品下具有特定标签的设备',
|
||||
},
|
||||
{
|
||||
label: '按变量',
|
||||
value: 'variable',
|
||||
image: getImage('/scene/device-variable.png'),
|
||||
tip: '选择设备ID为上游变量值的设备',
|
||||
},
|
||||
];
|
||||
|
||||
const filterTree = (nodes: any[]) => {
|
||||
if (!nodes?.length) {
|
||||
return nodes;
|
||||
}
|
||||
return nodes.filter((it) => {
|
||||
if (
|
||||
it.children.find(
|
||||
(item: any) =>
|
||||
item.id.indexOf('deviceId' || 'device_id' || 'device_Id') >
|
||||
-1,
|
||||
) &&
|
||||
!it.children.find((item: any) => item.id.indexOf('boolean') > -1)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const treeDataFilter = (arr: any[]) => {
|
||||
if (Array.isArray(arr) && arr.length) {
|
||||
const list: any[] = [];
|
||||
arr.map((item: any) => {
|
||||
if (item.children) {
|
||||
const children = treeDataFilter(item.children);
|
||||
if (children.length) {
|
||||
list.push({
|
||||
...item,
|
||||
title: item.name,
|
||||
value: item.id,
|
||||
disabled: true,
|
||||
children,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
item.children.find(
|
||||
(item: any) =>
|
||||
item.id.indexOf(
|
||||
'deviceId' || 'device_id' || 'device_Id',
|
||||
) > -1,
|
||||
) &&
|
||||
!item.children.find(
|
||||
(item: any) => item.id.indexOf('bolaen') > -1,
|
||||
)
|
||||
) {
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return list;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const sourceChangeEvent = async () => {
|
||||
const _params = {
|
||||
branch: props.thenName,
|
||||
branchGroup: props.branchGroup,
|
||||
action: props.name - 1,
|
||||
};
|
||||
const resp = await queryBuiltInParams(unref(data), _params);
|
||||
if (resp.status === 200) {
|
||||
// const array = filterTree(resp.result as any[]);
|
||||
//判断相同产品才有按变量
|
||||
// if (props.formProductId === DeviceModel.productId)// TODO
|
||||
console.log(array);
|
||||
const arr = treeDataFilter(resp.result as any[]);
|
||||
builtInList.value = array;
|
||||
}
|
||||
};
|
||||
|
||||
const queryRelationList = () => {
|
||||
NoticeApi.getRelationUsers({
|
||||
paging: false,
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
terms: [{ termType: 'eq', column: 'objectTypeName', value: '设备' }],
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
relationList.value = (resp.result as any[]).map((item) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.relation,
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const filterType = async () => {
|
||||
const _list = TypeList.filter((item) => item.value === 'fixed');
|
||||
if (unref(data)?.trigger?.type === 'device') {
|
||||
//关系
|
||||
const res = await NoticeApi.getRelationUsers({
|
||||
paging: false,
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
terms: [
|
||||
{ termType: 'eq', column: 'objectTypeName', value: '设备' },
|
||||
],
|
||||
});
|
||||
if (res.status === 200 && res.result.length !== 0) {
|
||||
const array = TypeList.filter((item) => item.value === 'relation');
|
||||
_list.push(...array);
|
||||
}
|
||||
//标签
|
||||
const tag = JSON.parse(
|
||||
props.values.productDetail?.metadata || '{}',
|
||||
)?.tags;
|
||||
if (tag && tag.length !== 0) {
|
||||
const array = TypeList.filter((item) => item.value === 'tag');
|
||||
_list.push(...array);
|
||||
}
|
||||
//变量
|
||||
if (
|
||||
builtInList.value.length !== 0 &&
|
||||
!props.parallel &&
|
||||
props.name !== 0
|
||||
) {
|
||||
const array = TypeList.filter((item) => item.value === 'variable');
|
||||
_list.push(...array);
|
||||
}
|
||||
list.value = _list;
|
||||
} else {
|
||||
if (
|
||||
builtInList.value.length !== 0 &&
|
||||
!props.parallel &&
|
||||
props.name !== 0
|
||||
) {
|
||||
const array = TypeList.filter((item) => item.value === 'variable');
|
||||
_list.push(...array);
|
||||
}
|
||||
list.value = _list;
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectorChange = (val: string) => {
|
||||
if (val === 'relation') {
|
||||
queryRelationList();
|
||||
}
|
||||
};
|
||||
|
||||
const onDeviceChange = (_detail: any) => {
|
||||
if (_detail) {
|
||||
emits('save', modelRef, _detail);
|
||||
}
|
||||
};
|
||||
|
||||
const onRelationChange = (val: any, options: any) => {
|
||||
modelRef.deviceId = 'deviceId';
|
||||
modelRef.source = 'upper';
|
||||
modelRef.selectorValues = val;
|
||||
modelRef.upperKey = 'scene.deviceId';
|
||||
modelRef.relationName = options.label;
|
||||
emits('save', modelRef, {});
|
||||
};
|
||||
|
||||
const onTagChange = (val: any[], arr: any[]) => {
|
||||
if (val) {
|
||||
modelRef.deviceId = 'deviceId';
|
||||
modelRef.source = 'fixed';
|
||||
}
|
||||
if (arr) {
|
||||
tagList.value = arr;
|
||||
}
|
||||
};
|
||||
|
||||
const onVariableChange = (val: any, node: any) => {
|
||||
modelRef.deviceId = val;
|
||||
// modelRef.deviceDetail = node;
|
||||
modelRef.selectorValues = [{ value: val, name: node.description }] as any;
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
Object.assign(modelRef, props.values);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.values.productDetail,
|
||||
async (newVal) => {
|
||||
await sourceChangeEvent();
|
||||
if (newVal) {
|
||||
const metadata = JSON.parse(newVal?.metadata || '{}');
|
||||
tagList.value = metadata?.tags || [];
|
||||
filterType();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
<template>
|
||||
<j-modal title="执行动作" visible :width="860" @cancel="onCancel" @ok="save" :maskClosable="false">
|
||||
<j-steps :current='DeviceModel.current' @change='stepChange'>
|
||||
<j-modal
|
||||
title="执行动作"
|
||||
visible
|
||||
:width="860"
|
||||
@cancel="onCancel"
|
||||
@ok="save"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<j-steps :current="current" @change="stepChange">
|
||||
<j-step>
|
||||
<template #title>选择产品</template>
|
||||
</j-step>
|
||||
|
|
@ -11,36 +18,82 @@
|
|||
<template #title>执行动作</template>
|
||||
</j-step>
|
||||
</j-steps>
|
||||
<j-divider style='margin-bottom: 10px;' />
|
||||
<div class='steps-content'>
|
||||
<Product v-if='DeviceModel.current === 0' v-model:rowKey='DeviceModel.productId'
|
||||
v-model:detail='DeviceModel.productDetail' />
|
||||
<j-divider style="margin-bottom: 10px" />
|
||||
<div class="steps-content">
|
||||
<Product
|
||||
v-if="current === 0"
|
||||
v-model:rowKey="DeviceModel.productId"
|
||||
v-model:detail="DeviceModel.productDetail"
|
||||
/>
|
||||
<Device
|
||||
v-else-if="current === 1"
|
||||
:name="name"
|
||||
:parallel="parallel"
|
||||
:branchGroup="branchGroup"
|
||||
:thenName="thenName"
|
||||
:values="DeviceModel"
|
||||
@save="onDeviceSave"
|
||||
/>
|
||||
<Action v-else-if="current === 2"
|
||||
:name="name"
|
||||
:branchGroup="branchGroup"
|
||||
:thenName="thenName"
|
||||
:values="DeviceModel"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class='steps-action'>
|
||||
<j-button v-if='DeviceModel.current === 0' @click='cancel'>取消</j-button>
|
||||
<j-button v-else @click='prev'>上一步</j-button>
|
||||
<j-button type='primary' v-if='DeviceModel.current < 2' @click='saveClick'>下一步</j-button>
|
||||
<j-button type='primary' v-else @click='saveClick'>确定</j-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="steps-action">
|
||||
<j-button v-if="current === 0" @click="onCancel">取消</j-button>
|
||||
<j-button v-else @click="prev">上一步</j-button>
|
||||
<j-button type="primary" v-if="current < 2" @click="saveClick"
|
||||
>下一步</j-button
|
||||
>
|
||||
<j-button type="primary" v-else @click="saveClick"
|
||||
>确定</j-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DeviceModelType } from './typings';
|
||||
import Product from './Product.vue';
|
||||
import Device from './device/index.vue';
|
||||
import Action from './actions/index.vue';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
import { detail } from '@/api/device/product'
|
||||
|
||||
type Emit = {
|
||||
(e: 'cancel'): void
|
||||
(e: 'save', data: any, options: Record<string, any>): void
|
||||
}
|
||||
(e: 'cancel'): void;
|
||||
(e: 'save', data: any, options: Record<string, any>): void;
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const current = ref<number>(0);
|
||||
|
||||
const DeviceModel = reactive<DeviceModelType>({
|
||||
steps: [],
|
||||
current: 0,
|
||||
productId: '',
|
||||
deviceId: '',
|
||||
productDetail: {},
|
||||
|
|
@ -58,38 +111,64 @@ const DeviceModel = reactive<DeviceModelType>({
|
|||
columns: [],
|
||||
actionName: '',
|
||||
tagList: [],
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
const cancel = () => {
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const onCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
const save = async(step?: number) => {
|
||||
let _step = step !== undefined ? step : DeviceModel.current
|
||||
if (_step === 0) {
|
||||
DeviceModel.productId ? DeviceModel.current = 1 : onlyMessage('请选择产品', 'error')
|
||||
} else if (_step === 1) {
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
const save = async (step?: number) => {
|
||||
let _step = step !== undefined ? step : current.value;
|
||||
if (_step === 0) {
|
||||
DeviceModel.productId
|
||||
? (current.value = 1)
|
||||
: onlyMessage('请选择产品', 'error');
|
||||
} else if (_step === 1) {
|
||||
DeviceModel.deviceId
|
||||
? (current.value = 2)
|
||||
: onlyMessage('请选择设备', 'error');
|
||||
} else {
|
||||
}
|
||||
};
|
||||
|
||||
const stepChange = (step: number) => {
|
||||
if (step !== 0) {
|
||||
save(step - 1)
|
||||
save(step - 1);
|
||||
} else {
|
||||
DeviceModel.current = 0
|
||||
current.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const prev = () => {
|
||||
DeviceModel.current = DeviceModel.current - 1
|
||||
}
|
||||
const saveClick = () => save()
|
||||
current.value -= 1;
|
||||
};
|
||||
|
||||
const saveClick = () => save();
|
||||
|
||||
const onDeviceSave = (_data: any, _detail: any) => {
|
||||
Object.assign(DeviceModel, _data)
|
||||
DeviceModel.deviceId = _detail.id
|
||||
DeviceModel.deviceDetail = _detail
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
Object.assign(DeviceModel, newValue);
|
||||
if(newValue?.productId){
|
||||
detail(newValue.productId).then(resp => {
|
||||
if(resp.status === 200){
|
||||
DeviceModel.productDetail = resp.result
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.steps-steps {
|
||||
width: 100%;
|
||||
|
|
@ -100,5 +179,8 @@ const saveClick = () => save()
|
|||
|
||||
.steps-content {
|
||||
width: 100%;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,12 +2,12 @@ import { ProductItem } from '@/views/device/Product/typings';
|
|||
import { ActionsDeviceProps } from '../../../typings';
|
||||
|
||||
type DeviceModelType = {
|
||||
steps: {
|
||||
key: string;
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
}[];
|
||||
current: number;
|
||||
// steps: {
|
||||
// key: string;
|
||||
// title: string;
|
||||
// content: React.ReactNode;
|
||||
// }[];
|
||||
// current: number;
|
||||
productId: string;
|
||||
deviceId: string;
|
||||
productDetail: ProductItem | any;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-if="actionType === 'device'">
|
||||
<Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" />
|
||||
<Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" :thenName="branchesName" />
|
||||
</template>
|
||||
<template v-else-if="actionType === 'notify'">
|
||||
<Notify v-bind="props" :value="data?.notify" @cancel="onCancel" @save="onPropsOk" />
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<template #title="{ name, description }">
|
||||
<a-space>
|
||||
{{ name }}
|
||||
<spn style="color: grey; margin-left: 5px">{{ description }}</spn>
|
||||
<span style="color: grey; margin-left: 5px">{{ description }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
内部独立应用的<span>后端服务</span>相互<span>独立运行</span>,互不影响。
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="img1" />
|
||||
<j-image width="100%" :src="img1" />
|
||||
</div>
|
||||
|
||||
<h1>2.接入方式说明</h1>
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
</div>
|
||||
<div>内部独立应用的<span>后端服务在同一个环境运行</span>。</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="img2" />
|
||||
<j-image width="100%" :src="img2" />
|
||||
</div>
|
||||
<h1>2.接入方式说明</h1>
|
||||
<div>1、页面集成</div>
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
<h1>1.概述</h1>
|
||||
<div>钉钉企业内部应用适用于通过钉钉登录<span>物联网平台</span></div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="img4" />
|
||||
<j-image width="100%" :src="img4" />
|
||||
</div>
|
||||
<h1>2.接入方式说明</h1>
|
||||
<div>1、单点登录</div>
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
<h1>1.概述</h1>
|
||||
<div>微信网站应用适用于通过微信授权登录<span>物联网平台</span></div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="img3" />
|
||||
<j-image width="100%" :src="img3" />
|
||||
</div>
|
||||
<h1>2.接入方式说明</h1>
|
||||
<div>1、单点登录</div>
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
。例如将公司业务管理系统集成至物联网平台,或者将物联网平台集成至业务管理系统。以实现多处访问、集中管控的业务场景。
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="img5" />
|
||||
<j-image width="100%" :src="img5" />
|
||||
</div>
|
||||
<h1>2.接入方式说明</h1>
|
||||
<div>1、页面集成</div>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,10 +2,10 @@
|
|||
<div class="form-label-container">
|
||||
<span class="text">{{ props.text }}</span>
|
||||
<span class="required" v-show="props.required">*</span>
|
||||
<a-tooltip>
|
||||
<j-tooltip>
|
||||
<template #title>{{ props.tooltip }}</template>
|
||||
<AIcon type="QuestionCircleOutlined" class="icon" />
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,52 +1,52 @@
|
|||
<template>
|
||||
<div class="request-table-container">
|
||||
<a-table
|
||||
<j-table
|
||||
:columns="columns"
|
||||
:data-source="tableData"
|
||||
:datj-source="tableData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === 'key'">
|
||||
<a-input v-model:value="record.label" />
|
||||
<j-input v-model:value="record.label" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'value'">
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="record.value"
|
||||
v-if="props.valueType === 'input'"
|
||||
/>
|
||||
<a-select
|
||||
<j-select
|
||||
v-else-if="props.valueType === 'select'"
|
||||
v-model:value="record.value"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in props.valueOptions"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>{{ item.label }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</j-select>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'action'">
|
||||
<a-button
|
||||
<j-button
|
||||
type="link"
|
||||
@click="removeRow((current - 1) * 10 + index)"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
</j-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
</j-table>
|
||||
<j-pagination
|
||||
v-show="props.value.length > 10"
|
||||
v-model:current="current"
|
||||
:page-size="10"
|
||||
:total="props.value.length"
|
||||
show-less-items
|
||||
/>
|
||||
<a-button type="dashed" @click="addRow" class="add-btn">
|
||||
<j-button type="dashed" @click="addRow" class="add-btn">
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</a-button>
|
||||
</j-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -120,7 +120,6 @@ function addRow() {
|
|||
label: '',
|
||||
value: '',
|
||||
};
|
||||
console.log(111);
|
||||
|
||||
emits('update:value', [...props.value, newRow]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<a-card class="save-container">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="14">
|
||||
<div class="save-container">
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="14">
|
||||
<EditForm @change-apply-type="chengeType" />
|
||||
</a-col>
|
||||
<a-col :span="10"><Does :type="rightType" /></a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</j-col>
|
||||
<j-col :span="10"><Does :type="rightType" /></j-col>
|
||||
</j-row>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
|
@ -21,3 +21,10 @@ const chengeType = (newType: applyType) => {
|
|||
rightType.value = newType;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.save-container {
|
||||
background-color: #fff;
|
||||
padding: 24px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="dialog.visible"
|
||||
<j-modal
|
||||
visible
|
||||
title="集成菜单"
|
||||
width="600px"
|
||||
@ok="dialog.handleOk"
|
||||
@cancel="dialog.cancel"
|
||||
@ok="handleOk"
|
||||
@cancel="cancel"
|
||||
class="edit-dialog-container"
|
||||
:confirmLoading="dialog.loading"
|
||||
cancelText="取消"
|
||||
okText="确定"
|
||||
:confirmLoading="loading"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
v-model:value="form.checkedSystem"
|
||||
@change="(value) => value && getTree(value as string)"
|
||||
@change="(value:string) => value && getTree(value)"
|
||||
style="width: 200px"
|
||||
placeholder="请选择集成系统"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in form.systemList"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>{{ item.label }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</j-select>
|
||||
|
||||
<p style="margin: 20px 0 0 0" v-show="form.menuTree.length > 0">当前集成菜单</p>
|
||||
<a-tree
|
||||
<p style="margin: 20px 0 0 0" v-show="form.menuTree.length > 0">
|
||||
当前集成菜单
|
||||
</p>
|
||||
<j-tree
|
||||
v-model:checkedKeys="form.checkedMenu"
|
||||
v-model:expandedKeys="form.expandedKeys"
|
||||
checkable
|
||||
|
|
@ -35,8 +35,8 @@
|
|||
<template #title="{ name }">
|
||||
<span>{{ name }}</span>
|
||||
</template>
|
||||
</a-tree>
|
||||
</a-modal>
|
||||
</j-tree>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -55,69 +55,60 @@ import { message } from 'ant-design-vue';
|
|||
import { getMenuTree_api } from '@/api/system/menu';
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
const emits = defineEmits(['update:visible']);
|
||||
const props = defineProps<{
|
||||
mode: 'add' | 'edit';
|
||||
visible: boolean;
|
||||
id: string;
|
||||
provider: applyType;
|
||||
}>();
|
||||
// 弹窗相关
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
loading: false,
|
||||
|
||||
handleOk: () => {
|
||||
const items = filterTree(form.menuTree, [
|
||||
...form.checkedMenu,
|
||||
...form.half,
|
||||
]);
|
||||
if (form.checkedSystem) {
|
||||
if (items && items.length !== 0) {
|
||||
saveOwnerMenu_api('iot', form.id, items).then((resp) => {
|
||||
const loading = ref(false);
|
||||
const handleOk = () => {
|
||||
const items = filterTree(form.menuTree, [
|
||||
...form.checkedMenu,
|
||||
...form.half,
|
||||
]);
|
||||
if (form.checkedSystem) {
|
||||
if (items && items.length !== 0) {
|
||||
loading.value = true;
|
||||
saveOwnerMenu_api('iot', form.id, items)
|
||||
.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
dialog.visible = false;
|
||||
emits('update:visible', false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
message.warning('请勾选配置菜单');
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
message.warning('请选择所属系统');
|
||||
message.warning('请勾选配置菜单');
|
||||
}
|
||||
},
|
||||
cancel: () => {
|
||||
if (props.mode === 'add')
|
||||
menuStory.jumpPage('system/Apply/Save', {}, { id: form.id });
|
||||
dialog.visible = false;
|
||||
},
|
||||
changeVisible: (id: string, provider: applyType) => {
|
||||
form.id = id;
|
||||
form.provider = provider;
|
||||
form.checkedSystem = undefined;
|
||||
form.checkedMenu = [];
|
||||
dialog.visible = true;
|
||||
|
||||
if (id) {
|
||||
getSystemList();
|
||||
getMenus();
|
||||
}
|
||||
},
|
||||
});
|
||||
// 将打开弹窗的操作暴露给父组件
|
||||
defineExpose({
|
||||
openDialog: dialog.changeVisible,
|
||||
});
|
||||
} else {
|
||||
message.warning('请选择所属系统');
|
||||
}
|
||||
};
|
||||
const cancel = () => {
|
||||
if (props.mode === 'add')
|
||||
menuStory.jumpPage('system/Apply/Save', {}, { id: form.id });
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
const form = reactive({
|
||||
id: '',
|
||||
checkedSystem: '' as undefined | string,
|
||||
id: props.id,
|
||||
checkedSystem: undefined as undefined | string,
|
||||
checkedMenu: [] as string[],
|
||||
expandedKeys: [] as string[],
|
||||
half: [] as string[],
|
||||
|
||||
provider: '' as applyType,
|
||||
provider: props.provider,
|
||||
systemList: [] as optionItemType[],
|
||||
menuTree: [] as any[],
|
||||
});
|
||||
|
||||
if (props.id) {
|
||||
getSystemList();
|
||||
getMenus();
|
||||
}
|
||||
/**
|
||||
* 与集成系统关联的菜单
|
||||
* @param params
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<div class="apply-container">
|
||||
<Search :columns="columns" @search="search" />
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
@search="(params:any)=>queryParams = {...params}"
|
||||
/>
|
||||
|
||||
<j-pro-table
|
||||
ref="tableRef"
|
||||
|
|
@ -10,7 +13,7 @@
|
|||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
:params="params"
|
||||
:params="queryParams"
|
||||
:gridColumn="3"
|
||||
>
|
||||
<template #headerTitle>
|
||||
|
|
@ -53,8 +56,8 @@
|
|||
<h3 class="card-item-content-title">
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
类型
|
||||
</div>
|
||||
|
|
@ -65,45 +68,45 @@
|
|||
)
|
||||
}}
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
说明
|
||||
</div>
|
||||
<div>{{ slotProps.description }}</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
>
|
||||
<a-dropdown
|
||||
<j-dropdown
|
||||
placement="bottomRight"
|
||||
v-if="item.key === 'others'"
|
||||
>
|
||||
<a-button>
|
||||
<j-button>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</j-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item
|
||||
<j-menu>
|
||||
<j-menu-item
|
||||
v-for="(o, i) in item.children"
|
||||
:key="i"
|
||||
>
|
||||
<a-button
|
||||
<j-button
|
||||
type="link"
|
||||
@click="o.onClick"
|
||||
>
|
||||
<AIcon :type="o.icon" />
|
||||
<span>{{ o.text }}</span>
|
||||
</a-button>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</j-button>
|
||||
</j-menu-item>
|
||||
</j-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</j-dropdown>
|
||||
<PermissionButton
|
||||
v-else
|
||||
:uhasPermission="item.permission"
|
||||
|
|
@ -117,7 +120,7 @@
|
|||
item.text
|
||||
}}</span>
|
||||
</PermissionButton>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
|
||||
<template #mark>
|
||||
|
|
@ -144,7 +147,7 @@
|
|||
></BadgeStatus>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
<j-space :size="16">
|
||||
<PermissionButton
|
||||
v-for="i in table.getActions(slotProps, 'table')"
|
||||
:uhasPermission="i.permission"
|
||||
|
|
@ -156,12 +159,18 @@
|
|||
>
|
||||
<AIcon :type="i.icon" />
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</div>
|
||||
<div class="dialogs">
|
||||
<MenuDialog ref="dialogRef" mode="edit" />
|
||||
<MenuDialog
|
||||
v-if="dialogVisible"
|
||||
v-model:visible="dialogVisible"
|
||||
:id="selectId"
|
||||
:provider="selectProvider"
|
||||
mode="edit"
|
||||
/>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
|
@ -263,11 +272,9 @@ const columns = [
|
|||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
const params = ref({});
|
||||
const search = (newParams: any) => (params.value = { ...newParams });
|
||||
const queryParams = ref({});
|
||||
|
||||
const tableRef = ref();
|
||||
const dialogRef = ref();
|
||||
const table = {
|
||||
refresh: () => {
|
||||
tableRef.value.reload();
|
||||
|
|
@ -359,8 +366,9 @@ const table = {
|
|||
},
|
||||
icon: 'MenuUnfoldOutlined',
|
||||
onClick: () => {
|
||||
dialogRef.value &&
|
||||
dialogRef.value.openDialog(data.id, data.provider);
|
||||
selectId.value = data.id;
|
||||
selectProvider.value = data.provider;
|
||||
dialogVisible.value = true;
|
||||
},
|
||||
});
|
||||
// 有api操作权限
|
||||
|
|
@ -415,6 +423,9 @@ const table = {
|
|||
return typeOptions.find((item) => item.value === val)?.label;
|
||||
},
|
||||
};
|
||||
const dialogVisible = ref(false);
|
||||
const selectId = ref<string>('');
|
||||
const selectProvider = ref<any>('');
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
|||
|
|
@ -17,10 +17,17 @@
|
|||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
<j-auto-complete
|
||||
v-model:value="form.data.id"
|
||||
:options="codeOptions"
|
||||
placeholder="请输入编码"
|
||||
:disabled="props.mode !== '新增'"
|
||||
/>
|
||||
>
|
||||
<template #option="{ value: val, message }">
|
||||
{{ val }}
|
||||
<span class="message">{{ message }}</span>
|
||||
</template>
|
||||
</j-auto-complete>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="名称"
|
||||
|
|
@ -122,6 +129,11 @@ const form = reactive({
|
|||
return Promise.resolve();
|
||||
},
|
||||
});
|
||||
const codeOptions = [
|
||||
{ label: 'add', value: 'add', message: '新增' },
|
||||
{ label: 'delete', value: 'delete', message: '删除' },
|
||||
{ label: 'update', value: 'update', message: '更新' },
|
||||
];
|
||||
|
||||
type formType = {
|
||||
name: string;
|
||||
|
|
@ -141,6 +153,11 @@ type formType = {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<h5>{{ selectApi.summary }}</h5>
|
||||
<div class="input">
|
||||
<InputCard :value="selectApi.method" />
|
||||
<a-input :value="selectApi?.url" disabled />
|
||||
<j-input :value="selectApi?.url" disabled />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -18,6 +18,14 @@
|
|||
<span>{{ `["/"]` }}</span>
|
||||
</p>
|
||||
|
||||
<div class="api-card" v-if="props.selectApi.description">
|
||||
<h5>接口描述</h5>
|
||||
<div>{{ props.selectApi.description }}</div>
|
||||
</div>
|
||||
<div class="api-card" v-if="requestCard.codeText">
|
||||
<h5>请求示例</h5>
|
||||
<JsonViewer :value="requestCard.codeText" copyable />
|
||||
</div>
|
||||
<div class="api-card">
|
||||
<h5>请求参数</h5>
|
||||
<div class="content">
|
||||
|
|
@ -26,12 +34,13 @@
|
|||
:dataSource="requestCard.tableData"
|
||||
noPagination
|
||||
model="TABLE"
|
||||
size="small"
|
||||
>
|
||||
<template #required="slotProps">
|
||||
<span>{{ Boolean(slotProps.required) + '' }}</span>
|
||||
</template>
|
||||
<template #type="slotProps">
|
||||
<span>{{ slotProps.schema.type }}</span>
|
||||
<span>{{ slotProps?.schema.type }}</span>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</div>
|
||||
|
|
@ -44,16 +53,17 @@
|
|||
:dataSource="responseStatusCard.tableData"
|
||||
noPagination
|
||||
model="TABLE"
|
||||
size="small"
|
||||
>
|
||||
</j-pro-table>
|
||||
|
||||
<a-tabs v-model:activeKey="responseStatusCard.activeKey">
|
||||
<a-tab-pane
|
||||
<j-tabs v-model:activeKey="responseStatusCard.activeKey">
|
||||
<j-tab-pane
|
||||
:key="key"
|
||||
:tab="key"
|
||||
v-for="key in tabs"
|
||||
></a-tab-pane>
|
||||
</a-tabs>
|
||||
></j-tab-pane>
|
||||
</j-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -65,21 +75,19 @@
|
|||
:dataSource="respParamsCard.tableData"
|
||||
noPagination
|
||||
model="TABLE"
|
||||
size="small"
|
||||
>
|
||||
</j-pro-table>
|
||||
</div>
|
||||
|
||||
<MonacoEditor
|
||||
v-model:modelValue="codeText"
|
||||
style="height: 300px; width: 100%"
|
||||
theme="vs"
|
||||
/>
|
||||
<JsonViewer :value="respParamsCard.codeText" copyable />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
import 'vue3-json-viewer/dist/index.css';
|
||||
import type { apiDetailsType } from '../typing';
|
||||
import InputCard from './InputCard.vue';
|
||||
import { PropType } from 'vue';
|
||||
|
|
@ -98,7 +106,7 @@ const { selectApi } = toRefs(props);
|
|||
|
||||
type tableCardType = {
|
||||
columns: object[];
|
||||
tableData: object[];
|
||||
tableData: any[];
|
||||
codeText?: any;
|
||||
activeKey?: any;
|
||||
getData?: any;
|
||||
|
|
@ -134,8 +142,36 @@ const requestCard = reactive<tableCardType>({
|
|||
},
|
||||
],
|
||||
tableData: [],
|
||||
codeText: undefined,
|
||||
getData: () => {
|
||||
requestCard.tableData = props.selectApi.parameters;
|
||||
if (!props.selectApi.requestBody)
|
||||
return (requestCard.tableData = props.selectApi.parameters);
|
||||
const schema =
|
||||
props.selectApi.requestBody.content['application/json'].schema;
|
||||
const schemaName = (schema.$ref || schema.items.$ref)?.split('/').pop();
|
||||
const type = schema.type || '';
|
||||
const tableData = findData(schemaName);
|
||||
if (type === 'array') {
|
||||
requestCard.codeText = [getCodeText(tableData, 3)];
|
||||
} else requestCard.codeText = getCodeText(tableData, 3);
|
||||
// console.clear();
|
||||
// console.log(schemaName, tableData);
|
||||
|
||||
requestCard.tableData = [
|
||||
{
|
||||
name: schemaName[0].toLowerCase() + schemaName.substring(1),
|
||||
description: schemaName,
|
||||
in: 'body',
|
||||
required: true,
|
||||
schema: { type: type || schemaName },
|
||||
children: tableData.map((item) => ({
|
||||
name: item.paramsName,
|
||||
description: item.desc,
|
||||
required: false,
|
||||
schema: { type: item.paramsType },
|
||||
})),
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
const responseStatusCard = reactive<tableCardType>({
|
||||
|
|
@ -200,102 +236,17 @@ const respParamsCard = reactive<tableCardType>({
|
|||
tableData: [],
|
||||
codeText: '',
|
||||
getData: (code: string) => {
|
||||
type schemaObjType = {
|
||||
paramsName: string;
|
||||
paramsType: string;
|
||||
desc?: string;
|
||||
children?: schemaObjType[];
|
||||
};
|
||||
|
||||
const schemaName = responseStatusCard.tableData.find(
|
||||
(item: any) => item.code === code,
|
||||
)?.schema;
|
||||
const schemas = toRaw(props.schemas);
|
||||
const basicType = ['string', 'integer', 'boolean'];
|
||||
|
||||
const tableData = findData(schemaName);
|
||||
const codeText = getCodeText(tableData, 3);
|
||||
respParamsCard.codeText = getCodeText(tableData, 3);
|
||||
|
||||
respParamsCard.tableData = tableData;
|
||||
respParamsCard.codeText = JSON.stringify(codeText);
|
||||
|
||||
function findData(schemaName: string) {
|
||||
if (!schemaName || !schemas[schemaName]) {
|
||||
return [];
|
||||
}
|
||||
const result: schemaObjType[] = [];
|
||||
const schema = schemas[schemaName];
|
||||
Object.entries(schema.properties).forEach((item: [string, any]) => {
|
||||
const paramsType =
|
||||
item[1].type ||
|
||||
(item[1].$ref && item[1].$ref.split('/').pop()) ||
|
||||
(item[1].items && item[1].items.$ref.split('/').pop()) ||
|
||||
'';
|
||||
const schemaObj: schemaObjType = {
|
||||
paramsName: item[0],
|
||||
paramsType,
|
||||
desc: item[1].description || '',
|
||||
};
|
||||
if (!basicType.includes(paramsType))
|
||||
schemaObj.children = findData(paramsType);
|
||||
result.push(schemaObj);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
function getCodeText(arr: schemaObjType[], level: number): object {
|
||||
const result = {};
|
||||
|
||||
arr.forEach((item) => {
|
||||
switch (item.paramsType) {
|
||||
case 'string':
|
||||
result[item.paramsName] = '';
|
||||
break;
|
||||
case 'integer':
|
||||
result[item.paramsName] = 0;
|
||||
break;
|
||||
case 'boolean':
|
||||
result[item.paramsName] = true;
|
||||
break;
|
||||
case 'array':
|
||||
result[item.paramsName] = [];
|
||||
break;
|
||||
case 'object':
|
||||
result[item.paramsName] = {};
|
||||
break;
|
||||
default: {
|
||||
const properties = schemas[item.paramsType]
|
||||
.properties as object;
|
||||
const newArr = Object.entries(properties).map(
|
||||
(item: [string, any]) => ({
|
||||
paramsName: item[0],
|
||||
paramsType: level
|
||||
? (item[1].$ref &&
|
||||
item[1].$ref.split('/').pop()) ||
|
||||
(item[1].items &&
|
||||
item[1].items.$ref
|
||||
.split('/')
|
||||
.pop()) ||
|
||||
item[1].type ||
|
||||
''
|
||||
: item[1].type,
|
||||
}),
|
||||
);
|
||||
result[item.paramsName] = getCodeText(
|
||||
newArr,
|
||||
level - 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const { codeText } = toRefs(requestCard);
|
||||
|
||||
const getContent = (data: any) => {
|
||||
if (data && data.content) {
|
||||
return Object.keys(data.content || {})[0];
|
||||
|
|
@ -317,6 +268,83 @@ watch(
|
|||
watch([() => responseStatusCard.activeKey, () => props.selectApi], (n) => {
|
||||
n[0] && respParamsCard.getData(n[0]);
|
||||
});
|
||||
|
||||
function findData(schemaName: string) {
|
||||
const schemas = toRaw(props.schemas);
|
||||
const basicType = ['string', 'integer', 'boolean'];
|
||||
|
||||
if (!schemaName || !schemas[schemaName]) {
|
||||
return [];
|
||||
}
|
||||
const result: schemaObjType[] = [];
|
||||
const schema = schemas[schemaName];
|
||||
Object.entries(schema.properties).forEach((item: [string, any]) => {
|
||||
const paramsType =
|
||||
item[1].type ||
|
||||
(item[1].$ref && item[1].$ref.split('/').pop()) ||
|
||||
(item[1].items && item[1].items.$ref.split('/').pop()) ||
|
||||
'';
|
||||
const schemaObj: schemaObjType = {
|
||||
paramsName: item[0],
|
||||
paramsType,
|
||||
desc: item[1].description || '',
|
||||
};
|
||||
if (!basicType.includes(paramsType))
|
||||
schemaObj.children = findData(paramsType);
|
||||
result.push(schemaObj);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
function getCodeText(arr: schemaObjType[], level: number): object {
|
||||
const result = {};
|
||||
const schemas = toRaw(props.schemas);
|
||||
arr.forEach((item) => {
|
||||
switch (item.paramsType) {
|
||||
case 'string':
|
||||
result[item.paramsName] = '';
|
||||
break;
|
||||
case 'integer':
|
||||
result[item.paramsName] = 0;
|
||||
break;
|
||||
case 'boolean':
|
||||
result[item.paramsName] = true;
|
||||
break;
|
||||
case 'array':
|
||||
result[item.paramsName] = [];
|
||||
break;
|
||||
case 'object':
|
||||
result[item.paramsName] = '';
|
||||
break;
|
||||
default: {
|
||||
const properties = schemas[item.paramsType]
|
||||
.properties as object;
|
||||
const newArr = Object.entries(properties).map(
|
||||
(item: [string, any]) => ({
|
||||
paramsName: item[0],
|
||||
paramsType: level
|
||||
? (item[1].$ref && item[1].$ref.split('/').pop()) ||
|
||||
(item[1].items &&
|
||||
item[1].items.$ref.split('/').pop()) ||
|
||||
item[1].type ||
|
||||
''
|
||||
: item[1].type,
|
||||
}),
|
||||
);
|
||||
result[item.paramsName] = getCodeText(newArr, level - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
type schemaObjType = {
|
||||
paramsName: string;
|
||||
paramsType: string;
|
||||
desc?: string;
|
||||
children?: schemaObjType[];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<h5>{{ props.selectApi.summary }}</h5>
|
||||
<div class="input">
|
||||
<InputCard :value="props.selectApi.method" />
|
||||
<a-input :value="props.selectApi?.url" disabled />
|
||||
<j-input :value="props.selectApi?.url" disabled />
|
||||
<span class="send" @click="send">发送</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -12,14 +12,9 @@
|
|||
<div class="api-card">
|
||||
<h5>请求参数</h5>
|
||||
<div class="content">
|
||||
<!-- <VueJsoneditor
|
||||
height="400"
|
||||
mode="tree"
|
||||
v-model:text="requestBody.paramsText"
|
||||
/> -->
|
||||
<div class="table" v-if="paramsTable.length">
|
||||
<a-form :model="requestBody.params" ref="formRef" >
|
||||
<a-table
|
||||
<j-form :model="requestBody.params" ref="formRef">
|
||||
<j-table
|
||||
:columns="requestBody.tableColumns"
|
||||
:dataSource="paramsTable"
|
||||
:pagination="false"
|
||||
|
|
@ -27,7 +22,7 @@
|
|||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:name="[
|
||||
'paramsTable',
|
||||
index +
|
||||
|
|
@ -42,13 +37,13 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="record.name"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
></j-input>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<a-form-item
|
||||
<j-form-item
|
||||
:name="[
|
||||
'paramsTable',
|
||||
index +
|
||||
|
|
@ -63,10 +58,10 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
v-model:value="record.value"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
></j-input>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<PermissionButton
|
||||
|
|
@ -82,60 +77,53 @@
|
|||
</PermissionButton>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-form>
|
||||
</j-table>
|
||||
</j-form>
|
||||
|
||||
<a-pagination
|
||||
<j-pagination
|
||||
:pageSize="requestBody.pageSize"
|
||||
v-model:current="requestBody.pageNum"
|
||||
:total="requestBody.params.paramsTable.length"
|
||||
hideOnSinglePage
|
||||
style="text-align: center"
|
||||
/>
|
||||
<a-button
|
||||
<j-button
|
||||
@click="requestBody.addRow"
|
||||
style="width: 100%; text-align: center"
|
||||
>
|
||||
<AIcon type="PlusOutlined" />新增
|
||||
</a-button>
|
||||
</j-button>
|
||||
</div>
|
||||
<MonacoEditor
|
||||
v-model:modelValue="requestBody.paramsText"
|
||||
v-model:modelValue="requestBody.code"
|
||||
style="height: 300px; width: 100%"
|
||||
theme="vs"
|
||||
ref="editorRef"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="api-card">
|
||||
<h5>响应参数</h5>
|
||||
<div class="content">
|
||||
<VueJsoneditor
|
||||
height="400"
|
||||
mode="tree"
|
||||
v-model:text="responsesContent"
|
||||
:disabled="true"
|
||||
/>
|
||||
<!-- <MonacoEditor
|
||||
v-model:modelValue="responsesContent"
|
||||
style="height: 300px; width: 100%"
|
||||
theme="vs"
|
||||
/> -->
|
||||
<JsonViewer :value="responsesContent" copyable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import VueJsoneditor from 'vue3-ts-jsoneditor';
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
import 'vue3-json-viewer/dist/index.css';
|
||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||
import type { apiDetailsType } from '../typing';
|
||||
import InputCard from './InputCard.vue';
|
||||
import { cloneDeep, toLower } from 'lodash';
|
||||
import { FormInstance } from 'ant-design-vue';
|
||||
import server from '@/utils/request'
|
||||
import server from '@/utils/request';
|
||||
|
||||
const props = defineProps<{
|
||||
selectApi: apiDetailsType;
|
||||
schemas: any;
|
||||
}>();
|
||||
const formRef = ref<FormInstance>();
|
||||
const requestBody = reactive({
|
||||
|
|
@ -163,10 +151,12 @@ const requestBody = reactive({
|
|||
pageSize: 10,
|
||||
pageNum: 1,
|
||||
params: {
|
||||
paramsTable: cloneDeep(props.selectApi.parameters || []) as requestObj[],
|
||||
paramsTable: cloneDeep(
|
||||
props.selectApi.parameters || [],
|
||||
) as requestObj[],
|
||||
},
|
||||
|
||||
paramsText: '',
|
||||
code: '',
|
||||
|
||||
addRow: () => {
|
||||
if (paramsTable.value.length === 10)
|
||||
|
|
@ -188,49 +178,142 @@ const paramsTable = computed(() => {
|
|||
return requestBody.params.paramsTable.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
const responsesContent = ref('{"a":123}');
|
||||
|
||||
const responsesContent = ref({});
|
||||
const editorRef = ref()
|
||||
const send = () => {
|
||||
formRef.value &&
|
||||
formRef.value.validate().then(() => {
|
||||
const methodName = toLower(props.selectApi.method)
|
||||
const methodObj = {
|
||||
get: 'get',
|
||||
post: 'post',
|
||||
patch: 'patch',
|
||||
put: 'put',
|
||||
delete: 'remove'
|
||||
}
|
||||
|
||||
let url = props.selectApi?.url;
|
||||
const urlParams = {}
|
||||
requestBody.params.paramsTable.forEach(item=>{
|
||||
if(methodName === 'get')
|
||||
urlParams[item.name] = item.value
|
||||
if(url.includes(`{${item.name}}`))
|
||||
url = url.replace(`{${item.name}}`, item.value)
|
||||
})
|
||||
const params = {
|
||||
...JSON.parse(requestBody.paramsText || '{}'),
|
||||
...urlParams
|
||||
}
|
||||
|
||||
|
||||
|
||||
server[methodObj[methodName]](url,params).then((resp:any)=>{
|
||||
responsesContent.value = JSON.stringify(resp)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
if (paramsTable.value.length)
|
||||
formRef.value &&
|
||||
formRef.value.validate().then(() => {
|
||||
_send();
|
||||
});
|
||||
else _send();
|
||||
};
|
||||
const _send = () => {
|
||||
const methodName = toLower(props.selectApi.method);
|
||||
const methodObj = {
|
||||
get: 'get',
|
||||
post: 'post',
|
||||
patch: 'patch',
|
||||
put: 'put',
|
||||
delete: 'remove',
|
||||
};
|
||||
|
||||
let url = props.selectApi?.url;
|
||||
const urlParams = {};
|
||||
requestBody.params.paramsTable.forEach((item) => {
|
||||
if (methodName === 'get') urlParams[item.name] = item.value;
|
||||
if (url.includes(`{${item.name}}`))
|
||||
url = url.replace(`{${item.name}}`, item.value);
|
||||
});
|
||||
const params = {
|
||||
...JSON.parse(requestBody.code || '{}'),
|
||||
...urlParams,
|
||||
};
|
||||
server[methodObj[methodName]](url, params).then((resp: any) => {
|
||||
if (Object.keys(params).length === 0){
|
||||
|
||||
requestBody.code = JSON.stringify(getDefaultParams());
|
||||
editorRef.value?.editorFormat()
|
||||
}
|
||||
responsesContent.value = resp;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取默认参数
|
||||
*/
|
||||
function getDefaultParams() {
|
||||
if (!props.selectApi.requestBody) return {};
|
||||
const schema =
|
||||
props.selectApi.requestBody.content['application/json'].schema;
|
||||
const schemaName = (schema.$ref || schema.items.$ref)?.split('/').pop();
|
||||
const type = schema.type || '';
|
||||
const tableData = findData(schemaName);
|
||||
if (type === 'array') {
|
||||
return [getCodeText(tableData, 3)];
|
||||
} else return getCodeText(tableData, 3);
|
||||
}
|
||||
|
||||
function findData(schemaName: string) {
|
||||
const schemas = toRaw(props.schemas);
|
||||
const basicType = ['string', 'integer', 'boolean'];
|
||||
|
||||
if (!schemaName || !schemas[schemaName]) {
|
||||
return [];
|
||||
}
|
||||
const result: schemaObjType[] = [];
|
||||
const schema = schemas[schemaName];
|
||||
Object.entries(schema.properties).forEach((item: [string, any]) => {
|
||||
const paramsType =
|
||||
item[1].type ||
|
||||
(item[1].$ref && item[1].$ref.split('/').pop()) ||
|
||||
(item[1].items && item[1].items.$ref.split('/').pop()) ||
|
||||
'';
|
||||
const schemaObj: schemaObjType = {
|
||||
paramsName: item[0],
|
||||
paramsType,
|
||||
desc: item[1].description || '',
|
||||
};
|
||||
if (!basicType.includes(paramsType))
|
||||
schemaObj.children = findData(paramsType);
|
||||
result.push(schemaObj);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
function getCodeText(arr: schemaObjType[], level: number): object {
|
||||
const result = {};
|
||||
const schemas = toRaw(props.schemas);
|
||||
arr.forEach((item) => {
|
||||
switch (item.paramsType) {
|
||||
case 'string':
|
||||
result[item.paramsName] = '';
|
||||
break;
|
||||
case 'integer':
|
||||
result[item.paramsName] = 0;
|
||||
break;
|
||||
case 'boolean':
|
||||
result[item.paramsName] = true;
|
||||
break;
|
||||
case 'array':
|
||||
result[item.paramsName] = [];
|
||||
break;
|
||||
case 'object':
|
||||
result[item.paramsName] = '';
|
||||
break;
|
||||
default: {
|
||||
const properties = schemas[item.paramsType]
|
||||
.properties as object;
|
||||
const newArr = Object.entries(properties).map(
|
||||
(item: [string, any]) => ({
|
||||
paramsName: item[0],
|
||||
paramsType: level
|
||||
? (item[1].$ref && item[1].$ref.split('/').pop()) ||
|
||||
(item[1].items &&
|
||||
item[1].items.$ref.split('/').pop()) ||
|
||||
item[1].type ||
|
||||
''
|
||||
: item[1].type,
|
||||
}),
|
||||
);
|
||||
result[item.paramsName] = getCodeText(newArr, level - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
type requestObj = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
type schemaObjType = {
|
||||
paramsName: string;
|
||||
paramsType: string;
|
||||
desc?: string;
|
||||
children?: schemaObjType[];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
@ -284,8 +367,6 @@ type requestObj = {
|
|||
.jtable-body-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.table {
|
||||
:deep(.ant-table-cell) {
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
<template>
|
||||
<div class="choose-api-container">
|
||||
<j-pro-table
|
||||
:columns="columns"
|
||||
:dataSource="props.tableData"
|
||||
:rowSelection="props.mode !== 'home' ? rowSelection : undefined"
|
||||
noPagination
|
||||
model="TABLE"
|
||||
>
|
||||
<template #url="slotProps">
|
||||
<span
|
||||
style="color: #1d39c4; cursor: pointer"
|
||||
@click="jump(slotProps)"
|
||||
>{{ slotProps.url }}</span
|
||||
>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
<div class="table">
|
||||
<j-pro-table
|
||||
:columns="columns"
|
||||
:dataSource="props.tableData"
|
||||
:rowSelection="props.mode !== 'home' ? rowSelection : undefined"
|
||||
noPagination
|
||||
model="TABLE"
|
||||
>
|
||||
<template #url="slotProps">
|
||||
<span
|
||||
style="color: #1d39c4; cursor: pointer"
|
||||
@click="jump(slotProps)"
|
||||
>{{ slotProps.url }}</span
|
||||
>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</div>
|
||||
|
||||
<a-button type="primary" @click="save" v-if="props.mode !== 'home'"
|
||||
>保存</a-button
|
||||
<j-button type="primary" @click="save" v-if="props.mode !== 'home'"
|
||||
>保存</j-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -88,8 +90,10 @@ watch(
|
|||
|
||||
<style lang="less" scoped>
|
||||
.choose-api-container {
|
||||
height: 100%;
|
||||
|
||||
.table {
|
||||
max-height: calc(100vh - 260px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
:deep(.jtable-body-header) {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-tree
|
||||
<j-tree
|
||||
:tree-data="treeData"
|
||||
@select="clickSelectItem"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
<template #title="{ name }">
|
||||
{{ name }}
|
||||
</template>
|
||||
</a-tree>
|
||||
</j-tree>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -141,9 +141,6 @@ const filterPath = (path: object, filterArr: string[]) => {
|
|||
|
||||
<style lang="less">
|
||||
.left-tree-container {
|
||||
border-right: 1px solid #e9e9e9;
|
||||
height: calc(100vh - 150px);
|
||||
overflow-y: auto;
|
||||
.ant-tree-list {
|
||||
.ant-tree-list-holder-inner {
|
||||
.ant-tree-switcher-noop {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
<div class="top">
|
||||
<slot name="top" />
|
||||
</div>
|
||||
<a-row :gutter="24" style="background-color: #fff; padding: 20px;margin: 0;">
|
||||
<a-col
|
||||
<j-row :gutter="24">
|
||||
<j-col
|
||||
:span="24"
|
||||
v-if="props.showTitle"
|
||||
style="font-size: 16px; margin-bottom: 48px"
|
||||
>API文档</a-col
|
||||
>
|
||||
<a-col :span="5">
|
||||
API文档
|
||||
</j-col>
|
||||
<j-col :span="5" class="tree-content">
|
||||
<LeftTree
|
||||
@select="treeSelect"
|
||||
:mode="props.mode"
|
||||
|
|
@ -18,8 +19,8 @@
|
|||
:filter-array="treeFilter"
|
||||
:code="props.code"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="19">
|
||||
</j-col>
|
||||
<j-col :span="19">
|
||||
<HomePage v-show="showHome" />
|
||||
<div class="url-page" v-show="!showHome">
|
||||
<ChooseApi
|
||||
|
|
@ -35,26 +36,29 @@
|
|||
class="api-details"
|
||||
v-if="selectedApi.url && tableData.length > 0"
|
||||
>
|
||||
<a-button
|
||||
<j-button
|
||||
@click="selectedApi = initSelectedApi"
|
||||
style="margin-bottom: 24px"
|
||||
>返回</a-button
|
||||
>返回</j-button
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey" type="card">
|
||||
<a-tab-pane key="does" tab="文档">
|
||||
<j-tabs v-model:activeKey="activeKey" type="card">
|
||||
<j-tab-pane key="does" tab="文档">
|
||||
<ApiDoes
|
||||
:select-api="selectedApi"
|
||||
:schemas="schemas"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="test" tab="调试">
|
||||
<ApiTest :select-api="selectedApi" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</j-tab-pane>
|
||||
<j-tab-pane key="test" tab="调试">
|
||||
<ApiTest
|
||||
:select-api="selectedApi"
|
||||
:schemas="schemas"
|
||||
/>
|
||||
</j-tab-pane>
|
||||
</j-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -76,7 +80,7 @@ const props = defineProps<{
|
|||
mode: modeType;
|
||||
showTitle?: boolean;
|
||||
hasHome?: boolean;
|
||||
code?: string
|
||||
code?: string;
|
||||
}>();
|
||||
const showHome = ref<boolean>(Boolean(props.hasHome));
|
||||
const tableData = ref([]);
|
||||
|
|
@ -127,7 +131,7 @@ function init() {
|
|||
getApiGranted_api(props.code as string).then((resp) => {
|
||||
selectedKeys.value = resp.result as string[];
|
||||
selectSourceKeys.value = [...(resp.result as string[])];
|
||||
})
|
||||
});
|
||||
} else if (props.mode === 'api') {
|
||||
apiOperations_api().then((resp) => {
|
||||
selectedKeys.value = resp.result as string[];
|
||||
|
|
@ -138,12 +142,20 @@ function init() {
|
|||
activeKey.value = 'does';
|
||||
selectedApi.value = initSelectedApi;
|
||||
});
|
||||
watch(
|
||||
() => selectedApi.value.url,
|
||||
() => (activeKey.value = 'does'),
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="less" scoped>
|
||||
.api-page-container {
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
.tree-content {
|
||||
padding-bottom: 30px;
|
||||
height: calc(100vh - 230px);
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #e9e9e9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export type apiDetailsType = {
|
|||
parameters: any[];
|
||||
requestBody?: any;
|
||||
responses:object;
|
||||
description?:string;
|
||||
}
|
||||
|
||||
export type modeType = 'api'| 'appManger' | 'home'
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<div class="api-container">
|
||||
<Api mode="api">
|
||||
<template #top>
|
||||
<p>
|
||||
<AIcon
|
||||
type="ExclamationCircleOutlined"
|
||||
style="margin-right: 12px; font-size: 14px"
|
||||
/>配置系统支持API赋权的范围
|
||||
</p>
|
||||
</template>
|
||||
</Api>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Api">
|
||||
import Api from '../Api/index.vue';
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.api-container {
|
||||
background-color: #fff;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -16,5 +16,3 @@
|
|||
<script setup lang="ts" name="Platforms">
|
||||
import Api from './Api/index.vue';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue